diff --git a/README.md b/README.md index bffd87ffa5..6267cc3a7a 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,150 @@ -# ThingsBoard -[![ThingsBoard Builds Server Status](https://img.shields.io/teamcity/build/e/ThingsBoard_Build?label=TB%20builds%20server&server=https%3A%2F%2Fbuilds.thingsboard.io&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAALzAAAC8wHS6QoqAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAB9FJREFUeJzVm3+MXUUVx7+zWwqEtnRLWisQ2lKVUisIQmsqYCohpUhpEGsFKSJJTS0qGiGIISJ/8CNGYzSaEKBQEZUiP7RgVbCVdpE0xYKBWgI2rFLZJZQWtFKobPfjH3Pfdu7s3Pvmzntv3/JNNr3bOXPO+Z6ZO3PumVmjFgEYJWmWpDmSZks6VtIESV3Zv29LWmGMubdVPgw7gEOBJcAaYC/18fd2+zyqngAwXdL7M9keSduMMXgyH5R0laRPSRpbwf62CrLDB8AAS4HnAqP2EvA1YBTwPuBnwP46I70H+DPwALAS+B5wBTCu3VyHIJvG98dMX+B/BW1vAvcAnwdmAp3t5hWFbORXR5AvwmPARcCYdnNJAnCBR+gd7HQ9HZgLfAt4PUB8AzCv3f43DGCTQ6o/RAo43gtCL2Da4W9TAUwEBhxiPymRvcabAR8eTl+biQ7neYokdyTXlvR7xPt9etM8GmZ0FDxL+WD42FdBdkTDJd0jyU1wzi7pd473e0+qA8AM4AbgkrK1BDgOWAc8ChyTaq+eM5ud93ofcHpAZiY2sanhZaDDaTfAZ7HJUmlWCJzm6bqLQM6QBanXkfthcxgPNbTEW9z2AT8AzgTmANdikxwXX/d0XOi0bQEmFNj6GPAfhuKnXkB98kNsNjsITwacKkI3MNrrf4UnswXoiiRfwyqgo4D8L2hVZglMw456DDYCRwR0jCH/KuWCgE2oysjX8KsA+V+2jHzm3CrP4PMBx/4JfAU4qETP+EAQ/gKcA/w7gnwNbl5yD7bG0DLyM7DZXw3d2f9PA+YD5wIzK+gLBSEFA/XIA2cAVwLvbSQAt3mGP5Gs7IDO8dg1ZYDGcAfOwujZuIwDn+ObUx09hHx+v7Eh5nndCyIIDgBbgd0lMiv9IABfIF+LeDnVyU97xj5XR/6bwI5sZEaXyH2UuHd+WSbfRXktYjAIAfL9wGdSA/Cgo+gtSio12IKJa3hNKAgZ+TciyL+AlwECKzI/ioLgTvsa+YtTyXeSz8ZW15E3wN88p3JBwCZNMeShIKkBTsRmmSG4a0o/sDSJfGboBE/5pRF9pgI9oSBUJP8mXpLk2bm6pO9Aw+QzI8s8xVFbXRaEf3h911cgD7Cyjg0/L/GxnoLdoUoA3O1vDxUyLWyO4AehCpYX6D2L/LpUhtsaCkIWxRoeT+g/DVsqT8EWYDowC5jh6FxUUc+tJJblOmSPqWp4JUFHl6TDUoxLOlnSdknPSnK3sA2S9lfQs0zS7SkzwQ/A61U6A6dKWufpSMVg5mmMeUPSXyv2v0zSN6oa7ZAdwRqiA5CRf0TS+KpGAxiQ1OFN4z8l6PErVXUxSvmp1hvTqUnk35adPWskPWSM6fPaq84ASXqscg/gi9gcvJuC6o0nfwrhw5EYvIpNn88HStcN4M6KulfTys/lzKlO0lb8P2Lrf6VbLDAF+DLweEX998aSx372bwP6gPlVA3BEAvm9FJwVYtPqjwDXA08n6AZbOYoeeeAWp++mSlPGGLMLeFjSuRW6Iektx4GDJc2TdJ6khZKOruKDh/skXWSM6a/Q5yjn+dDKFrE1vw0VR2m2039x4kj7uJ+SslyJ/+7rtaly4mCM+a+kBaq2TbnVpfWy216jmCzpkIR+7kK/MymHNsbslX0NYoMweMpsjNklaWuKXQ9zJf2eOocvAbzHee5N/ojIgvBVxY3madh3v4b1iWZ/o3zw5kpaS+SFDGCq8jPguUQ/CmsCZfi403dhwjv/AHAQMAl41mvbGBMEhq4/c1PJTwmQr1f7u97pfzj5EnwUead/KAg/ivD7Zkf+HSBpFwiRfwibI3SXkOj29PgEivAggdU+C8JWR+6+CN9dm1tSyHcBLwbIj87ax1Kcxe0DJmVyY4CdEeR/TXnVeRLwc+C3wHF1fP+Qp/uGlABc6Cl5mPziVi8IzwDfAZ6KIN9LyhQt9v1GT/+sFCXTOVBBXuOTd+TGkp+eqWjKSTBwMPAvR+9TjSibjK35l93mWIxdZFKOxPzFseEgAJd7Olt6v+AC8jdIqwRhLbZM758HRH3tYa/vnoqtKZ4JHIk99tvh6HqNVl3RLSB/JfBEBPnBwxXsJ2uf176qxO7hwE3ALq/PfuyVXhdXt4r8+QHyK7K2cXWCMLiTOPqODwTh2IDdD2CP12LwCnUKMankO8kfiAySd2SKgjCEfEEQ+nznsZc7eyLJA9zddPKZIx0c2NcHgMsL5MZhr83XULiTeCSXAEcG2m4PjPCXsEWWBdhbZ/4h6knN4u07Mxv4MbCojtxo7DW6RTRwopMFxt0xeoCJAblLvCDdlWpzRAG42CO2sET2UUfuVbetsYPF9mKq8zwg6Q8lsm7bRJxt8N0cAPdar5FUupYU9X03B2C782wknVUi+0nneacxZk9rXBpGABO8RXA72demJ7fcWyvubIe/TQN2y11MuJ6wA5v3z8HeMbjba+8n5StwJCDb9lYUEI/Fde3mEQ1svnBKRvp32K/LEPYQd1z3XQJfsG3/Sw/gKElLZev8tb8rnizpBEmF1SDZ06ZbJN0saa+kayQtV77qi6QnJF1njFnXdOebAcIXssvQB3yfcGrcCZwEnAfMC8mMKGArNUVT28VubF4/nyZflx8Jr8BVkr4tm83tzn5ek/S8pM2SnpT0gv8H283C/wGTFfhGtexQwQAAAABJRU5ErkJggg==&labelColor=305680)](https://builds.thingsboard.io/viewType.html?buildTypeId=ThingsBoard_Build&guest=1) +![banner](https://github.com/user-attachments/assets/3584b592-33dd-4fb4-91d4-47b62b34806c) + +
+ +# Open-source IoT platform for data collection, processing, visualization, and device management. + +
+
+
+ +💡 [Get started](https://thingsboard.io/docs/getting-started-guides/helloworld/) • 🌐 [Website](https://thingsboard.io/) • 📚 [Documentation](https://thingsboard.io/docs/) • 📔 [Blog](https://thingsboard.io/blog/) • ▶️ [Live demo](https://demo.thingsboard.io/signup) • 🔗 [LinkedIn](https://www.linkedin.com/company/thingsboard/posts/?feedView=all) + +
+ +## 🚀 Installation options + +* Install ThingsBoard [On-premise](https://thingsboard.io/docs/user-guide/install/installation-options/?ceInstallType=onPremise) +* Try [ThingsBoard Cloud](https://thingsboard.io/installations/) +* or [Use our Live demo](https://demo.thingsboard.io/signup) + +## 💡 Getting started with ThingsBoard + +Check out our [Getting Started guide](https://thingsboard.io/docs/getting-started-guides/helloworld/) or [watch the video](https://www.youtube.com/watch?v=80L0ubQLXsc) to learn the basics of ThingsBoard and create your first dashboard! You will learn to: + +* Connect devices to ThingsBoard +* Push data from devices to ThingsBoard +* Build real-time dashboards +* Create a Customer and assign the dashboard with them. +* Define thresholds and trigger alarms +* Set up notifications via email, SMS, mobile apps, or integrate with third-party services. + +## ✨ Features + + + + + + + + + + +
+
+
+ Provision and manage devices and assets +

Provision and manage
devices and assets

+
+
+

Provision, monitor and control your IoT entities in secure way using rich server-side APIs. Define relations between your devices, assets, customers or any other entities.

+
+
+
+ Read more ➜ +
+
+
+
+
+ Collect and visualize your data +

Collect and visualize
your data

+
+
+

Collect and store telemetry data in scalable and fault-tolerant way. Visualize your data with built-in or custom widgets and flexible dashboards. Share dashboards with your customers.

+
+
+
+ Read more ➜ +
+
+
+
+
+ SCADA Dashboards +

SCADA Dashboards

+
+
+

Monitor and control your industrial processes in real time with SCADA. Use SCADA symbols on dashboards to create and manage any workflow, offering full flexibility to design and oversee operations according to your requirements.

+
+
+
+ Read more ➜ +
+
+
+
+
+ Process and React +

Process and React

+
+
+

Define data processing rule chains. Transform and normalize your device data. Raise alarms on incoming telemetry events, attribute updates, device inactivity and user actions.

+
+
+
+
+ Read more ➜ +
+
+
+ +## ⚙️ Powerful IoT Rule Engine + +ThingsBoard allows you to create complex [Rule Chains](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) to process data from your devices and match your application specific use cases. + +[![IoT Rule Engine](https://github.com/user-attachments/assets/43d21dc9-0e18-4f1b-8f9a-b72004e12f07 "IoT Rule Engine")](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) + +
+ +[**Read more about Rule Engine ➜**](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) + +
+ +## 📦 Real-Time IoT Dashboards + +ThingsBoard is a scalable, user-friendly, and device-agnostic IoT platform that speeds up time-to-market with powerful built-in solution templates. It enables data collection and analysis from any devices, saving resources on routine tasks and letting you focus on your solution’s unique aspects. See more our Use Cases [here](https://thingsboard.io/iot-use-cases/). + +[**Smart energy**](https://thingsboard.io/use-cases/smart-energy/) + +[![Smart energy](https://github.com/user-attachments/assets/2a0abf13-6dc5-4f5e-9c30-1aea1d39af1e "Smart energy")](https://thingsboard.io/use-cases/smart-energy/) + +[**SCADA swimming pool**](https://thingsboard.io/use-cases/scada/) + +[![SCADA Swimming pool](https://github.com/user-attachments/assets/68fd9e29-99f1-4c16-8c4c-476f4ccb20c0 "SCADA Swimming pool")](https://thingsboard.io/use-cases/scada/) + +[**Fleet tracking**](https://thingsboard.io/use-cases/fleet-tracking/) + +[![Fleet tracking](https://github.com/user-attachments/assets/9e8938ba-ee0c-4599-9494-d74b7de8a63d "Fleet tracking")](https://thingsboard.io/use-cases/fleet-tracking/) + +[**Smart farming**](https://thingsboard.io/use-cases/smart-farming/) + +[![Smart farming](https://github.com/user-attachments/assets/56b84c99-ef24-44e5-a903-b925b7f9d142 "Smart farming")](https://thingsboard.io/use-cases/smart-farming/) -ThingsBoard is an open-source IoT platform for data collection, processing, visualization, and device management. - - - - -## Documentation - -ThingsBoard documentation is hosted on [thingsboard.io](https://thingsboard.io/docs). - -## IoT use cases - -[**Smart energy**](https://thingsboard.io/smart-energy/) -[![Smart energy](https://user-images.githubusercontent.com/8308069/152984256-eb48564a-645c-468d-912b-f554b63104a5.gif "Smart energy")](https://thingsboard.io/smart-energy/) - -[**SCADA Swimming pool**](https://thingsboard.io/use-cases/scada/) -[![SCADA Swimming pool](https://github.com/user-attachments/assets/0878a2f5-d358-47c5-b295-03b4533685cf "SCADA Swimming pool")](https://thingsboard.io/use-cases/scada/) - -[**Fleet tracking**](https://thingsboard.io/fleet-tracking/) -[![Fleet tracking](https://user-images.githubusercontent.com/8308069/152984528-0054ed55-8b8b-4cda-ba45-02fe95a81222.gif "Fleet tracking")](https://thingsboard.io/fleet-tracking/) - -[**Smart farming**](https://thingsboard.io/smart-farming/) -[![Smart farming](https://user-images.githubusercontent.com/8308069/152984443-a98b7d3d-ff7a-4037-9011-e71e1e6f755f.gif "Smart farming")](https://thingsboard.io/smart-farming/) +[**Smart metering**](https://thingsboard.io/smart-metering/) -[**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://github.com/user-attachments/assets/adc05e3d-397c-48ef-bed6-535bbd698455 "Smart metering")](https://thingsboard.io/smart-metering/) -[**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/) +
-## Getting Started +[**Check more of our use cases ➜**](https://thingsboard.io/iot-use-cases/) -Collect and Visualize your IoT data in minutes by following this [guide](https://thingsboard.io/docs/getting-started-guides/helloworld/). +
-## Support +## 🫶 Support - - [Stackoverflow](http://stackoverflow.com/questions/tagged/thingsboard) +To get support, please visit our [GitHub issues page](https://github.com/thingsboard/thingsboard/issues) -## Licenses +## 📄 Licenses -This project is released under [Apache 2.0 License](./LICENSE). +This project is released under [Apache 2.0 License](./LICENSE) diff --git a/application/pom.xml b/application/pom.xml index 36dfcac42f..d2cc11f069 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard application 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 81f9e6a14d..e614c9b54c 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 @@ -10,27 +10,9 @@ "externalId": null }, "metadata": { - "firstNodeIndex": 0, + "firstNodeIndex": 2, "nodes": [ { - "additionalInfo": { - "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", - "layoutX": 187, - "layoutY": 468 - }, - "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", - "name": "Device Profile Node", - "configuration": { - "persistAlarmRulesState": false, - "fetchAlarmRulesStateOnStart": false - }, - "externalId": null - }, - { - "additionalInfo": { - "layoutX": 823, - "layoutY": 157 - }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", "configurationVersion": 1, @@ -41,13 +23,12 @@ "type": "ON_EVERY_MESSAGE" } }, - "externalId": null + "additionalInfo": { + "layoutX": 823, + "layoutY": 157 + } }, { - "additionalInfo": { - "layoutX": 824, - "layoutY": 52 - }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", "configurationVersion": 3, @@ -60,25 +41,23 @@ "sendAttributesUpdatedNotification": false, "updateAttributesOnlyOnValueChange": true }, - "externalId": null + "additionalInfo": { + "layoutX": 824, + "layoutY": 52 + } }, { - "additionalInfo": { - "layoutX": 347, - "layoutY": 149 - }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "Message Type Switch", "configuration": { "version": 0 }, - "externalId": null + "additionalInfo": { + "layoutX": 347, + "layoutY": 149 + } }, { - "additionalInfo": { - "layoutX": 825, - "layoutY": 266 - }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log RPC from Device", "configuration": { @@ -86,13 +65,12 @@ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" }, - "externalId": null + "additionalInfo": { + "layoutX": 825, + "layoutY": 266 + } }, { - "additionalInfo": { - "layoutX": 824, - "layoutY": 378 - }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log Other", "configuration": { @@ -100,97 +78,92 @@ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" }, - "externalId": null - }, - { "additionalInfo": { "layoutX": 824, - "layoutY": 466 - }, + "layoutY": 378 + } + }, + { "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "name": "RPC Call Request", "configuration": { "timeoutInSeconds": 60 }, - "externalId": null + "additionalInfo": { + "layoutX": 824, + "layoutY": 466 + } }, { - "additionalInfo": { - "layoutX": 1126, - "layoutY": 104 - }, "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", "name": "Push to cloud", "configuration": { "scope": "CLIENT_SCOPE" }, - "externalId": null + "additionalInfo": { + "layoutX": 1126, + "layoutY": 104 + } }, { - "additionalInfo": { - "layoutX": 826, - "layoutY": 601 - }, "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", "name": "Push to cloud", "configuration": { "scope": "SERVER_SCOPE" }, - "externalId": null + "additionalInfo": { + "layoutX": 826, + "layoutY": 601 + } } ], "connections": [ { "fromIndex": 0, - "toIndex": 3, + "toIndex": 6, "type": "Success" }, { "fromIndex": 1, - "toIndex": 7, + "toIndex": 6, "type": "Success" }, { "fromIndex": 2, - "toIndex": 7, - "type": "Success" - }, - { - "fromIndex": 3, - "toIndex": 1, + "toIndex": 0, "type": "Post telemetry" }, { - "fromIndex": 3, - "toIndex": 2, + "fromIndex": 2, + "toIndex": 1, "type": "Post attributes" }, { - "fromIndex": 3, - "toIndex": 4, + "fromIndex": 2, + "toIndex": 3, "type": "RPC Request from Device" }, { - "fromIndex": 3, - "toIndex": 5, + "fromIndex": 2, + "toIndex": 4, "type": "Other" }, { - "fromIndex": 3, - "toIndex": 6, + "fromIndex": 2, + "toIndex": 5, "type": "RPC Request to Device" }, { - "fromIndex": 3, - "toIndex": 8, + "fromIndex": 2, + "toIndex": 7, "type": "Attributes Deleted" }, { - "fromIndex": 3, - "toIndex": 8, + "fromIndex": 2, + "toIndex": 7, "type": "Attributes Updated" } ], "ruleChainConnections": null } -} +} \ 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 5cf929e3ce..f24afb231d 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 @@ -17,6 +17,7 @@ "gateway_widgets.gateway_custom_statistics", "gateway_widgets.gateway_general_chart_statistics", "gateway_widgets.service_rpc", - "gateway_widgets.gateway_status" + "gateway_widgets.gateway_status", + "gateway_widgets.markdown_card" ] } 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 a30d6ee76f..2701abcb84 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 @@ -13,6 +13,7 @@ "home_page_widgets.quick_links", "home_page_widgets.documentation_links", "home_page_widgets.dashboards", - "home_page_widgets.usage_info" + "home_page_widgets.usage_info", + "api_usage" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/action_button.json b/application/src/main/data/json/system/widget_types/action_button.json index 3ea996e48e..4be3c87414 100644 --- a/application/src/main/data/json/system/widget_types/action_button.json +++ b/application/src/main/data/json/system/widget_types/action_button.json @@ -12,12 +12,10 @@ "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 self.ctx.$scope.actionWidget.onInit();\n}\n\nself.actionSources = function() {\n return {\n 'click': {\n name: 'widget-action.click',\n multiple: false\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n maxDatasources: 1,\n maxDataKeys: 0,\n singleEntity: true,\n previewWidth: '200px',\n previewHeight: '80px',\n embedTitlePanel: true,\n overflowVisible: true,\n hideDataSettings: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", "settingsDirective": "tb-action-button-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-action-button-basic-config", - "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#FFFFFF01\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Action button\",\"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,\"displayTimewindow\":true,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"borderRadius\":\"4px\",\"configMode\":\"basic\"}" + "defaultConfig": "{\"datasources\":[],\"showTitle\":false,\"backgroundColor\":\"#FFFFFF01\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Action button\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":false,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"borderRadius\":\"4px\",\"configMode\":\"basic\"}" }, "tags": [ "button", 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 index 9dc9a99194..a12fb7a5c3 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 5bf555d92c..801b91330b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/air_quality_index_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/air_quality_index_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 7f9dd32b45..c9851a4f8b 100644 --- a/application/src/main/data/json/system/widget_types/alarm_count.json +++ b/application/src/main/data/json/system/widget_types/alarm_count.json @@ -12,12 +12,10 @@ "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)\"}" + "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}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.54)\",\"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}}" }, "tags": [ "alert", 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 index 4f86957f1c..704cb8fb43 100644 --- a/application/src/main/data/json/system/widget_types/alarms_table.json +++ b/application/src/main/data/json/system/widget_types/alarms_table.json @@ -12,18 +12,12 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.alarmsTableWidget.onEditModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n supportsUnitConversion: 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 'cellClick': {\n name: 'widget-action.cell-click',\n multiple: true\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}" + "defaultConfig": "{\"timewindow\":{\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":2592000000}},\"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\":\"originatorDisplayName\",\"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}" }, - "tags": [ - "alert", - "alerts" - ], "resources": [ { "link": "/api/images/system/alarms_table_system_widget_image.png", @@ -36,5 +30,10 @@ "data": "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==", "public": true } + ], + "scada": false, + "tags": [ + "alert", + "alerts" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/api_usage.json b/application/src/main/data/json/system/widget_types/api_usage.json new file mode 100644 index 0000000000..5c269f4595 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/api_usage.json @@ -0,0 +1,35 @@ +{ + "fqn": "api_usage", + "name": "API Usage", + "deprecated": false, + "image": "tb-image;/api/images/system/api-usage-widget.png", + "description": null, + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "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.apiUsageWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n hideDataTab: true,\n hideDataSettings: true,\n datasourcesOptional: true,\n previewWidth: '400px',\n previewHeight: '300px'\n };\n}", + "settingsForm": [], + "dataKeySettingsForm": [], + "settingsDirective": "tb-api-usage-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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0\",\"settings\":{},\"title\":\"API usage\",\"decimals\":null,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\".tb-widget-header {\\n height: 48px;\\n align-items: center !important;\\n padding: 5px 10px 0 10px;\\n}\",\"titleStyle\":{},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"actions\":{\"headerButton\":[{\"name\":\"Go back\",\"buttonType\":\"stroked\",\"showIcon\":true,\"icon\":\"undo\",\"buttonColor\":\"#305680\",\"buttonBorderColor\":\"#0000001F\",\"customButtonStyle\":{\"padding\":\"0 16px\"},\"useShowWidgetActionFunction\":true,\"showWidgetActionFunction\":\"return widgetContext.stateController.getStateId() !== widgetContext.settings.targetDashboardState && widgetContext.settings.targetDashboardState;\",\"type\":\"custom\",\"customFunction\":\"const state = widgetContext.settings.targetDashboardState?.length ? widgetContext.settings.targetDashboardState : 'default';\\nwidgetContext.stateController.updateState(state, widgetContext.stateController.getStateParams(), false);\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"1ea1cca6-47d1-3539-d051-9535129fb12b\"}]},\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":null,\"weight\":\"500\",\"style\":null,\"lineHeight\":\"21px\"},\"borderRadius\":\"4px\"}" + }, + "resources": [ + { + "link": "/api/images/system/api-usage-widget.png", + "title": "\"API Usage\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "api-usage-widget.png", + "publicResourceKey": "1TAYhxLWInZC20Xrn8PgbNbQs6fX8Pir", + "mediaType": "image/png", + "data": "iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAChVBMVEUAAADs7Ozd3d3f4+Xd4eLN0tLP0dTg4+Xp6en////y9ffx9/Pt7e0ZgDjl7uvh5Oawvs/7+/vx8fHO1+H9/f31+ffu7u78/f3s8fH9/v6Eu5Xg7uX6/PxHaY7i5efI0t1VdJf5+vvx9PaNornv8vVOb5NffJ262cRphaNaeJrs7/Ouvc2Wqb+BmLKmpqZjgJ9ip3ft8PXBzdmsu8yEm7Rviaf3+Pra4empqann7PHQ2eK/y9i3xNPD3stXdpi8vLyGnbVsh6VRcZV+t5A7X4ff5evW3ebU3eXN1+GzwdHOzs6pucuarcGQpbt7k66OwZ46klXc7OKcrsKx1Lytrq53kaxyjKhDZYs+Yoni5+3L4tOitMeKn7dmgqH09vn09PS6x9V1jqrm6/DT2+TEz9vX2NnV1dbHyMmltsisrKxhfp5cepuJvplLbZHj6e7d3d2hssWer8PBwcGoz7R0sojp7fLj8Oe/3MiYq8C4ubm1tbWvsLCeyauCuZQ3XIRepnTl6u/p8+zc4+rk5OTK1N/O5Na4xdSxv9CTp71+lrCSw6F4tItnq30cgTr29vbt8vTq6urh4eHU59rR5tefn6CMv5zd5OvX3+fIy8yt0riHvZj19/nm5+ja2tvR09S+v8B7to1ur4FkqXlPnmdHmWDz9fjZ4Oi8ydagscOVxaRxsYQqiUchhD/3+/jq8O/o6uzZ6t7Ly8uw07yysrKjzLCho6SVlpZao3Le4eOx1LuozbeZx6dWoW7t9e/c3uDC3czKysrDw8OytbaZm5xrrn8wjUze7eLO0NDI4c8zWYLg5uzV5t7FxcWOjo7v9vHY5+C01r7AydLKzc6nzLWBgoNCllzu8fMzg2RWAAAACXRSTlMA3Vrh2mBe3tXDgts5AAAPHUlEQVR42u3ch1sTZxzAcbr7eyEWaGw8uDR7r0JCAgkz7BFk7733kr2UvYTK0jpQqnW3bm3VWrXaveff0/cYLbXYQgiS0Hyf5y4hPjzw8T1y8X5EBweHl154zcZ74WUH3ItcZPPxX8TrsQ0cWPKywwtoW/SKw5toW/SaHWLG17qE0OVj6LWPEHrzE4SjdsWlP6PLpcdQyWV8/7JNQAK++wiVPnoHBdxC6GYHfoCbhPC94yVoZm6G++hTxP/1uE1A0Kd4Kd7BkEcfXZ6hIMe/wLtb5SV8JSq/fPMm951HNgZ5J6ntZwrSwacOr12PdilR0uWZ0i9unrAxSIny5msYculT/Bj/BJrhz6Cb/Jnfb31qK5B3jiFUXIy++Kj4i8ulCJ14DeG+TSpF7+CtDZ04dvwT24A80S682ebT75PZIf8XSHgrzgf9Wz7W+0p5BaR3yD9rSIreRsgD4fDt4h3+0gd4l1pA3XL5Cx/zKZXH0oYfw4/89Xmr1HmRtnof7jC7XasfWkG+SMZkR7CFPJSWwnTeJxF7IWZQtTM3v9JfF10tqe9ORuhgWlBqsiQIydjGojNslQKliCXpkf5sGUrx98+SqiqDuMMV7Aj0ZDvGaZaG4LhPhaSgM2d9U1Gajyw5wsjyfZsZGuHPqkB1muh8tLAiB5kosJXbzU11jgnKq4yI5Kb6eobqz9ar9GyUkBWdbWCzhOdYhejJ4mmbAeE/FRKLyoIiQ1CaNDIfFSn8+cyaXkFeChpKjlYsQaoRrx6FvN0tb1WjvORsFMYzhms09RUsFTqXlSBsbQ2riWHnoSd7+7NnuiIYMuyVtQgpqpQxm5iB+RofQb0kj4IY6/6E8PNj5ZlD2dHVPgK1pChFk18prW6tzAqvrsv3kWSm1KF/9MP8W6u3e5fZcdGqkKJQ1NuP+JEsX74v9yALva4+g5iGIT5Kl0UgHT7sC4cQkuYhfRiK5HIHIz0QS40/RcZCPrKIQaSXKWKQTq1HPs5nuciMNvU8wqxBay1ZVRGK1pP9hGg9kG/wCcDdHS106RtkRu6NcXFjO7YE8nFwcMby/a4fELp2By00fQStv0txNFzcjq2AnMTb7tkLlwaCg3d1BTfGX7uzo2vePb5xzBzILOUYCx7bohXZtf/S3ZaM+2MtXQPfdF27s/vGndngbzLMgXx46iJt7JfxuK1akf2oU3Tk8BFR1w87Gq/dCT5/J6Nxx7R5kPtuv4zT4rZoRe5TkLuNe691Ne6/f+3OwP7g+/f3N5oD2U071YlfvAVv6bPW28u7hT3fvH8nL/6wX0I2fx7ZEfzbb8GXtvw8spTtnxCRHWKH2EqvOZxz3hadc/jp9W3RTw5vwLbIyQ6xsuwQa+uZQhjAYcDChveAc8GPuQIQrgRw6IA3m4AcKC+GpACAR1GUqQ0gt+Mm0JUNbdDxsBxmHgB8V2sTkKSSY5CkJJxuUZDjkxhDeMOBE+CNbzq03t4wecs2ILdqla5JD4rbSilIO1Dhb/5bUHKSoN1V2dDX0WAZiJSF48Aa6meBGSnh28mkXO/yEgxxfbgEcQpYWJFyhjLX+3qpZSA6kyDFhODfEucAVUIMUNUZYD2VXE+it00EFM+VADTkAiyuy/X2B3CiPQCuQ8exqEkLHVpentCvEEuTxXmQwhMWkpqgTEjWqMJBXaE5ylJ4qbsDqe9fpYrJGRGrdQJJ05A4FoEZHYPVsxzEV9VU2B/KhLR0tUJfqW0lmb2e2aFsjilmKFsKTARQI9AGxvQaPNLoWTIoOz3iDFbRPyAKYKl4aZDGORtL1FdqgMmXCjyTwRA7pIEFSL8XPrQOCXndnCwZka8QZoJVtArEZDKELED6swpSEXOwLqWpOjw5moJU6wE8mIVeMWWK/m6tadgj7YzYGiGRB+HMWfDglcnJVlI3SKg1BmAm8KQQERhNhBkA8oYBoChQzcoZjkngfq3xyQtUF4FV9J/nESYfLNTAyfeogndarg/oa4foGGCZPlgas+91tGTEsz+z99A2A8J49pCd7z/7FTkkhU1I9F4cVeNOC8aAf4XkHQIqdSTYTisgHrFByac5gRVlkMnKC8zOSjey04fEIwhsohWQfQJSk5mZRUrCFJF1I2QqX54JzFC8OjbRSkglyLI0vqAYxBA5CAowhBWUXQBWGf1244UfnwJhhnk5R6aECWqWIAmBSJweKwML5ebmNgVLVTUDiBJhofjdsP6IvTTcSfqqELbpHJ2QBbLg7KEIFmSiAk26PjCBAxZqP95aDncSjuf3EFWd5z8QJdL3dJLk+fPmQA5TjlPzd59yaG1qH7q5kY/jdw/4NY9OV/n5nRcl7hG13Djv12IO5ORn47RTGRffWw1ChsKmRq3Iu7DH7/bsBb+q5p1josSuI6OiRtKsQ+vjC9c+zrhIi9uKiw97ExPdKYjb4f0tVaOziaLEFrfbGYmzu82BdNHmp/CLt8dbAYmfnr7XDN/vJDN6vu9pngJHR/h8AGDKvcecV24Xafglz/vNtn/JdPoU/mG/si2u/X6w034R25qzQ6wtJ4cDLtuiAw6Trtuiye1zaNkh1pUdYm3ZIeuvVukEtR1JLn0d5W9MKgGKywH3kAMQ0BYAJcpyp76k8omS6wBRbVYNORZwALyJqOIoeu2Due+coNwbAOjtAC5R4A0NfQwodymOivpuApRJVg0BDFHCXBQQSblzAW2ubUoAKK4F3IF2uNo246qEvodRAQ/7AiwFOcqC1SLQhiHeUFICAZ/AXGl5mxMFaSfwzrXcBd6A4ihvovZqVIn3daeNQdBXEnYmUKWzYbV6szYM+bZBmXt1pnRurrR4BjAktwEAiFsNpa4ND5VOUQHeb0SVPCif2CAkFXJCGDHkQV8M2WcqYwAAi8eC6EGeDvQ8Z899Mm1dtPwonOXpwZwmGABOE5Dr5JTLmSDwHYA+Vwri5OTkQv0JvJELHA5DS+RuDJLGKmND6lHP5HQ2YfRUlAHo2Hp2jTChTHyayYrlRYg9uotS1CxVhMD6rmyvhHSb0g4tQQpC8lWBAGWCfKOn8FCvpDAb1BSEiUfTw+x8QThYW38/tDJHgOkjwxBtqtSjAMBTTNeRFKSpWhuzDFHHwiECrK0VkKNi4GRLoyXJJp9kMFRkhwEQw8LknJGva2Ihs1LIOxTI9QJnGRko1JBgba31PCI3VHjCxnK867aG9jDWE7FuyMGyfthYjDjaWtprM+Npy0Lozx7iaAUQLlii2XHLQz6AtUOGwkBMWuZirfsa6rHAs1YhLw/KCvSDRKRJB+kmNWEIB7mO7ZUjp5+OkfPpCc4mLhFtCgNrbAWEK4io0PumsHVDKSwBR1AUKwt0hm6uVz0ZQnplyirIrwyx9Z7i8CCwxlZACpn5EhkYY2GkMp+pZwMABYGRQQgh00gI4YRANE+aLTaAVdS8Z0/zU8bT/pxQbYREIJWbIEzLzPFk8TL5X4HCF0MkYenVJAXRFzWlEWBWN1aMpzt7AL5sWfqOboAZjeKnjvHRVSGQKUyRxoZHJuTECk3EoDDfQ1eZXwEGSZOKEZatimCoIK/MI1lcv4Fh6JX4aSAy4qHqywFqzn5lGmDArKluBg332d4vt2QYeuMG+fjwu/GJtxs/rzo/eliU6Dc7Kjo8OmoO5MI4dgy8d2ErICfd3enUVHd69/7l8fR8cKe54+muYOygfbwlEEdHkoKMuXf5VYla7uJfGLjdfOV8i8gcyNj44fj3aLTGrYDgg2hKBFPNPaOiK18mdrp8Hk8/fNeR7LydAetvanw8Dv+0T9n+lcY91Jh9z3a4ZLqzpcXRfu3XirNDrK1tBKl12hbVbp8VsUOsKzvE2rJD1t9kexTgXCcBXL6YBE5b28QbJwCcGmwNoiQ6cvHNCbybO3EVSj/pa+j71RXaZ2wNgmfpxwCIdsDVXoX2XG1H3/UAl3LvrYRwPcyFzBUvQ1wnrvc1eEcVKzcBkhUBK6ObKlN8YKnYUKAKL8A7ffUg/K1+bk72f0IOKDkA7fQlyPHSq1F9DcdvEZsB8fKEQk+ttBcONmkNhVBUQUSOQJOhCfS6dH1OaKiB7yEe5oBWMSIt1OaEcwrDWQSkG1ABW032A6NID9rwwn5YvdwHBwBcShfv469aUkJMHGD0weSmQAa9hr1C/UFyMKi+wnMfs04KPsZzRq4g6HVVuCZ72FgjVByF014p6f5fhwl9QlrZBr1/jP8hgZzPhJQsr5jQtBgjC8zI0pDT9SNMkBT6QzivUg7SmGpfNQ90SFAAGBIJ7BqFLwAkJMACRADyOp4aCklVGJ+ZkwpNAvyXwIsGM7Ig5IwMKvRZ5/AIuk5ST6Q21cvz1MBS+WqgiC/woSDOBFO6DJH0Fi5AzskzYXABQqaSvf7WAPEwqsSkrzEojfD5qgDE2ewsqVBsjNRWiLM5ixB/YSyUGX0WIHJ/1SKkwD87FgIlC362pzVAAHLwpqWv+ABOk9QAHhbTRFL3EAELnYblji5P5zgk/FsE3dIR5p1HZBEbm7w5Wr6dW3BmJxzXltWPp7cNBB9amxCsHRLuA0LSMmtCt3iwKoRrqIHXtdIzEGrIAY4nvm0ClodYzmERRD/eInRFdDhjsL5fOnsCwjHWG9Nbh8We/ZJWIaEaFhZRU919Qo02hMFLVijIr+RCtV5SxgZrbAUkTGCqVHMEXqAIMqWFG6GpZnE8nQchjFQtPW1hPK0zxuwDq4j4PJ6xOkQnrNGhfdVGTqC8RufBJHrDsmT0JUi1NCeVpCAHw84yCTCraT+/HliKGm1cmYKF7vmBGWXE4WnotVUhkJItqQkaHJbXsIO8CFMFW1dUHdQNZWxpCMOX7a9egOgk+V5gXm9RELoLAEmHqmYXajzNIAFczBqGxr9PwxOruIGnvEShw2IIb1oCb4zl1yQkZ3lFT29oqjv7Yc+PwfvvVc0/zhAlxl+4MJ3xeMwcyEnKIXp8cqvm7NRU995d6t3TjvOiRLcutyNjO81akbjGU++L3t2ad08vvw187F6nX9WVz6tEiaNTjO+PuE+ZAzl1MePLd/G7dbcCMuvmNtAJflcy5qsGbs/O90xnOLp1xd+bn/3RrP9h4OJb+OjqtP0rjcE03F7S9iGQUXVERNiv/Vpvdoi1tY0gz8G26DmH5+mwDaI/7+Dw3DaQ0F91wD3/nJON9xxejz8ATqyBto+N2i0AAAAASUVORK5CYII=", + "public": true + } + ], + "scada": false, + "tags": null +} \ 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 index 103382ddf5..3737ba86a8 100644 --- 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 @@ -12,13 +12,11 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.entitiesTableWidget.onEditModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true,\n supportsUnitConversion: 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 'cellClick': {\n name: 'widget-action.cell-click',\n multiple: true\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}" + "defaultConfig": "{\"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\"},\"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\":{},\"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}" }, "tags": [ "provisioning", @@ -38,5 +36,6 @@ "data": "iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAm6SURBVHja7d3pV1PXGsBh/7LobW+txes9YYhiSsAoRKKtA63YWsHSWhwQcQATB4oo9QKptnWgqFhFMbEFLBVEoggyRMIUEwImJif53Q8BynVd5YDpUuneH5KwszmbZ+Xd+7xrveSceQQHuh+95a17IMC8YI9H5i1vsqcnOG/AwxxonoF53fJcgMjd8x4xJ9ojAREQAREQARGQNwDS2Tmj4f13J+Zv/8sgNftCszjON7nPdbRk+18yvHzV+Iv87JnP5XAogYRSpdsvPUz2aUWQ9oLA/3ac3BktiMmkBNKUkJM3/jL43HMku1x7fHJsMDyOD01AApMP4y08mZIWZUY6ghOQ4BSIHH1I3s6GBC8QKl6uXlMPoZLlaqMNaFonrbTQoFVrtJGgaf1E0uwcgdBhjSZvSy4B7amM2OXVDQYp5RrUaYMc2XFME/fVGIBXmxCrbeLJ7gT1xjYo15fo1FuHI5BwZVLshuYoQ7yaW6GUc8BpXcvg0YR+zmrvDJXE9dIeV9lfG3/L71xd5AwDDC89Otxu2AdnNDW959S5BCTj3Z6C2PSmnn0aL9ekIAfVR3vrNRUAIWfeRqefzzO7BnelhCiXCrub0r6KQP6jq+8/pB2NLuRCUpDijUD+p2Gene7lwIYw8ukedm8FCnL+DC1XnR/KDKAvBnJyCUg10C9Vg0O6G4Gsm7J6ijKBm33QJnVTrguBTd1PfjbPEs+DrL2oaJ2bTCZTVpbJZDI5Xg7JMMND6SG0JRnLOoEHuvSTD4G09UVFRRnGKWvEU7E7e7WeMelm5M8NSNfAK9nAJTVGIJunLOeiTCB4Zd+2TKk9stiHpSbys3ko5RYVFemORxXSKekMBoP6COC5lK3O8oO3Jlez5Sn6z0pLS0sr/oS4V223Nh3Q45HqlUN2Gi81VU9Ankj15Gdjlw6WlpaW2qIaWkdW19XV1RV9+AxbL/Sqr/BrDzjjqvliYi+bgFzWhKBCD8vPANsVQZ5ITdAhtVOuB+5I3eRn45Xqor79BnTlAMPqG2ze4qNTfYutm5/SE1tHtdpG6NQ5+CQ/MrZW3UpHmg6O6HtpS5wWUpwawqsuC3tyJTvlUnV4bMuGMPnZkGPsx5P7MJqQOrUzctLbRq9xWVrs/hCOj5amxe4NES6JTUkydMIZSecBkHOk5Um71WN4MyTd6sxpIS1x2t/4IXZpfJF0g3JD5oexOnvk3Sefq1M1uf5oQkb6xzfhfgg9bB4ECHfeifSOtraHADraxnOYrja/3xmE0P0HAc8Twk4fhJz+yIPPGcYzDLhdE/lVyxgMt7hwjuIdDrU3+ybfHRifIpopikjjBURABERABERABERABERABERABERABERABERABERA5hgk7HgKPscsjzXscDgcDt+bAJFVmWBVzfK/HLMWL5y/eLHtzYCormNVyQTsXeB2DdplX6sbcNwLKDnazx/AsIuwwzcw9sABPI2UJl4DZH38mFUlOxLT1PspjE9e/HGydtFjzEt0yaMKIce13Jvv1C9NfqeSTvXKD6pfD6Q2dZ9VJd89zy//DBemhxtU98PxPz2a3xVOsyiEOOZ3Fq9Bf5ifFoU27+JK3OuB1NnfKVbJ7q0rtCq5cBP3VW5WVFyZn5i4sEAhhPQTq86gr6BTNZSwJDFe9fT1QCh8VyWb14Yap0BuL+gbcfmUQr5PXOBCX0H9/GcG08iIK/yaIE81Krly8bEVKvckRDYYS1fUK4W4FmSAPqFEt41LCw/v+vR1hFaorBfuloXlc0fulA03XsVV5ufCPXxnDl1XcrSOSoDkC6A3fVvhhztHv3e9rWd2V8kiL+gr3voU5eGmeuCQVeRaAiIgAiIgAiIgAiIgAiIgAjItpGtONBFaAiIgAiIgAiIgAiIgf1tI04n/8x2yroKCgoKutwrSlG7Ie05iN0w2+0wgoQCAPPPKuwwXALgYZLZXJxoc2mIwnHiu7/RkG3wx5McckNP+OLUsNWX7GMCJlNStQU6sSN2h4LIDI0tSV6ZPXAPh0EXeA0A9Wps/O0ejsXZoS3rTc722gvFme0loBZbZqf6UkmOEdh8FHuhlMn/pSpbZ0Dj9xJ5/gTX9z5/fAzyoIwX60SAQmEmFtzHdsLp26HkHZyci6+zL1sjlTfLydkqOwc9fAa4OyKkFyFAIaTSwt4qB5ey9wHuMGtdsWDh6aRe6L9fF27mc8NH6PTNwGAxrhpgdJJz2zQ4o+bqh5sPxT+5eehDMxh0Kisyef2xct6RtKuTkATzvj17ahe5XKg/JUh+lexTHlcFgSP+NWUJofHcASozm1O8oS10LzpQeYKAx5YECyOIR9xV9eApkmxXUo5d2oeujOu+xDn7e84oOpRCXBJQcw64NArhT70BfA5grlYUW74/uOz8J2VkzFeKOVwx5sWOmEHIsgN943G7v6ltm717VqgCyyN5avBrLth7zOMRq6KxaMAlhraVj455XdCiFjB0CrDfAcRhw5uXl5ZXRvP1LJdXmp3l5e06N4DNlXTjG5WYKoCrLctTfWs23blouMrxn+4F9Ste58QXbi0LIX9rO9PkyLykYt+fFn8ebAfn9izVlYWUQ4wu3+5oJSM2bn2tZCgpefNrynYic2E/6RBovIAIiIAIiIAIiIAIiIDOGiDq7CC0BERABERABERABEZC3GiLq7DfdIF9n6OrVOieEb/1oB5CvKazGtp+96o+iY2jWdXZic8Abg01bVqytZtfX55OvAeWqx4omtqRaDiV7o+ZoXDPrOjuxmht4Y7BtgpaVwVSZmm/AYUx8zLOqyq5pJh6RPGCqocp+EWdllQ/H71Dj62ir/sE9G4fxFerssdYE7zjEZgQoshDOuJf0mOyD1ZppLgbR/HHkOSazujfxXGmaXLMTEocs8T+a9PJsHK9QZ48dPLrTG4NNnf2ZtgW4vUbm3EGSHmOsCnRM8/3h65/jNJttxIxReAqyro9DTGBsnpVj9nX22MFA8tUYbEb7D+uBjhQnQ1LV1fgzo/37k/c+e/ncf6Qz2rDbTEyAnFowVY5DzJBzPVoOxRBa4mKwbSKcbqVf3wm9ZWVlS0zDFuQtV18++bN/d8J3ZmICHP8WMqzXc5DjhiwHIeV+tBzKIRRG1sjvybI+w2w2y0DSY3JzDmt7p5neurRwv+Y3YgJ49IVfb5JdcabNC4cs8aatmVFzKIT8EQD/bdz3gaaxxoaGhoYQ0OwnbL85/dbjsd4agdsh8Dc0hWDI2tcSsJhbG+WZO968OrvFPNPfeEPr7B13ZwERdXaRxguIgAiIgAjI3xwyZ24QPDdu2ewemBeYGzfRlufNjduay/wXtgu0d9kLWo8AAAAASUVORK5CYII=", "public": true } - ] + ], + "scada": false } \ 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 index 2beedd2a76..7dd5a068fb 100644 --- a/application/src/main/data/json/system/widget_types/attributes_card.json +++ b/application/src/main/data/json/system/widget_types/attributes_card.json @@ -14,7 +14,7 @@ "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\n .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 = $(\n '.tbDatasource-title',\n datasourceContainer);\n self.ctx.datasourceTitleCells.push(\n datasourceTitleCell);\n\n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx\n .$container);\n\n for (var a = 0; a < tbDatasource.dataKeys\n .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\n .length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data\n .length > 0) {\n var tvPair = cellData.data[cellData.data\n .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 ||\n cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey\n .decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(\n value, decimals, units, true);\n } else {\n txtValue = self.ctx.utilsService\n .customTranslation(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(\n datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells\n .length; i++) {\n self.ctx.datasourceTitleCells[i].css(\n 'font-size', datasourceTitleFontSize +\n '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',\n valueFontSize + 'px');\n self.ctx.valueCells[i].css('height',\n valueFontSize * 2.5 + 'px');\n self.ctx.valueCells[i].css('padding', '0px ' +\n valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size',\n labelFontSize + 'px');\n self.ctx.labelCells[i].css('height',\n labelFontSize * 2.5 + 'px');\n self.ctx.labelCells[i].css('padding', '0px ' +\n labelFontSize + 'px');\n }\n}\n\nself.onDestroy = function() {}", "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\",\"decimals\":null}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\",\"decimals\":null}" }, "resources": [ { @@ -29,4 +29,4 @@ "public": true } ] -} +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/bar_chart.json b/application/src/main/data/json/system/widget_types/bar_chart.json index 5818f3d610..b195da4e3c 100644 --- a/application/src/main/data/json/system/widget_types/bar_chart.json +++ b/application/src/main/data/json/system/widget_types/bar_chart.json @@ -20,7 +20,7 @@ "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-time-series-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"type\":\"bar\"},\"_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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"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\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":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\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Bar 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\":\"\",\"decimals\":null,\"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\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"type\":\"bar\"},\"_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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"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\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Bar chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":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\":null,\"noDataDisplayMessage\":\"\",\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\",\"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}}" }, "tags": [ "chart", diff --git a/application/src/main/data/json/system/widget_types/bars.json b/application/src/main/data/json/system/widget_types/bars.json index c5ef85be52..3ed89042cc 100644 --- a/application/src/main/data/json/system/widget_types/bars.json +++ b/application/src/main/data/json/system/widget_types/bars.json @@ -15,7 +15,7 @@ "settingsDirective": "tb-bar-chart-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-bar-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Bars\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Bars\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" }, "tags": [ "bars", diff --git a/application/src/main/data/json/system/widget_types/bars_deprecated.json b/application/src/main/data/json/system/widget_types/bars_deprecated.json index 82590680bf..64598dac67 100644 --- a/application/src/main/data/json/system/widget_types/bars_deprecated.json +++ b/application/src/main/data/json/system/widget_types/bars_deprecated.json @@ -16,10 +16,9 @@ "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\"}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars\"}" }, "tags": [ "bar", 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 index ab9ede08ab..62007b61ae 100644 --- 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 @@ -12,10 +12,9 @@ "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.flex-1 {\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}}}" + "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;\"}]}]}" }, "tags": [ "pin", 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 index 08c6c6bd0a..45b3378a51 100644 --- a/application/src/main/data/json/system/widget_types/battery_level.json +++ b/application/src/main/data/json/system/widget_types/battery_level.json @@ -15,7 +15,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "accumulator", 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 index eb2b8fde5f..7fdb133b93 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "enviroment", 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 index 3c53033b15..ed9e2203b4 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/CO-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/CO-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "enviroment", 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 index 3edb6cb025..5d48dbb64f 100644 --- a/application/src/main/data/json/system/widget_types/co2_card.json +++ b/application/src/main/data/json/system/widget_types/co2_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 8f7936e919..ddee660eb3 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/co2_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/co2_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", diff --git a/application/src/main/data/json/system/widget_types/compass.json b/application/src/main/data/json/system/widget_types/compass.json index 5ad01066e4..497491aee0 100644 --- a/application/src/main/data/json/system/widget_types/compass.json +++ b/application/src/main/data/json/system/widget_types/compass.json @@ -12,12 +12,11 @@ "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\"}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\"}" }, "tags": [ "direction finder", 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 index 3142fec16e..14ee5be560 100644 --- 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 @@ -12,10 +12,8 @@ "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}" + "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;\"}]}],\"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}" }, "tags": [ "embed", 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 index 11732d5ee0..f7395524f7 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "time-window", 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 index 515b3b10ea..9510e3a7a1 100644 --- 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 @@ -12,13 +12,11 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.entitiesTableWidget.onEditModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true,\n supportsUnitConversion: 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 'cellClick': {\n name: 'widget-action.cell-click',\n multiple: true\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\"}" + "defaultConfig": "{\"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\"},\"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\":{},\"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", 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 index ca68456833..12cdfe57f7 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "provisioning", 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 index e76926e46b..b17877cd51 100644 --- 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 @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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\":{},\"configMode\":\"basic\"}" + "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;\"}]}],\"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\":{},\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "tags": [ "provisioning", 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 index c1f3a73697..61f1c64836 100644 --- a/application/src/main/data/json/system/widget_types/digital_speedometer.json +++ b/application/src/main/data/json/system/widget_types/digital_speedometer.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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\":{},\"configMode\":\"basic\"}" + "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;\"}]}],\"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\":{},\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "tags": [ "velocity", 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 index af1ecc3c82..9d379e002f 100644 --- a/application/src/main/data/json/system/widget_types/digital_thermometer.json +++ b/application/src/main/data/json/system/widget_types/digital_thermometer.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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\":{},\"configMode\":\"basic\"}" + "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;\"}]}],\"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\":{},\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "tags": [ "pyrometer", 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 index da895874c0..7ccab13fd7 100644 --- 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 @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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\":{},\"configMode\":\"basic\"}" + "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;\"}]}],\"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\":{},\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "tags": [ "vertical stripe", diff --git a/application/src/main/data/json/system/widget_types/doughnut.json b/application/src/main/data/json/system/widget_types/doughnut.json index 1982810161..fd1a320fb3 100644 --- a/application/src/main/data/json/system/widget_types/doughnut.json +++ b/application/src/main/data/json/system/widget_types/doughnut.json @@ -15,7 +15,7 @@ "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\":{},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"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\":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},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + "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\"]}}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "ring", 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 index 93a5a21a95..2359749a00 100644 --- a/application/src/main/data/json/system/widget_types/doughnut_deprecated.json +++ b/application/src/main/data/json/system/widget_types/doughnut_deprecated.json @@ -16,10 +16,9 @@ "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}}" + "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;\"}]}],\"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}}" }, "tags": [ "ring", 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 index 6b7a54dbe6..f57e2153dd 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "defaultConfig": "{\"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\"},\"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\":{}}" }, "tags": [ "gateway" 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 index 3ee0c4ea87..92cb86ab35 100644 --- a/application/src/main/data/json/system/widget_types/efficiency_card.json +++ b/application/src/main/data/json/system/widget_types/efficiency_card.json @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "productivity", 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 index 31712b3b81..f8bef025dd 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/efficiency_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/efficiency_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency card with background\",\"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\"}" }, "tags": [ "productivity", 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 index e216dcd9be..a2090db66d 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "productivity", 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 index e52de65f59..b337d40439 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/efficiency_progress_bar_background.png\",\"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)\"}" + "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\"]}}],\"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;/api/images/system/efficiency_progress_bar_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "productivity", 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 index 90f6528f80..17b96bdbaa 100644 --- a/application/src/main/data/json/system/widget_types/entities_hierarchy.json +++ b/application/src/main/data/json/system/widget_types/entities_hierarchy.json @@ -12,10 +12,8 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesHierarchyWidget.onDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.entitiesHierarchyWidget.onEditModeChanged();\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\":{}}" + "defaultConfig": "{\"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\"},\"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\":{}}" }, "tags": [ "administration", 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 index 01034a1d1d..70bba1c7a4 100644 --- a/application/src/main/data/json/system/widget_types/entities_table.json +++ b/application/src/main/data/json/system/widget_types/entities_table.json @@ -11,19 +11,13 @@ "resources": [], "templateHtml": "\n", "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.entitiesTableWidget.onEditModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true,\n supportsUnitConversion: 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 'cellClick': {\n name: 'widget-action.cell-click',\n multiple: true\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.entitiesTableWidget.onEditModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true,\n supportsUnitConversion: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'displayName', 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 'cellClick': {\n name: 'widget-action.cell-click',\n multiple: true\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", "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}" + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"entitiesTitle\":\"Entities\",\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"pageStepCount\":3,\"pageStepIncrement\":10,\"defaultSortOrder\":\"displayName\",\"useRowStyleFunction\":false},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"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\":\"\"},\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#607d8b\",\"settings\":{},\"funcBody\":\"return 'Device';\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#4caf50\",\"settings\":{},\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#f44336\",\"settings\":{},\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"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\"]}}],\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"list\",\"iconColor\":null}" }, - "tags": [ - "administration", - "management" - ], "resources": [ { "link": "/api/images/system/entities_table_system_widget_image.png", @@ -36,5 +30,10 @@ "data": "iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAnhSURBVHja7d3rU5NXHsBx/7KAa8WuBRahaEAIiQRFQSAKXlrUpagYAmgBJbXKin3qKlaKYAOmtFlQLpWkgCgSRC6GiwZFQAi3J/nuC9Da2RGSTNtZmXNeJWfmOfl9Juc2c855zjoWnU8HPvD01LnAukXHpMwHnuRJx+I65yRrIE061z2V1wJEfrpugDWRBgREQAREQAREQATkQ4A4X/sBkXW34IHO7d9PfqXT6XS6Tr9DztXpsv/zP7mJZf5AFEEjNCn8nEZ2WE4HWSxjfkPUxyyXAxv/IEjoAZoUsnxxx24b5ScOaaqMquwFHqSoL3u8KK1mM0h6xrT9WYXalF7mC2MODnsPKYXdxfycFFdOR2qJKkfmB83hqDK601RFc2Sc1e61HlJVeQep/NjSpJDrEvuMERRHtF9UlHZs+nk6+N8PQhu9hLSuf10V4dakdx1RY4zvyU/2HlI43LKp9rmysUXR3xhQ3RpoHQysatlc5gr5qlt9lvAs++6/35M2LHoFuVuxxayQaSw5qJCLM+hRTBBXfjdQklTnvITIYebDxWiu0a14vf2AVBDodUVVb/xEUeKhtzQvoLnxI9hSW7UNEstaAxe5GU14DcZ99CvGvIO4E6MV8g8RTZVvIeprdwIlSWrxEkLRgaBHaK7Ro5iMPiRJ0oIPVSvtCI6NVY8Cmxs/gvCam9GQWGZbL1P1KeE1GHXeQ+hdr5CLdoycVcy/hYxvKnd++8RbSJciCjSZz/O2U6Ad6pR8aSMPA7qa1/fUBzQuQfoC7jwKKZsOufYs0eAbRGmDUqU8unvLKaXj8in6lVNk/IBVG3z4hReh1O8AUH4NmpStqk5mcsKjzV5DMq7DiVNydmhmgskWC4n1XI1IzqigPSHkn69JrOdKNkPK8b9qQBzd1A+aax/8yN4WkMeagMjjYq4lIAIiIAIiIAIiIAIiIALyZ0MG10QSVUtABERABERABERABERABOQDh3TchuYGuN3x62kZ6DeMAy3lUG8wGDrp0J9aYT+Ay2Aoaf1dTleVL7G4/vVFpZupkpNLy5XdBoOhDsau1PgOeZQIGamwy14R0ADkBzhgIiINci5YLCNPtj9sC3//hoDJTdb6/SXv5ox1+QLJPTeYcZMMqUfdCXAj22LpoU9Z3u87RA6enY/WuOZC5IqkDHB9qnbAcWMapPcCLT+Cru39kGBY3OrgSXZWtysXGuvuV/K6+OBVGXtW9uNVY0noozZ3PsRDTQHAVzUAGc1+tZH0tl9yC5vaM6goiRml6ssdDpqz7qeBWvtprgw83z63IoQTNRNRXfaomaQe0jvNeg5dGfvixliUvStqdrVYTJ/XJXe6wxZo+AzgpGrrgVeEHIk/NO075JtvCu+0nJEkKr68+jU7+2McM/Hj99OgdWp+fzXM7bGxMiTvpinFYtllqzo/Fesx62c3u3nZ9f1+i2XHqrttftpbstvOqayquFMA9mFPUcHMhl6KLvoO6Tiocc1pDndSUTChfJhClOPcLmN25A2A6jPIn1WyCmRva0WSJEmDU+rai5j1M1sArqRKkrTqppTQ19j2IdfVSpeXc7qT5kLgXqbvkIXwdMiIWKSigOPRdUQ5Hlut1+O7ZiLnOF1O7iVWgdSq5K74RboWyYxxYNYT20/9d22JMg9W3coR7qTxADC0fRgZVCNUnyDhAZLRj3Ek5TpcT4WKAjpDF4hyAJ1pUKlO3udqCtRqtRfeDwnURmc+hzJV0iEXdxLBrOd+3L7UF3yt2pM5t1osDap0zWM82lQb6NqxqXXxw9jVKUkTf+SAKLu870fl37VOF4A848Vznt82y7kBz1IxM/70WmKKIiACIiACIiACIiACIiAC8kFAxDq7qFoCIiACIiACIiACIiACIiDvJL8OlM+/+bDgPyRmnPH4jp4UAKKUSqW8WH/4KsB0dN1qZU2FaGOyfneAtD7fh1AcOp1ubyyt0QlZCwDlqp0Zs0D1Nj8gW164Ekx0awEWIgFqC7MlgNzIVdftJ4PBpJHB9e4C7tK5xJk5rzDVRs+2QU5WAcPRC2RVg1PziT+QZ/svswwZ3WVrdwOlEmDLzPMKwl4bl7QJ52fD5zh/zazHFpuhdXJ+Z7w3Lwpwq52z/4DG48DkY8irgc+b/YKkR8hvID0RZ5KOLkNmtU4vIfk37Qmye8dQTgOqF2a9RzmA6XzHHo8c+3z1aOqPg3KIsuVl3L74WX7KnfULcvLYuTcQQI4cXoKcvYmXkOOmGxE6Xdhd2xf9+zHrJ8MAvo3U6UKtq0ezuwcsW1N1ZwAY1zxmYsdr/yAvZmLuLEPsfUsll0rMRWu1IVvrvIAsRA7U5kxNTS26VedrMevlYDdz45X5U1NTq/dgHakAslx4C2Am8R7cjNHGB+7yB8KTiJHuGLvd/qptz0jztvnlNoI3/8jH9ub0LxmPbB06PYUxxIVZzxHJmVPpjPz1af7qq7MHmwDaL+ycp2l8Mc1ot/cDfv0jJdPQcHPUYDAY2rEczR0EGpd2HZlX3YLhMhiKmoD+3EwLOK5DVy2ui0eroVefeXfVWFzFHsBTZJoFaXDcYDAYSoHFIjGyC4iACIiACIiACIiACIiACMj/OUSss4uqJSACIiACIiACIiACIiAfOMQ6Bcz+eOsl4G7zqaxFi6W+782Xh8McAOCY65mXr5L3NFf2APS9Ws6YvMOoxWKx2PyAvNpwBRa0pd8px7msCvQJMhkkXdr55uxoXScbAQib7jZ59/zJE7diG/hp18Y3h8ezg+mTJCnzlB+Q8jNxMHIRjluwzHzkGyQY+mO4P4irjvuDbISe8s6w6aF2fh6+UedBNlU+6Xjv4/NaN7U5tIwlL0OaMoMBSO31A6IZzegEGIwbAnyEbB52nMunwIQzigITG7Fvu1UQOG3WE3uwZreJ7OxbcXkrFlFYASxDZuKfBQN07fOjjTxKoU4PnFFmy75D/nb0s6iGdyEFlRA6bdYT68BUMB0iY1oRYkuWf4Pk354NBjjS7AckP9l4ZpML8GT+6DskGCY/WXgHktkMYdNmPbGj1BqG1awM6Y1z8hbSEWm5HWQBh9rjO2QurMVq/dzUWwLnKvyCzATNF1fgWIaUXEUOfguZC3Xx/QqQUc0gv0HuS1JZ0BXIq/aj+zUfA2zJ84n5xrhx3yEbDLlxl7inNO5dhowqiw8HvYVwYU9x1AoQVbrRaHQvQQzdwGwwjEfO+wEZeA64bR75119cADafILLV2vES6G9+1cngGDaYaHZ2yi8H6JzjxQDuh7arKxyyt1mtVqsHumdgYA6Q2+DlE/yA/Mkp52KVsvdPKvsvhcw3Vo2IuZaACIiACIiACIiAeANZMxcEr40rmyec6xbWxiXa8rq1ca25zH8BTrZIsxZexqkAAAAASUVORK5CYII=", "public": true } + ], + "scada": false, + "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 index 3bd2f26c9d..16b0b05168 100644 --- a/application/src/main/data/json/system/widget_types/entity_count.json +++ b/application/src/main/data/json/system/widget_types/entity_count.json @@ -12,12 +12,10 @@ "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)\"}" + "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}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.54)\",\"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}}" }, "tags": [ "total", 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 index 9d9215cf0e..a6f6e610b9 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 20fa345a25..727122f32b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/flooding_level_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/flooding_level_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index b081f8aa99..e14d49d98b 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "weather", 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 index 2048b17063..4c8cd2808b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/flooding_level_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/flooding_level_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "weather", 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 index bfe1c34f15..c78fcb704b 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "liquid", 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 index cf127d5bf7..689b6fc5e9 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/flow_rate_value_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/flow_rate_value_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate card with background\",\"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\"}" }, "tags": [ "liquid", 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 index 83176686ac..d67ef8ad90 100644 --- 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 @@ -18,7 +18,7 @@ "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\"}" + "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\"]}}],\"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\",\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" }, "tags": [ "liquid", 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 index 8e63ab04e8..a7bdcaeeb9 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "liquid", 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 index d7ee1333e3..69192e5682 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/flow_rate_progress_bar_background.png\",\"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)\"}" + "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\"]}}],\"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;/api/images/system/flow_rate_progress_bar_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "liquid", 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 index 2c881da205..32724202b5 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "fluid pressure", 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 index d059d9b026..6e1175f4f2 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/pressure_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/pressure_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure card with background\",\"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\"}" }, "tags": [ "fluid pressure", 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 index 98318d30a4..c555fae338 100644 --- 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 @@ -18,7 +18,7 @@ "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\"}" + "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\"]}}],\"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\",\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" }, "tags": [ "fluid pressure", 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 index 0a4e80dd60..cd1c1571ac 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "fluid pressure", 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 index 8000ff00af..f97347d1e5 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/pressure_progress_bar_background.png\",\"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)\"}" + "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\"]}}],\"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;/api/images/system/pressure_progress_bar_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "fluid pressure", 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 index 9eb9ab816b..b87ce49d05 100644 --- a/application/src/main/data/json/system/widget_types/gateway_configuration.json +++ b/application/src/main/data/json/system/widget_types/gateway_configuration.json @@ -42,4 +42,4 @@ "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 index a02c0112a3..29440d4c64 100644 --- 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 @@ -42,4 +42,4 @@ "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 index 54ad85048c..b295b1341f 100644 --- a/application/src/main/data/json/system/widget_types/gateway_connectors.json +++ b/application/src/main/data/json/system/widget_types/gateway_connectors.json @@ -40,4 +40,4 @@ "ble", "bluetooth" ] -} \ No newline at end of file +} 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 index dd1a8ea3ee..1781c15e90 100644 --- 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 @@ -44,4 +44,4 @@ "ble", "bluetooth" ] -} \ No newline at end of file +} 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 index 7b9542d2c5..88945de073 100644 --- a/application/src/main/data/json/system/widget_types/gateway_events.json +++ b/application/src/main/data/json/system/widget_types/gateway_events.json @@ -12,10 +12,9 @@ "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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "router", 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 index 1c89f7ae77..e6fb8c0f54 100644 --- 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 @@ -44,4 +44,4 @@ "ble", "bluetooth" ] -} \ No newline at end of file +} 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 index 314f7e8d0c..21675e550a 100644 --- a/application/src/main/data/json/system/widget_types/gateway_logs.json +++ b/application/src/main/data/json/system/widget_types/gateway_logs.json @@ -41,4 +41,4 @@ "ble", "bluetooth" ] -} \ No newline at end of file +} diff --git a/application/src/main/data/json/system/widget_types/gateway_markdown_html_card.json b/application/src/main/data/json/system/widget_types/gateway_markdown_html_card.json new file mode 100644 index 0000000000..bb69b66b94 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_markdown_html_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "gateway_widgets.markdown_card", + "name": "Gateway Markdown/HTML Card", + "deprecated": false, + "image": "tb-image;/api/images/system/markdown_html_card_system_widget_image.png", + "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": [ + { + "url": "tb-resource;/api/resource/js_module/system/gateway-management-extension.js", + "isModule": true + } + ], + "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", + "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}" + }, + "resources": null, + "scada": false, + "tags": [ + "web", + "markup" + ] +} diff --git a/application/src/main/data/json/system/widget_types/gauge.json b/application/src/main/data/json/system/widget_types/gauge.json index 06883eae69..0683383b53 100644 --- a/application/src/main/data/json/system/widget_types/gauge.json +++ b/application/src/main/data/json/system/widget_types/gauge.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "tags": [ "measure", 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 index cd69b304e9..22750ae737 100644 --- a/application/src/main/data/json/system/widget_types/google_map.json +++ b/application/src/main/data/json/system/widget_types/google_map.json @@ -12,10 +12,8 @@ "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-legacy", - "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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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\":{}}" + "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}]}],\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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},\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ "mapping", 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 index 77c2c0ccfb..d93653fb8d 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 3263548bb2..3b59b417d5 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/ground_temperature_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/ground_temperature_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index b49e1dcf41..117bf9e420 100644 --- a/application/src/main/data/json/system/widget_types/here_map.json +++ b/application/src/main/data/json/system/widget_types/here_map.json @@ -12,10 +12,8 @@ "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-legacy", - "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\":\"tb-image;/api/images/system/here_map_system_widget_map_image.svg\",\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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\":{}}" + "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}]}],\"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\":\"tb-image;/api/images/system/here_map_system_widget_map_image.svg\",\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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},\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ "mapping", 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 index f0e9731837..d1d282febb 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 8190e9fcaf..3dc899c2d8 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 2bf3dcbe23..989512f7e1 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_air_quality_index_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_air_quality_index_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 00dfc56e43..0983ebf8f7 100644 --- a/application/src/main/data/json/system/widget_types/horizontal_bar.json +++ b/application/src/main/data/json/system/widget_types/horizontal_bar.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-gauge-basic-config", - "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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 1eb8158318..706deb93fd 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 02482fb5c9..ad9a952d30 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 38391e23d6..f6f003894a 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/CO-value-card-horizontal-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/CO-value-card-horizontal-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index e6c8e1eb6d..300686a0d1 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 34a4e1aba4..78868b1a7e 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_co2_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_co2_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 6a17402bac..7df8104a86 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index c3cc132d9d..47e40673af 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 7dd001ae96..a5cc708e04 100644 --- a/application/src/main/data/json/system/widget_types/horizontal_doughnut.json +++ b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json @@ -15,7 +15,7 @@ "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\":{},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"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\":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},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + "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\"]}}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "ring", 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 index a955d68869..2d30185a5e 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "productivity", 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 index 78f8863965..44d80a7b96 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/horizontal_efficiency_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/horizontal_efficiency_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal efficiency card with background\",\"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\"}" }, "tags": [ "productivity", 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 index 441a5a520c..6571efec85 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 333d983be9..94179c9507 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 7c6794b4f7..c65691497e 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_flooding_level_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_flooding_level_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 15aca34b02..b0e2626fbb 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "liquid", 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 index 4f6de487cd..7b2c4ceb51 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/horizontal_flow_rate_card_background_(1).png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/horizontal_flow_rate_card_background_(1).png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal flow rate card with background\",\"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\"}" }, "tags": [ "liquid", 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 index b9432be3ca..ebeec0fe51 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "fluid pressure", 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 index 5598b8ed1c..e15bd24d4d 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/horizontal_pressure_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/horizontal_pressure_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal pressure card with background\",\"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\"}" }, "tags": [ "fluid pressure", 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 index 6b0f064707..ac87fdd54f 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index bb03947926..f3e44ba702 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_ground_temperature_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_ground_temperature_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index fa576d2ee0..220a94e9e7 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 450d4046f3..05ad28b2d9 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_humidity_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_humidity_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 456268a8fa..01d6d22c96 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index a3ff36784f..232a07701b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_illuminance_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_illuminance_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 8cd5159aab..07db7700b8 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 8cc3664ad4..2498e25b89 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/IAI-horizontal-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/IAI-horizontal-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index f31a0a7bb3..aeeae67d47 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 637c6182cc..8d2cb283ab 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_leaf_wetness_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_leaf_wetness_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index ce820784bd..aaa8170eef 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index d36eb56e95..369d7de0ec 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/NO2-value-card-horizontal-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/NO2-value-card-horizontal-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index a6a5cc1004..39ba22f0e7 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 02d041e1d6..c45812b66e 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_noise_level_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_noise_level_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index de7b3dfb75..c31ef7e522 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 61a63dd7b6..bf75f97846 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 0a0886ca7b..5b7c3464bc 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/ozone-horizontal-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/ozone-horizontal-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 5f9eb7d645..a50435db0e 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index ca3b39eac9..12e6a9904f 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_pm10_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_pm10_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 617031cc7e..658c51a0b9 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index f85406bd75..3aec6a698f 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_pm2_5_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_pm2_5_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index b7d93c0cbc..e56f1d307b 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "power", 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 index d74452d787..fc2c20d623 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/horizontal_power_consumption_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/horizontal_power_consumption_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal power consumption card with background\",\"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\"}" }, "tags": [ "power", 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 index 64ab997988..197ebc2488 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 0b4d9dbf35..8872f4e0e1 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_pressure_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_pressure_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index de91fdde12..cc522255e1 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "vibration", 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 index c787de5a2c..9e4f306b0d 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/horizontal_vibration_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/horizontal_vibration_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal vibration card with background\",\"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\"}" }, "tags": [ "vibration", 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 index e5edbd240c..3d27d7a9a2 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index f15bce1cb9..9ace2ca876 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_radon_level_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_radon_level_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 22a7cf3675..7608e5b765 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 19f0333ac8..86adcd593f 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_rainfall_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_rainfall_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 6e041e0fd8..59733ff38f 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "angular speed", 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 index 90cc5dbb51..6e2cccf0a2 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/horizontal_rotational_speed_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/horizontal_rotational_speed_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal rotational speed card with background\",\"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\"}" }, "tags": [ "angular speed", 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 index d94b6c218a..b222b915de 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index da6e3f16cb..6f271d3b67 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_snow_depth_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_snow_depth_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 7c8094006c..5ac1793475 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 970acb6bc6..c53214bc37 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_soil_moisture_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_soil_moisture_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index c9b3cbfa93..bf5b30bba8 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 7220911d6d..341003da53 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_solar_radiation_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_solar_radiation_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index ad8d850963..7869a8d0a9 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index f8c2601a28..197f62850c 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/SO2-value-card-horizontal-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/SO2-value-card-horizontal-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index d38d5412bd..8685ab39d6 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index 6ed35afab5..795daa25de 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_temperature_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_temperature_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index 57eed149c4..e25029b492 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 2a542e7c62..12cb3b8dca 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_uv_index_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_uv_index_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 7ae3422352..012bd91b9f 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "resources": [ { 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 index df72fa560f..b43b513437 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index c7137ba291..eafcdbea5b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_vibration_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_vibration_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index f36ba0adca..3f82e76027 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index e278d5c9a6..df818339b2 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_visibility_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_visibility_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index cccfe7c18b..8adb9110e5 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index a97f139a51..d864241952 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_volatile_organic_compounds_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_volatile_organic_compounds_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 9883066d90..257374763f 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 0b6700ebc8..d228e88c17 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/horizontal_wind_speed_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/horizontal_wind_speed_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 8dc2dd91a7..7ac6bbbfef 100644 --- a/application/src/main/data/json/system/widget_types/html_card.json +++ b/application/src/main/data/json/system/widget_types/html_card.json @@ -12,10 +12,8 @@ "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}" + "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;\"}]}],\"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}" }, "tags": [ "web", 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 index 56a731fbb6..f647b767a6 100644 --- 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 @@ -12,10 +12,8 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "web", 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 index 28b7c88e7c..9d4f1cc177 100644 --- a/application/src/main/data/json/system/widget_types/humidity_card.json +++ b/application/src/main/data/json/system/widget_types/humidity_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 2f33b5ec33..4dd3d87fcc 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/humidity_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/humidity_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index b8d80a2040..9ba12ab343 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "weather", 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 index 4d3ddc7d21..818012bd4c 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/humidity_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/humidity_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "weather", 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 index 6e24dcaa4a..b3cb88702c 100644 --- a/application/src/main/data/json/system/widget_types/illuminance_card.json +++ b/application/src/main/data/json/system/widget_types/illuminance_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 0c990bb1b9..3242efe285 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/illuminance_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/illuminance_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index de3b03cf95..3743fd8eb7 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 181208cb24..90011a21ef 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/illuminance_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/illuminance_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 991c0b463f..1091bd4232 100644 --- a/application/src/main/data/json/system/widget_types/image_map.json +++ b/application/src/main/data/json/system/widget_types/image_map.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-map-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-map-basic-config", - "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"image\",\"imageSource\":{\"sourceType\":\"image\",\"url\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"entityAliasId\":null,\"entityKey\":null},\"markers\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8239425680406081,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details\",\"patternFunction\":null,\"trigger\":\"click\",\"autoclose\":true,\"offsetX\":0,\"offsetY\":-1},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var temperature = data.temperature;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\\n\"}},\"markerIcon\":{\"icon\":\"mdi:lightbulb-on\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xOTEuMzUgLTM1MS4xOCAxMDgzLjU4IDE3MzAuNDYiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjZmU3NTY5IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMzciIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM1MS44MzMgMTM2MC43OGMtMzguNzY2LTE5MC4zLTEwNy4xMTYtMzQ4LjY2NS0xODkuOTAzLTQ5NS40NEMxMDAuNTIzIDc1Ni40NjkgMjkuMzg2IDY1NS45NzgtMzYuNDM0IDU1MC40MDRjLTIxLjk3Mi0zNS4yNDQtNDAuOTM0LTcyLjQ3Ny02Mi4wNDctMTA5LjA1NC00Mi4yMTYtNzMuMTM3LTc2LjQ0NC0xNTcuOTM1LTc0LjI2OS0yNjcuOTMyIDIuMTI1LTEwNy40NzMgMzMuMjA4LTE5My42ODUgNzguMDMtMjY0LjE3M0MtMjEtMjA2LjY5IDEwMi40ODEtMzAxLjc0NSAyNjguMTY0LTMyNi43MjRjMTM1LjQ2Ni0yMC40MjUgMjYyLjQ3NSAxNC4wODIgMzUyLjU0MyA2Ni43NDcgNzMuNiA0My4wMzggMTMwLjU5NiAxMDAuNTI4IDE3My45MiAxNjguMjggNDUuMjIgNzAuNzE2IDc2LjM2IDE1NC4yNiA3OC45NzEgMjYzLjIzMyAxLjMzNyA1NS44My03LjgwNSAxMDcuNTMyLTIwLjY4NCAxNTAuNDE3LTEzLjAzNCA0My40MS0zMy45OTYgNzkuNjk1LTUyLjY0NiAxMTguNDU1LTM2LjQwNiA3NS42NTktODIuMDQ5IDE0NC45ODEtMTI3Ljg1NSAyMTQuMzQ1LTEzNi40MzcgMjA2LjYwNi0yNjQuNDk2IDQxNy4zMS0zMjAuNTggNzA2LjAyOHoiLz48Y2lyY2xlIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBjeD0iMzUyLjg5MSIgY3k9IjIyNS43NzkiIHI9IjE4My4zMzIiLz48L3N2Zz4=\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}},{\"dsType\":\"function\",\"dsLabel\":\"Second point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7826299113906372,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1,\"patternFunction\":null},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"edit\":{\"enabledActions\":[],\"snappable\":false},\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"icon\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"size\":40,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var colors = ['#488bc7','#549c5d','#ed7546','#be2b29'];\\nvar temperature = data.temperature;\\nvar res = colors[0];\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120;\\n var index = Math.min(3, Math.floor(4 * percent));\\n res = colors[index];\\n}\\nreturn res;\"},\"icon\":\"thermostat\"},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/shape1.svg\",\"imageSize\":34,\"imageFunction\":\" \",\"images\":[]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}}],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[]},\"title\":\"Image Map\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\",\"titleIcon\":\"map\",\"iconColor\":\"#1F6BDD\",\"actions\":{}}" + "defaultConfig": "{\"datasources\":[],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"image\",\"imageSource\":{\"sourceType\":\"image\",\"url\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"entityAliasId\":null,\"entityKey\":null},\"markers\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8239425680406081,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details\",\"patternFunction\":null,\"trigger\":\"click\",\"autoclose\":true,\"offsetX\":0,\"offsetY\":-1},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var temperature = data.temperature;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\\n\"}},\"markerIcon\":{\"icon\":\"mdi:lightbulb-on\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xOTEuMzUgLTM1MS4xOCAxMDgzLjU4IDE3MzAuNDYiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjZmU3NTY5IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMzciIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM1MS44MzMgMTM2MC43OGMtMzguNzY2LTE5MC4zLTEwNy4xMTYtMzQ4LjY2NS0xODkuOTAzLTQ5NS40NEMxMDAuNTIzIDc1Ni40NjkgMjkuMzg2IDY1NS45NzgtMzYuNDM0IDU1MC40MDRjLTIxLjk3Mi0zNS4yNDQtNDAuOTM0LTcyLjQ3Ny02Mi4wNDctMTA5LjA1NC00Mi4yMTYtNzMuMTM3LTc2LjQ0NC0xNTcuOTM1LTc0LjI2OS0yNjcuOTMyIDIuMTI1LTEwNy40NzMgMzMuMjA4LTE5My42ODUgNzguMDMtMjY0LjE3M0MtMjEtMjA2LjY5IDEwMi40ODEtMzAxLjc0NSAyNjguMTY0LTMyNi43MjRjMTM1LjQ2Ni0yMC40MjUgMjYyLjQ3NSAxNC4wODIgMzUyLjU0MyA2Ni43NDcgNzMuNiA0My4wMzggMTMwLjU5NiAxMDAuNTI4IDE3My45MiAxNjguMjggNDUuMjIgNzAuNzE2IDc2LjM2IDE1NC4yNiA3OC45NzEgMjYzLjIzMyAxLjMzNyA1NS44My03LjgwNSAxMDcuNTMyLTIwLjY4NCAxNTAuNDE3LTEzLjAzNCA0My40MS0zMy45OTYgNzkuNjk1LTUyLjY0NiAxMTguNDU1LTM2LjQwNiA3NS42NTktODIuMDQ5IDE0NC45ODEtMTI3Ljg1NSAyMTQuMzQ1LTEzNi40MzcgMjA2LjYwNi0yNjQuNDk2IDQxNy4zMS0zMjAuNTggNzA2LjAyOHoiLz48Y2lyY2xlIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBjeD0iMzUyLjg5MSIgY3k9IjIyNS43NzkiIHI9IjE4My4zMzIiLz48L3N2Zz4=\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}},{\"dsType\":\"function\",\"dsLabel\":\"Second point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7826299113906372,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1,\"patternFunction\":null},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"edit\":{\"enabledActions\":[],\"snappable\":false},\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"icon\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"size\":40,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var colors = ['#488bc7','#549c5d','#ed7546','#be2b29'];\\nvar temperature = data.temperature;\\nvar res = colors[0];\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120;\\n var index = Math.min(3, Math.floor(4 * percent));\\n res = colors[index];\\n}\\nreturn res;\"},\"icon\":\"thermostat\"},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/shape1.svg\",\"imageSize\":34,\"imageFunction\":\" \",\"images\":[]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}}],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[]},\"title\":\"Image Map\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\",\"titleIcon\":\"map\",\"iconColor\":\"#1F6BDD\",\"actions\":{}}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/image_map_deprecated.json b/application/src/main/data/json/system/widget_types/image_map_deprecated.json index 23cf7f7a14..f497b1e8fb 100644 --- a/application/src/main/data/json/system/widget_types/image_map_deprecated.json +++ b/application/src/main/data/json/system/widget_types/image_map_deprecated.json @@ -12,10 +12,8 @@ "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-legacy", - "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\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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\":{}}" + "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}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"image-map\",\"mapImageUrl\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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},\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ "building", 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 index a578136d2b..5e5b722af2 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "resources": [ { 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 index 7f6cfe39a5..1e9df7e26b 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/IAI-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/IAI-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "resources": [ { 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 index 1133460238..21a4ca75cb 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index e1ddcb3b6b..c624a97f49 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_co2_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_co2_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index b453998ddb..0707b87752 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 4ca050fd88..66a3cfa70b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_horizontal_co2_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_horizontal_co2_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 0c62fdfe5b..3e76013cfa 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 04f8faefcb..c5e7071561 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_horizontal_humidity_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_horizontal_humidity_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index b921411cd8..7810e9003e 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index d81be63a9c..e2c832cb52 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_horizontal_illuminance_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_horizontal_illuminance_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 5d315d0e01..2aead4005e 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index ea3833494c..b8bb16de8e 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_horizontal_pm10_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_horizontal_pm10_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 5ae0d04108..19fbff1e3b 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 6e47de67a8..8af6ab208f 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_horizontal_pm2_5_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_horizontal_pm2_5_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 4b5f76317c..2a63e7432c 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index 1b1231720f..eb3d2c0319 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_horizontal_temperature_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_horizontal_temperature_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index 1310fcf9e0..9e489f5d80 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 998d0cbe8b..24a8689d70 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_humidity_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_humidity_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index bc02349832..2b7df3ea28 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 8f3f721ec5..612fc9a0ce 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_humidity_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_humidity_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 73732e8f4b..d6627cf344 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index b6f143b9f8..0e0acf7698 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_illuminance_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_illuminance_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index d58ec4f73d..5067c90a8d 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 676428dbcd..bd150ccfaf 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_illuminance_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_illuminance_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 1a58d29bd4..8e28ea4d3c 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 14f1445ebc..88fb98770d 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_pm10_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_pm10_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 990b7caecc..fcb7017d57 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index ef1fde22b2..8d7cdf1681 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_pm2_5_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_pm2_5_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 0a442fa4e0..728abc34fd 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "environment", 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 index 537f95b673..41146249d0 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/indoor_simple_co2_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/indoor_simple_co2_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "environment", 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 index eb278e4c0f..8f9c801dd5 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\":\"%\",\"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}}" }, "tags": [ "environment", 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 index eda7d8919d..7c881e9347 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/indoor_simple_humidity_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/indoor_simple_humidity_chart_card_with_background_system_widget_background.png\",\"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\":\"%\",\"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}}" }, "tags": [ "environment", 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 index 4362cdf807..2eeca64aca 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "environment", 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 index 5652a3693f..7f47b543ac 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/indoor_simple_illuminance_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/indoor_simple_illuminance_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "environment", 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 index 995181bff6..85e703440c 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 26b4c74ca6..5006398499 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/indoor_simple_pm10_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/indoor_simple_pm10_chart_card_with_background_system_widget_background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index 550bb8d03e..35a6f30a81 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 368e31b0f6..05f3b8591f 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/indoor_simple_pm2_5_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/indoor_simple_pm2_5_chart_card_with_background_system_widget_background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index 3b118b8994..e6101557f0 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "temperature", 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 index e62cbba7fd..e9b50ba2b8 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/indoor_simple_temperature_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/indoor_simple_temperature_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "temperature", 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 index 5f55ace191..1058baae12 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index 23466d55ec..3deb1c409a 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_temperature_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_temperature_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index a935befa6a..f0726e05f3 100644 --- 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 @@ -18,7 +18,7 @@ "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\"}" + "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\"]}}],\"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\",\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" }, "tags": [ "temperature", 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 index ae5196ce89..3c392c9d26 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index e2b6443efe..d6bb4a992e 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/indoor_temperature_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/indoor_temperature_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 8840787f58..9c56db0fe5 100644 --- a/application/src/main/data/json/system/widget_types/knob_control.json +++ b/application/src/main/data/json/system/widget_types/knob_control.json @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", "dataKeySettingsForm": [], "settingsDirective": "tb-knob-control-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2,\"units\":null}" + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false,\"actions\":{},\"decimals\":2,\"units\":null}" }, "tags": [ "dial", diff --git a/application/src/main/data/json/system/widget_types/label___value_card.json b/application/src/main/data/json/system/widget_types/label___value_card.json index 352ee4de48..ce1a376a71 100644 --- a/application/src/main/data/json/system/widget_types/label___value_card.json +++ b/application/src/main/data/json/system/widget_types/label___value_card.json @@ -15,7 +15,7 @@ "settingsDirective": "tb-label-value-card-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-label-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}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Label & value card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"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}}" + "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}]}],\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Label & value card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "label" 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 index 6a7d0f2e50..b8bfc94f41 100644 --- a/application/src/main/data/json/system/widget_types/label_widget.json +++ b/application/src/main/data/json/system/widget_types/label_widget.json @@ -12,10 +12,9 @@ "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\":\"tb-image;/api/images/system/here_map_system_widget_map_image.svg\",\"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}}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"backgroundImageUrl\":\"tb-image;/api/images/system/here_map_system_widget_map_image.svg\",\"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}}" }, "tags": [ "tag", 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 index e5588b104b..f81fcdf328 100644 --- 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 @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-gauge-basic-config", - "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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "resources": [ { 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 index fa8faf3255..5334fd86aa 100644 --- a/application/src/main/data/json/system/widget_types/lcd_gauge.json +++ b/application/src/main/data/json/system/widget_types/lcd_gauge.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 65109597fd..2e9efa3027 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 3258de6b32..b7d3b0edec 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/leaf_wetness_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/leaf_wetness_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index fe3919e6bf..750d2846a7 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 271dca2485..1af8f7de7b 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/leaf_wetness_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/leaf_wetness_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 7b45863b1c..d9f9d228bf 100644 --- a/application/src/main/data/json/system/widget_types/led_indicator.json +++ b/application/src/main/data/json/system/widget_types/led_indicator.json @@ -12,10 +12,9 @@ "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}" + "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},\"showLegend\":false,\"actions\":{},\"decimals\":2}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/line_chart.json b/application/src/main/data/json/system/widget_types/line_chart.json index bf67d765f1..69719c5032 100644 --- a/application/src/main/data/json/system/widget_types/line_chart.json +++ b/application/src/main/data/json/system/widget_types/line_chart.json @@ -20,7 +20,7 @@ "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-time-series-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"fillAreaSettings\":{\"type\":\"opacity\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.3409583261715494,\"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\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":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\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Line 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\":\"\",\"decimals\":null,\"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\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"fillAreaSettings\":{\"type\":\"opacity\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.3409583261715494,\"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\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Line chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":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\":null,\"noDataDisplayMessage\":\"\",\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\",\"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}}" }, "tags": [ "chart", diff --git a/application/src/main/data/json/system/widget_types/map.json b/application/src/main/data/json/system/widget_types/map.json index e498922d36..7106cfc4b6 100644 --- a/application/src/main/data/json/system/widget_types/map.json +++ b/application/src/main/data/json/system/widget_types/map.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-map-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-map-basic-config", - "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8239425680406081,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\"},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\"},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var temperature = data.temperature;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\\n\"}},\"markerIcon\":{\"icon\":\"mdi:lightbulb-on\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xOTEuMzUgLTM1MS4xOCAxMDgzLjU4IDE3MzAuNDYiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjZmU3NTY5IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMzciIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM1MS44MzMgMTM2MC43OGMtMzguNzY2LTE5MC4zLTEwNy4xMTYtMzQ4LjY2NS0xODkuOTAzLTQ5NS40NEMxMDAuNTIzIDc1Ni40NjkgMjkuMzg2IDY1NS45NzgtMzYuNDM0IDU1MC40MDRjLTIxLjk3Mi0zNS4yNDQtNDAuOTM0LTcyLjQ3Ny02Mi4wNDctMTA5LjA1NC00Mi4yMTYtNzMuMTM3LTc2LjQ0NC0xNTcuOTM1LTc0LjI2OS0yNjcuOTMyIDIuMTI1LTEwNy40NzMgMzMuMjA4LTE5My42ODUgNzguMDMtMjY0LjE3M0MtMjEtMjA2LjY5IDEwMi40ODEtMzAxLjc0NSAyNjguMTY0LTMyNi43MjRjMTM1LjQ2Ni0yMC40MjUgMjYyLjQ3NSAxNC4wODIgMzUyLjU0MyA2Ni43NDcgNzMuNiA0My4wMzggMTMwLjU5NiAxMDAuNTI4IDE3My45MiAxNjguMjggNDUuMjIgNzAuNzE2IDc2LjM2IDE1NC4yNiA3OC45NzEgMjYzLjIzMyAxLjMzNyA1NS44My03LjgwNSAxMDcuNTMyLTIwLjY4NCAxNTAuNDE3LTEzLjAzNCA0My40MS0zMy45OTYgNzkuNjk1LTUyLjY0NiAxMTguNDU1LTM2LjQwNiA3NS42NTktODIuMDQ5IDE0NC45ODEtMTI3Ljg1NSAyMTQuMzQ1LTEzNi40MzcgMjA2LjYwNi0yNjQuNDk2IDQxNy4zMS0zMjAuNTggNzA2LjAyOHoiLz48Y2lyY2xlIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBjeD0iMzUyLjg5MSIgY3k9IjIyNS43NzkiIHI9IjE4My4zMzIiLz48L3N2Zz4=\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":1},{\"dsType\":\"function\",\"dsLabel\":\"Second point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7826299113906372,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"edit\":{\"enabledActions\":[],\"snappable\":false},\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"icon\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"size\":40,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var colors = ['#488bc7','#549c5d','#ed7546','#be2b29'];\\nvar temperature = data.temperature;\\nvar res = colors[0];\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120;\\n var index = Math.min(3, Math.floor(4 * percent));\\n res = colors[index];\\n}\\nreturn res;\"},\"icon\":\"thermostat\"},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/shape1.svg\",\"imageSize\":34,\"imageFunction\":\"\\n\",\"images\":[]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}}],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[]},\"title\":\"Map\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\",\"titleIcon\":\"map\",\"iconColor\":\"#1F6BDD\",\"actions\":{}}" + "defaultConfig": "{\"datasources\":[],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8239425680406081,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\"},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\"},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var temperature = data.temperature;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\\n\"}},\"markerIcon\":{\"icon\":\"mdi:lightbulb-on\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xOTEuMzUgLTM1MS4xOCAxMDgzLjU4IDE3MzAuNDYiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjZmU3NTY5IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMzciIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM1MS44MzMgMTM2MC43OGMtMzguNzY2LTE5MC4zLTEwNy4xMTYtMzQ4LjY2NS0xODkuOTAzLTQ5NS40NEMxMDAuNTIzIDc1Ni40NjkgMjkuMzg2IDY1NS45NzgtMzYuNDM0IDU1MC40MDRjLTIxLjk3Mi0zNS4yNDQtNDAuOTM0LTcyLjQ3Ny02Mi4wNDctMTA5LjA1NC00Mi4yMTYtNzMuMTM3LTc2LjQ0NC0xNTcuOTM1LTc0LjI2OS0yNjcuOTMyIDIuMTI1LTEwNy40NzMgMzMuMjA4LTE5My42ODUgNzguMDMtMjY0LjE3M0MtMjEtMjA2LjY5IDEwMi40ODEtMzAxLjc0NSAyNjguMTY0LTMyNi43MjRjMTM1LjQ2Ni0yMC40MjUgMjYyLjQ3NSAxNC4wODIgMzUyLjU0MyA2Ni43NDcgNzMuNiA0My4wMzggMTMwLjU5NiAxMDAuNTI4IDE3My45MiAxNjguMjggNDUuMjIgNzAuNzE2IDc2LjM2IDE1NC4yNiA3OC45NzEgMjYzLjIzMyAxLjMzNyA1NS44My03LjgwNSAxMDcuNTMyLTIwLjY4NCAxNTAuNDE3LTEzLjAzNCA0My40MS0zMy45OTYgNzkuNjk1LTUyLjY0NiAxMTguNDU1LTM2LjQwNiA3NS42NTktODIuMDQ5IDE0NC45ODEtMTI3Ljg1NSAyMTQuMzQ1LTEzNi40MzcgMjA2LjYwNi0yNjQuNDk2IDQxNy4zMS0zMjAuNTggNzA2LjAyOHoiLz48Y2lyY2xlIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBjeD0iMzUyLjg5MSIgY3k9IjIyNS43NzkiIHI9IjE4My4zMzIiLz48L3N2Zz4=\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":1},{\"dsType\":\"function\",\"dsLabel\":\"Second point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7826299113906372,\"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}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"edit\":{\"enabledActions\":[],\"snappable\":false},\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"icon\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"size\":40,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var colors = ['#488bc7','#549c5d','#ed7546','#be2b29'];\\nvar temperature = data.temperature;\\nvar res = colors[0];\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120;\\n var index = Math.min(3, Math.floor(4 * percent));\\n res = colors[index];\\n}\\nreturn res;\"},\"icon\":\"thermostat\"},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/shape1.svg\",\"imageSize\":34,\"imageFunction\":\"\\n\",\"images\":[]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}}],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[]},\"title\":\"Map\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\",\"titleIcon\":\"map\",\"iconColor\":\"#1F6BDD\",\"actions\":{}}" }, "resources": [ { 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 index b66e3ee88e..e8862ec177 100644 --- 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 @@ -13,7 +13,7 @@ "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 hideDataSettings: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", "settingsDirective": "tb-markdown-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"const baseTemp = 20;\\nconst dailySwing = 10;\\nconst hourlyVariation = Math.sin((time % 24) * Math.PI / 12) * dailySwing;\\nconst randomness = (Math.random() - 0.5) * 2;\\nconst smoothingFactor = 0.8;\\nreturn (prevValue * smoothingFactor) + ((baseTemp + hourlyVariation + randomness) * (1 - smoothingFactor));\",\"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\":{\"useMarkdownTextFunction\":false,\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Temperature}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\",\"applyDefaultMarkdownStyle\":true,\"markdownCss\":\"\"},\"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,\"useDashboardTimewindow\":true,\"displayTimewindow\":true}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"const baseTemp = 20;\\nconst dailySwing = 10;\\nconst hourlyVariation = Math.sin((time % 24) * Math.PI / 12) * dailySwing;\\nconst randomness = (Math.random() - 0.5) * 2;\\nconst smoothingFactor = 0.8;\\nreturn (prevValue * smoothingFactor) + ((baseTemp + hourlyVariation + randomness) * (1 - smoothingFactor));\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"useMarkdownTextFunction\":false,\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Temperature}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\",\"applyDefaultMarkdownStyle\":true,\"markdownCss\":\"\"},\"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}" }, "resources": [ { 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 index bf721b05b7..780ae57e92 100644 --- 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 @@ -12,10 +12,8 @@ "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-legacy", - "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}" + "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;\"}]}],\"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},\"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\":\"\"}" }, "tags": [ "mapping", 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 index c4bddab86d..37f085c531 100644 --- 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 @@ -12,10 +12,8 @@ "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-legacy", - "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\":\"tb-image;/api/images/system/markers_placement_image_map_system_widget_map_image.svg\",\"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}" + "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;\"}]}],\"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\":\"tb-image;/api/images/system/markers_placement_image_map_system_widget_map_image.svg\",\"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},\"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\":\"\"}" }, "tags": [ "building", 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 index da96abb49e..f7e30bd51c 100644 --- 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 @@ -12,10 +12,8 @@ "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-legacy", - "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}" + "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;\"}]}],\"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},\"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\":\"\"}" }, "tags": [ "mapping", 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 index 6deb0a821c..9214452325 100644 --- a/application/src/main/data/json/system/widget_types/mini_gauge.json +++ b/application/src/main/data/json/system/widget_types/mini_gauge.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-gauge-basic-config", - "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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 78c51a0d8c..0a9a5fd0c1 100644 --- a/application/src/main/data/json/system/widget_types/navigation_card.json +++ b/application/src/main/data/json/system/widget_types/navigation_card.json @@ -12,10 +12,9 @@ "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}" + "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;\"}]}],\"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}" }, "resources": [ { 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 index 4864ef9e53..dddaad3c4d 100644 --- a/application/src/main/data/json/system/widget_types/navigation_cards.json +++ b/application/src/main/data/json/system/widget_types/navigation_cards.json @@ -12,10 +12,9 @@ "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}" + "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;\"}]}],\"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}" }, "resources": [ { 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 index ea4449ab43..abfe988b4a 100644 --- a/application/src/main/data/json/system/widget_types/neon_gauge.json +++ b/application/src/main/data/json/system/widget_types/neon_gauge.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-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() * 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\":{},\"configMode\":\"basic\"}" + "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;\"}]}],\"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\":{},\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 95605deae6..c47e0977d2 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "enviroment", 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 index 7fee7b991a..37671527dd 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/NO2-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/NO2-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 4b214b63a0..4a1da677ea 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 564d3d665e..44475dc2ce 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/noise_level_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/noise_level_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index b0294e0988..063aa8a9d9 100644 --- a/application/src/main/data/json/system/widget_types/openstreet_map.json +++ b/application/src/main/data/json/system/widget_types/openstreet_map.json @@ -12,10 +12,8 @@ "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-legacy", - "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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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\":{}}" + "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}]}],\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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},\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ "mapping", 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 index 77f12bc288..f8a49252c5 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "enviroment", 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 index 02145f1e0a..4af0a7b712 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/ozone-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/ozone-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 69327395cc..fc676380ab 100644 --- 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 @@ -15,7 +15,7 @@ "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\":{}}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"\",\"saveToGallery\":true,\"usePublicGalleryLink\":false,\"imageFormat\":\"image/png\",\"imageQuality\":0.92,\"maxWidth\":640,\"maxHeight\":480},\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/pie.json b/application/src/main/data/json/system/widget_types/pie.json index 1a07ce785d..78983b06b4 100644 --- a/application/src/main/data/json/system/widget_types/pie.json +++ b/application/src/main/data/json/system/widget_types/pie.json @@ -15,7 +15,7 @@ "settingsDirective": "tb-pie-chart-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-pie-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Pie\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"pie_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Pie\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"pie_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" }, "tags": [ "pie chart", 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 index 629d0f0f81..920833d9f1 100644 --- 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 @@ -16,10 +16,9 @@ "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\"}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Pie - Chart.js\"}" }, "resources": [ { 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 index 9a07685f21..e8bdc2a1d9 100644 --- a/application/src/main/data/json/system/widget_types/pie___flot.json +++ b/application/src/main/data/json/system/widget_types/pie___flot.json @@ -12,11 +12,9 @@ "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}}" + "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;\"}]}],\"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}}" }, "resources": [ { 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 index 3412808d60..05556452a2 100644 --- a/application/src/main/data/json/system/widget_types/pm10_card.json +++ b/application/src/main/data/json/system/widget_types/pm10_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index fbb8c7251e..0af84a9fc3 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/pm10_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/pm10_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 0770923327..b9be83e86d 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index e82a840d27..41ef55b0ac 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/pm2_5_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/pm2_5_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", diff --git a/application/src/main/data/json/system/widget_types/point_chart.json b/application/src/main/data/json/system/widget_types/point_chart.json index 860e8c22e8..372b376439 100644 --- a/application/src/main/data/json/system/widget_types/point_chart.json +++ b/application/src/main/data/json/system/widget_types/point_chart.json @@ -20,7 +20,7 @@ "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-time-series-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.5534217244004682,\"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\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":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\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Point 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\":\"\",\"decimals\":null,\"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\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.5534217244004682,\"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\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Point chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":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\":null,\"noDataDisplayMessage\":\"\",\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\",\"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}}" }, "tags": [ "chart", 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 index d3f7fadcf9..9ae33a6829 100644 --- a/application/src/main/data/json/system/widget_types/polar_area.json +++ b/application/src/main/data/json/system/widget_types/polar_area.json @@ -15,7 +15,7 @@ "settingsDirective": "tb-polar-area-chart-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-polar-area-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Polar area\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Polar area\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" }, "tags": [ "polar", diff --git a/application/src/main/data/json/system/widget_types/polar_area_deprecated.json b/application/src/main/data/json/system/widget_types/polar_area_deprecated.json index de6e657b47..bc73865f87 100644 --- a/application/src/main/data/json/system/widget_types/polar_area_deprecated.json +++ b/application/src/main/data/json/system/widget_types/polar_area_deprecated.json @@ -16,10 +16,9 @@ "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\"}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Polar Area\"}" }, "resources": [ { 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 index e99cc76bb4..1b568491c6 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "power", 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 index 2f08a16a5b..4f9fb7fbb4 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/power_consumption_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/power_consumption_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Power consumption card with background\",\"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\"}" }, "tags": [ "power", 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 index 9138ab6391..49c4b6478f 100644 --- a/application/src/main/data/json/system/widget_types/pressure_card.json +++ b/application/src/main/data/json/system/widget_types/pressure_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index d87db84e8a..bc35530963 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/pressure_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/pressure_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 93d19780ae..12fd5b917e 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index e5189912d2..57fa40f2d5 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/pressure_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/pressure_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 43b4f97210..0ece481c8c 100644 --- a/application/src/main/data/json/system/widget_types/progress_bar.json +++ b/application/src/main/data/json/system/widget_types/progress_bar.json @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 414cfaeb36..688c8b03df 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "vibration", 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 index c3e90f61a3..05114909c1 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/vibration_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/vibration_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration card with background\",\"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\"}" }, "tags": [ "vibration", 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 index 2059719db5..ba7b8f0ddc 100644 --- a/application/src/main/data/json/system/widget_types/qr_code.json +++ b/application/src/main/data/json/system/widget_types/qr_code.json @@ -12,10 +12,8 @@ "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\"}" + "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;\"}]}],\"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\"}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/radar.json b/application/src/main/data/json/system/widget_types/radar.json index be88baa238..24b97698d4 100644 --- a/application/src/main/data/json/system/widget_types/radar.json +++ b/application/src/main/data/json/system/widget_types/radar.json @@ -15,7 +15,7 @@ "settingsDirective": "tb-radar-chart-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-radar-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Radar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"radar\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"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\",\"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},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"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;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Radar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"radar\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"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}}" }, "tags": [ "radar", diff --git a/application/src/main/data/json/system/widget_types/radar_deprecated.json b/application/src/main/data/json/system/widget_types/radar_deprecated.json index 6a510f54ff..a842d4df40 100644 --- a/application/src/main/data/json/system/widget_types/radar_deprecated.json +++ b/application/src/main/data/json/system/widget_types/radar_deprecated.json @@ -16,10 +16,9 @@ "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\"}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Radar\"}" }, "resources": [ { 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 index 93922f45d4..139a1cf3d0 100644 --- a/application/src/main/data/json/system/widget_types/radial_gauge.json +++ b/application/src/main/data/json/system/widget_types/radial_gauge.json @@ -17,7 +17,7 @@ "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,\"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\"}" + "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;\"}]}],\"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,\"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\"}" }, "resources": [ { 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 index 8c8acc6a88..ad789adea4 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 9f5b6f79ad..4560fdb5eb 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/radon_level_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/radon_level_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 04ce14806b..ca4eb9ad21 100644 --- a/application/src/main/data/json/system/widget_types/rainfall_card.json +++ b/application/src/main/data/json/system/widget_types/rainfall_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 81ede92ad1..6a31586919 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/rainfall_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/rainfall_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index e017fe0cec..5d8293debe 100644 --- a/application/src/main/data/json/system/widget_types/range_chart.json +++ b/application/src/main/data/json/system/widget_types/range_chart.json @@ -20,7 +20,7 @@ "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\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"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}},\"tooltipDateInterval\":true},\"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\"}" + "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}]}],\"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\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"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}},\"tooltipDateInterval\":true},\"title\":\"Range chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":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\":\"\",\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\",\"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}}" }, "tags": [ "range", 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 index ef121fe1fb..c7f104bce1 100644 --- 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 @@ -12,10 +12,9 @@ "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.flex-1 {\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\":\"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}}}" + "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;\"}]}]}" }, "resources": [ { 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 index 67130346aa..1810aa21df 100644 --- a/application/src/main/data/json/system/widget_types/rectangle_tank.json +++ b/application/src/main/data/json/system/widget_types/rectangle_tank.json @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 681d53c66d..c4597d3a84 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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\"}" + "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\"]}}],\"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\",\"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\"}" }, "tags": [ "angular speed", 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 index efaa958688..e51158d89d 100644 --- 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 @@ -12,12 +12,11 @@ "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 supportsUnitConversion: 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;/api/images/system/rotational_speed_card_background.png\",\"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\"}" + "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\"]}}],\"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;/api/images/system/rotational_speed_card_background.png\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed card with background\",\"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\"}" }, "tags": [ "angular speed", 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 index f42d801090..cf8f0189d5 100644 --- 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 @@ -18,7 +18,7 @@ "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\"}" + "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\"]}}],\"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\",\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" }, "tags": [ "angular speed", 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 index 7288b70478..96c8889e11 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "angular speed", 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 index c8d2f0e188..b9e5834f01 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/rotational_speed_progress_bar_background.png\",\"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)\"}" + "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\"]}}],\"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;/api/images/system/rotational_speed_progress_bar_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "angular speed", 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 index 07781c59b2..b93d6fc996 100644 --- a/application/src/main/data/json/system/widget_types/round_switch.json +++ b/application/src/main/data/json/system/widget_types/round_switch.json @@ -12,10 +12,9 @@ "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}" + "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},\"showLegend\":false,\"actions\":{},\"decimals\":2}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/route_map.json b/application/src/main/data/json/system/widget_types/route_map.json index 37346f1157..4e6f70d119 100644 --- a/application/src/main/data/json/system/widget_types/route_map.json +++ b/application/src/main/data/json/system/widget_types/route_map.json @@ -20,7 +20,7 @@ "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-map-basic-config", - "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[],\"trips\":[{\"dsType\":\"function\",\"dsLabel\":\"First route\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.17490048149347315,\"funcBody\":\"var value = prevValue;\\nif (!value) {\\n value = 45;\\n}\\nif (time % 500 < 500) {\\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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH\",\"offsetX\":0,\"offsetY\":-1,\"patternFunction\":null,\"tagActions\":null},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"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];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"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];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"image\",\"markerShape\":{\"shape\":\"tripMarkerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"icon\":\"arrow_forward\",\"size\":48,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/tripShape1.svg\",\"imageSize\":34,\"imageFunction\":\"var speed = data.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;\",\"images\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\"]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"rotateMarker\":true,\"offsetAngle\":0,\"showPath\":true,\"pathStrokeWeight\":4,\"pathStrokeColor\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var speed = data.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).setAlpha(0.65).toRgbString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).setAlpha(0.65).toRgbString();\\n }\\n}\"},\"usePathDecorator\":false,\"pathDecoratorSymbol\":\"arrowHead\",\"pathDecoratorSymbolSize\":10,\"pathDecoratorSymbolColor\":\"#307FE5\",\"pathDecoratorOffset\":20,\"pathEndDecoratorOffset\":20,\"pathDecoratorRepeat\":20,\"showPoints\":false,\"pointSize\":10,\"pointColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"pointTooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-1}}],\"tripTimeline\":{\"showTimelineControl\":false}},\"title\":\"Route Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"assistant_navigation\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"titleFont\":{\"size\":null,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":null},\"titleColor\":null,\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"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\":\"24px\"}" + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":5000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"layers\":[{\"label\":\"{i18n:widgets.maps.layer.roadmap}\",\"provider\":\"openstreet\",\"layerType\":\"OpenStreetMap.Mapnik\"},{\"label\":\"{i18n:widgets.maps.layer.satellite}\",\"provider\":\"openstreet\",\"layerType\":\"Esri.WorldImagery\"},{\"label\":\"{i18n:widgets.maps.layer.hybrid}\",\"provider\":\"openstreet\",\"layerType\":\"Esri.WorldImagery\",\"referenceLayer\":\"openstreetmap_hybrid\"}],\"trips\":[{\"dsType\":\"function\",\"dsLabel\":\"First route\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.17490048149347315,\"funcBody\":\"var value = prevValue;\\nif (!value) {\\n value = 45;\\n}\\nif (time % 500 < 500) {\\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;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH\",\"offsetX\":0,\"offsetY\":-1,\"patternFunction\":null,\"tagActions\":null},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"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];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"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];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"image\",\"markerShape\":{\"shape\":\"tripMarkerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"icon\":\"arrow_forward\",\"size\":48,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/tripShape1.svg\",\"imageSize\":34,\"imageFunction\":\"var speed = data.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;\",\"images\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\"]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"rotateMarker\":true,\"offsetAngle\":0,\"showPath\":true,\"pathStrokeWeight\":4,\"pathStrokeColor\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var speed = data.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).setAlpha(0.65).toRgbString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).setAlpha(0.65).toRgbString();\\n }\\n}\"},\"usePathDecorator\":false,\"pathDecoratorSymbol\":\"arrowHead\",\"pathDecoratorSymbolSize\":10,\"pathDecoratorSymbolColor\":\"#307FE5\",\"pathDecoratorOffset\":20,\"pathEndDecoratorOffset\":20,\"pathDecoratorRepeat\":20,\"showPoints\":false,\"pointSize\":10,\"pointColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"pointTooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-1}}],\"markers\":[],\"polygons\":[],\"circles\":[],\"polylines\":[],\"additionalDataSources\":[],\"controlsPosition\":\"topleft\",\"zoomActions\":[\"scroll\",\"doubleClick\",\"controlButtons\"],\"scales\":[],\"dragModeButton\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"defaultCenterPosition\":\"0,0\",\"defaultZoomLevel\":null,\"minZoomLevel\":16,\"mapPageSize\":16384,\"mapActionButtons\":[],\"tripTimeline\":{\"showTimelineControl\":false,\"timeStep\":1000,\"speedOptions\":[1,5,10,15,25],\"showTimestamp\":true,\"timestampFormat\":{\"format\":\"yyyy-MM-dd HH:mm:ss\",\"lastUpdateAgo\":false,\"custom\":false,\"auto\":false},\"snapToRealLocation\":false,\"locationSnapFilter\":\"return true;\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"8px\"},\"title\":\"Route Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"assistant_navigation\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":null,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":null},\"titleColor\":null,\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"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\":\"24px\"}" }, "resources": [ { @@ -29,7 +29,7 @@ "type": "IMAGE", "subType": "IMAGE", "fileName": "map_marker_image_0.png", - "publicResourceKey": "LPbcriZ2v053mkWb33T5JdK7Agkt1jGg", + "publicResourceKey": "K4kMFb95GFkCPhuXboJgtQH9vykL8riR", "mediaType": "image/png", "data": "iVBORw0KGgoAAAANSUhEUgAAAFAAAABdCAYAAAAyj+FzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAH3gAAB94BHQKrYQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7b13uB3VdTb+rrX3zJx6i7qQUAEJIQlRBAZc6BgLDDYmIIExLjgJcQk/YkKc4gIGHH+fDSHg2CGOHRuCQ4ltbBODJIroIIoQIJCQdNXLvVe3nT4ze6/1/XHOlYWQAJuWP37refYz58yd3d6zyt5rr1mX8B7S5Xo5/0nPYaNFM1PY0gGqOhfAgQCNBGlWFFUAYEIeihigbhFdZQwt85BV5Gj9r/718R2XX365vFdzoHe7w6d77xnPkn4YpAtU0YiizNJcmPNkMQFkDiSlowHt2HNtGlTSJ6B+pTpsKTfKgTj3Pi8SMtFtEZnFs8d8dPu7OZ93BcCHtt0+OiL+FJjOiqy5K5dtLwD4PBHGvy0dKLYo8B+1+lAldv50FfmFzWX+84i2M3a8Le2/Dr1jAKqCHtl2y1wC/pEMP9ZRLBaYzF8CCN+pPluUkOKfB6qlmk/dBwTyt8eOv2AZCPpOdPaOAPjA1h9/SJX+TyGXuz0TZi4EcPBeOk+U+RErZh2YyMAyQJEoZUjFgtkCAEScgDyx1hmInTglqDj2U1X0WILaPbWvwHO1WummeuLONhaXHTf2wsfe7rm+rQDe133j/i5xPyrmCr+OouhSKPbdQ5fLiezTIYUBQGMJBgYWxMYSISZhbxgQT8wGAgDiwWxUvCiBxKhSKOqdh4OyV5+6XiEfK/kjVOXQ13apG+I0+adKpXaG0/Si0yZdvPbtmvPbAuCNT98YTBhT/8fAmEpHoXgKgPe/6gFGP0nwG8s2YykcaRCAYYQ5tKTkDVuArDEwMRF5AICS4VZ1AQBSr6oEgL36CBAvlKqIsyLOKQl5TZH4uN+TawDuY6o64lWTJX20v1S633uJNvfmvnbRERelb3XubxnAX26+5gDy6Y9HtrU/wERff1XjSt0WwULDmZEMawPOgilgQ4FaGCEygaXQMQyRMaxiUijUkAEAImIGAFURAOrVA1AmI1ZExGuqoqkVFefhyGtKDql4X4eHc6LxJof0VIVM3nVc4uXaHUPlo0Tpc2fv/zer38r83xKAd6y74iImO31EMf9REA7cpdVBY8NbA5+dFNqsCTQipkitBjAUsLUZNd4qm8AyjDMmJAIRhDzDEBEbJkBVAyJWQJ14AEaciIeSGicOgBeBWNHEeXLkXIM8UvFI4bVBCVJNfdk7STd5xOcp0LZzjIqV/eXq/4i61edM/eaN7yqAqpfzf62Nf5LP5lbko/DbCuxU4saEN1mN2kKTzQbIkuEIEWfVagRDEVkOyXCkVq0aDg2p9YYNAySVerU0WN1R27Jjo6ulMQ1V+ggAOgsjNRNEus/IiUFnYUy2kM23AcrivXh2RiTxjhx5iSmVWEWdpmhQ4qvwSBBrXVPfqDmuVsT7C3aZvKslyZcr9dpxdr81F8ynO/w7DuD1q/8y6kDw2872ticN0deG7wvQHXHmdxGK+1ibQag5ikweliIElNUAEayNYBCSRQRiYzf2rNtx11O/rC5d9dj+1aQyM2Pyz3WGozaNisYNWY7SYtgWA0A5KUVO4qAn3t4+lOzYt+Grh+bDwstHzvjA2tPfd1Z+39FTRhGpi7VBKrE4nyBFDKcNJL5OCerqUEXdVeEQb0mk8lECjR0euxe9cqBUOnoQ6RkXT78hfscAvH71X0Z5kf8Z0dH2CgNf2NkI0d0ZbmtElMtFVEAQ5BFIlkKb00AzFJqCGooQcJjv7t868P3/ubayZvua48ZlJt57xLjjB/cpTssXokK7IQNrbeoZ3pIRJm1aYSUW9cwixglZ7xNU40ppY7mr+sy2ezt7G1s+vP+EGfd/+fS/Ko5pH9/pJK04X6MUDSRapcTXkXJN46QKp1UkqNVqvpxVyLzhOajihh1DpVkmrJ7+uak/bbztAF6/+i8j62p3j20vbgXR+cP3LYU/Djg/KcsdEnIWERcRIk+hzWtEOYSch2U76tk1T6+84Tf/NCdni2tOmbRgy6T26WOiKDBhGFEQhrBhiNAyjDGiQp4DFgI8AChg1BGBXOC9p8QJ0kas3jvEcUxxnLgNpTW9izfdOqGWlve7+OOXrThk6qEHKtKehq9xIlWkvoaYytrwFYqlglgrcZxW+oXSz+ycpOLmnsHypDTIfuTNcuKbAvD2288x22dn7hrVnt/ATBftBE/CH2aCtqkZU6CI2hHZomS4YCPK+5AKHFB2ZNe2Nev/739/e9qY3KRnPzHtQp/LtnfkMhnKZDMa2oDCTIjQhghDC2MCCQITAyYxpmkhAIAZDDA7l4bOSeR9YpLEwfkUjXqMOE0QN2LU4waq9aGBX6/+d7O9sXnu3579jbVTx02dlEilL0FDG1pJG64cJX5IGr6MupY5duU1npIv7sTQ4196ytUDx8+sf+TN6MQ3AyBd8+L8W0a15zYw0d8O3ww4vC7ijlkZU5QctVPE7QhNEVlTRNYUjHcy7tu3fuuVSqXBF8z66962fMeIfDaHfD4nmUyWsrk8BdaYIAh9EFoxzExEysYoAQ5A0ioAEIpIBGZmAM459iKaJo6cT209TnyjWkOSNLRWi1GtV9A3sGPg56uvG1vIZ9N/OO9rM8jS9oavSOwqaEhZYh3khq9K3fdpXWsbvdR3MoYCV/UOVadcOvv2C/AG9IYAfue5j1/U0R5mIhNctxM8yvxLyMVpOduJyLRRnto1MkXK23axlB27sXtT1z//8vqDTt3vk/fMGnX4xGyhiEI2Qi6X1Ww2S7lCIQ3DkCxzQEQKYADANgCbW6UHvwcRaO6fAwCjAewLYAKAcao6UkRIBEniEtRqNVOrVKjeSFCP61oaqurKvqe237P2lnkXn/X/PT9l3OT9Eql2V90QN1wZdRqSuhukhi9T3Q2s9ki+NDzHWppeUqnG/qsH/+b7fzSA33ruI7ODIDh/RCH6KkEZAEINfhia4n4ZO0KzphN5005Z06aRaeOAcjP++4Ff3P/86hWTLjr08i3FfEeurS3LUTanhVwe+XxOwjAw1loLoB/ASgBrAdSAV232Gc0NyJGt70+27mlrzNT6nAEwDcBMACO892kcx1KvN6hUqWu9Xka9XsfgUP/Qjcu+Nf3g6bO7zj7urBNT1F+quxLXfUkaMmDrviQ13+8THdqYqvuLZpfq+qrJNXFDbrp87t0v/cEAXr5iduiTMQvHd2QnKDC9+bC9NUfF9kwwgvNmBGW5Q3O2SFkzAkaCg/71Nz9+2MTZ6rlzLs4Vi0WbyWS5o63N5fM5G0VRaoxpA7ChBVw3ANMq1AKoHUAewCwARwHYvzWctQCeaNUrt4pvgeha17Gtevt47+M4jrVSqZlSqepqjQpVyyX/8xU3VBHF2T//+OeOFbgXaq5fa75ENR3SarzDxDToYz846FTORbPRV7oHG9sm+qEPX3TEM3vc9pm9AfiBP53+T6Pbwo0Cd4aog4p/yXK+lDX5IDIFZDinGS7CckEM+JB//u9/e3Z8NGPTgjl/Maq9s8N2FNtcPpc1bW1tFIZhaIxJATwFYA2AtAVWh4hERBQByIgIE1Gsql8gou8AeAjAfQAeVdUvEtE9reFFIpIloiyATgARgCqALQAGmHmUtTYTRWHDhhaGYE0YYmbHEXZj//rBRc/fXTly5qGHEus2FUceCbxP4DShRJ2mvuIFboyqG5kNcNuWVM965MbNd71pAC99+vADA+MnR6F+TeAg6h1TeE/I2bbAFjVLBbJcpIDzZNke8qNf//yxKblZWz42+9Pj2opFbutop7ZCQdva2hAEQZGZXwGwDEBDRCJV7VTVfVV1BDNPUtXZqnomER2tqi8S0REAzgJwUqvMI6JBAM+p6pdU9f1ElGu1E6lqUVVZVYWI6gA2EFFijJmSiUIPsDbXmGT3b59V6Kv0dd334uLGYTPmHK7Q7lRi65DCawqviXWSrEm1PlvgWMh9KPbut+/77Ohtj/97d98bA6igo7aM+O/Ogp0l8BNFPQhyY2RyE0MqcC7Ia2jyGpksBYj2//WDCx9uk/EDZ8783JhiW5HbigXpaG9HNpvNMXMGwAoR6SWiUKS5KhERS0QqIgmAHcz8sqrOA7AdwCcB9AK4CcBvAdwP4EVV3V9VPwGgC8B4Zv4PIqqoqgPQYObEOadExC1A60RUJaLxURQaZqoRW0NEsm/xgI6u7rV9L295vmvGlKmHQ32vk0QdxfA+oYTq+Vgbi70mR4p6BEaKlTid98S/9f4MV7wBgF/66AEnFbPUz+z/VNTBiywLgxxCFDgwGQqR5wznOeR8+6p1657r6uopfu7wv4mKbW0oFvIoFovIZDIBEXkReUlVG6o6Fs2N/EjvfSczj2Hm/YnoY6r6Ae/9w0T0cVXdSkTfE5FsC8iTAZwI4DAAjxDRj0TkUABTACxS1csAzG39MHlmzqvqGCLKt1xZA0Q0QERtQRBkDZMngrcmNAeMmB08uHpxNsrz2pFtbft4TWInDZtSLE5T8i7uSKRS8XDjBX4fYbnusI2jMkt/tGP9rnjxrl+gICP4Riagrzb1ssKa4CkrYRhwwBFHYGSUOZJKo8oPP/vCoV846opSoZCnQj7HxUJRMplMgGblR5h5wHtfbE1oZAvIHBFtVtX7RKTQ4pSrnHOXAThQRK4BcIaqNkTkRRF5UVUTVf1462/TVPVSEfm2974qIm3MvBhAl6pGAEYAaBcR45zLiUiPiDxKRC6bzZpsNhtGUaj5fIG/dNTltYeeWja3ltbVcGgMZX1IWbUUqDUBbBA+OYxDPuDLSORq6KsN76s48MvzZnwwlzNDgaFzAIBAi0LKtGVtEQHlOaQCQpOHoWDWL+9+ZODCuV99cnTbmM5cIY+2JudZIpronHukxUWemavOuZIxpuG9H8fM8wDMJaJHVfV0ANcDOIyIPg5ghTHm+0S0UETWq2oCoA/AI6r6C2PMgyKyD4BPM/MggJ8COIGIFqnqV1T1YADbVXUjEfUaYxrOOcPMBVXdCmCutbZirQGIlIBwavucl2577NaJM6ftO1nJ9aY+YfEpvDryknamSNdAMQ1AGwxdc/DqDjz9k/7Nw5i96ixBSK/MhTRxJ7oUbracmWAoVGNCtRSCYOxLazfcN7VjdjK+beK4KAqpkMtpJpNRABNVdT2AowHUvffjAYgxZpNz7hUiuk9VT1LVWFX/iojuBfA1IrpfVRcS0Xne+6tUX33+M/zdew8AzxljLvPefxTA3xPRIufcpQA8EYUAFhPRSCKaKSL7EFGgqjtU1RDRZmaeGIbh1sh78s7LxM59R09um7585fqNdtqUMZOMMc4igE0DthSppcYWL80VTNbyX1QCPgNN1fJqDvzi0tnjQviObGia3Ee0JEAml+E8DOUo4pxaE4GUJz3yxJr9/vSIv+8uFAu2kM8jl8vBGNNJRE+q6grn3AZV3QRgi6q2AZjHzHNE5FEAp3vvv8HM8wFQSywvADAPwDgAi0TkPwDcBWDhcFHVh9FcXH9ARE4BMI6ZvyEiHwYwSVW/CeB0IlpERJeo6hwiepmIlnrvVzLzemZex8yDzDwZqlUikGGm6R0H66+evuPYafuNynvFkCCF4xjiBd67otN4C4GmEDAqTuVnR3++beWT/z5YfRUHio8/0dEe7DynJTUvswmmEiwxWcCDwGyee37j4ydNO6ucy+YmZMJQM5kMWWvHqmqPc24eADCzENEGAMvTNH2AiM5Q1W1E9GkR2cLM3yOiS0TkO0R0lao+zMy/8N7PBHAmEZ2C3YiIoKrdqnqjqq5i5j/x3n8bTQt8iapeKyKbjDGfFpEhAGOccw8EQdBhjPmQqk723rP3PrTWvhxF0Xgi6vHeayaTyx075fS7nlvxcPGgg8ZNIjHeSKRMdbEUIEHwEuCOA4DOvB25vSRnAfghMGxEFNRb7ZoM0HFNadFeIjvRgMFkhEDKbEl8Oqq7u3bs+/c9cXQUWo2iCGEYsqrG3vvHAPwEwL2qulZETnXO/Zm1FqoKVf2Bqh6qqr8SkW3e++tU9T4i+ntVnem9vw7ARQA6ReQ5AL9yzl3vnLsewK8APIfmovkiIrpWVWeo6t977x/w3l8nIluI6Dcicqiq/quqgpnJOfdnIvJR59wmEVlCRD9S1QeJKLHWmmw2hyAM9bhpp47q7q4d733aSVBlkBoNQGxgYPdVRZ82N5In9lS7dp42GgA483hMyUY0RXgwXzAjQgUtshp1WhOR5YgDzoiB0U2baqsPLB7z0oxxBxWz2Rxls1lh5gNVdbn3/rwWR68moi5VPZWZt4nIvgBGquoRAH5BRH+OprH4oYh8XlVPQXMvfIOI/BJAFxF1qupxRPRBIjpKVSe3dOtdInKbqj5PRIe3RHayiHydiMYDOIuZfyIin0HTfI4kIgAYa4y5UUQaAI4QkY8ZY5YR0aGq0kcE8k5NNS4t665u6G9r47xDCi8pqabsNbFe9WkoRvU0upYl8GunnqebX7kZQ00O9DipLbKjRfQTPWnXYyBTBxMBBiIML2IVkt20sf6B46d9rJjJ5chaQ0EQRAC2pWm6VlVXq+rZIvIXSZKELcX/Y1U9RlW/AWC8iJyqql9V1aOcc99W1SXMfAmAh1X1qy3O+rKIHCMiGRGptUqude9iIrqWiC4brisiDxHRt1X1KFX9qnPuowDGe++vUNUPishNLQkIiOjPVPVs7/02EVkLYHsYhtYYg0wm1FNmnZPftKF2lFPJisCIkhE1DFiFaNLr1i5R+PntGR5lFMcBLWfCxxbhrgkjgqMAjCKgkrWFX48KZ7RHJm8CziJLOXJpUNu4omAuOfbKOMxkKBOGHIbhHBG576qrrtLHH3/8QmaOdtdd/5tIROLTTjvtyc9//vN3BUGQs9aOA3CyiDxXr9dRrzfo2gf/Ljt1TpyYIMnWtQ4nVW2kNd+bri41fOlMADkQerb1p4/f+WGcaS9X8HOLUQIwCgCUdFGi6ehBt7k+3k4DqQ8cOd2+mQdPnP6xijHB+MAYhGEoqppL03T/J5544iRmpvnz5z+4Zs2a1dOnT5/+8ssvr5o5c+aMWq1WSdM0VdXORYsWHW+tXXbmmWcONV2jQG9v744dO3b0jR07dvSIESNG3HbbbbNFpHPBggWPtMTvVUREWL58ee2VV145bcSIEU+ddNJJ1RY4unLlytXTpk2bEoZh2N/f37dw4cKTrLUdxWLxvnnz5pnf/e53unDhwhPa2tpWnnfeecekabopCIIMEYGIyBjGCfufvmbpltuKY6a4LKkzCh8PpZu913g0oIsAOhOKMQTElyvYPrsY43IRP6uK8wCAYHrUo+gpiXoaG+LR0X5VaNgxNEAHz5pz6PIgMGBmBTCKiJZVKpUjjDEmTdPG/PnzPwSgLCJHoLlY/omqXgLgWSJauHjx4uNPP/30obPPPnsAwGNoLl+O32Xdt/a3v/3txnK5HM6fP/+3aJ2JAAi89zkAUwGcdOqpp+YvvPBCnH322fEJJ5yQA3CH9/5YY8yft0C+SkTmP/roo72NRqPjhhtuODCTyRTPOuusRy+88MJVd9xxx8cWLFiwiog+oqp3ARgVBMEO7xVzJ70/v2jdHbNGqu/16uq98WakmuQgANhsU98MRQwMP7N0iYxhUuybD/n3WzqlAMROROElzfY3NrXHrtTNFHTkMvkiGQNiZhGZ7ZzbPDx5IoKIXK2qZzDzd9F0T/0pEV2qqoeKyN8BwLZt27ap6hmq+l0RmQXgZhH5iohcpaqrwzA0RATn3DXOueta5buqeoWqnqWqT9dqte8DwPbt2zeKyBGq+l1m/giA7wL4map+jYj2S5LEA0AYhp0AvsvMp5577rn3Axi/YcOGxaoKEdkCYBYzqzGEMMgUWILRjXSopzfekFUf5wUKYXYQCoZhykcM08C+DMUMw7Rva8sHqHZCJFD1VtTDaYLuoe3xrLGH/Yu1NiZVtcYAQEVVy7vpmPNU9VHv/RUArgZQ9d5f473/qYj8OwBMmDBhPIBnnXNfAfAj59w5AK4F8DURmcfM1JrY/4jIrSJyq/f+XlV9vmVMPlEoFC4GgM7OznEicmPrB3hJRC4Tkc+IyI+897cFQWBay5lrVfVKVX30lFNOOUZV/aJFiz7YMi79RFQiIgbg2NrazHEHf7+70q1eGiwkROoteQkhOmIYp8DQBGUcYIVwOJMepCCAkBCooCAnUPVwXoU1rrXVoyi7nwgoDO1QyymwzTn34d7e3p8B+NsWFx4AYLP3/l4iuoKIHhaR/yaiLw1z6rp169Z57+cR0bUiAiIaVNU7ReR5Y0xcrVbPbf0ek1U1DwCq2qOqG4jofhHZUi6XAeC7IkIAvqCqIKItaG4LZ4jInxERvPevtK5fY+b7W+0eBGD78uXLx6nqd51z85i5G0Bore1rNJJsxuan1EumFo3w3mtKSupAMASNRJEACBk6ixWphWCaKs1tqegVUIWyiBcPIYhRQlLKhQccNDtW9YEIh0TkiciJyGFtbW29LfCCxx577PtHHHHEhdbabd77bzLzFap6jPf+X5o46Jf333//qWh6kP+P934HMx8F4HQA53rvkc/nl9frdYjIQbsw99SWy6opPvl8BQC6u7u3ENFfq+poVb1IRK4iIvHeX7dy5UpKkuR8Zka9Xv9WNps9n4j2B/DNkSNHnrV9+/ZRIvIhIjpMVZeoqlfVEcyQ6WNmpQ8+nyva9m4IO/XeQ1XFE6UKfYkUhyrTEVDEFkAWO4NuZAuAsPnDKlgFzih8ku0cU5y4NQiCxFrLAPYDUCOizxpjrgAAY4y54YYbvtwS5f1E5B9UdSgIgloURR8BIESEO++8c8qmTZtetNYeHYahdnR0wHv/pIhsrVarvX19fQsA5H71q1/dYq01pVKpkCRJXCqVaGBgwDcaDdfX1zcRwDELFy788JIlS96XJEnBOQcADSIKmfkSIsKwpXfO/bmItBljLlHVa6dNm/bIE088sR+AMUT0WRG5kIgmWWtfIWPcuPZJDJ9r90hIRVTEq5KAlBIIdYH0UCg6FMhZUvDvjSDVnZBhUhUSUijICxHCbDFXZGOMqKoH0KmqQ/l8/ptdXV0/rlar38rn8zs5hJmJmUM0jyPb4/j3h/ze+ylLly6dgr2QaepX3Hnnnefv7ZmdoyUamyTJWABoHvTtmbq6un4xa9asSQCuA7DSWvtSo9E4zHt/dbFYvKLRaKwF0E5EwoBENlKVMOPFkcJDCRBVUlEloLQTLgWz1987FAhImCECJVEh8Z6cdzBk20ITkIg4Y4xX1ZFoHuJM3XfffT/S29uLLVu2oFKp7HQ9/W8ia+2RzHyGqv6TiPzjsccei97e3kxbW9uZACYTURVNb7mIiIYmJIOwLUWqTqQVIqFEDFHV6nC7orDMBB22LOzhWbRC0LJRLalqGYqyQWAJVDPGVJIkqQPYrKq9AGCMmQoAaZpix44d2Lx5M/r7+5Gmbzn4822jVatWvei9/9M0Ted77/9j5syZawAk27ZtswCgqt0AtohIzRhTssZWDdvQkA4RtETaxAOqZSWWnXgR1Kr8/kTbG2ThtaAE9QQSZWIQ2EilFteyhoJCa4lxYMvf9xry3qNUKqFUKiEMQxQKBeRyudcVsXeC0jRFrVZDtVrFzTffnOnp6Tl2/Pjx944ePXrt9OnTzyGirY888sjLCxYsOERExhPRDGvtswACrz4m60pOqIMIBIX4ZqCYAWsZLXumAtid6z8A5DSvlgkKFkcMiBERqHUDiUu8994SkQCoEFF+jyPfhZIkQX9/P/r7+xEEAbLZLKIoQhRFbzugzjnEcYxGo4FGo/EqCejp6Tnv5ptvfk2dH/zgB8sWLFgAVS0CqHjvyTlnq2mFYF3VORnJICKwI2IFI0Qi7TCtLaYCVgnbAdoA6GRhaoPXhipIVJkEUCXP7CrleBAd2RHsvYcxpopmfMreaICZN6LpQWYRmZSmaeeuk7LWIggCWGsRhiGstWBmWGuxqwUFABEZ9ilCROCcQ5qmcM7BOYckSYbd/XuiTczcT80YHHjvZ6MZZ4O+vr5hx+14Va1Qa/M9WB0Asa+SUCcIRuAtg5QEBKDYrEJrwdhiIXhBRQyIJkMxQxQvkELh4RUq4kCJ2VHdOLiOx+YmmTC0trWwnQOgsvtoiegFInKdnZ3rRo0aJT09PTw0NAQAm0VkzvBzw5N/B0mMMU+pqhk7dmxXsVjkzZs35xuNhojICDSPRpPt27c/WSgU5hLRC95722g0aOPgWnbcW5VUBYCSJYBBChgQzWnt2J4BsJyheFkVr7Q6Hc2kZYU6ARSejCjZFN259UOrc6reOucMEfWpqnXOPQIAhULhN8PgMXNl3rx5Y4IgOIuZz46i6KyTTz55JBFVmXnFO4nYrmSMeTKKooEPfvCDs40x8621Z3d2dp566qmnxsxcArC1s7PzkVWrVi1X1QBAv/eeiYg2DK0upOgpiCBQIlIBBOrBOgTCCAAQ0jUQrGS1WF1vUPewLlTlKoQCOARewOqVUgzmtlXWTWuKiqiIVAAgjuOtuy1bgtNOO21ET0/PhO9973sQEXznO99BT0/PxJNPPrkDQAO/97C8k7RBVaO5c+ce19nZmb3yyisxZcoU/NVf/RVWrFjx/kMOOWQ9M3dXKpVRjUYjbKmGinOOnPPYWt04PZGhjHoQCZigAQsFpFwbxqlRpx6k6LI6gK5Kpz8zm20d0JHWQFAYTSUlALDexSNdEB+Y+nQxpZRlppSZ4ZybdPvttz9QqVSOt9Y+SkR+xYoVxx522GF4/PHHceCBB2LZsmWYPn06nnrqqQOZ+REiekZERr+T6BFR37hx47rWr18/NwxDvPLKKygWi3jhhRdw5JFHolarzXvuuee60jSdYFordxFJnHNI0rghiGc4jb3xUDEQEngyYEBrwx7KcuJHZzux1t79KZQ++iv5AHTnCadVBZGQhULh1SsIMfoe7KlsGRqTm5Q1xmkQBJtV9dijjz766f06bwAAEgVJREFUnpUrVy4EgIMPPjh300034bjjjsOaNWtQqVQgIjjqqKOwZMkSzJs3b/Xy5cstgFUA3rZF954cr6eccsrYxx57DJ/85CexcOFCDA0N4cQTT0S1WsWjjz4azp49+4l6vc5Tp049TVU3eu/hVXVbZUN/TH33k8c4DVRIiMFEohCjCIdXLC6VY+44DV+zACCEXiiWgnCkEp1EpKsEqqTEIsTq1Axg+eCy/kczp+QmqDZfuXpRVedNmjRpx9VXX32hiEBEsHTpUtx5551YsGABnHM47LDDcNNNN+GAAw7Al770pc8NPzdsUXe1rsOA7n4dBmjXK3NzgbHrZ2beWQDg7rvvxq233oqLL74YS5YswY4dO/Dkk09i7ty5uOCCCz4bx/FPRGSUiNydph71ap2W9T9eGGgsr4iqZSVVsLJ6Z5lIlU5srfmWAlgHtE7lDjgP5SjgAWb6MBTtoroMgpwoERTwniiJhwq5aPrxB+YOWwuQIaKEmWd573NBEHSoKosIpk+fjltvvRWqitWrV6O7uxvLli3DV77yFRQKhVeBtzcgd/2+exmm3bl3dy4kIowfPx4LFy5EpVLBpk2b0Nvbi+7ublx22WWw1ro4jgsARgJYVq/XUG/Uk2fK95+ypXxfrESGGUIEMhYGTP1ovQOYOr2+kcjvVt+K9c130cp4slyX4nDnBqYbRCAGkTZXUELIVtPeezeUu3rjOEaSJFDVpwEcmKbpLcMTnDhxIm644QYEQQDTPDvBNddcg3322ec1IL1e8d6/qryZOruDffTRR+PrX/866vU6kiTBAQccgOuvvx5hGKI15hki8lTz76lura/fUUt6F4siJIKCiREAakhB6BnGp1ST9lwbngJ2CfE99Zd4cPzIcDqg4xl4wQl64EE+BlyicCnYanHz4RMumviR9vO7C4UC5fN5JqKzVfXlKIomtzzGr5nwGwGwOxe+ngi/ntjuXowxe/s+0Gg0+ohofxG5o1KpoFqv6+LBn496dssPt6dcmWAtlCOCNRDKgJgxEopDoLRl60Cy5p5P4Hhgl/A2NbgmTuUGBeCBOUTokVZAtyiIFJSk5QmJlJKeyvaeer2u9XpdVPVxVZ1Zr9dv25PI7Q7M3sDbEwe+0Q+wt/b21vdwqdVqv1XVaar6eJwkqNdj9JY3bW9IKU5cZRwUDNPcuagBE2G7Kg5RAKnI9SD832HcdgJIARYOVdyknXtjoTpBoaRsTPOMHQy7fMutQy/qQzOr1arW63VNvd+kTc/NfO/9I3vTXXub0N5E9/U+v57Yvp7+VFWkabpYVc8DMJSm6aZyqcSNRk1fxOMHPb/5v+pQtWwgUBCxErGCiOJhXHYMuRkU4r7XAHj3aYhTAaC4rakI9dNkMMSWPBhMSsRKmjRKIyuuZ3Bzfe32crnGlVJJReQ+Vc3HcdyuqgPD4re3ib1ZHfhmVcDuYO4JxNaYetI0HYvmMen91WqVqo1YNqVdW2uutz9NSp3KTNpcxMEYgjEYVNULmvVxiwLVu09D/BoAAcAZXL6j7F9SBVRgiUwPkRJYCQaqrEoMWrrqp4WN2ZfmxXGtWq7UqFwuJyJyP4A5cRw/qKryelywNw7ck+58I336ZvtR1Uaj0XgewMEicl+5XPblcpXqtXJtk33x1KUr/6MAbnKdgQKsDFUVMTtUYFWBvpLvohRX7orZqyJU192K6tSz9Qv5HPcQaCpBZyvjRSiyEFIVkDioiBbL1W3LglGduWJ9LKDExnAtCIJEVU/w3t/MzIfsbiD2dn0jHbkrF+1qSPZkXHY3MMNX59ydaB5ePdNoNLZUqlVfrpSxOvO4earr5xvqvm8iGfggBFNIyiGYQwwQ4xwABqqLhmo+c885eJVf7NUx0gDE4iv9Q/JYc1+MDABvDJQs2DDYhlBmxD2Da6YNxOulW9dsr1TLWiqVtF6vrwawXFU/7Zz7TwB/FCf+MUuW1ylJmqY/F5GzVXVZvV5fWy6XaahU5q26asuA22L7hlbvR4a8NVAYKFsgMBACJZDm7mNHSZ41HpfujtdrovS7bkV58p/oRwpZ8zIIhwM0C0SLoBipCmqNnaHAhq3L7MT9D9mfhjIrrYRt3nu0fG9VAKd673+Npq8t82a5cW9ADdOb4bZdljfbRWSpNt9BeSJJknVDQ0MYHBqiwXRHd9+IriPvffpa4YBCE0I5grCFMRlSGFoF4DMt3ffDUtXLPfPxyzcEEADGnoNH01gWFLNmChQhgTJEOqiKQIQEAiPNU09+Zf3jfZNnH3yY9mVWasoFL16sMWVm3gzgNO/9KiJaq6qTdlfyewNv9+f+QNCGPz8qIgLgaFVdVK83egcGBk25UtWBel9f/4Q1x931yFUbYLWNIxgOoDYgDSJYE6IB8CEEjFKg1D2QdscVfHn9r/EaB+YeAdx8B9z0+Sgz8HxgeR6AMVB6hgzaVMk3Q/2JSQHvJOra+GTXlMPmfEi6o+d87NpTLyTeN5j5ZWae6b3fV0RuIaKZqmr3ZJ33BNzuAO4G0B7vMfOQiNyqzcBN8t7fN1QuN0pDJVQqJe2v9u2oTt9w0l0P/uNz3iQjghA2CMmEGXgOCSYDIqJuAk4AgHrDf7We6u/uPx97zO6x13fl1tyOtfucqRcXM+ZFAHNAmA2iu4gwRkBKos0jAVXy4vKvrHvslWlHHHZk2m1eQKJ5VfXOOauqG4Mg6FXVj4nIalVdpKoHqSrtsrzYed1VXAHsDaQ9caAQ0S0iMoqIPkBEDzWSZHWlXI6HBkvBUKWsQ2nf5uSA7SfeueTqFxPUxtpQAxMSmxBqAhKTBZhoBYALAUCBW3ZU/D6Lz8E1e8NprwACwKQv4nf1fvlUMWsJwEgC5oDpIVJ0EhGrJ6sAICCXuvYVqx8uzXj/YZPSWFbWelyHeA/nPRLvqwxa3XRN4COqugrNKPwx2ozifxVww1y3K4CvA95WAHdQ8xWHDwJY4b1/tlwupwNDVVTKQ9rfP6j19h3dsv+Ow29bdEWvUmO0CWBshowJCTZL3kQAW1pPTb1noPTK9oG0no7Cp9b/7LWi+6YAXP8zuMnn4rFG4kfnQ3MYgIgIU5jxDCmKCigBpE1xZlEfvPDSErffrFkU7BNQpSutxQ1PLo6zSerFi9RV/CvMXFXVQ1R1H1VdhGaIbxnAzgQ5u4vtLsUx8yMA7mPmbQAOJKI2VV2XJMlLtVqtViqVaLBUlUqpn0vloTofOhBVMptzv1h4dd4Yn7cR1GSJwwhiQhIbIjUBthBwJoC8ElzvUHqzKL5+/+l4zQuGu9Kbyplw4m04Ix/xjI68+W6r2gZifdI1dFSaEEtdOW2AJYG6hnqXEMaOnL7ptGO/+L5kjVks2/JjM5nIZKJAoihLmUyIIAjIGANjTEBEHSIyWUQ6RWSdqm5V1YqIpC3RDImoQETjiGgKM5eIaKOIDKpq4r2Hcw6NRgO1egzvUq3V6l5Hxhuys9OPP7T0lke7tj41nQNiG0FtBmojeBMR2yzIRNhKQh9U6L6kkMGq/7t6Ii8uXoDfvRE2bzprx0n/hc93FLiQi8x1zYq0CdAHvcdkV4V3Dupi9b6OgosR+wRGvU3PPuXSHcXcPiMGnvAvcJIZlwsjG2UzMESUzWa16SExZGxLGFS9sVbFK5SUAGBYWYoIMzN5BbnUgSCaph5xXCfvvSZJouVaw1NWejrfL3NK1a07frHwmpFsXcgRvA3hTRahNeRsHmKaXpZtIDoa0P0AoBb7SwZqEt+/AP/6ZnD5g/LGnHwbvtlZCAYzAYbzJwwo4U5xOl0aUB8jcDHUxUSuoQ4pJE0gmbCt9vFTLm4UM2NHDCxNlidDweiQOAyCUDkwFLBBEFhSZrVEqkDzHLEVAiA6PFBFE0pFkjhS9YjjVJ1Lkfg0sZ3SO+rI8NBSo7vvznuuz8S+lDMhwBbWhmRtVr3JgmwAmAhqAlolij+h5svfqMW4ZKiaFu49F1e/WUz+4MxFJ92GS3MR246M+bYSGEAizD8mJ4d6p+oa8L4OcQnUJzA+hhWnqU+gUdA2cPKxnylNHj/rmOrW9N7+F5JGOiQjyXIYcgC2zRejiVXFw5Np5Y3xMGxgxBMJPMSlFHtPUI1NG/eNmhNm8uODUzZse+nB+x78WVs9KXXaDMgYspyBNyG8iQATwIRZwIawYPOCQj4LICSFDNX9V6qJ5O5bgH/8Q/D4o3JnnfhzfC6yvM/IdvPXADpaLd0KoaJPNS+xmjSF1QYkTeEkVfYpGR8j9Q5WRKvjRkztPf5DC3j0iCkn+AQvlDdUu6rbXaPWn5KrCEEErTwXTTKALbDmRgSaGxNk26bmppoQc7p7ux546PE7ZHvfutHGUJ4DOGMRmEi9sSQcwgYR2GTgOCRvDFXVaJUU81sA9PcM+X92Trru+yT+8w/F4o/O3nbyrTiaGF8cUwgOIMZRreZegerDgB6YJiQSw0uqgYsh3sFrjMB5eE1gfAovHka9pjaM+ke2TxiaNnWujBkzOcxnO/KFXKHNBpnRAODSRm+lVh6q1odqPT0bkjXrnuW+oS3tLo1HsKGADIQDsAnhjEFAFgmHsDYCmYBSG4BMRgMQvQTQcYBOBwBVPN5TStd6hxvuPx9L/xgc3lL6u5N+hpGwuHl0u33a2N/nDiTSXxBIRHWCNMilMdQ7DSVF6h1YUxXvyKhD6h0CCKCCVLxa9YASKYlyK/AOIJAyCUFBDGImB4KlEEoMbywCCtQbQ8QhxFiEJqDYWLDJakBEm4g1UKFPDI/Rq16xY9AdZQzOXzgf/X8sBm85AeM5t8P0eXwtItYRbfZToOavCyDxKj81RCPgaKJ3iL1TAw9xCVgdvHcw6uBVm/pNvQIKpwJV2pkKBQCEFKoMYoKFITVGQQxPBsZYeLIwNoQQw3BAjiNEzNioQKzAebQzkJRW9lXcbXEqctx5uOryYUv1R9LblkP1+JsxjS1+MDJn7wkDuhKEHACQQqD4OUgExJPFq/EpqTglcXDqEXoPJYETDwbgROBVAQY7ABCIJQKYYQBYZogaWGMAMkhhEJiQPLMaG5BTlvWUsgXjvJahAxS1RqpfH6i5eYjxhfs/i7clj+rbm8VXQSf/HB8T4LOj2uwzgaF/0GZ2oeHuVqjq48zIQzHee4QiSLUZgwN4kDYdt0Kkqq38BM1XhYnAMMwKGDQ979y0rERIRbENQJWIPgDorF0m2Ei9Xt0/5N4njH+//zzc9XamRH5H0iAffiOC9gLOVeD8kXl7bxjyxYC+OqMv0VaoPsCEukAigNqg1EEEFlWBQKHUFC9SBoOYiEUhRDoIaInBiSgyBDpJoeN2m9qG2Mv1/SV3iir+s1zFbc9chLc97vgdzWR+uYIfugUnC/C3keUlHQXTaQiX7LUCox9en1XwIBENCqTcvM1FVe0gSAcMzYVgxN6a8IrrBit+IHFyrCF850Orcf/ll781Pfd69K7l0j/mJxhtLb4+ot2uDy3t1T30Vihxeml/2U1WxpVLPol3PA088O7/MwI6/ib819j2YDOb154vvBVSxfXdA+nEBz6Ns4G3T8e9Eb3mUOkdJsW++NT2UjpHVO/V5vrvrRfVh7f3pTNLdZyLdxE84N0HEEtOgMsRzukdcBUV2vRWwYOnbTuG3HZXw4J3wki8Eb2uQ/WdojW/RLz/n+CluKaZTMhzm4eJwB9aFHADFf1X7+X6h/4MG9+LubzrHDhM934KLyhoaSPB3/yx3Nco42+811UPfBbvWvD67vSu/0eb3enEn/K17RkeNExXvPHTvyfxeuVQQ0be9zn50hs//c7Re8aBw3T/Z+TScl3niuBm9cCbLLeXGjr3mA3yl+/1+N9zAEHQ6oA/rxLLBPF49o1Fl54vxVJ08Ge/kwvkN0vvPYAAHv8K6ur8BbVEnlNF6XUArNQS/ziJv2jJ5/Cm07W/k/SeWOE9UddvUJ5+pimpYhODTtyT1Y29fsOrv2fxhXj+vR3t7+l/BQcO0z2fc0ucEyeil+7OfV7xFYXI4gvx4Hs9zl3pPbfCeyA67cfmFiaziVX/BgCUcL1XGf27z/vz8S7vNN6I3t23oN8caW0//+lcF/0PC+4VIBJgZm2aPw3/y8AD/peJ8DAtOQEuZLfAQ0sK7Q0rbv6SE/Yen/L/017ojH8LZ5/xb+Hs93ocr0f/D6s769KBP+5xAAAAAElFTkSuQmCC", "public": true @@ -40,7 +40,7 @@ "type": "IMAGE", "subType": "IMAGE", "fileName": "map_marker_image_1.png", - "publicResourceKey": "TwKYnwJfaCIgDbJsetgcj3q7AYK4HUSA", + "publicResourceKey": "FcgQbKh1RSWJYo4JPQV3aCBurt1NB9tu", "mediaType": "image/png", "data": "iVBORw0KGgoAAAANSUhEUgAAAFAAAABdCAYAAAAyj+FzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAH3gAAB94BHQKrYQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic3bx5uF1Flff/WVV77zPeIQMJIYQxYRRBpBGcQFEEbVQQUXB6xW5tWx9+Cm07IYIitiJog2P7qu3UCN22aDs0KIIyg0CYyUhCyHiTmzucce+qtd4/zrkhQIIogz6/9Tz1nHP22buG715D1VpVS/gLktnZjg3P2wGz3RC/N9hBCHtjMgOxGjDZv3UAkyZim4EHQO4i2j3UkxXUb9kkcrb+pcYgz3aDNvLjOah7BSZvRnwLX/8D2axILM0Dtx9ODgGGt/P4GNgtqD6A764i3+iJ44dC9CD/jaRXyqzXrHs2x/OsAGhrL9sB8W/B7ARKwz/H7TAE2btAZj89DbAW6X4HHRknnzgW7KdU5fsyeMKmp6X+J6BnDEAzhLWXHYz4c3GlG8l2HgL3frDsmWqzR5KDfpnOykkIL8D4OHPecIcI9oy09kxUaut//CKCfp7qtMuwwb8DnrOd5nOcXIfJCryAOgEpASUwh5HiBMwKEAW6YF1chIghthtqL97uSxG5C8a/TXvsBLx9VGa/8Yane6xPK4C2/kd7EuWblIZ+itU+BMx93E1OFmLchqQpTmaDA0kAlyDSwSwizkAFfN84RAfOQASLCVACDVgAESOGDZjmSDwEtYO20bVVaPMCionjiPoe2eXNy56uMT8tAJp9I2X10GfxyQTJ4GtADn3UDd6NYv5niKtgyQxcYjinkCSYi3gHikeSLhDwXjHzTNlWB4hEYnRAgoUSmIIqxAQsYEFQA/JRNHZw+lqiTn90R/VGYuNKQl5j/cTH5JD3FE917E8ZQFv23b0oJf9GNnQd8PFH1y7rkORKLJ2BTxJcAqQOSQyXGEiC+IB4wZxHXMABeIeZQ3q/MBQRhdiDNGqCaMSiYTFBKIiF64FYKBbAigKLD6PFsYjt+uhOcwHdiUMRe5fMe8uSpzL+pwSgrfje+/CyK1n1dcBeW/7wMoZll4Kfhy95SARXMsSDSxxkIInifIK4gHpH6kAlggjOOwTBJEFV8FIQDcQCGIh6ighOFdMELQKYYF1Bg0KgB2ZuxG4kFqvw+cmoDG7V/cXk7Z9iulx2efvX/1wM/iwA7bLLPIe1v4m4RSTJuWDJI/+675OUB3ClCmSC9yA1cKn1dF3mSFLDRJEsAYk9wJxSTEzQGW3TXlEQC6G7sde/0gzDZ0Zlt5Ty9Arp4GBfnBWCgxghOkIhkBsxGK4QYgs0gHZAQxPtNLH41q2GH4jhNFRfwrzK20ROis84gLbkohLp4C/IuAXso1v+UFmLlK4gLc/Bl4AMfEWQDHzZIAFJhKQMLgWzhMayjTz0kyYbrt2T2Nq3a5WFE3HWqgYzNxeU81ao5ADVpJ2ldLI6G6YN+o3zStI+CF+9n1kvWcYub6hR330mIgEtIHSAYFgOVkDREegYmoO2QfOHCJ3X4thqDirn0rUX0Kr9rex/Uv6MAWhLLiqRDPwPafdBxN79SC3yS1y9S5JVoQpSEnylB5wrgSvTAzCt0Vqzmfs/32By8cs2xZ2vXGJHjo/KnlVz1aEsLZsXKVQkpIlXTHo6T8wFjU5iTELULFpEQmN8Gsub87lq2gy/5pUM7ftb9jtjgNJO07DQQHPBerMeYqsnztaGmBvabhNbVYivemRwfJWitADktbL7OztPO4C25KISvnolWWMN8OZHasj+L5Luih9QfAWSmvVEtwquAi4DyWay6aYHuO/8A1phYOkd9sbVTbdgVlYqJVk5wycpWZaRJY7E+2i44L2Y0Zv8CkgMCMQ0xOCKGAndnCJG8m5ueacba7pkw/Pksp2rvrkH+3/kXqY9fx+cbiA0HbEDdIzYBmvRE+12l7y1GRffsRUj/JBubR6xdbQsOK37tAFol13mOXjzzymNrQL+/pGnS1/FZXvg64IfAKkqSTnBVRRXEaQ8g7HFK7jnU/NHde7tC5N3RpKB4WqpKuVq2cpZSpJlkvrMSqVEvE8ty9JClSJJJIr44BwWY0xjNC9iWZ4HH2OUdrsjzpm1Wi3X6RTW7XZpdNviwsToQfodP92tPpj9z1nG8Pxd0M4mYtugEygaGdpWQgO05aCxFI3/+Mhg5WsUQ3vx0Npj5GVnh6cHwCVfv4x0ZAViH9py0WcXIpUDcPVIOiBIFZIauKrgKg6z2Sw8c0m72XK3uA+MuMq06dVqzSqlTGq1uqVp6iuVsqRpGtIsM++ceO8NEQQi0AGmuKAElAEfYxTAQowUeZBu0U3zTkc7nVzzvGOtVpdGqyHa3rTpUPvSjpVyreDgc/dGZB3aVrRlFE3QlmBtoxhXtPUQlr/nEVTceRQzd5c9/+GUpwygLf7Ke0jHykjrS1suavpV0vqeuDqkdUEGlKTsSQYUqcxmcvky7v38c+6yt/xqvHTwvEp90OqVTKrVitVqNcrVasiyTBLnUhEBGAXW9ssqYD2QA1MT3bQP4g7APGBOv0w3M4kxxm5RxG67nUxOTkq7ndPO29YYb9pQ55a1z3U/Opb9P3wXA7vtgbbXow1HbBk6aYSGoA2hmFgK+ggnxoEPYENR5r/3y382gHb/xQeQFm/Gr/0ImOuD9zXS6p4kg4ofBKkJyYD1gCzty9IfXlVsumuXm7NPrC6Xh6sDA1XJyhWmDQ1ampa0XM689z4FNgP3A0uBdr8vSk/veXpceFj/9y39/6ccAq7/vQLsCewHDMUYQ7fbtWaz6VqtXJutCWt3OtJubBo7rPjMXsmMA5cz/60vJ3TuJzQEm1TiREJsRsKEElqrcEWfE0UJcz4P2Q9kwfvv/ZMBtD98I6XW+QXZkj0Q9uzdLZcgA8OkdYdMF5KakgwK1AWfPof7Lr52vJk27qufVq0PDKTltOoGBqo2OFi3crmszrkB4CHgAXpc5vpgSf/7MFAD9gVe0AcHYAlwK3Af0AQm+gDrVmV2H8i5ZtZpt9s2MdGwRqNJq9WUVqsZ9m1f1BqqxAr7v++laH43sWHEhhAaRhjzMBkJExNgJ/XhWUK+YDVx9FWy/9nbnN4k27oIQK39FUqLFmLhlX1beB+U67jMIRlIGiF1kApen8PCzy0cYZ91Dw2/dadp9ZqrVWtFrVZLa7UyWZaVRGQc+D2wiR73zARKqhqccwqIquKcmzSz14rImUC135uWmZ0nIrf0fw+oqjjnhJ54d4AGPU6dJSL7VqvVoSRJOlmWSKmU+KxSssVjH6zOa/5g8453nn8bzz3tEHzpDrTrECeQKnjBJSmhdQ+izwEWkNx/Oez9ReB924Jpmxxo91+wl0rjjc4vO7d3wQKu8kP84CyyIUHqgh82/ICQVA7k3m9evybstXb98FtnD9Xrrj5Qp5RlWq/Xnfd+AFgILANSVfVAHZjVf4E1EZlhZs8VEWdm3xCRD/QBfqSjIhtU9SIR+XszUxG5T0Q2quokPV25EZjsv4wA7AI838wajUaDZrujk+NjyWSzyZzJ/3pwTnrffPb5u79B2wsJEylxUonjkTAhxPENaOcURBIADfM/6bT+H7L/6Uv/KIBmiN1z/tWS3TqESM81JMlXSQb3QIYgGwQ/CH5QoDaflT+7cXNjYGLl4Lt3qA/W3WC9rtVqxdfr9QxIVPVeYKNzzoCOqjpVLSdJUg0huCRJusC4qp5FT7wPAlYCP1XVkf5zs4DXAbsCtwN7OOfO7nNiRk+EWzHGwntvzrlSn0N3APYzszjZbE50Wu1sfGKcZqNtu05+ffO0aitlj+NeSphcTGyATkCcgDhmhInlWJziutst/E1D9v2nIx/rmH08gPd+7ijSpc/BRnpW1+RWXGUd6WCGH+5xXjJo+MFhRpcu6q6+a2jJzHO65UpdBgeqbmBgwMrlciYiqqqLnXOFqk7vc4WPMRpQTZJkB1U9FEhF5Fwzu9DMbnfO/VBEXqyqrwGmHKW5c+4XZnatqr5dRA7y3p+uqh8DVFVvE5H1QAtwIhLo6dSNzrkKsEeMMQ0h5GNjY9rpFH5iYizutemTrjTvuS0G91iANcYJE544HoljQpyMxOZcsAN7SM3+AHHPhbLvP/9ua7zc47jP7OPEhz+KdcA64MIdSJKBBxFFpPcGigmx1Tc/f9G0syZrtZofHKi5en3AyuVy0gfveufc5qIo6mZWAWaZ2Rzvfdl7/3AI4SozGzCztqqeG0L4ELCPql4QYzzezFRVH1DVB8xMY4zHq+qFwHwzOyOEcF6Msamqg865K2KMK0XE05vaTDOzUgihqqojqnqD9z5kWZYMDAwmpVJGvT4gS6af1baHbzyY0FQwD67nzBVngMNz4xYcbOXHLMRzzEy2CyB3nfdicQ/8FOvMRrsQu78A2xWJYCaY6zGwxf1Y+quwbNrp19QHB6u1Wo16vUalUk5EZGdVXaiqQ3meO+/9BhFZrqqr6OnAE83sH/ttV4HvAB3n3Plmttg5d6Zz7sNm9i0zu69fvqWqH3bOnWlmy83sAhFpiMh/0JtgO+/9e1X1TTHGATNbb2YPOefGVbUEDKvqQmBOqZS5wcE6lXpdqgODlWXTP/Iblv48RePeOOt5wrUvpZrvDt3/7WMxS9zi/+aezx2+NWSPssImeqbY4j23XJDSeszvBAJODFHBWcrIA1c1sr1zN7DHjqVKhUqlQqlUcsA8M3tQVV8MNEVkjqoGEVkLPKCqVwEvA3Iz+yDwG+BMEfmtmV0hIifHGM81e3T8Z+p3jBHgTu/9h4qiOM4591ERuTKEcIb0JCMTkSuAGWa2v3Nujpk5MxsFEhFZ7ZybWyqVHq6pOrQaJuPOcxvNve6sjy2+l+Gdd8EkIAJ9LFHWYFMLokXvN9vzQWCLE2ILgPbA53bS7qrfi3WP7l+6GvxcJPRG4Kwn5LE1l/FVu63e4fM31CsV6pWKZVlm3vshVb0S2BBjTAG890PAAar6ahE5EjjPzOohhLO89xeaWQ6caWZnAS/vA3Wlqv5eRFqPAbEmIkeIyCtCCAeKSEdEPqaqfw/MVdXTReRCEXHAe4B6COEqEbk7y7LNIQRCCEWaprO894eWsmyTxuhDCG7NzH8s77X+jBcyML2AsBIxQ0xwKgTdCcl/h9kRwAJh/fds4blz5aAzVz+aA7vtE527fi7WXz87/wCwO+ZAVIgFuODZuOqmkfrrJirV+k5ZkkiWZWRZtqOZbYgxHglUnXOJiKwMIdyRZdnVIYTXmdl6EXm7qq52zn1BRD6gqv8CnAtc65z7sarua2avF5GjeQyJCH3R/IaZLXLOvSHGeB498f+AmV2oqqu89/8nxjgmIrPSNL0qhDAjhPBiM9sdiCGEpvd+JE3TOTHGDZVyOSpUNtVe/fMZI9cNMn32LliMmBnmIs4ghnshHtHrybU7Iq9/A3DRFgDNEL1l/U6uUhzRN8wbUD+vF8PAepecoHEandburbkvv7mSpZTLFSuXywnQCSFc1xezIefcc83sOOCQoig+18fgy2Z2gZl92cyON7MvAb9wzl2vqqfHGKfW2rmqLnTOPaiqK1XVJUkyD9iD3grlPX0wNwIfU9WXmNmXzGyVmf1cRN7rnDtdVS+MMTpVfTcwA/gVsFBExsyscM4dVy6XnRmxiGqTw6/cYcbqKw9nmo6CbEREURFIDPW7IGETyAwIL9PO+oZZ7516gLOP/budTZfuIXp3FT89Q9yvcaUhpNTzKLuy4TJlcmLZWPqi+2P9wIFqtUq5XDLv/d5mdqeqntLXQaNm9gBwrIisMbN5fZ10sHPuJ8C7gbu9919X1XeZ2dH0ph8Xq+p/A8tFZJr1RObFIvICM9vVzB4Efq6ql5rZXSLyfOBvRWRXVf2EiMxxzh3vnPt2jPEdgJjZcF+kZznnvuGcK6vqwar6ehG5XUSeZ6YbBcOMxIrx20v5is2UXA0NPYesRgfqIf4Bs5l0H/yDWHUZq45eec63/jDpALTg1c7dNouox9Nafh1KgKRnrhWPakKINZrtlzSnv7ZWq5UlTb1kWVYG1hZFsczMFpnZ61X1VBFJrfeKvm1mL+nruLkhhKPN7MNmdngI4Twzu8Y59wHgWjP7sIhcaGbvV9WXqGpZVVv9UlXVl6rqaX0996GpZ/v68jwze4GZfTiE8BpgjpmdZWYvCSF818wwszSE8E4zO9HMVqnqMhFZm2VZ4n0qWVay1ow31Gg0DydaBTMH3qHiEGeoy2kuv4YY3wy3zUL1WOjLq133ritxP3w+2HSgQTrwc8p7DeBqDl/ueVxi1o7Nimza5VNFqVqlkmWSpulBqvrrT3/603bTTTed6pwrPVZ3/TWRqnZf/epX3/ye97znZzHGwVKpNEtEXhVCuL3b7dLpdGzmio9XZKBb4PIKoQXSNEKrS3dJh2LytfRiFqMWTvmDe+m3X5WYne30+kUbnFkvCC38L0U+HdZ2KO9uSMxw0Wh3xyanvXHCe79TImLee8ys1O125998881HOec46aSTfrd06dIlCxYsWHDvvfcu2n///fdutVqNPM8LYNqvf/3rI733d5xwwgnjU4MaGRnZuHHjxk2zZ8/eYfr06dMvvfTS/VV12pve9KbrZWrSvhWJCHfeeWdz8eLFr5kxY8YtL3/5y1t9cOyBBx5YMn/+/N2yLMtGR0c3XXHFFUclSTI8MDBw1THHHON/+ctf2hVXXPGyer1+/ymnnHJkURSrsiwrpWlqeZ6Lc07Gh49bOty4ZJB6UcXFnh+gvcbQfAbGlcDrwaabdcfMznYJt66Yha2+A3hLr4tuBGQIbWe0V7Yp7xlJY5mQPjfUD16YJok453DOzVTVmycnJ1/kvXdFUXROOumkF8cYJ0XkkBNPPPEgM/secBpwu4hc8etf//rI4447bvyEE07YDNwAvA04cqt535L/+Z//WTk5OZmedNJJP6PnsgJIY4xVYHfgqGOPPbZ26qmn8sY3vjE/4ogjqsB/xhhf6r1/N4D3/lxVPfG6667b0O12hy+++OJ9yuXywAknnHD9qaeeuujHP/7x604++eQlIvJKVf2ZiMzy3m/IshJh2iE1Ri/dn8I2o/lGOg8NQLeKCuDWYr04l0tX38rNuoOjU+zpdHHSW2EAKiWE0PO2FTW6K0qE8fWmyXBWqdfTNLM0TUVV91fVDVtzhqp+xjl3HHC+mVXN7FQz+5CZHaSqHwVYt27dGjN7jZmdr6r7hBC+r6qnq+q5ZrYsy7LEOSchhAtCCF/ql/PN7BwzO8HM/tBut7/Sr2uVqh5iZuc7514FnA98N8Z4ppnNz/M8AmRZNg04X0SOPeWUU64WkTkrVqy4sq8bVwP7eO/FewdpdQjxMwgTG2g9tAMxr6BqGBGTdAtO4QFH7ua7qLoXrjHvEQBtGmopph6LghZCc023M/yCr5mZgk2tCBpm1th61aCqJ5vZ9WZ2DvAZoGlm58cY/11V/y/ATjvtNBe4S1VPB74FvBG4EDhTVY9xzomqmpn9j6r+SFV/FGP8jZnd1Tcmx1er1dMAhoaGZqvqN/ov4D5V/ZCqvkNVvxljvDTLMm9mOOcuNLNPm9mNr3zlK1+oqvHKK698oZmpmY2LyIRzzkTEvEinW33ORbTXdqEbUQUjwUiINn0LTtaYi9meiWg8ACkO6GPQQaRCtBwfCyIlEEOHO6jf1bmkSJIkAENmtjaE8KrR0dHvAh/pc+FewMMxxt+IyDkicq2q/peIvK//tlm+fPnyGOMxwIV9Sz1mZpc75xYCRbPZfDO9KcjuZlYDMLMNZrZSRH6rqqsnJiYAzldVAd7br2d1COHMNE33VtW/FxFijIv6n2c6537rnFNVPRxYd9ddd80xs/NDCMc659aratk5twFI1dUXEJM2lnchRlwUMI+TKpCjZEjYT0PsJBJ1fxwHA2ByD6KG8wENAWcZBYKUM4b2ayeJK8eolqZJbma5qh5YrVbX98FLb7jhhi8fcsghpyZJsjbG+Enn3Dn9acyXVRURef+ee+65B70A0b/EGDc5514A/G0I4c0A1Wr1rna7japuvadwd9VHtkEPDAxM9EV4tYicEULY0Xv/9yJyboyRGOMX77vvPpfn+SnOObrd7qdKpdJbzGxP4JMzZsw4ft26dTNijEc65w4ErnbOdfO8GEqSxJLpB25ktFzDaUSDoNERDEQdjvsxDgQ7VFRDglgZo95TZPogJlUsRpCIRkEAqQ672ryuiQQRSVV1DzObEJH/k2XZJ/uK21988cXv74vyHqr6MTMbT9O0VSqVjqHn9OTyyy/fddWqVfckSXJ4lmU2PDxMjPFmVV3TbDZHNm3a9Gag+pOf/OSHSZL4iYmJep7n3YmJCdm8eXPsdDph06ZNOwMvufLKK1/5+9///tA8z2tFUQB0RCTz3n8QwLmesynP83enaTrovf+AmV04f/7862666aY9RWSWiLwjxniqiOyRJH5pURQastkxteowRW6g1tsVZgZiRHsIOBBjEI2VBNXe3kUAtSaQoma4QsEJFoQ0rbq0hjmX9yTKhoHRWq32yRUrVnyn2Wx+qlqt0g9R4pyT/pywBAx1u48E+WOMu91yyy27sR1Kkt7y/PLLL3/L9u6ZIhGZ3e12Z2/93LZo2bJl//2c5zxnLvAl4L4sy+7tdDoHxxg/MzAw8KlOp7PUzIaT3to+NwYMyUpYXiBqRAOHEdWDTiJTLkHFoYVsUYxCCwgQlRiFEAQtDEmHVBLnvS9UNdCLV7SA3efOnfuqkZER1qxZQ6PR2OJ6+muiJEkOFZHXmdmFIvK5I488UkZGRkoDAwOvA3YVkWY/LlOYmSVpDciGevtrQi/Or7FvPGg/YnCDc72NnvRKdEqkidDEaKK2DmMN4hLvk2az2eyISINe7GIDgPd+N4CiKNi4cSMPP/wwo6Oj9EXqr4IWLVp0D/B3RVG8Kc/z7+y9995Lgc7atWt9/5YNwKoYY8PMmnnezXGuRKBFpAk0UBqYTqDoFrxMSTDskTCJ1VCGCQgmZg4vZoZnMoa8UqkMls0siTHuY2YPbauzMUYmJiaYmJggyzJqtRq1Wu0JReyZoKIoaLVaNJtNfvCDH5RGRkZeOmfOnN/ssMMOyxcsWPBGEVl37bXXPnDyySc/L8Y4R0T2EZGFIhLE2ySiE5jMwBCi9QL+Ig7byotvWALxkXi/yRCQRMU5jFhYCXGaxDgeuk2DctL39TXpBcCfkPI8J89zNm/eTJqmU55rSqXS0w5oCIH+epZOp/MoCdiwYcPJ3//+9x/3zNe//vWFJ598MmY2ADRU1RVF4V0+TmahEYxpBBVBAog6ByJWe4ThIglqG3CyCmwepkNmKIZEwUV1DlTF8gZx3GBGGmN0fZ3x+B34j9Bm59xD9BjdqequRVEMbz2oJElI05QkSciyjCRJcM6RJAkissWCAqgqU/NIVSWEQFEU9L3M5Hk+NbnfFq1yzo1OratjjPvTC8azadOmV/bvmWNmDeecxBgTjZMSi9CIQYcR5zykeFUiINQQAZEHzeLaxDTeLUYKzEPcAWLcY6ZmipppjMFI8tEGzeXW9TtnWZYCrFfVA+jtBngUOefuEhEdHh5ePnPmTN2wYYMbHx8HWNV/BmDL4J9BUu/9LWaWzJ49e/nAwIB7+OGHa51OR60XtdsdyNevX39zrVY72Dl3dwghiTGaH1+UabGpFQszEg3OgSgizjnE9u8bkdscdqczC/ejyeL+Mm6WYQ3MopmoBefMJOk21laTxuKKmbrY83aPmlkSQrgeoF6v/wxARO4WkearXvWqHdI0PcE5d2KpVDrhqKOOmiEiLefcfc8kYluT9/7mUqk0/qIXvWh/7/1JaZqeOG3atGOPPfbYrohMAmumTZt23ZIlSxaaWaqqm83MVFVKnQdrne66ajRJXHSiEcQkGjaO0lvOkSxB7QHnE1lCHFyLWc+3H2l5xKtapqYuRpNue6ySFSsXhKCxv05tAHS73dWPEZ3k2GOPnT4yMjL3C1/4AqVSic9+9rOMjIzsfNRRRw3S24X1bJysXGlmpec973kvnT59euUTn/gE8+bN4/TTT+fee+89/MADD1zhnFvfaDRmttvtrK8eGj3VECzLVy0IjfGKRiOqOtRSU0vFaE3hRJi2nsKWJ2xuL9fa7Nc7t7HXtLNGjOwgaoWpYEoSY3emaPeAPG//CkoVEcmdc4QQ5l166aVXNxqNI5MkuQ6we++99yWHHXYYt912G7vvvjs33XQTCxYs4NZbb91XRK53zt1mZjOfcPhPkURk0+zZsx9cuXLlwcPDw6xcuZKhoSHuuusuDj30UFqt1jELFy5cVhTFXO990teteVEUxJi3he4+MXYjgDnUjAg4orWm9nJo2GGmm2bLEnnrzRP6k8MPe8QS41EwkwTFFIsaRIr25qt8vmY8uHlV770lSbIaOOKFL3zh/y5evPhKgOc+97nV733vexx++OEsXbqUiYkJ5s2bx2GHHcbvfvc7jj322MV33nnnI6HUp2nSLfL4PVJHH3307BtuuIHjjz+eK664gvHxcV7xilcwOTnJ9ddfnx1wwAE3NZtNt+uuu74GeKgoCosxGq1VY3lr9CoN7CjO1KI4ExEUxZNN4SSUXiwvu+YT/bCbbRLldoSDMTnSO1seogU1cTEXb2YyvvaOscHB31dG0zdrjFG893cDx+yyyy4bP/OZz5yqqqgqt9xyCz/72c94xzveQQiBgw46iO9973ssWLCA973vfe+cum/Kom5tXacAfeznFEBbfzrnEJFHfe87erdY8F/96lf86Ec/4rTTTuOaa66h0WhwzTXXcPDBB/O2t73tnd1u99uqOlNVfwVYu92VOe3rapMjCyei2lwfxXDOnMTgRQSTl4OB8QczVkJvcyPnvGnuJDI+ihWvAKaJ2kIzqmoiqhBUpNMZr8/ace8j1snzljrnvHMud87tF2Ospmk6bGZOVVmwYAGXXHIJeZ6zdOlS1q9fzx133MEZZ5xBrVZ7FHjbA3Lr348t2+Pex3KhiDBnzhyuuOIKGo0Gq1atYs2aNaxfv54PV+O33AAAECZJREFUfehDJEkSut1uDZihqre3Wm2X5+3unPy3x2548Kpu6sV7QROPpIL3XkYxDu8teev/Kjrjl+dctnpF71W1uFnjzvVHnIV+rSBCRDDBDDWl3GmM/LrUWTbS6XQkxqiqehuwT7fb/Y+pAe68885cfPHFpGmKc45SqcQFF1zAnDlzHgfSE5W+W2pLeTLPPBbsww47jLPOOot2u02e5+y1115cdNFFZFlGv8/7qOqt3W5Xut3cyvmDGzutkSs0UlLFMHEoiIlhbJjCR8PcIWLj1p4o90kvPegaSe/bC2wOcLsZY50CaRdYNzfJI6gbWL3LIe+euyh9+4ZSqSKDg3UnIieq6n3lcnm3vsf4cQP+YwA8lgufSISfSGwfW7z32/s93ul0NojIfFX9z0ajRbPZtP35wZyVt/zbKomTcyspVkqFSoaWUkSEGcCBmKy2uN9id9LCl8NWu7NU9UKsdHEf5YMFNgjgpLcBFpCiPbkTYbKgvXp9nnes3W6rmd0I7Nduty/dlsg9FpjtgbctDvxjL2B79W2v7anS6XQuN7MFwPV5nlur1RLXfXi9FRPtojM5B3D9bXwighNYh3Fgb65culgtfmEKty0A+qHWFZrvNG/LPEddI3WGiLkE8JiKt/TB2340viD9/X7tdtva7bZ18/xhM5swszfGGK/bnu7a3oC2J7pP9P2JxPaJ9KeZEUL4dYzxFBEZy/N89cTEpO902rpP6boDlt98adNjPsE0wSQRIxEDle4ULhp3nu/Xt696HIDy6qVd1Asql/ZMdXy7F5ssiUURvDcRr1i3NT5DWxsmKvnitZONhms1m1oUxdVmVu92u0NmNj4lftsb2JPVgU9WBTwWzG2B2O/ThjzPZ5tZmuf5NZONhmu22jpkS9fE9sho0R4fRhDvRRMxSxLDY2OIvq0nmXIJKm05bWn3cQACOHFna5hzX1+MM8ytS5yId+AEBMErsui671b3Gbjn2G6n02w0GrRarY6ZXQUc0Ol0rrZetOsJOWFbYG5Ld/4xffpk2zGzTp7nd5rZc4HfdDqdvNloWrc12dqrfs+rF1373YokvU2ECeC9+FTEsGQjPbcfxLlLXJJ/+lGYPcr0n3LPerS8D/DbHhfaWxOxsVKCeYdkHhMvRAvDS2/58Y0H1X9fGZ9o0Gw26Xa7m+htAH99nuc/2NoIbP35ZET6sRZ4ayCfTB3barvb7f48xvhK4A+NRmt0YmLSJpsNe/70m+tLbrrsRrUwLe0fB08F6Z2rtzGI7+jpPq6MoTRfTlo6sl0AATq+/U+az76h/1AVlZg6o5xg3uNSj6WefHz9kj1orrCdSsvWTUxMyOjoZtrt9lLgTjN7ewjhB8CfxYl/zpTlCUpeFMUlZnYicHur1Vo+OTnpGo0GOyVLVkvzIR1bt3yPzBNTJ5Y5LHGQelPE5T1JBHTObb7onvFYvB4HYO3kVWuIpRSTb/aXLSd6WJQ6c5nHSg4p9xqS+67+9tCe9QcPk+66NZOTEzI6Okqz1VpsZjer6luLovjZ1jrxyXLlE80Dnwy3TX0C62KMv1PVk4GbGo32srGxMRkd22x0143sOfTQYXf+5t/qpQQyJ5qmWOKQLDHxxiLM3tTXfV/TIknlnSselxXpcQACuEp+Tsx3HERp9Pz/7kWZp1tySEkg8bjUiROscsvln1v7kl2Wvrzb2LhhbGxSxsfGtNlsPqSqV5jZ60IIK1X1uq3r/1PB/BNBm6Lr8zzfqKqvUNX/nZxsPjw2ttmNj08Smps2vnju4iNv+cnn1qVi1VRESg5KhpQTXObIUXdEP/bRiPnsHZzaJ7aJ1bYuykkPt73xbbR+Zu8N2P6ibqycQrmMlZ1ZKYVyIi6VOHzzjz9/99ELlh8dWhvXjI6OsmnTJhsbG5sIIfwXsIOqHhRj/Da9I1mPom0BsD0An+iZrWg8hPDdEMLzgel5nv98fHx8cnRs1MbHN2unuWHkmL0ffMXNl3/+LtEwVErFlRJcmpiVykgpRcXcJrB9eqJbP9OL/5q8c8U2T7E/4WnN8J2df+iTtQKc3If7KyGyf7ONNbqUOoXQbFtoBaJKZc3hJ33igF89sOPVOcNzhoYGQpqWy/V6RUul8qCZHqGqS4CFMcZTVHvO2a0t67asLvC41cTUiuIxn+qc+w/ghc65nUTkd3mej7dardhqtbLJZlMzHXv41c/Z8MqbL/nMIrHG9EqCr1TEVVOjVibUM0g89wFTx14viWHHkLxz9du3h5Hf3h8A57x++i9jrLzV+ZZgzACe6+B3HqYhaIw4J+JMkbwIQyvuvHryZS9//i6tXB9YuSFMC0Xs5W2KoeVElgCY2dFmttjMfk7v8M1g//qWds1sm56XKRAfc20N8J/0gvgvBO4piuKOZrMZx8YmmZycYPPmUXYb3LT+pXtu+ptrf/iJEbHujEpKUstEaplRKxNrKaTCCoR3AB6RJTGfPekle/s5Px3bbuzhjx64bn9tx92yrPigS8b+AcgQNqP8ohuZ28zxrQ7SynHNDtIulE5wxaEn/PP6WJnDT24faJfSSlKtlsvV6oCVKxmlXgCpDOypqrur6m/NbF2Mcb6qvlh7Z+keJbZbr3+dc8E5d4OILPXe7ygiR3rvV5jZgzHGVp7ntNtt2p3cmq2m73ZbjeOfN1nz3TXx1h+fv2PJaVYtOStnSK2C1lJirUxeSlmPcQwwo7e9b+gifPlf5e1rthm+fdIAAoRvzXytSHcv51rn959aCdzcDcxsdPCtHNfuIO0ca+UaWwEGZu+16pDj3vc3Ny1Nfr1oXW1WuVz2pSyxUqki5XK2dSQuU9VhM9vFzKap6gozW2NmDVUt+qcvMxGpi8iOwK5JkkyIyEOqOm5mRVC1kOd0Ojndbod2u0On0427zypWHLlv53X3XfWD69ctv3V+NcPXMmeVBKplQqWEq5eQUsZalBfSOw2PhuqHTct3J+8e+dUfw+bJZ+345vR3kbTrkE8dR1iF8LsisGujQ2xFrNUmtrvUmwXdboHP1RUvPOmfNyYDO02//BbuGu9k8yqlMlk5w7uEUlYy55A0TcQliTkRw0xxHouxd2Cot3PT+kcbxEzEQIIG0SISo1qet6UoAt08aLvb1uGyrj/hMD0gTK7ZeP2PPj+zlGhazoiVBK1lZJUSRb2CVlMk9ayldzJ+DwBi9gG01pV3b3xS2Yz+pLwx8RvDn3RZdwzrgyhsxvhJMPZq52izLVmzwFq5SbdLaEeNndyZlOutw97wwU5anz39ZzeHO9dPMCvxaZp4j08z0iQlSz1Kz/XhxHdxRFR7ESvnPIqPUTPV6LwX63aDKLEXEy6ChVDkc4Z15G8Pyw4qJtZvvOm/v1ixTqNaTpRSySXVFF/NROslpJwY1RKWeBZjnIAw1BtP9gGKclXevfmzTxaTPzlzUfzawBnOhwSXn9dPDpZj9i1DDmp2oR0Iza5qJzhrFfhuR5NOQdENTpPKwNiBx5w6MXOXfY9YtiZcef097e7IhE2XLMlSvDjn8IngncfMgllvQ7JzIoqkGsRUC4kWpCiCodadPuw2vXi/cmXBTslRIyvvvfauX/37YNGdnFbNVDJHUi67WMmIlVSpJs5XMqRWwouzuzB5J5BhqMbkDGflivzD+JMG788CECB8tfZO8cVOzsd/4pF8pz9CGOjm1DoB1+yStgq1dk6RF851g/puTtE10iLSGNpx95EDjjrFTZu928taOXcvebizfMWabvfhTV3f6UI3qqC9o6WC0ywVyiXYeUYp7rZTWpq/c3WPWsYBm9c9ePU9v7lEx9Y/uEOaUMs8oZSQVTJXZF6tZ22dK2eESkYsJf2NU0I/LwKjhPSLEb8i+YfmD/5ULP7s7G321cphEXuv98XeiL2gf3kxwu8N269VENtdF9sFaadQ6/aSDaXdSMgLfKFoEUhiJCcpjw7OnDu+497P1xk77pqV6tNr5drAoE/THUSchLy7odOcnOg2Rpub1q3M1y26zU1sXD1E6ExPElLv0MzjspRQ8iSlhKKSkpRTJ6WUolZSygmZiNyH8VKmMs2Z3BhDtlyRf83e17r1z8HhKaW/m/jywIy65N+XJP4B9JGljsiPMTSiO3cCRTt32im01A3keeF8oap57nxhWsRA0lEwJQQlMcNCz502ldqk109BEwERJHEEcSQlB6knJN6lmdOYlpzLnMZKQpZlrltJ1ZVLpB63CiPF7PgtfTT3KYv+b0TKb5F/HN/852Lw1BMwXobX9dmZmJrL9K3Agv5fOSr/jmdGVJ2bF3Q76nxRqHZy52LUmCsuj06jqu8qgjmiEtTUmEqF0vumgDlx4h2Jd2qJgHcuJk592ROT1PlSopomzpW9xiwl896tQumAvZlH0gcs1sJd4oTAxnCenP3Udko8bTlU7SvMN/NfFW9XAJ9iKmVJL/vkDzEM0V0Ldb5bqObRuagaikBWmLMQCKAuGAFDowKO3gpASbwDBJcICeI08SSJU8kceZKSpGCl1EnqNWJuBYLD7C1bsmBCC+MTMcqrfIjvlQ+y/OkY99ObhNaQ+K8ch+OdPnG3KXwco7xVY/eqyY3iqRk6xyJZUFeEqC4oFOYExaKh9JzaPSMiGOLECw6HpKKWeMyLI/XqxVMIbq1Fmk7scIP9txphxymfiUGfD3zL/3/84ulMifzMpEH+Bmls8yaMU8y535rnNOnP8rdqeA3I1eKsbSolB4NRdFgMb0Y0wcR64mXSSzggggeCw40rTIizrqlUwF5msNOj+gArPVykhR6t8P27q1x2yHt42vcdP6OZzO1sXBjmKODDqvxeEjfN4ANP0JlRRG43GBdljN5OWDCrmWNYYAizgw2mP0EdXzJ0s1NeivDZZDNXP1U990T0rOXSty8wsyuchXMrTLjgmWhDjDNQ3a1kfEr+iY3PRBuPa/PZaGSKDKR9AZc47x6KulUuwqelbrnIqe5c+SdOFJ4+HffHaJse6WeKBKwyyVtDoQca/Eb7qbSfarHItVbovpUB3vxsggfPMoAAcjahW+KNVlhDlZVPGcDIWjFbF5U3yTNgJP7oeJ7tBqdo9F84wMOpivwjsmWS+6dSMLUvpsJ3Bz7CdpMkPpP0FwMQYPyznByUHXFy4Z9VgdrpzjE+7aN8+2nu2pOmvyiAAJvO44tmboNh5/1pT8qnPTpj+se3nRjx2aK/OIBmyKbP8FMzN6bY257MM+LkMjGtzQy89pmc4z0ZetaNyGNJBGtv4k2gczVy+5MwHHcRdVoROOkvDR78FQAIMO+LtIm8zYndr8bodqcrRkuwG73jXTudTeuP1/zM019chLemtWdzpCkvir2EZI8jBx9xCTfNOYvfbev/vwT9VXDgFM05m2sMgiinP477lNMd6F8TePBXxoHQW+6tOYsfFsrDpnyof/GiJGHmzp/mrc/2SuOP0bN7CvpJkICZ4+2rjF8G5RcmDDrHvjt7Xv3XBh78lYnwFMnZhOg5SRxF6hmhyUlyNs/o2dj/X9LKT7D/yo+y31+6H09E/w/wHJVcjfUH5AAAAABJRU5ErkJggg==", "public": true @@ -51,7 +51,7 @@ "type": "IMAGE", "subType": "IMAGE", "fileName": "map_marker_image_2.png", - "publicResourceKey": "FazBQsEp1uSeIsT1XL31o2npLAx5s3zJ", + "publicResourceKey": "l9DKBOJ74XPdTzii6RiynM7lP6iI6FyD", "mediaType": "image/png", "data": "iVBORw0KGgoAAAANSUhEUgAAAFAAAABdCAYAAAAyj+FzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAH3gAAB94BHQKrYQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHiczZ15vF1Fle9/a1Xtvc98780cEjJCAgkgiUwiIghCgqLIQyK2qI0D2g6PlmfbbaONCmo/habB4dF+tBX0KdC2PFGZB4GEgECYIQmZp5vc+Yx7qFrr/XHODSEkiDKuz6c+Z9+z9zlV9T2ratWwal3C6yh64YW8Y8Ex4431M4yhOQAtVMUBTOgRQRGEWvtBlJnRgGJABSthdIWHrIyI1l9y3339F154obxedaDXOsPGr2+anFL2TgXOJKZWEIYP5oslL0z7EtE8Zj4M0O49f5qGofKAF32GRTe1GnWTZOkR5DUi0muC0Nxaete7el/L+rwmALdde+34nOcPgei0ILK/j4rlLmbztyBMfkUyUGwT8f+ZNGojWeLeBchvjOR+Xvngqf2vyPe/iLxqABWg/p/+YqFR+WYQREsLXeUuMH8WQPhq5dmRVMRfUR8eqquTt7Din7rOOXsFAfpqZPaqABy88spjlPhfi8XytSbKfZxAB+3l0ZSZ7hXD6wkWCAggClURGWJSggUAUjivokRIoJpq6gkkyl5miOJYqNq9VO6xer3+UxfHp4P9l8Z+8pPLXum6vqIAhy+/fLYXXFksdd1go9wXQZjyggyJH1HDDyEIQ1iawMZAjAWTsWS55QHHbEREGQwPABAYZhLxzjDBIpOcQBx7BwhUvOtDkiXk/WGqcugeirbJxc1LGvXaqY5x7sTPf37NK1XnVwTgg1deGcwcHvkWOKpXuiqLiPjI3XIZJBv91lub59CORWBVAysmDKyQ8QgsQGSNsakaUhAYIAPqlE+hgHpRVeMB730IFcfOK8RbSVPHTghZCsn8IJI0hk/fA8WYXYuhovfVa8O3eudy69eWLzjsP87NXm7dXzbA6oXfnJNBflwaM+4uJrpgt6/fTlFwC+dyY7w1Fvk8KAwYHChHRsQGAVvrEBgSsGE2DtYATAwFE4MBQAUCgkBU4D3EewtRD3WK1FmkzsE7gstI00zQaoEynyFNNkucLCZg+q6lUpVLRoYGj1KWv53wla+sfjn1f1kA+750wblBYGcXunpOJcUBu3zrsIbBryhfnGaiyEghIoSRahiAcgEjyCkCqxwEFsY4BCHBEAnIIzAEsGEGRDVgYgXUiQAMcfAeEDXwqshSFe8t0syxcyRZTIgz0TSDacYkaapoNb2kySaK07MAVEaLqIRnGkNDv3ferR7/rxdd+ZoC1Asv5L6R+k9yudJTuXz+YgA7O3EOwquQjyoo5POI8oRCBC7m1YcRKIgIUUgcRSqhVUSRARtvrGEPkiRuVBsDw83B3m3Ot2KqDQ8TAJR7utXkcjpm4qSgOK4nn88XK1BlOC8iziBOPWeOkCTkk0SROqU0Jm00QEkKtFqqzVZLG406vHxol6q4pNn6XBw3jh23Zf3ZdN11/lUHuPpzn4t6Mr2h3DX2T8T85eeoYjuKuT9QobwPFfPQYp4oX4Tmc0AxD+RyanIBxIZk8hE8sx3cuLnv4ZtubD774MOz01bzQI4KjwTdXRt57NhhDYO00FXOAKA5UgsozUIZGOjOhkemSdI8NMwXnt7vsIVrDl20uDhu2tRxRtT5OCZOUvFJqhTHhFYMbbSIkpai3oSvN0Ct1lY0mqeAMHFn0UUuqo0MHDkU0Kn7X3FF8qoBXL34c1HXxNYNlZ5xawj0qZ03DN3I5UqMYqFgSiX4UhEo5IkKBUUhDxTy4FweEobF4R19g7f+6D8a29etPz6cPPGWniOOHM5Nn1qKyuUKk+UgCMSwigoLMwQARNr9ocucEUC9TzWu1WqNDRvrw8sf6HHbd7xz0uxZd5z0iY+VK+Mm9HCa1aXZJG01iZJYfbVJHDfV1BvwtQbQbDSlVsvB6+JdQHxvZLDvwDq5d8/86U/jVxygfu5zUV//4I1dXeN7QXTWzvdN+GNTKU5DpSJUKYKLRaBShi8UCaWCUr4Ijuy4NY8/9syNP/rPg6mYf3bSu9+zpTJj3/FhaG0YRWRsiCC0yFkLgAWgzFgSMkYBQL0n74iZJRDxnDiHLM3Ue4eklVCaZVlt3fr+3j/8YYrWG7NOOfeTT86cP+8AybIdaLRI63Uy9ZZKowodqZOv10G1WiLVxiD77CO7VPPqkaG+aSMjAyfvf+ONL0kTXxLAa9//fvN2p7+rlMdsYDbn7oQXhj+kSnkmd5cJlS5QpSwoly2KBUGpRFTIj92+YcP663/4g/2CSZMfnnrG6T6sdHUXopByuYKGoaEwDBGEIaIwJGOMhGHovKfEGPVBEGUAkGVJoEqGCGGaJoH3npPUiXcpxXGKJEmRpC3EcapxbWR463X/bZLebQvf95nPrpm4777TpNUYQD1WrtWdr9dCDI8IqjVItcpUra3WNPvMzjqpfH9kZOCACQGd/FL6xD8LUAHaccq7flEuj9/ARP+480YuvIzK3fO4uyzo6iLT1QXpKkMrZZhSyWTQiddd/r1n62lMsz/+sb6ouzKmmC+gWCxImMtTFAQmn89REEQuzIXKABtjhIgUAAPIOgkAAnQMlarCe8+i6tMkYeecbSapxs2WZGmszWaCRquOWt/g0Iaf/WxiJcxl7//8Z+ayoldrdaFaXTEyDD9cI1NtiBseUNtobHDN+FPP1Vkuqg4Pzph40w1nv2yAW4874dxKqZyzJnfZ6Hucz31fyuX9TM9YoKdC6KkIlbuYuyuiheLEbZu3rL3h5z87aNJp77lp7PyDpuZLZZTyEQqFvBaLRQRRzufzeVimoANsAMB2AFsAbAbQByDZDWAEYAKAKQD2ATAZwBhVhXMuSZ3nZrNhm/U6teIUraSl1ZGGDj3xWO/263+76D0f/chjEydPmaWN+nYdqTKGa0B1WGRomDA8QjpYfRZZ/HejdXRZfF6tWfeT77rte381wIHD3zpfQ/s3xfKYL0GVAQA2+iF3lWfpuDFK3V2Erh6Ysd2qlRJToTD3jzffcseza9dMm/s/P7clX6wUKpU8R/mCVkpFLRSKEgTWWmstgEEATwNYC6Cxh3IJgKM6fy9HWyu1c4861wUAswDMA9DtvXdpmvp6vW6arVQazRparRZqQ0Mjq//9+/vvt/9+a4898Z3v0Li1CsNV9cNVz4ODVoeqIsNDHsNDG5G5tiaqukZ96JLM61WT77/nqb8Y4JPz54djw8LN3eVxUxS6PwCA7a+op9JF3V3MEyYQuiqCMV2M7m4gCg/6f7/573uauUJj9t+eXSiXyzaXK3JXuaiFQh6FQsExcxnAJgDPoK1xhHbTpA6g7g6UgwAcDmB2pzhrANwP4KkO7CoA34HoOq9jOp/b13ufJEmi9XpDq9Um4rhOtVrVr/np1Y1CKy68532nH0NZ+gQGhtQPjxCGhlX7Bw2GB70OD4/A65JOvqtGagPbJrK8kx56aI/TPrM3gF8ZO/HfugpdG9W5U+EcyPmnqFioarEYULkCKhWEymVGpUxgc9A11123ws7cb+P+HzprXHdPt+0qV1y5VOCuroqGYRgZYzIADwJ4Fu2mWQHQIyIREUUAciJCRJSo6qeI6NsA7gZwO4ClqvppIrqpU7xIRPJElAfQg/YSWQJgG4AhZh5rrc1HUZgGATMRGRuGKM87yA5v2Tz02N33NOfPPeBQUfSyS1nTDEidauqIUie+Uffk3AQ4NzYy9prBZv307/bt+N1LBrh1zpwDQoTTAzIXtKdO4jSMbuJysULlippKCShXiIolImsPuf6m3y+LDpy7ZeYZp0+qlMvc091FpVJJy+UyBUFQYeanADwCIBWRkIjGiMg+qjqWmaep6nxVPY2IjlLVJ4joMACnAzihkxYR0TCAR1T1M6r6FiIqEVFFRHKqWlFVUlUhohjAWiLKjDEzoyhyxhhlMrCWbGn//UrVoaG1Ty9dGs+fvf+bSWgL0tSqy0BxBmSppSx7FnE8H84zvBzjnLvhf47r2XZpf//AnwWoAFXHTfivclSeR0RTAYA4uJIqpamolJkrJUi5ApTLxLlw9u0P3HdPOnnS0KwzzpjQVSlTsVhAuVxGoVAoMHMOwOMiMkBEkYgAABMRqSqJSAqgn5mfVtVFALYC+Bu0jchVAG4AcAeAJ1R1tqq+T1WfJaJ9mPknABqq6gDEABLvPYjIEFFJRFJVbTDz5CAIDDO1mNmoshRnzOgeWr9+oPfZZ9fOnDr1MFHpJ+cUzqv6lJH5IpL0VqgcAQChCcstFy+6ZKD/Z1/7cwA/PWfeCcWwMMhsP94mqiu4XAJVykzlElG5i6irCCrku9f2bnnk2aGB8rxzz43KlQrKpSKVyxUtFAqWiLyIPKWqCYBJqlpBu5/qEZGJzDybiN6jqkd77+8hoveq6nYi+g4RBar6QQDvBPAOAAuY+W4APyKiBao6A8AtqvpFAAtFBERUZOYKgPGqWlTVHiIa6qRyEAR5a61T9T4IIi4fOCdcd+/SfJGwprvctQ9EEnZi1XmFc6Qu60Ka1gGaDMI+AfFlQ2PG5i7p37F+V168u/YJ9KvWRl/a+UCY+xNyUai5HFMxUuSNIoyQthK6f+XKhQed9/fVUqlIpWKBS6WSz+dzQfurcC+AQe99WVXzqtqtqhNVNSKizap6u4iUVLVFRBc5574IYI6IXOK9f5+qiog8IyLPqKp4798nIpeKyH6qer6IfNN73xCRCjPfCmCt954BdAHoIiIjIkUR6RORZUSURVFki8VykM9HWiyW+KC//3zz/pXPLEySRCm0RiIryAVKuZyafJ45Ch4Y5WBN9EURf7HuZnifp4F/N3f+WwthfoTJvL/9Dt2ihahiKmVoscDIFxWFAlEQzvvtI38anvPpTy/vmjC+p1AqoburolEUBUQ01Tl3D9rW1DNzQ1VrzJyo6j4AFgFYSERLVfXdAC4HsICI3gvgSWPM94joZhFZq6pxpznfraq/Nsb8UUT2AfBhZh4G8FMAxxPRLar6BQBv8t73qeomADuYuQmARKSMdvew0FrTMIYVRAAQFvaf89T9N/x26gGTpkwn7/uQesCnUPHkY99NLlkNYH8AFct6SX+lB5cO9m/eCXZXgF79N4wJp47uvpgo2CxhYYoEgSIMlYKANLB2Ze+W28v7z0m7pk6elM/nUCzkuT20w1Tv/UYAbxWRhqqOrnhscs6tIqJbARyvqomq/j0R3QbgAiK6Q1VvJqKzvPcXqT5//2f0b+89ADxijPmi9/5dAL5MRLc4584H4IkoJKKbiWiMqh4gIlNUNWDmfhExxpgtzDwlDMMtBRHyzvvuqVPGl/af9ejqbVvs3DFjp0kQOAojFROQKQQqLtymSdrmwflPESenAjjxBQDXzZ8/iV1wO4CLAEAJd4kNpyCygAmI2KgGBkjdlCd3bJ+54NOfWFbI5xFFEedzOW+M6QFwFxFtUdXAew9jTMV7fygzv5uZ6977bxNRwXt/gTHmUlV1InIBM38VwDs6oG4RkbuJqLkbxAIRHUdEJzrnDgUQM/OXReQTAKZ0NPBSImIAnwJQAnAnET3mnBs0xkBVM2aeYK09wlo7kM/nDaA44MNnRw9dePFb9ytVMmbaAGtgQqPehIAJ9oFmfwTp20E4gNn+cu3MgybOWvfE9ucBDBJ5X7EY7dynZWOfhjEzjbWkAUOJiRRmxY5t901bfHItly9MyefzUiwW1Vo7SVV3OOcWoz2wtcaYDQBWiMidRHSqqm4jog+LyBZm/i4RnSci3yaii1T1Hmb+tff+QACnEdFJ2E2ICKq6XVWvVNWVzPw/vPff7IA8T1UvFZFNxpgPi8gIgAnOuduDIOgGcKyq7uu9Z+993Vq7PYqifYhou4jXLPOFKSce9/tH7vtTeUHPuGlK7MFWOTAiQQAT8FPe+bcDQCksjvVu5HQAPwQ6RkQB8mtXTwf47e3GQn1gM1WtgbckwqywRN6l4zYn8dsnvfWt46PQahAEFIYhq2rivV8G4Ceq+v9UdbWInOyc+4S1FqoKVf2Bqh6qqr8RkW3e+8tU9XYi+rKqHui9vwzAuWhb6UcA/MY5d7lz7nIAv0F7HNkD4FwiulRV91fVL3vv7/TeXyYiW4jotyJyqKr+H1UFM5Nz7hNEtNg5t0lE7iKiX6vqnUQUW2tNEIQU5iLd9x3vGLclTY7zWdoDqLbrzGBr4GH3VcWAAgDTcbJ29b6jP6xBW99nGJOfEYy0ijSmO1TCLRREPchFZIKANQyFQqPPpunq8C1HPjl+3oHlKMpRsVgQZj5AVR/z3p/V0egBVV0JYDEzbxORfQGMVdXDAPyaiD6JtrH4oYh8TFVPQnscdzkz/5f3fnVnnDiLiBYR0WGqKqq6XkRuNcb8ynv/eGew/W4imi4iXyGiyQBOZ+afiMhH2nqBsdQ2FhONMVeKSAzgMBF5LxGtYOZDiagPquS8mrReWxFv3jw4hrlIaQpJM1LvGN4ZdemDrDRG1qx9VLys+TvJNl8OjFgASGBO6Db58ar+fbxu3dUye78WDBMRVNgAAguHYJ1Lj15w8onLC4UchWGo1tqIiLap6urO2OwMVd3CzFd0xmY/VtVvA/gCgEtFZDERfUlVv+WcewuAW4wxfxCRt6vqlzpGAp0BN9BeUACAQzoJncEyVPW/jTF3O+feTUTfVFUB8CXn3BeIaKL3/nxmvkRV/5GIvq2qARF9QlWnA1gqImuMMbOCIAicc1k+r5h16ruKDz348JGzDe0gIAUzQExEVmBMitVr7obXsyObW5e6+O2Av5oAYC3s78YVxhypinEA1blS/i3PmlnWUtFSPgcuFCjOBc3l3RXz5i9/MQnDkHO5HIVheLCI3P6Nb3xDly9ffg4zR7v3XW8kEZHklFNOuf9jH/vY74IgKFhrJwE40Tn3aJIk2opjevDib+WPGm6lhTTNS6OpiJvQetPrug01P1x9L0ELRNgx0By4byb8aVYBXg+qimIcACj0FsTJOOzojbk40wMSiM90HezwtFNOrhtjJodhKNZaUtVCkiSz7r///hOZmc4888w/rl69etWcOXPmPP300ysPPPDAuc1ms56maQag55ZbbjnOWrvitNNOG2Fuj+H7+vr6+/v7ByZOnDh+zJgxY6655pr5ItKzZMmSezvN73lCRHj00Ucbq1ateteYMWP+dMIJJzQ6cPSZZ55Zvd9++80IwzAcHBwcuPnmm0+w1naXy+XbFy1aZP7whz/ozTfffHylUnnmrLPOeluWZZuCIMgZY5SIyDBj2knvfHbDdb8pz3U+r+otvCayfYeXZnOcQm8BcJoqJgCcKDzbTcCknA0fBnAWAKjyDiWUtZmEfv2mxM6a2VAbdG8L7SGHHzTv0SAIgPZofJyqPtJsNt9sjDFZlsVnnnnmMara6PR3C4noJ6p6HoCHmfmmW2+99bh3v/vdI2ecccYQgGUAzgZw3C7jvjU33HDDxlqtFpx55pk3ABhdUg+89wUAMwGcsHjx4uI555yDM844Izn++OMLAK7z3h9rjPlkB/JFInLm0qVL++I47r7iiisOyOVy5dNPP33pOeecs/K66657z5IlS54mopNV9XcAxllr+7xX2ufNC4sPXH/DvLnS6JPEx1i/UaXZKrR/S9422qtYGz603mUT2AP7Rhxyu89VcOADYnWqHuLSfLpxY5dWa9vZmO6wUCiTseC2+swTkY2jlSciiMjFqvouEbkEQE5VP6mq56vqId77LwPAtm3btqnqqar6HRGZB+BqEfmCiFykqqvCMDRERM65S5xzl3XSd1T1a6p6uqo+2Gw2vwcAvb29G0XkMFX9DjOfDOA7AH6mqhcQ0aw0TT0AhGHYA+A7RLT4Ax/4wB1ENHnDhg23qSpEZDOAedZaMobI5HIltcF4DNd28IZ1OfGuCCiUycP4YJRTyCEE2Jc9zFwY3rf9NqDe9DjhQAUW3oNcipH+HcmEgw/5PhEnDIWIKIC6qtZ362POArCUiP5FRL6lqjVVvURVfy4iPwKAKVOmTAbwsHPuCwB+5Jx7P4BLAVwgIouZmbQtN4jIr0TkV97721T1MREpiMj7SqXS5wGgp6dnkohc2fkBnhKRL3Ys8JUicl0QBKYzhLpMVb8BYOlJJ530NhHxt9xyyzGde0NEVG3rAKVgbk6cP/97IwPb1WWO4T2p91aFAxUaM8rJGp4CmDkWoDcTuON+RqmSlgyJI/Uq3nhKM9SisKX5cJYxrNbamjFmnKpuc869s6+v72cA/rGjhXMAbPbe30ZE/wLgHhG5jog+O6qp69atWy8i7ySiSzuWelhVrxeRx4wxSaPR+EC7K9GZqlrsXO9Q1Q1EdIeIbKnVagDwHVUlAJ9WVRDRJufcBcaYA7335xIRvPerOv3ol9FeFgPaq9a9jz766CTv/XcALGLm7QBCIhokIOJibkY1CJs5X/fshYyqcyQGRBEpUkBDEM0jaGYB7KekC6EEgj7JAOC9aHvnQSAW/cVSuO/cuYn3PmLmnDEm7UzDFlQqlb4OvGDZsmXfO+yww86x1m7z3v8LM39NVd/mvf9+54f77OzZs2eoatF7/x1rba+IHAngFAAf8N6jWCw+2mq1SER29SmcucvQBsVisQ4AW7du3UxE56vqJBE5tzOrEefcZStXruQ0TT/IzGi1Wl/P5/N/Q0SzmfkrY8eOPaO3t3ccgGNEZIGq3sXMKRH1QFXGzp2bbSyWypPTXqgXdd6BvRdVZEr6FBSHMnCYgBMLIA+lCgAo6RZRhFAFeQ8IQSwQR2FPYZ9JW4MgSK21LCJzRaRJRB81xnwNAIwx5oorrvhspynPEpF/BlC11jaiKDoZnd73+uuvn7Fp06bHrLVHhGGo3d3dEJGHsizbGsfxjoGBgTMBFK6//vqrjTFBtVottVqtuNFooL+/X7Msc0NDQ1MBvO3WW289aenSpUfEcVzy3ouIxEQUGmPOA4BRS++c+6SIVIwx53nvL91vv/3uXb58+SwAE4jooyJyjqrOtNauIiJXnLoPJ8Woy2cZwYuyqIoqoEgFWMvAoarUDaBgAfCo96sqWiA15L1CmMApICDJh/l8qcTGGFFVj/aUaqRYLP7L2rVrf9xoNL5eLBZ3aggzEzOHaO9VVJLkuU1+7/2MBx54YAb2Isa0V9h+85vf/Pk9WaKJrVZr4iisUWC7y9q1a389b968aQAuA/CMtfapOI4XeO8vLpfLX4vjeA3aa4gCQE0QqURRDmlKgEK8AAqCihK0iueGV8yA2tGOkYAUIIEoqfckzpPLHBAEFY4iEhFnjPGqOhbt3bGZ++6778l9fX3YsmUL6vX6zqWnN5JYa49g5lNV9d9E5FvHHnss+vr6cpVK5X0AphNRA8BY770AEJsLSYytqMtUUwd4bbMDRBWNUV6AWn6e87WSV9KmGtRhUGdwPYCpE1srzmVE1ErTNFHVLd77AQAwxswEgCzL0N/fj82bN2NwcBBZ9rKdP18xWbVq1ZPe+49nWXam9/4/DzzwwGcBpNu2bTMA0FmE3UpELWNM3RiTsA1CgOpgqpNBnRk1z9QU2J28aFT7dr5BmldFyQIqIPKq5OFh4evqfaiq+c48dC6AjXsqrPce1WoV1WoVYRiiVCqhUCigs+D6mkmWZWg2m2g0Grjqqqui7du3Hzt58uTbJk6cuHb27NnvJ6Kt995779NLlix5E4CJqjqHmR8CYLMkSQP4qgrKBJAwhKBgJVZyFjs9jwEreE4FhajIopRAGSAGkRG1CJwfcnEqlMtZIhIiqhNRcc9Ff07SNMXg4CAGBwcRBAHy7QVYRFH0igN1ziFJEsRxjDiOn9cCduzYcdbVV1/9gs/84Ac/WLFkyRKoahlA3XtPzjmbtVrMiWs66BgQiIWcQpVZQwJ17eQFwBLQC9UNIEyHoqJGYwhIIO21QvbepGktHhpyQXeFOyvNDeCFHvi7yBAzb+zkwSIyLcuynl0rZa1FEASw1iIMQ1hrwcyw1oKInmcQRGR0TREiAuccsiyDcw7OOaRpOrrcvyfZxMyD1PbBgfd+Ptq+NhgYGDgJAIhoHwB1dFQrHhlxuSyteUg3g4xv93VKYFJomaCA0hoCtliBPi4EQ8B0IZ1LgscZUKfwgIoQo7S9v9nYtDGMpkwxYWhtZ2B7SCfT5wkRPU5ErqenZ924ceNkx44dPDIyAgCbReTg0edGK/8qihhj/qSqZuLEiWvL5TJv3ry5GMexV9UxqjoTQNrb27u8VCq9mYgeFxEbxzE11m8Mcn39Dc8QFaiFAkSkKoZID1YlgOQhQB9lBT2tKqsAgBTjhaTmSb0C6kDGiQa8dWuhsWZ9TtVb55wBMKiqxjl3LwCUSqXfjsJj5vqiRYsmBEFwOjOfEUXR6SeeeOJYImow85OvJrFdxRhzfxRFQ0cfffR8a+2ZQRCc0dPTs3jx4sVpZ+q2paen596VK1c+pqoBgMHMeyYiaqxdVwy2bCtnQoEA5Nu+TJ6gI6o0BgC86rMCeoYZfnXm0+0Ytc3CDXiygASqYK9MNFwrNNavnZ2mKURERaQOAEmSbN1t2GIXL148pq+vb8p3v/tdiAi+/e1vo6+vb+qJJ57YjfbK81/syP1XyAZVjRYsWHDc2LFj81//+tcxffp0nHfeeXjyySffsmDBgg3MvKNer49LkiTqdA11FYFzHsn6TbN4qJYnBYmCFQjEU6BKzVFO4tMdgF9rCVib+Oy00OQAAMTUVFEVpUyhUKhFko5F4g5Mk+QOIgqIKGVmOOemX3vttXfW6/XjrLX3EpE89dRTxy5cuBDLly/HvHnz8MADD2D69Ol45JFHDmDmewE8rKrjXk16RDQwadKktRs2bFgYRRFWrVqFSqWCe+65B4cffjhardaihx9+eG2WZVOMMRYARCTN0hQuzRLN0nk+i58ASAOwCOANgcHaVGlb4KbPxjtgjd0fqK4Uf/ROWyxqASYlsVBWBbwCCPv778x6dwzQPvtMMsZoEASbVfVtRx111E3PPPPMzQBwyCGHFK666iocwXXn3QAAEZlJREFUe+yxePbZZ1GtVjF16lQcc8wxWLp0KRYtWrT60Ucf3Wl+X6lB954WXk866aSJy5Ytwwc/+EHcfPPNGBkZwTve8Q40Gg0sXbo0nD9//vJWq8UzZsw4RUQ2dgyVtjZtGgj6+m9X8EQAQlBWKBFYSBBqh1Mq/m3zgQvabrNAnwIPEHAEgBMIspJUIKTkARaFMQ89OlJbvjw/5vTT1DlHxpgnACyaNm1a38UXX/wxEYGI4IEHHsD111+PJUuWgJlxyCGH4Oqrr8acOXPwmc985m9Hnxu1qLta11Ggu7+OAtr1lZlBRM+7Hp3OjVrwG2+8Eddccw3OO+883HXXXRgYGMD999+PBQsW4Oyzz/5okiT/KSLjVPVGL6KNRoOaD64o24cfq3nIFEOkAlIDdYAnAb+jw+sBAtYBnV25v4OphWyHmPidALqgtEIIxbbnIkOg5KrDJT3ggLebgw9ew4aNtTYlonne+2IQBN2qyiKCOXPm4Je//CVEBKtWrUJvby9WrFiB888/H8Vi8Xnw9gZy1793T3vT3t21kIgwefJk3HzzzajX69iwYQN27NiB7du344tf/CKstS5JkhKAsSKyIm61qNVspcnd95yst92RWMAYYjEABYCxsIMKfQsAOPGXpz77w/cg6xkAArj7W65VHs1cGNsZpBYMQKAgiFIevX23tTas64vjmFqtFlT1QQBzkyT5xWgFp0yZgiuuuAJRFIGIEEURLrnkEkyePPkFkF4see+fl17KZ3aHfdRRR+GrX/0qms0m0jTF3LlzcfnllyMMQ2RZ9gtVnauqf0rTVFutBK11m/pNb98tqhIqoKTC3LbAqup3jPJpuVaXh/sTsIun0dMwf+zJd+0PxWRVPA7CjhSglgIJhDxAaSm/OffZT0+17z+9t1AscqlYZCI6Q1WfiaJoWmfF+AUV/nMAdtfCF2vCL9Zsd0/GmL39PRTH8QARzfYi1zXqdbRaseh//fe41uXf326ayZQA0BwMAlLJA8SKsUp4EwhbBlsjz86DPw7Yxb1NgEuc91e0C4qDFboDECK0zY4KyNdbU2iknqT9/X1JHEur1VIRuU9VD0iS5Jo9NbndwewN3p408M/9AHv7vr3lPZriOL5BVfcDsCxNErRaLcRbt/XpSD1zzXiSgtgAyvAwbSPSq4Q3AYB6fzmA/z3KbSfAEP7melqfNrppQqKtAIAhUAiAWD1BbO2XvxyJ7l52QL1eR6vVEieySVWr3vszvff37K3v2luF9tZ0X+z6xZrti/Wfqoosy2713p9FRMNpmm6u1eucpqkU7n/wwPov/m+DAGsgQgAZAhkoWDQZ5TKY1ueuh7/9BQD3BxLfNtHXdHxAPgyiEduZzzJAEFI3PDxO+3aMYP3GvlqtybWRKrz3d6hqMcuyblUdGm1+e6vYi/V7f8n13mDuCWKnTL1pmk4CEKRpeme90aBGKxa/ZsN237d9OKnWxrQdKEm4c9qbCcPKdLa2rcEvAG2c0nZofz7AdjOWC0fSxuiZCMuCHQYg204IIEoA9f7kqlLlqZXvTONGrd5ool6vp9r2OD04juO7te3L8qKa8FIMyksxIC81H1WNW63WU0R0sIjcFsdxVq/VEdfr9a6nn17c95OfFRhCFkohFBZqLKAQ7kfHi62aNtcmkG/syux5AA8GtntxBwB0BwAo0YcD5WFDpAYEA4YBQzPXvf26/75vzP0PlhuNmtZqDWo2m4MAHgLw3iRJfrWrEdj19aU06d0t8K4gX8p37CnvJEl+h7YP4oOtOB6oVqtaq9do3EMrituvuW4ZMt8TgikAwxBgicDKwyD9CAAocIsTN3th22N2zwABIIb8r6G0vqzd4jUnLD4E1HS2OgJAQ6KkuXrlfn79Rp9fs663Xq9ptVbzjUZjjao+AuCDzrlfAHhFNPFlal6aJMkvVfUMAA83m821tWrVjNTqlHt27WbevJmba9bMMgRvALWAsBIFgIA1BRAqgKG08TBBzt+d1wsALgS2irhAIT8CFFCcYYFVAUHzgIYAWRKyUF37o590j9u89ah0247eWrXKw8MjaLZaq1V1uYj8TZZlN6jqyN60cW9a+WLjwJeibaOvALamaXoPgLMUWN5KkjXDw8M0ODSk0rejf0LvjiPX/OA/ihaAhUoHIIekxJCVpLqkzUB+KOLsfOAFUZH2eNDmQ9ClcG5J3uZmKBBCKWcNDQsQCEQ8wSiIRMT0Llvef9CCQxZsjIKnHbikIgKgGQTBJlU9xTm3GsBqANN2b3Z7et2TMdh1/Lf7GHBPY0IigjHmHu89EdERAG6O47hvcGjI1OtNifsHBg5cv/HYJy765gbrfaVAxCGBilDNEWyOTaxCbwJhHIGqQ0ltex/kcz9re9/+eYA/BtynYaqG6HHDZhEIE6D6EClXAHgHYgAsBPLqo833P7Bm4cELjt1A9GhGWnaqEOdbzPS0MWauiExX1Z9re+Qf7KkP2xO43QHuCmhP0Dpz4CERuVZVTyYicc7dPlStJrWRmtZrVY37h/oXbu094bFvfXuFSZOxEZGNFDYCfA6MHDGp6nYiHA8Aqc++lIn//RGQPUb32OtZuR9C1nxc/GcjGz5JoIMBmk+svyPQBAACUqiSVVJIlhW3LLt/5eGHLzx8o8NTKUkh88555wNAN1pr+1T1VBFZx8w3icjB2j6a9bwmt2uzHJU9QdqLBgoz/1xVJxDRUQD+2Izj1fVaLalXq+FwraZZ//DWI/r7j3/4m998gprNSTkgyIE4YmgBLDkiWKUniXAOAAjwi2ra2Ocg+Ev3xmmvAAHgH6A31lz6oZzJEYCxUD6YCHczoUeU2DMMQCBSylzWtfHOu2tHHnXk9DjLHu/1bqzKzj4sZrarARUROUnbHq2/Q9tFrmtXiKPXe1p5GZ2K7QZvC4D/ApADcDSAJ0RkRa1Wy2rVBtXqVQwODMvkoZGtbxoZPuyBr3ytj5JkfJ5gQmWTJ0IR7AsEWKb1qvhIh8uq4WSkVYJ+6N/30HRfEsB/B9zHgWVO3fjIBAsIGhHRDIY+xNAygRVQgjBDlcW7YN0dd2bzDj7YTjXWPJVltVbqOUvTXOYz8c63VGU1M9cBHKKqU1T1NgB3S9uzfho68/Pdm+0uyRHR3UR0B4DtRHQgM5dUdV2apk83m83myMgI1RpNHR4aMLVavX5stV6sbNpaWP71i4qBl2IEaF6ZCyxSAEsemgXEW1RwGkGLANxI1riaVL4yp30YfK/ykmImPApzap7t3EKQ+067ctigwP2xYlwq4AYLN6AcgzQW9S0oeubM2fTmv/v0kY8Q3bUxF3bnCpHJBYFEUZ7CMEQYtnfjmDnsaOE07/047/16Vd3S8RZIvPdgZgugQkSTmHm6MWaYiDZ2oKejO3Rx6rXValAaJ9qMWzohTre9FXTy4z//5dLtDz64fx7gHLdHE3kYXxDhHINyxFsVeCsU+wKQetb6p1T844fA3/jn2LzkqB2PAOeUOSqHQefoP2EThP6YkUxvgnwLoi2QT1RLTZUkUTXehukxXzp/MJw8acyt3j1RZTOhkM/bMBeQNYHmcyEAAxMwhUEgaLurdcJmAUTtU/LS9kfU9jUABrIkJS8eBGiSJJRlmaZpqtV6S3oEAydZO6+1bVvfsv99yVjj0jBH5AsgHzKFecAVwFIAyCi2EeMoKGYBQJrF5zUlSQ4G/s9L4fIXxY15BPhqV5AfsRx2INIQAdd7yP4tQJseQQvQJpRarC4VlRQQU+5qHP2Fz6e5CRPG3dGsr9geBOMtIQzDnLBlCkyAIGBSJTWGoKoZGePEOQ8AbK1R7y0RWSfKDEWaeiiJpnEK5zJkzmfjVftOyOXfnG7v237vv12Wk1qtEAKImGxOyOZBvgClvAHyIDWglar0PwjtfjiV9Lx61iq8CfjWS2XyF0cuegz4+zyHYRTkvwmAFUiJ8WNRPTSGaqzwTYEkDI2hpuVhHSFLoGq7K4NHfvQjtfEHHHj8mlZ828NJq9HnsnHGhNYGhgwD1rYNhaq6dgwZoDM5sCKs4jLy4uEyp+KzrDsIB95aKJSmBdEJ2598+s4HfvaziqtWe3IgChQ2b8hHUJ8TQp5h8gREIEtKjwP4KLU9yKSZxV9IJPmL4P1VAAHgUeAjAduppaD0v9CJd2oIvxKlcgJfjAHTFLIxqcaKLGXlTGBa0CxTsWK40TNjZt9hH1hiumfMeEcs+sS6VmP1hjh221KniXrK4I2gHXiH4cnA+DwZnRQEPCOfs7ML+f0j4gMH1qy/88Hrfikj69aPZ9FijthZUBAxfCQkOYYtClHI6nNgH4AalrThFWd2CAwOp41/F3FrDwV+/pey+Kujtz0EHMXgT48NS3NBGI0XuAqge5TkgFhJEqiPgSCBaiJwKSRogbxTNY7IZ6rGkWYmFw10T506MnXBm3X89Olhvru7GJXLlSCKxgNAliR9rWp1JBkZafZt2JBufPghHtmyuUviZEygFAREYlXZErkIGoTgNMewEYhCIMsBlCMERvkpJX07FPsDACnuG0rrazzk8gXAn/4aDi8r/N2TwJgU+HlPWHqQiL/y3B39NROJE0yJiVwM0VQ0TIE0ZVgH+EzEZKAsgwYegIdmClivnCl5B2KFSHv8xWyhQlBjLElIgCOQDUFqAR9AA8vsQ4ADgc8BgWVKc2DOqQbM2KSqAYHeN1pCUflaNa0fTsCHDgGG/loGLzsA47WA2Q/4Z8sWXUHhQ9o+nAwFUkB/ysAYrzQ1IU0cqYmVxAGcifoU4IwhHmIgTI4FIuQEUAWI2lNGKFQIHZcxVmuElVgQgL0RmBDwAZOxgIQEEylcpBQxY6MRJI4weo4PAJ5pZM1rMnHuTcA36bnjZH+VvGIxVB8HZjvghyVbvCkw9hsKLXRuCSn+rzIEqtMzgnEKTQnkPJwzCJwCHuqkbTUcAeIERNSeAajCWoZqe2XcctuqBJahgUdmGUEAeEswgcIR0XoVWGqD43ZFqZn49CtN11rkgE8d3g7487LlFQ1CqwCtgDmVIH9bDIsPWeJ/1vYUa/T+kwq9j5WKRDQ5UwmFkHmAPRSOQOpJhSHtlcT2fI6gCiYigWGjsAplkBoAAWAMOBPVbSBtEOhotCMZjVYwdioXN9LGmwX844Xwv38lQyK/KmGQHwQCMmYJRD6UM7nbQms+D6V9n/cQ6VYAdwLUIiBSRQVAt5IaArwHgbQ9jFESNu1ItKbtLIFhIlQ73UQOwAlQmrRbzTaIc5c3fHyiMv9Cvb/2sOdicb1i8qpGMleAH7T2BPL6T9aYu3Im7CHQeXsvDQ1CZQVAI6QY0fZ0DqRaVEIXoF0gXgDVMXv7CoVeFvt0KPP+WGPoWwucu/Pl9nMvJq9ZLP0HgXFg89U8h+uZ7SWvRh4i7vyGZNNZ3DcOA171MPDAawhwNL8/sfllzuQ2M/EL9hdejojq5bFrTT1M/RmvVtj3Pclr/t8c7gRswdjfFzjHoOfCh7wcIcU9dYlj6927Xo1+7sVkz0d7XkU5HnDq3ftjadWVdNPOU6J/bSLd1pBWb+bdktcaHvA6AASAo4Bq5s1Xmz79tQKpoN3L/6XJAy7x2VXkzdfe9jJmEy9HXheAAHA00sdJ6QFR/Ye/FmCq+g9edeURSF8z5/Xd5TXvA3eXZRxcam00zMDukeX+nHwj88nYt/jnIvC+HvK6A1SA7jPhb4wJqtSOofBS5NpMsuKtLn3Pha/iGO+lyOsOEACWAXm1we+Ywm4iLPwzjz/mJNuSufT049vHJl5Xed36wF3laKClLjhbJHtEQdW9Gw2qZ97f5505940AD3iDaOCo3GNzx4HxVlZz0Z7ui8o/KrLlxzr3x9e6bHuTN4QGjsrbXHwXRJywnP8C7WP5AkHkjQQPeINpYEfoHpP7hRrapEr/0H5LL2fR8W/z8d/gNZymvRR5IwLEnYBlW/i9gJiACNBYXfOU41/ExeL1kjdUEx6V4wEnLlxCkCogfeqaZ74R4b3h5d6wNP/esDT/9S7Hi8n/B3LrBEUxxEM2AAAAAElFTkSuQmCC", "public": true 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 index 6eda23ef00..98a7fafbe3 100644 --- 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 @@ -12,10 +12,8 @@ "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\":[\"tb-image;/api/images/system/map_marker_image_0_(1).png\",\"tb-image;/api/images/system/map_marker_image_1_(1).png\",\"tb-image;/api/images/system/map_marker_image_2_(1).png\"],\"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\":{}}" + "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;\"}]}],\"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\":[\"tb-image;/api/images/system/map_marker_image_0_(1).png\",\"tb-image;/api/images/system/map_marker_image_1_(1).png\",\"tb-image;/api/images/system/map_marker_image_2_(1).png\"],\"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\":{}}" }, "tags": [ "mapping", 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 index 92bb2516a9..cf562b848e 100644 --- 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 @@ -12,10 +12,8 @@ "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\":[\"tb-image;/api/images/system/map_marker_image_0_(1).png\",\"tb-image;/api/images/system/map_marker_image_1_(1).png\",\"tb-image;/api/images/system/map_marker_image_2_(1).png\"],\"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\":{}}" + "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;\"}]}],\"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\":[\"tb-image;/api/images/system/map_marker_image_0_(1).png\",\"tb-image;/api/images/system/map_marker_image_1_(1).png\",\"tb-image;/api/images/system/map_marker_image_2_(1).png\"],\"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\":{}}" }, "tags": [ "mapping", 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 index 3de304c8fc..45991884f6 100644 --- 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 @@ -12,10 +12,8 @@ "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\":[\"tb-image;/api/images/system/map_marker_image_0_(1).png\",\"tb-image;/api/images/system/map_marker_image_1_(1).png\",\"tb-image;/api/images/system/map_marker_image_2_(1).png\"],\"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\":{}}" + "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;\"}]}],\"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\":[\"tb-image;/api/images/system/map_marker_image_0_(1).png\",\"tb-image;/api/images/system/map_marker_image_1_(1).png\",\"tb-image;/api/images/system/map_marker_image_2_(1).png\"],\"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\":{}}" }, "tags": [ "mapping", 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 index 409731aa12..ed62302240 100644 --- a/application/src/main/data/json/system/widget_types/rpc_button.json +++ b/application/src/main/data/json/system/widget_types/rpc_button.json @@ -12,10 +12,9 @@ "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\":{}}" + "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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "command", 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 index e65e9a1204..67a7fd15b1 100644 --- 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 @@ -11,11 +11,10 @@ "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.targetEntityName && subscription.targetEntityName.length) {\n deviceName = subscription.targetEntityName;\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": "", + "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.targetEntityName && subscription.targetEntityName.length) {\n deviceName = subscription.targetEntityName;\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}", "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\":{}}" + "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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "command", 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 index 9c14e1d155..f25a141494 100644 --- 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 @@ -12,10 +12,9 @@ "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.targetEntityName && subscription.targetEntityName.length) {\n deviceName = subscription.targetEntityName;\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 (command.trim() === 'exit') {\n if (!simulated) {\n self.ctx.controlApi.sendTwoWayCommand('sendCommand', {\n command: 'exit',\n cwd: cwd\n }, requestTimeout).subscribe();\n }\n this.disable();\n return;\n }\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\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, function (remoteTermInfo) {\n if (remoteTermInfo) {\n terminal.echo('Remote platform info:');\n if (remoteTermInfo.platform) {\n terminal.echo('OS: [[b;#fff;]' + remoteTermInfo.platform + ']');\n } else {\n terminal.echo('OS: [[;#f00;]Unknown]');\n }\n if (remoteTermInfo.release) {\n terminal.echo('OS release: [[b;#fff;]' + remoteTermInfo.release + ']');\n } else {\n terminal.echo('OS release: [[;#f00;]Unknown]');\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(response) {\n let termInfo;\n if (typeof response === 'string') {\n try {\n termInfo = JSON.parse(response);\n } catch (e) {\n terminal.error('Error parsing response: ' + e);\n callback(null);\n return;\n }\n } else {\n termInfo = response;\n }\n if (termInfo && termInfo.cwd) {\n cwd = termInfo.cwd;\n }\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 < commandStatusResponse.data.length; i++) {\n var dataElement = commandStatusResponse.data[i];\n if (dataElement.stdout) {\n terminal.echo(dataElement.stdout);\n }\n if (dataElement.stderr) {\n terminal.error(dataElement.stderr);\n }\n }\n if (commandStatusResponse.done) {\n commandExecuting = false;\n cwd = commandStatusResponse.cwd;\n terminal.resume();\n } else {\n var interval = commandStatusPollingInterval;\n if (!commandStatusResponse.data.length) {\n interval *= 5;\n }\n setTimeout(pollCommandStatus.bind(null, terminal), interval);\n }\n },\n function () {\n commandExecuting = false;\n onRpcError(terminal);\n }\n );\n}\n\nself.onResize = function () {\n if (terminal) {\n terminal.resize(self.ctx.width, self.ctx.height);\n }\n};\n\nself.onDestroy = function() {\n};", - "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-rpc-shell-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 remote shell\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "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 remote shell\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "command", diff --git a/application/src/main/data/json/system/widget_types/service_rpc.json b/application/src/main/data/json/system/widget_types/service_rpc.json index eab1bccf0a..3eb8aefa34 100644 --- a/application/src/main/data/json/system/widget_types/service_rpc.json +++ b/application/src/main/data/json/system/widget_types/service_rpc.json @@ -35,4 +35,4 @@ "inter-process communication", "server request" ] -} \ No newline at end of file +} 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 index 4784808baa..4a6fa85a8d 100644 --- a/application/src/main/data/json/system/widget_types/signal_strength.json +++ b/application/src/main/data/json/system/widget_types/signal_strength.json @@ -15,7 +15,7 @@ "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)\",\"noSignalRssiValue\":-100,\"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)\"}" + "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\"]}}],\"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)\",\"noSignalRssiValue\":-100,\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "wifi", 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 index 812aa8694b..130fea3cc9 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 1c22c060f2..e20970e9a8 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_air_quality_index_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_air_quality_index_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index d494338fee..8174b77794 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 66364f8d06..25a13d1989 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/CO-simple-value-and-chart-card-background.png\",\"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}}" + "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;/api/images/system/CO-simple-value-and-chart-card-background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index bcaebb7f73..9f3d83f41e 100644 --- a/application/src/main/data/json/system/widget_types/simple_card.json +++ b/application/src/main/data/json/system/widget_types/simple_card.json @@ -12,12 +12,10 @@ "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}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 9e4be18356..4a50cd60a7 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index d6ef0088b4..e3ff71d64c 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_co2_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_co2_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index d957a82a58..80079e409f 100644 --- 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 @@ -20,7 +20,7 @@ "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\"}" + "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\":\"%\",\"margin\":\"0px\",\"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}}" }, "tags": [ "productivity", 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 index 9fe02a7725..f1b9f961fe 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple_efficiency_chart_card_background.png\",\"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\"}" + "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;/api/images/system/simple_efficiency_chart_card_background.png\",\"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\":\"%\",\"margin\":\"0px\",\"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}}" }, "tags": [ "productivity", 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 index 55a4da3aba..a808a4c930 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index dab9a38433..07976f61a4 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_flooding_level_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_flooding_level_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index 05fd114507..8061c87659 100644 --- 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 @@ -20,7 +20,7 @@ "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\"}" + "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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "liquid", 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 index bea92879b0..436d45b9c4 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple_flow_rate_chart_card_background.png\",\"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\"}" + "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;/api/images/system/simple_flow_rate_chart_card_background.png\",\"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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "liquid", 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 index 57417c5986..647c5fd971 100644 --- 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 @@ -20,7 +20,7 @@ "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\"}" + "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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "fluid pressure", 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 index e7b8f22933..b6b8fdd8f6 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple_pressure_chart_card_background.png\",\"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\"}" + "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;/api/images/system/simple_pressure_chart_card_background.png\",\"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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "fluid pressure", 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 index 1358e21aae..76fda7bc37 100644 --- a/application/src/main/data/json/system/widget_types/simple_gauge.json +++ b/application/src/main/data/json/system/widget_types/simple_gauge.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-gauge-basic-config", - "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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "resources": [ { 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 index e57b06fdd1..28a8c2e5d9 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 81f971a8ba..dbe53fb714 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_ground_temperature_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_ground_temperature_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index e45f5f63c5..9532878d13 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\":\"%\",\"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}}" }, "tags": [ "weather", 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 index 484c9c1a08..21853dafd1 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_humidity_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_humidity_chart_card_with_background_system_widget_background.png\",\"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\":\"%\",\"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}}" }, "tags": [ "weather", 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 index 43c8a79361..0c525ea708 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 373fac46dc..a8f7092e15 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_illuminance_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_illuminance_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index 81764c775f..58f093daff 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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,\"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}}" }, "tags": [ "weather", 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 index 283ff4a8e0..a729bdc5b0 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple-IAI-value-and-chart-card-background.png\",\"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}}" + "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;/api/images/system/simple-IAI-value-and-chart-card-background.png\",\"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,\"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}}" }, "tags": [ "weather", 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 index 2205278c9f..dd8a5c4913 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\":\"%\",\"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}}" }, "tags": [ "weather", 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 index d71fc4a5bb..81e32813f3 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_leaf_wetness_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_leaf_wetness_chart_card_with_background_system_widget_background.png\",\"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\":\"%\",\"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}}" }, "tags": [ "weather", 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 index 9526b0512b..562cad5f60 100644 --- 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 @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-gauge-basic-config", - "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\":{},\"configMode\":\"basic\"}" + "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;\"}]}],\"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\":{},\"showLegend\":false,\"actions\":{},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 0a1c1d8657..2caf26bcf0 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 892238dc9b..72f4425456 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple-NO2-value-and-chart-card-background.png\",\"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}}" + "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;/api/images/system/simple-NO2-value-and-chart-card-background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index d33eed776f..6fd4708209 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 225d30b14c..41a360c8a6 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_noise_level_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_noise_level_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index b2c9eb5e97..acb65d35f6 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 88995bb4f5..129a0e2056 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple-ozone-value-and-chart-card-background.png\",\"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}}" + "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;/api/images/system/simple-ozone-value-and-chart-card-background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index 6e7d5c1369..a0e450dd2f 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index a0b3d74e54..d10f51d454 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_pm10_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_pm10_chart_card_with_background_system_widget_background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index 25502227f3..5a9110d70d 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index ccdf3ba0a1..b8f90f9b44 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_pm2_5_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_pm2_5_chart_card_with_background_system_widget_background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index befea52bb3..766a1e1956 100644 --- 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 @@ -20,7 +20,7 @@ "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\"}" + "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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "power", 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 index f0bb8ba9e9..58c5fc8beb 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple_power_consumption_chart_card_background.png\",\"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\"}" + "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;/api/images/system/simple_power_consumption_chart_card_background.png\",\"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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "power", 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 index dcc4b11510..62178bbf2a 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 0c177e6364..fde44a0366 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_pressure_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_pressure_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index a84a49e2de..92673ec6d2 100644 --- 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 @@ -20,7 +20,7 @@ "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\"}" + "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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "vibration", 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 index 5cb064c807..b077b31919 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple_vibration_chart_card_background.png\",\"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\"}" + "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;/api/images/system/simple_vibration_chart_card_background.png\",\"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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "vibration", 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 index 27faab79ac..72b811f2d1 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 84e90026ad..0ee5a825ae 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_radon_level_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_radon_level_chart_card_with_background_system_widget_background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index ddbf9c66ee..8cb70f6b3d 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index c5f82d49b9..92927e78de 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_rainfall_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_rainfall_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index 9e39e6ad0a..5050fc8184 100644 --- 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 @@ -20,7 +20,7 @@ "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\"}" + "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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "angular speed", 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 index 2f338515c5..c1433bb2b4 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/simple_rotational_speed_chart_card_background.png\",\"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\"}" + "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;/api/images/system/simple_rotational_speed_chart_card_background.png\",\"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\",\"margin\":\"0px\",\"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}}" }, "tags": [ "angular speed", 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 index e23671c563..f4509d1669 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 48010dbcc9..706f03e477 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_snow_depth_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_snow_depth_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index 3f9e4450aa..8627fc8430 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\":\"%\",\"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}}" }, "tags": [ "weather", 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 index 916c7d7d37..096e88941f 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_soil_moisture_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_soil_moisture_chart_card_with_background_system_widget_background.png\",\"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\":\"%\",\"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}}" }, "tags": [ "weather", 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 index e3ebfac43a..c009adb989 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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²\",\"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}}" }, "tags": [ "weather", 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 index f3fc53349f..39b76d6c41 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_solar_radiation_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_solar_radiation_chart_card_with_background_system_widget_background.png\",\"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²\",\"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}}" }, "tags": [ "weather", 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 index 0a91102cdc..193e1f9042 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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³\",\"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}}" }, "tags": [ "environment", 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 index 2a016e3072..f13af9970c 100644 --- 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 @@ -20,7 +20,7 @@ "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;/api/images/system/SO2-simple-value-and-chart-card-background.png\",\"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}}" + "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;/api/images/system/SO2-simple-value-and-chart-card-background.png\",\"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³\",\"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}}" }, "tags": [ "environment", 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 index bac4ec201e..5faeaace2a 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "temperature", 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 index 50145be107..c0d44315ce 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_temperature_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_temperature_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "temperature", 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 index d10f55833b..25792079bc 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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,\"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}}" }, "tags": [ "weather", 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 index a1ba49e7bc..a7173133ab 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_uv_index_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_uv_index_chart_card_with_background_system_widget_background.png\",\"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,\"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}}" }, "tags": [ "weather", 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 index 0fea1b6682..342c15c917 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "resources": [ { 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 index a045af5ef6..e1be525d80 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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²\",\"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}}" }, "tags": [ "weather", 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 index 9e39eed359..367dece05a 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_vibration_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_vibration_chart_card_with_background_system_widget_background.png\",\"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²\",\"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}}" }, "tags": [ "weather", 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 index 7f098c8d96..d7bc882f73 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index 6deff18090..c61471fa6d 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_visibility_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_visibility_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index d176526dcb..3d60d3479a 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "environment", 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 index 1eba6b3fd0..7adedd8c81 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_volatile_organic_compounds_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_volatile_organic_compounds_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "environment", 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 index 6660f67d78..ea60ba3edf 100644 --- 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 @@ -20,7 +20,7 @@ "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}}" + "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\",\"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}}" }, "tags": [ "weather", 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 index fadebc2925..d08cbc8a49 100644 --- 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 @@ -20,7 +20,7 @@ "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\":\"tb-image;/api/images/system/simple_wind_speed_chart_card_with_background_system_widget_background.png\",\"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}}" + "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\":\"tb-image;/api/images/system/simple_wind_speed_chart_card_with_background_system_widget_background.png\",\"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\",\"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}}" }, "tags": [ "weather", 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 index 6da8532586..74cbadc52f 100644 --- 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 @@ -12,10 +12,9 @@ "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\":\"\"}" + "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},\"showLegend\":false,\"actions\":{},\"decimals\":2,\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\"}" }, "tags": [ "command", 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 index 1d8f56a2fb..031db09beb 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index c9f1b12488..9c294fbe70 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/snow_depth_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/snow_depth_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 03df119344..65e0e6311a 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index a66fbc1532..38864fb686 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/soil_moisture_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/soil_moisture_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index efd88de86c..0acba2ec65 100644 --- 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 @@ -17,7 +17,7 @@ "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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index da1ec3617a..d3e63ede6a 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/soil_moisture_progress_bar_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/soil_moisture_progress_bar_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "progress", 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 index 286668be56..43e5cfc398 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 7264f59fe2..c49effbfec 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/solar_radiation_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/solar_radiation_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index e0e3bc4732..820ca79d50 100644 --- a/application/src/main/data/json/system/widget_types/speed_gauge.json +++ b/application/src/main/data/json/system/widget_types/speed_gauge.json @@ -17,7 +17,7 @@ "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\"}" + "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;\"}]}],\"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\"}" }, "resources": [ { 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 index d2a3031318..2b30a6b22f 100644 --- a/application/src/main/data/json/system/widget_types/state_chart.json +++ b/application/src/main/data/json/system/widget_types/state_chart.json @@ -12,15 +12,15 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n self.ctx.$scope.timeSeriesChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n chartType: 'state',\n previewWidth: '80%',\n embedTitlePanel: true,\n embedActionsPanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('state'),\n defaultDataKeysFunction: function() {\n return [{ name: 'state', label: 'State', type: 'timeseries', units: '', decimals: 0 }];\n }\n };\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}", - "latestDataKeySettingsSchema": "{}", + "settingsForm": [], + "dataKeySettingsForm": [], + "latestDataKeySettingsForm": [], "settingsDirective": "tb-time-series-chart-widget-settings", "dataKeySettingsDirective": "tb-time-series-chart-key-settings", "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-time-series-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"yAxisId\":\"default\",\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":true,\"stepType\":\"end\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"enablePointLabelBackground\":false,\"pointLabelBackground\":\"rgba(255,255,255,0.56)\",\"pointShape\":\"circle\",\"pointSize\":12,\"fillAreaSettings\":{\"type\":\"opacity\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"enableLabelBackground\":false,\"labelBackground\":\"rgba(255,255,255,0.56)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"tooltipValueFormatter\":\"\"},\"_hash\":0.676226248393859,\"funcBody\":\"return Math.random() > 0.5 ? true : false;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null,\"units\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#FFC107\",\"settings\":{\"yAxisId\":\"default\",\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":true,\"stepType\":\"end\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"enablePointLabelBackground\":false,\"pointLabelBackground\":\"rgba(255,255,255,0.56)\",\"pointShape\":\"circle\",\"pointSize\":12,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"enableLabelBackground\":false,\"labelBackground\":\"rgba(255,255,255,0.56)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"tooltipValueFormatter\":null},\"_hash\":0.1106990458957191,\"funcBody\":\"return Math.random() <= 0.5 ? true : false;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null,\"units\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":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\":\"NONE\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":\"\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0,\"interval\":null,\"splitNumber\":null,\"min\":null,\"max\":null,\"ticksGenerator\":\"\"}},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"showLegend\":true,\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipValueFormatter\":\"\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{\"millisecond\":\"MMM dd yyyy HH:mm:ss\"}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"12px\",\"states\":[{\"label\":\"Off\",\"value\":0,\"sourceType\":\"constant\",\"sourceValue\":false},{\"label\":\"On\",\"value\":1,\"sourceType\":\"constant\",\"sourceValue\":true}]},\"title\":\"State 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\":\"\",\"decimals\":null,\"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\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"yAxisId\":\"default\",\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":true,\"stepType\":\"end\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"enablePointLabelBackground\":false,\"pointLabelBackground\":\"rgba(255,255,255,0.56)\",\"pointShape\":\"circle\",\"pointSize\":12,\"fillAreaSettings\":{\"type\":\"opacity\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"enableLabelBackground\":false,\"labelBackground\":\"rgba(255,255,255,0.56)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"tooltipValueFormatter\":\"\"},\"_hash\":0.676226248393859,\"funcBody\":\"return Math.random() > 0.5 ? true : false;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null,\"units\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#FFC107\",\"settings\":{\"yAxisId\":\"default\",\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":true,\"stepType\":\"end\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"enablePointLabelBackground\":false,\"pointLabelBackground\":\"rgba(255,255,255,0.56)\",\"pointShape\":\"circle\",\"pointSize\":12,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"enableLabelBackground\":false,\"labelBackground\":\"rgba(255,255,255,0.56)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"tooltipValueFormatter\":null},\"_hash\":0.1106990458957191,\"funcBody\":\"return Math.random() <= 0.5 ? true : false;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null,\"units\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":\"\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0,\"interval\":null,\"splitNumber\":null,\"min\":null,\"max\":null,\"ticksGenerator\":\"\"}},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"showLegend\":true,\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipValueFormatter\":\"\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{\"millisecond\":\"MMM dd yyyy HH:mm:ss\"}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"12px\",\"states\":[{\"label\":\"Off\",\"value\":0,\"sourceType\":\"constant\",\"sourceValue\":false},{\"label\":\"On\",\"value\":1,\"sourceType\":\"constant\",\"sourceValue\":true}]},\"title\":\"State chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":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\":null,\"noDataDisplayMessage\":\"\",\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\",\"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}}" }, "tags": [ "chart", diff --git a/application/src/main/data/json/system/widget_types/state_chart_deprecated.json b/application/src/main/data/json/system/widget_types/state_chart_deprecated.json index e5bfdda095..9c33341ce4 100644 --- a/application/src/main/data/json/system/widget_types/state_chart_deprecated.json +++ b/application/src/main/data/json/system/widget_types/state_chart_deprecated.json @@ -12,14 +12,12 @@ "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\"}" + "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;\"}]}],\"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\"}" }, "resources": [ { 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 index 25b950df3e..166a0842c5 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "enviroment", 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 index a097776152..7a0c911bf4 100644 --- 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 @@ -17,7 +17,7 @@ "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;/api/images/system/SO2-value-card-background.png\",\"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}}" + "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\"]}}],\"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;/api/images/system/SO2-value-card-background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "enviroment", 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 index eab256525e..8747cf0131 100644 --- a/application/src/main/data/json/system/widget_types/switch_control.json +++ b/application/src/main/data/json/system/widget_types/switch_control.json @@ -12,10 +12,9 @@ "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}" + "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},\"showLegend\":false,\"actions\":{},\"decimals\":2}" }, "tags": [ "command", 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 index 1308512c13..93219788e6 100644 --- a/application/src/main/data/json/system/widget_types/temperature_card.json +++ b/application/src/main/data/json/system/widget_types/temperature_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index f711b715d5..fee17a4a87 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/temperature_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/temperature_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "temperature", 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 index 0db4b047f0..a89477ccbf 100644 --- a/application/src/main/data/json/system/widget_types/temperature_gauge.json +++ b/application/src/main/data/json/system/widget_types/temperature_gauge.json @@ -18,7 +18,7 @@ "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\"}" + "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\"]}}],\"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\",\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" }, "tags": [ "temperature", 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 index a508264166..77114b91cb 100644 --- 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 @@ -17,7 +17,7 @@ "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\"}" + "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;\"}]}],\"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\"}" }, "resources": [ { 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 index ec41717ca4..1c30d6df27 100644 --- a/application/src/main/data/json/system/widget_types/tencent_map.json +++ b/application/src/main/data/json/system/widget_types/tencent_map.json @@ -12,10 +12,8 @@ "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-legacy", - "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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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\":{}}" + "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}]}],\"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\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"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},\"showLegend\":false,\"actions\":{}}" }, "tags": [ "mapping", 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 index f1270c3e2b..d75e664ec5 100644 --- a/application/src/main/data/json/system/widget_types/thermometer_scale.json +++ b/application/src/main/data/json/system/widget_types/thermometer_scale.json @@ -17,7 +17,7 @@ "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\"}" + "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;\"}]}],\"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\"}" }, "tags": [ "pyrometer", diff --git a/application/src/main/data/json/system/widget_types/time_series_chart.json b/application/src/main/data/json/system/widget_types/time_series_chart.json index 80afebe8bf..2e6c0dec03 100644 --- a/application/src/main/data/json/system/widget_types/time_series_chart.json +++ b/application/src/main/data/json/system/widget_types/time_series_chart.json @@ -20,7 +20,7 @@ "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-time-series-chart-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"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 < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"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\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":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\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Time series 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\":\"\",\"decimals\":null,\"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\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"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 < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"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\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"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}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Time series chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":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\":null,\"noDataDisplayMessage\":\"\",\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\",\"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}}" }, "tags": [ "chart", 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 index 83ef6d6bee..7c956dbef4 100644 --- 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 @@ -12,14 +12,12 @@ "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\"}" + "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;\"}]}],\"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\"}" }, "resources": [ { 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 index 8343eb918c..24443cfecf 100644 --- a/application/src/main/data/json/system/widget_types/timeseries_table.json +++ b/application/src/main/data/json/system/widget_types/timeseries_table.json @@ -1,6 +1,6 @@ { "fqn": "cards.timeseries_table", - "name": "Timeseries table", + "name": "Time series table", "deprecated": false, "image": "tb-image;/api/images/system/timeseries_table_system_widget_image.png", "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.", @@ -17,7 +17,7 @@ "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\"}" + "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\":[]}],\"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,\"dateFormat\":{\"format\":\"yyyy-MM-dd HH:mm:ss\"},\"displayPagination\":true,\"useEntityLabel\":false,\"defaultPageSize\":10,\"pageStepCount\":3,\"pageStepIncrement\":10,\"hideEmptyLines\":false,\"disableStickyHeader\":false,\"useRowStyleFunction\":false,\"rowStyleFunction\":\"\",\"sortOrder\":{\"property\":\"createdTime\",\"direction\":\"DESC\"}},\"title\":\"Time series table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"titleIcon\":null}" }, "resources": [ { @@ -32,4 +32,4 @@ "public": true } ] -} \ No newline at end of file +} diff --git a/application/src/main/data/json/system/widget_types/trip_map.json b/application/src/main/data/json/system/widget_types/trip_map.json index 8f54f64b6b..b68bc62981 100644 --- a/application/src/main/data/json/system/widget_types/trip_map.json +++ b/application/src/main/data/json/system/widget_types/trip_map.json @@ -20,7 +20,7 @@ "latestDataKeySettingsDirective": "", "hasBasicMode": true, "basicModeDirective": "tb-map-basic-config", - "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[],\"trips\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"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)];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"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)];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"tripMarkerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"icon\":\"arrow_forward\",\"size\":48,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"/assets/markers/tripShape1.svg\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":0.5,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null},\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-0.5},\"click\":{\"type\":\"doNothing\"},\"edit\":{\"enabledActions\":[],\"snappable\":false},\"rotateMarker\":true,\"offsetAngle\":0,\"showPath\":true,\"pathStrokeWeight\":2,\"pathStrokeColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"usePathDecorator\":false,\"pathDecoratorSymbol\":\"arrowHead\",\"pathDecoratorSymbolSize\":10,\"pathDecoratorSymbolColor\":\"#307FE5\",\"pathDecoratorOffset\":20,\"pathEndDecoratorOffset\":20,\"pathDecoratorRepeat\":20,\"showPoints\":false,\"pointSize\":10,\"pointColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"pointTooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-1}}],\"tripTimeline\":{\"showTimelineControl\":true}},\"title\":\"Trip Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"assistant_navigation\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":null,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":null},\"titleColor\":null,\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"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\":\"24px\"}" + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"selectedTab\":1,\"history\":{\"historyType\":0,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":5000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"layers\":[{\"label\":\"{i18n:widgets.maps.layer.roadmap}\",\"provider\":\"openstreet\",\"layerType\":\"OpenStreetMap.Mapnik\"},{\"label\":\"{i18n:widgets.maps.layer.satellite}\",\"provider\":\"openstreet\",\"layerType\":\"Esri.WorldImagery\"},{\"label\":\"{i18n:widgets.maps.layer.hybrid}\",\"provider\":\"openstreet\",\"layerType\":\"Esri.WorldImagery\",\"referenceLayer\":\"openstreetmap_hybrid\"}],\"trips\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"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)];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"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)];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"tripMarkerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"icon\":\"arrow_forward\",\"size\":48,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"/assets/markers/tripShape1.svg\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":0.5,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null},\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-0.5},\"click\":{\"type\":\"doNothing\"},\"edit\":{\"enabledActions\":[],\"snappable\":false},\"rotateMarker\":true,\"offsetAngle\":0,\"showPath\":true,\"pathStrokeWeight\":2,\"pathStrokeColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"usePathDecorator\":false,\"pathDecoratorSymbol\":\"arrowHead\",\"pathDecoratorSymbolSize\":10,\"pathDecoratorSymbolColor\":\"#307FE5\",\"pathDecoratorOffset\":20,\"pathEndDecoratorOffset\":20,\"pathDecoratorRepeat\":20,\"showPoints\":false,\"pointSize\":10,\"pointColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"pointTooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-1}}],\"markers\":[],\"polygons\":[],\"circles\":[],\"polylines\":[],\"additionalDataSources\":[],\"controlsPosition\":\"topleft\",\"zoomActions\":[\"scroll\",\"doubleClick\",\"controlButtons\"],\"scales\":[],\"dragModeButton\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"defaultCenterPosition\":\"0,0\",\"defaultZoomLevel\":null,\"minZoomLevel\":16,\"mapPageSize\":16384,\"mapActionButtons\":[],\"tripTimeline\":{\"showTimelineControl\":true,\"timeStep\":1000,\"speedOptions\":[1,5,10,15,25],\"showTimestamp\":true,\"timestampFormat\":{\"format\":\"yyyy-MM-dd HH:mm:ss\",\"lastUpdateAgo\":false,\"custom\":false,\"auto\":false},\"snapToRealLocation\":false,\"locationSnapFilter\":\"return true;\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"8px\"},\"title\":\"Trip Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"assistant_navigation\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":null,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":null},\"titleColor\":null,\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"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\":\"24px\"}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/two_segment_button.json b/application/src/main/data/json/system/widget_types/two_segment_button.json index d32d139dce..e9e77fac75 100644 --- a/application/src/main/data/json/system/widget_types/two_segment_button.json +++ b/application/src/main/data/json/system/widget_types/two_segment_button.json @@ -12,12 +12,11 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n maxDatasources: 1,\n maxDataKeys: 0,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '80px',\n embedTitlePanel: true,\n overflowVisible: true,\n hideDataSettings: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-segmented-button-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-segmented-button-basic-config", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#E8E8E8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"initialState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":true,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"},\"executeRpc\":{\"method\":null,\"requestTimeout\":null,\"requestPersistent\":null,\"persistentPollingInterval\":null}},\"leftButtonClick\":{\"type\":\"updateDashboardState\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null},\"rightButtonClick\":{\"type\":\"updateDashboardState\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null},\"disabledState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":false,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"appearance\":{\"layout\":\"squared\",\"autoScale\":true,\"cardBorder\":1,\"cardBorderColor\":\"#305680\",\"leftAppearance\":{\"showLabel\":true,\"label\":\"Traditional\",\"labelFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":14,\"sizeUnit\":\"px\",\"lineHeight\":\"18px\"},\"showIcon\":true,\"icon\":\"rocket_launch\",\"iconSize\":24,\"iconSizeUnit\":\"px\"},\"rightAppearance\":{\"showLabel\":true,\"label\":\"Hi-Perf\",\"labelFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":14,\"sizeUnit\":\"px\",\"lineHeight\":\"18px\"},\"showIcon\":true,\"icon\":\"rocket_launch\",\"iconSize\":24,\"iconSizeUnit\":\"px\"},\"selectedStyle\":{\"mainColor\":\"#FFFFFF\",\"backgroundColor\":\"#305680\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"disabled\":null}},\"unselectedStyle\":{\"mainColor\":\"#000000C2\",\"backgroundColor\":\"#E8E8E8\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"disabled\":null}}}},\"title\":\"Segmented button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"datasources\":[],\"actions\":{\"click\":[{\"id\":\"56ebc389-f91d-fc25-36ef-0d18329fbeda\",\"name\":\"onClick\",\"icon\":\"more_horiz\",\"type\":\"doNothing\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null,\"openInSeparateDialog\":false,\"openInPopover\":false}]},\"borderRadius\":\"4px\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}" + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#E8E8E8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"initialState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":true,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"},\"executeRpc\":{\"method\":null,\"requestTimeout\":null,\"requestPersistent\":null,\"persistentPollingInterval\":null}},\"leftButtonClick\":{\"type\":\"updateDashboardState\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null},\"rightButtonClick\":{\"type\":\"updateDashboardState\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null},\"disabledState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":false,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"appearance\":{\"layout\":\"squared\",\"autoScale\":true,\"cardBorder\":1,\"cardBorderColor\":\"#305680\",\"leftAppearance\":{\"showLabel\":true,\"label\":\"Traditional\",\"labelFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":14,\"sizeUnit\":\"px\",\"lineHeight\":\"18px\"},\"showIcon\":true,\"icon\":\"rocket_launch\",\"iconSize\":24,\"iconSizeUnit\":\"px\"},\"rightAppearance\":{\"showLabel\":true,\"label\":\"Hi-Perf\",\"labelFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":14,\"sizeUnit\":\"px\",\"lineHeight\":\"18px\"},\"showIcon\":true,\"icon\":\"rocket_launch\",\"iconSize\":24,\"iconSizeUnit\":\"px\"},\"selectedStyle\":{\"mainColor\":\"#FFFFFF\",\"backgroundColor\":\"#305680\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"disabled\":null}},\"unselectedStyle\":{\"mainColor\":\"#000000C2\",\"backgroundColor\":\"#E8E8E8\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"disabled\":null}}}},\"title\":\"Segmented button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"datasources\":[],\"actions\":{\"click\":[{\"id\":\"56ebc389-f91d-fc25-36ef-0d18329fbeda\",\"name\":\"onClick\",\"icon\":\"more_horiz\",\"type\":\"doNothing\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null,\"openInSeparateDialog\":false,\"openInPopover\":false}]},\"borderRadius\":\"4px\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" }, "resources": [ { diff --git a/application/src/main/data/json/system/widget_types/unread_notifications.json b/application/src/main/data/json/system/widget_types/unread_notifications.json index b6f97724b8..7d699e7018 100644 --- a/application/src/main/data/json/system/widget_types/unread_notifications.json +++ b/application/src/main/data/json/system/widget_types/unread_notifications.json @@ -11,12 +11,10 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n self.ctx.$scope.unreadNotificationWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", "settingsDirective": "tb-unread-notification-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-unread-notification-basic-config", - "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\":\"0\",\"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}\",\"maxNotificationDisplay\":6,\"showCounter\":true,\"counterValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"\"},\"counterValueColor\":\"#fff\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"enableViewAll\":true,\"enableFilter\":true,\"enableMarkAsRead\":true},\"title\":\"Unread notification\",\"dropShadow\":true,\"configMode\":\"basic\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"#000000\",\"showTitleIcon\":true,\"iconSize\":\"22px\",\"titleIcon\":\"notifications\",\"iconColor\":\"#000000\",\"actions\":{},\"enableFullscreen\":false,\"borderRadius\":\"4px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + "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;\"}]}],\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0\",\"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}\",\"maxNotificationDisplay\":6,\"showCounter\":true,\"counterValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"\"},\"counterValueColor\":\"#fff\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"enableViewAll\":true,\"enableFilter\":true,\"enableMarkAsRead\":true},\"title\":\"Unread notification\",\"dropShadow\":true,\"configMode\":\"basic\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"#000000\",\"showTitleIcon\":true,\"iconSize\":\"22px\",\"titleIcon\":\"notifications\",\"iconColor\":\"#000000\",\"actions\":{},\"enableFullscreen\":false,\"borderRadius\":\"4px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" }, "resources": [ { 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 index e338b7af15..8d88f7d168 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 763ba93bc9..0c48a999d4 100644 --- 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 @@ -12,10 +12,9 @@ "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\":[]}" + "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},\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[]}" }, "tags": [ "command", 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 index 20aae30b30..760dd2e932 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 579e233d59..06ef1e46b6 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 95c648204c..f33c05bc6a 100644 --- 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 @@ -12,10 +12,9 @@ "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}" + "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;\"}]}],\"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}" }, "resources": [ { 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 index 9724e8e6b6..1542eed426 100644 --- 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 @@ -1,9 +1,9 @@ { "fqn": "input_widgets.update_location_timeseries", - "name": "Update location timeseries", + "name": "Update location time series", "deprecated": false, "image": "tb-image;/api/images/system/update_shared_location_attribute_system_widget_image.png", - "description": "Simple form to input new location for pre-defined timeseries keys.", + "description": "Simple form to input new location for pre-defined time series keys.", "descriptor": { "type": "latest", "sizeX": 7.5, @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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 time series\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index f4aee4f0cf..5c2327d4c5 100644 --- 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 @@ -12,11 +12,9 @@ "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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 83d6d69b66..a0a2a1ebbe 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index c7f2c27dab..9629a0d70c 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 3baca60720..8e0cb20131 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 6b6432331c..8e5373ecd0 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index f00183c76d..bc4506b462 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 59cb78339f..97b8d81e71 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 8e1eeabfad..b303cf5113 100644 --- 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 @@ -12,10 +12,9 @@ "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 if (utils.isDefinedAndNotNull(settings.minLength)) {\n validators.push($scope.validators.minLength(settings.minLength));\n }\n if (utils.isDefinedAndNotNull(settings.maxLength)) {\n validators.push($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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index d47e9087d2..1c5ca79f40 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 6f40c1f047..13091ffedf 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 1e44faea0e..3a93247ce3 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 0aa39ac649..83d6f8659e 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 349540df10..69810272af 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 4f15d1f29a..e88e92cf78 100644 --- 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 @@ -12,10 +12,9 @@ "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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 135fd74b46..0882d1fc41 100644 --- 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 @@ -12,10 +12,9 @@ "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 if (utils.isDefinedAndNotNull(settings.minLength)) {\n validators.push($scope.validators.minLength(settings.minLength));\n }\n if (utils.isDefinedAndNotNull(settings.maxLength)) {\n validators.push($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\":{}}" + "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));\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 93bc4c4be2..40c3c0d7d5 100644 --- 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 @@ -12,10 +12,9 @@ "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 var validators = [];\n if (utils.isDefinedAndNotNull(settings.minLength)) {\n validators.push($scope.validators.minLength(settings.minLength));\n }\n if (utils.isDefinedAndNotNull(settings.maxLength)) {\n validators.push($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 !== \"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\":{}}" + "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;\"}]}],\"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},\"showLegend\":false,\"actions\":{}}" }, "resources": [ { 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 index 797948b1cb..e0f9f3ea22 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 4b3f255401..fc94104bb7 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/uv_index_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/uv_index_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index a0cce85fa2..1b539fb360 100644 --- a/application/src/main/data/json/system/widget_types/value_card.json +++ b/application/src/main/data/json/system/widget_types/value_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "resources": [ { 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 index bd5737cb49..725dcbda62 100644 --- a/application/src/main/data/json/system/widget_types/vertical_bar.json +++ b/application/src/main/data/json/system/widget_types/vertical_bar.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-digital-gauge-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-digital-simple-gauge-basic-config", - "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},\"configMode\":\"basic\"}" + "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;\"}]}],\"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},\"configMode\":\"basic\"}" }, "resources": [ { 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 index 7258e76c85..f1ef3ec8fb 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 4432ffc40d..a8d1ae5f6d 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index fa61324ca7..b461ea7b62 100644 --- 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 @@ -12,12 +12,11 @@ "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\":\"\"}" + "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\"]}}],\"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\",\"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\":\"\"}" }, "tags": [ "reservoir", 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 index 033da084fb..fd2c4be2a3 100644 --- a/application/src/main/data/json/system/widget_types/vibration_card.json +++ b/application/src/main/data/json/system/widget_types/vibration_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index f078b93194..3c77ef51b3 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/vibration_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/vibration_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index c640c702e9..c30455251a 100644 --- a/application/src/main/data/json/system/widget_types/visibility_card.json +++ b/application/src/main/data/json/system/widget_types/visibility_card.json @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 02e12105b8..4103cf657c 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/visibility_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/visibility_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 517ecaf1ce..7a00e37826 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index fb5bf45da1..b76fb80d53 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/volatile_organic_compounds_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/volatile_organic_compounds_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "environment", 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 index 78c4fdabf0..5ad343c61e 100644 --- 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 @@ -12,12 +12,10 @@ "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 supportsUnitConversion: 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)\"}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "wind", 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 index f96a21781f..2b4f9055c1 100644 --- 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 @@ -12,12 +12,10 @@ "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 supportsUnitConversion: 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\":\"tb-image;/api/images/system/wind_speed_and_direction_with_background_system_widget_background.png\",\"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)\"}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/wind_speed_and_direction_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"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\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"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}}" }, "tags": [ "wind", 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 index 0727d4a905..8f3d311d16 100644 --- 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 @@ -17,7 +17,7 @@ "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}}" + "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\"]}}],\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 index 1fdc2e2ec3..8a37be56e8 100644 --- 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 @@ -17,7 +17,7 @@ "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\":\"tb-image;/api/images/system/wind_speed_card_with_background_system_widget_background.png\",\"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}}" + "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\"]}}],\"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\":\"tb-image;/api/images/system/wind_speed_card_with_background_system_widget_background.png\",\"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,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"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}}" }, "tags": [ "weather", 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 305dc04961..8773a2d6aa 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 @@ -10,12 +10,12 @@ "configuration": null }, "metadata": { - "firstNodeIndex": 6, + "firstNodeIndex": 2, "nodes": [ { "additionalInfo": { - "layoutX": 822, - "layoutY": 294 + "layoutX": 824, + "layoutY": 156 }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", @@ -30,8 +30,8 @@ }, { "additionalInfo": { - "layoutX": 824, - "layoutY": 221 + "layoutX": 825, + "layoutY": 52 }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", @@ -48,8 +48,8 @@ }, { "additionalInfo": { - "layoutX": 494, - "layoutY": 309 + "layoutX": 347, + "layoutY": 149 }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "Message Type Switch", @@ -59,8 +59,8 @@ }, { "additionalInfo": { - "layoutX": 824, - "layoutY": 383 + "layoutX": 825, + "layoutY": 266 }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log RPC from Device", @@ -72,8 +72,8 @@ }, { "additionalInfo": { - "layoutX": 823, - "layoutY": 444 + "layoutX": 825, + "layoutY": 379 }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log Other", @@ -85,27 +85,14 @@ }, { "additionalInfo": { - "layoutX": 822, - "layoutY": 507 + "layoutX": 825, + "layoutY": 468 }, "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "name": "RPC Call Request", "configuration": { "timeoutInSeconds": 60 } - }, - { - "additionalInfo": { - "description": "", - "layoutX": 209, - "layoutY": 307 - }, - "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", - "name": "Device Profile Node", - "configuration": { - "persistAlarmRulesState": false, - "fetchAlarmRulesStateOnStart": false - } } ], "connections": [ @@ -133,11 +120,6 @@ "fromIndex": 2, "toIndex": 5, "type": "RPC Request to Device" - }, - { - "fromIndex": 6, - "toIndex": 2, - "type": "Success" } ], "ruleChainConnections": null 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 a988c9d5eb..c48dab1964 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 @@ -9,7 +9,7 @@ "configuration": null }, "metadata": { - "firstNodeIndex": 6, + "firstNodeIndex": 2, "nodes": [ { "additionalInfo": { @@ -92,27 +92,9 @@ "configuration": { "timeoutInSeconds": 60 } - }, - { - "additionalInfo": { - "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", - "layoutX": 204, - "layoutY": 240 - }, - "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", - "name": "Device Profile Node", - "configuration": { - "persistAlarmRulesState": false, - "fetchAlarmRulesStateOnStart": false - } } ], "connections": [ - { - "fromIndex": 6, - "toIndex": 2, - "type": "Success" - }, { "fromIndex": 2, "toIndex": 4, diff --git a/application/src/main/data/resources/dashboards/gateways_dashboard.json b/application/src/main/data/resources/dashboards/gateways_dashboard.json index 078f913570..4fe9e180e1 100644 --- a/application/src/main/data/resources/dashboards/gateways_dashboard.json +++ b/application/src/main/data/resources/dashboards/gateways_dashboard.json @@ -11,37 +11,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680624762213, - "endTimeMs": 1680711162213 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -70,7 +39,6 @@ "fontWeight": 600, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -184,7 +152,6 @@ ] } ], - "displayTimewindow": true, "actions": { "actionCellButton": [ { @@ -275,9 +242,9 @@ "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", + "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 == \"Launch command\");\r\n setTimeout(() => {\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n });\r\n goToConfigState();\r\n }\r\n\r\n \r\n function 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", + "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, vm.validators.pattern(/.*\\S.*/)]],\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 == \"Launch command\");\r\n setTimeout(() => {\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n });\r\n goToConfigState();\r\n }\r\n\r\n \r\n function 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.trim(),\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, @@ -318,37 +285,6 @@ "dataKeys": [] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680685647526, - "endTimeMs": 1680772047526 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -369,7 +305,6 @@ "pageSize": 1024, "noDataDisplayMessage": "", "showLegend": false, - "useDashboardTimewindow": true, "borderRadius": "4px" }, "row": 0, @@ -424,37 +359,6 @@ ] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680769601167, - "endTimeMs": 1680856001167 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#FFFFFF01", "color": "rgba(0, 0, 0, 0.87)", @@ -474,8 +378,7 @@ }, "pageSize": 1024, "noDataDisplayMessage": "", - "showLegend": false, - "useDashboardTimewindow": true + "showLegend": false }, "row": 0, "col": 0, @@ -553,8 +456,8 @@ "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var newValue = value == 'true' ? \"Active\" : \"Inactive\";\nreturn newValue;" + "usePostProcessing": false, + "postFuncBody": null }, { "name": "ALL_ERRORS_COUNT", @@ -571,6 +474,20 @@ "color": "#03a9f4", "settings": {}, "_hash": 0.007837366355011532 + }, + { + "name": "lastConnectTime", + "type": "attribute", + "label": "lastConnectTime", + "color": "#ff9800", + "settings": {} + }, + { + "name": "lastDisconnectTime", + "type": "attribute", + "label": "lastDisconnectTime", + "color": "#673ab7", + "settings": {} } ], "alarmFilterConfig": { @@ -612,37 +529,6 @@ ] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1683116949383, - "endTimeMs": 1683203349383 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -650,9 +536,9 @@ "settings": { "useMarkdownTextFunction": true, "markdownTextPattern": "# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.", - "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 ${ctx.sanitizer.sanitize(1, value)}\n `;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\" ? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\nif (data[0].Version) {\n createDataBlock(data[0].Version, \"Gateway Version\", '');\n}\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 (Enabled | Disabled)\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `
${blockData}
`;", + "markdownTextFunction": "var blockData = '';\n\nfunction createDataBlock(value, label, dividerStyle, mobile, id) {\n blockData += `\n \n
\n \n ${id ? `` : ``}\n ${label}\n \n ${ctx.sanitizer.sanitize(1, value)}\n
`;\n}\nconst lastConnectTime = Number(data[0].lastConnectTime);\nconst lastDisconnectTime = Number(data[0].lastDisconnectTime);\nconst isActive =\n data[0].Status === 'true' &&\n (!lastDisconnectTime || lastConnectTime > lastDisconnectTime);\ncreateDataBlock(isActive ? \"{{'gateway.active' | translate }}\" : \"{{'gateway.inactive' | translate}}\", \"{{ 'gateway.status-widget.status' | translate }}\", isActive ? \"divider-green\" : \"divider-red\");\ncreateDataBlock(data[0].Name, \"{{ 'gateway.status-widget.gateway-name' | translate }}\", '', ctx.isMobile);\n\nif (data[0].Version) {\n createDataBlock(data[0].Version, \"{{ 'gateway.status-widget.gateway-version' | translate }}\", '');\n}\n\ncreateDataBlock(data[0].Type, \"{{ 'gateway.status-widget.gateway-type' | translate }}\", '');\n\ncreateDataBlock(\n `${(data[1] ? data[1].count : 0)} `\n + \" | \" +\n `${(data[2] ? data[2][\"count 2\"] : 0)} `\n , \"{{ 'gateway.status-widget.devices' | translate }} {{ 'gateway.status-widget.active-inactive' | translate }}\", '');\n \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 , \"{{ 'gateway.status-widget.connectors' | translate }} {{ 'gateway.status-widget.enabled-disabled' | translate }}\", '', '', 'connectors');\n \ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"{{ 'gateway.status-widget.errors' | translate }}\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', 'logs');\n\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}" + "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}\n\n.tb-home-widget-link {\n cursor: pointer;\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, @@ -667,18 +553,16 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "margin": "0", "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "", "enableDataExport": false, - "displayTimewindow": true, "borderRadius": "4px", "actions": { "elementClick": [ { - "name": "Connectors", + "name": "connectors", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -692,7 +576,7 @@ "id": "ee7216d8-ad7a-20db-1abc-2531e221f24c" }, { - "name": "Logs", + "name": "logs", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -711,7 +595,7 @@ "row": 0, "col": 0, "id": "79f59106-758f-c428-8b93-4341faea705d", - "typeFullFqn": "system.cards.markdown_card" + "typeFullFqn": "system.gateway_widgets.markdown_card" }, "60dcf518-8fc3-3539-8ff8-ce94bda39f3a": { "type": "alarm", @@ -872,9 +756,6 @@ } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, @@ -1042,7 +923,6 @@ "fontSize": "16px", "fontWeight": 400 }, - "useDashboardTimewindow": true, "showLegend": false, "actions": {}, "datasources": [], @@ -1239,7 +1119,6 @@ "fontSize": "16px", "fontWeight": 400 }, - "useDashboardTimewindow": true, "showLegend": false, "actions": {}, "datasources": [], @@ -1550,7 +1429,6 @@ "fontSize": "16px", "fontWeight": 400 }, - "useDashboardTimewindow": true, "showLegend": false, "actions": {}, "datasources": [], @@ -1584,44 +1462,13 @@ "dataKeys": [] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1685437116892, - "endTimeMs": 1685523516892 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "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
", + "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 line-height: 36px;\n}" }, @@ -1638,11 +1485,10 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "actions": { "elementClick": [ { - "name": "Launch command", + "name": "launchCommand", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1671,7 +1517,7 @@ "row": 0, "col": 0, "id": "fb9df382-6ef3-4aa6-bc13-8bf8e300ba19", - "typeFullFqn": "system.cards.markdown_card" + "typeFullFqn": "system.gateway_widgets.markdown_card" }, "61d149e8-b249-5526-e5d7-6ad58413982e": { "type": "latest", @@ -1704,37 +1550,6 @@ ] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1685437116892, - "endTimeMs": 1685523516892 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -1742,7 +1557,7 @@ "settings": { "useMarkdownTextFunction": true, "markdownTextPattern": "# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.", - "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 == 3) {\n const conf = data[0].general_configuration ? JSON.parse(data[0].general_configuration) : {};\n disabled = typeof conf.statistics?.enable === 'boolean' && !conf.statistics.enable;\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}
`;", + "markdownTextFunction": "const conf = data[0].general_configuration ? JSON.parse(data[0].general_configuration) : {};\nconst disabledLogsBtn = data[0] && data[0].RemoteLoggingLevel ? data[0].RemoteLoggingLevel == \"NONE\" : true;\nconst disabledStatisticsBtn = typeof conf.statistics?.enable === 'boolean' && !conf.statistics.enable;\nconst disabledRemoteShellBtn = !conf.remoteShell;\nreturn `
\n \n \n \n \n \n \n
`;\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: 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 line-height: 36px;\n}" }, @@ -1759,11 +1574,10 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "actions": { "elementClick": [ { - "name": "General configuration", + "name": "generalConfiguration", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1777,7 +1591,7 @@ "id": "337c767b-3217-d3d3-b955-7b0bd0858a1d" }, { - "name": "Connectors configuration", + "name": "connectorsConfiguration", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1791,7 +1605,7 @@ "id": "00469ea7-f4c4-e39d-a735-c74b6ba6a026" }, { - "name": "Logs", + "name": "logs", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1805,7 +1619,7 @@ "id": "d8607421-28b4-7c3b-949b-f69c3a46b461" }, { - "name": "Statistics", + "name": "statistics", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1824,7 +1638,7 @@ "id": "425ba0d8-8e26-18a5-881d-bebe27fb2a7a" }, { - "name": "Remote Shell", + "name": "remoteShell", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1838,7 +1652,7 @@ "id": "2c4c7dcc-009f-64eb-5f1b-a991df6d25a2" }, { - "name": "RPC", + "name": "rpc", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -1862,7 +1676,7 @@ "row": 0, "col": 0, "id": "61d149e8-b249-5526-e5d7-6ad58413982e", - "typeFullFqn": "system.cards.markdown_card" + "typeFullFqn": "system.gateway_widgets.markdown_card" }, "3d661190-7463-ba61-6793-503c85af67ec": { "type": "latest", @@ -1891,37 +1705,6 @@ } } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1686652103417, - "endTimeMs": 1686738503417 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -1946,8 +1729,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "", @@ -1957,44 +1738,13 @@ "row": 0, "col": 0, "id": "3d661190-7463-ba61-6793-503c85af67ec", - "typeFullFqn": "system.cards.markdown_card" + "typeFullFqn": "system.gateway_widgets.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", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -2023,7 +1773,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -2103,7 +1852,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -2138,37 +1886,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -2197,7 +1914,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -2277,7 +1993,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -2312,37 +2027,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -2371,7 +2055,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -2451,7 +2134,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -2485,37 +2167,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -2544,7 +2195,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -2624,7 +2274,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -2658,37 +2307,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -2717,7 +2335,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -2797,7 +2414,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -2831,37 +2447,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -2890,7 +2475,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -2970,7 +2554,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -3004,37 +2587,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -3063,7 +2615,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -3143,7 +2694,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -3177,37 +2727,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -3236,7 +2755,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -3316,7 +2834,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -3350,37 +2867,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -3409,7 +2895,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -3489,7 +2974,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -3523,37 +3007,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -3582,7 +3035,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -3662,7 +3114,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -3696,37 +3147,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -3755,7 +3175,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -3835,7 +3254,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -3869,37 +3287,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -3928,7 +3315,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -4008,7 +3394,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -4042,37 +3427,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -4101,7 +3455,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -4181,7 +3534,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -4215,37 +3567,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -4274,7 +3595,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -4354,7 +3674,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -4388,37 +3707,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -4447,7 +3735,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -4527,7 +3814,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -4561,37 +3847,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -4620,7 +3875,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -4700,7 +3954,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -4734,37 +3987,6 @@ "sizeX": 7.5, "sizeY": 6.5, "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, "showTitle": true, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -4793,7 +4015,6 @@ "fontWeight": 400, "padding": "5px 10px 5px 10px" }, - "useDashboardTimewindow": false, "showLegend": false, "datasources": [ { @@ -4873,7 +4094,6 @@ } } ], - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "widgetStyle": {}, @@ -4903,7 +4123,7 @@ "typeFullFqn": "system.cards.entities_table" }, "b4194e1e-97af-ed0b-a6a4-70011c71c579": { - "typeFullFqn": "system.cards.markdown_card", + "typeFullFqn": "system.gateway_widgets.markdown_card", "type": "latest", "sizeX": 5, "sizeY": 3.5, @@ -4930,37 +4150,6 @@ } } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1732109250067, - "endTimeMs": 1732195650067 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -4968,7 +4157,7 @@ "settings": { "useMarkdownTextFunction": true, "markdownTextPattern": "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}
`;", - "markdownTextFunction": "const states = ['storage_statistics', 'machine_statistics', 'custom_statistics'];\nconst currentState = ctx.stateController.getStateId();\nlet buttonsHtml = \"\";\nlet disabled = false;\nconst buttonName = \nctx.actionsApi.getActionDescriptors('elementClick').forEach((btn, index)=>{\n if (states[index] === 'custom_statistics') {\n const conf = data[0].general_configuration ? JSON.parse(data[0].general_configuration) : {};\n disabled = !conf.statistics?.enableCustom;\n }\n buttonsHtml += ``\n});\n\nreturn `
${buttonsHtml}
`;", + "markdownTextFunction": "const currentState = ctx.stateController.getStateId();\nconst conf = data[0].general_configuration ? JSON.parse(data[0].general_configuration) : {};\nconst disabled = !conf.statistics?.enableCustom;\nreturn `
\n\n\n\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: 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 line-height: 36px;\r\n}" }, @@ -4985,51 +4174,40 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "actions": { "elementClick": [ { - "name": "Storage", + "name": "storage_statistics", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "storage_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, + "type": "custom", + "customFunction": "widgetContext.stateController.updateState('storage_statistics', widgetContext.stateController.getStateParams(), false);\nwidgetContext.$scope.markdownWidget.onDataUpdated();", "openInSeparateDialog": false, "openInPopover": false, - "id": "fc4c338d-5799-f7dc-9ecb-448e65f8045d" + "id": "56342fb4-e698-1db3-a8f1-34a3b44735c5" }, { - "name": "Machine", + "name": "machine_statistics", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "machine_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, + "type": "custom", + "customFunction": "widgetContext.stateController.updateState('machine_statistics', widgetContext.stateController.getStateParams(), false);\nwidgetContext.$scope.markdownWidget.onDataUpdated();", "openInSeparateDialog": false, "openInPopover": false, - "id": "b35fc758-b21a-f5d3-e968-3572f1bb9ad4" + "id": "dffa7e63-2382-12fa-ef1c-13e162b1acaf" }, { - "name": "Custom", + "name": "custom_statistics", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "custom_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, + "type": "custom", + "customFunction": "widgetContext.stateController.updateState('custom_statistics', widgetContext.stateController.getStateParams(), false);\nwidgetContext.$scope.markdownWidget.onDataUpdated();", "openInSeparateDialog": false, "openInPopover": false, - "id": "fcff099d-8164-08fd-7ffa-e1565772f368" + "id": "f6d267d0-3e3e-d519-bf7f-836fd59fd10f" } ] }, @@ -5045,7 +4223,7 @@ "id": "b4194e1e-97af-ed0b-a6a4-70011c71c579" }, "3a005e49-327c-beaa-8d19-c36b833001f0": { - "typeFullFqn": "system.cards.markdown_card", + "typeFullFqn": "system.gateway_widgets.markdown_card", "type": "latest", "sizeX": 5, "sizeY": 3.5, @@ -5057,37 +4235,6 @@ "dataKeys": [] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733234218432, - "endTimeMs": 1733320618432 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -5191,39 +4338,6 @@ ] } ], - "timewindow": { - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "interval": 300000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_MONTH_SO_FAR", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 300000, - "timewindowMs": 86400000, - "fixedTimewindow": { - "startTimeMs": 1733156328824, - "endTimeMs": 1733242728824 - }, - "quickInterval": "CURRENT_MONTH_SO_FAR", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 5000 - } - }, "showTitle": true, "backgroundColor": "rgba(0, 0, 0, 0)", "color": null, @@ -5347,24 +4461,7 @@ "pageSize": 1024, "noDataDisplayMessage": "", "useDashboardTimewindow": true, - "displayTimewindow": false, "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": "5px", "enableDataExport": false @@ -5443,39 +4540,6 @@ ] } ], - "timewindow": { - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "interval": 300000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_MONTH_SO_FAR", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 2, - "interval": 7200000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733156705839, - "endTimeMs": 1733243105839 - }, - "quickInterval": "CURRENT_MONTH_SO_FAR", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "NONE", - "limit": 5000 - } - }, "showTitle": true, "backgroundColor": "rgba(0, 0, 0, 0)", "color": null, @@ -5599,24 +4663,7 @@ "pageSize": 1024, "noDataDisplayMessage": "", "useDashboardTimewindow": true, - "displayTimewindow": false, "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": "5px", "enableDataExport": false @@ -6811,37 +5858,6 @@ } } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733240126084, - "endTimeMs": 1733326526084 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -6852,8 +5868,6 @@ }, "title": "HTML Card", "dropShadow": true, - "useDashboardTimewindow": true, - "displayTimewindow": true, "enableFullscreen": false, "margin": "16px 16px 16px 0", "borderRadius": "5px", @@ -7451,7 +6465,7 @@ "id": "7543e668-35b7-bb97-2f14-163399f8d038" }, "7f3ebf9f-d967-44fe-fbe4-4667a2abcf88": { - "typeFullFqn": "system.cards.markdown_card", + "typeFullFqn": "system.gateway_widgets.markdown_card", "type": "latest", "sizeX": 5, "sizeY": 3.5, @@ -7512,37 +6526,6 @@ } } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733155841882, - "endTimeMs": 1733242241882 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -7567,8 +6550,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "borderRadius": "5px", "widgetCss": "", "pageSize": 1024, @@ -7580,7 +6561,7 @@ "id": "7f3ebf9f-d967-44fe-fbe4-4667a2abcf88" }, "ea75a492-d149-9a53-b45f-840a0ce42a40": { - "typeFullFqn": "system.cards.markdown_card", + "typeFullFqn": "system.gateway_widgets.markdown_card", "type": "latest", "sizeX": 5, "sizeY": 3.5, @@ -7655,37 +6636,6 @@ } } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733155841882, - "endTimeMs": 1733242241882 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -7710,8 +6660,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "borderRadius": "5px", "widgetCss": "", "pageSize": 1024, @@ -7723,7 +6671,7 @@ "id": "ea75a492-d149-9a53-b45f-840a0ce42a40" }, "13b57ab5-e5ed-701c-4c67-5bb1198e9a53": { - "typeFullFqn": "system.cards.markdown_card", + "typeFullFqn": "system.gateway_widgets.markdown_card", "type": "latest", "sizeX": 5, "sizeY": 3.5, @@ -7798,37 +6746,6 @@ } } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733155841882, - "endTimeMs": 1733242241882 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -7853,8 +6770,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "borderRadius": "5px", "widgetCss": "", "pageSize": 1024, @@ -7866,7 +6781,7 @@ "id": "13b57ab5-e5ed-701c-4c67-5bb1198e9a53" }, "867613d5-9440-fac1-9156-cd8773df5c97": { - "typeFullFqn": "system.cards.markdown_card", + "typeFullFqn": "system.gateway_widgets.markdown_card", "type": "latest", "sizeX": 5, "sizeY": 3.5, @@ -7878,37 +6793,6 @@ "dataKeys": [] } ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733234218432, - "endTimeMs": 1733320618432 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -7927,495 +6811,20 @@ "titleTooltip": "", "dropShadow": false, "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "enableDataExport": false - }, - "row": 0, - "col": 0, - "id": "867613d5-9440-fac1-9156-cd8773df5c97" - }, - "56274c38-33fc-fe9d-aa39-ff3e1215276b": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": "", - "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", - "dataKeys": [ - { - "name": "general_configuration", - "type": "attribute", - "label": "general_configuration", - "color": "#2196f3", - "settings": {}, - "_hash": 0.5374662835906896 - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1732109250067, - "endTimeMs": 1732195650067 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "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}
`;", - "markdownTextFunction": "const states = ['storage_statistics', 'machine_statistics', 'custom_statistics'];\nconst currentState = ctx.stateController.getStateId();\nlet buttonsHtml = \"\";\nlet disabled = false;\nconst buttonName = \nctx.actionsApi.getActionDescriptors('elementClick').forEach((btn, index)=>{\n if (states[index] === 'custom_statistics') {\n const conf = data[0].general_configuration ? JSON.parse(data[0].general_configuration) : {};\n disabled = !conf.statistics?.enableCustom;\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 line-height: 36px;\r\n}" - }, - "title": "Markdown/HTML Card", - "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, - "actions": { - "elementClick": [ - { - "name": "Storage", - "icon": "more_horiz", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "storage_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "fc4c338d-5799-f7dc-9ecb-448e65f8045d" - }, - { - "name": "Machine", - "icon": "more_horiz", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "machine_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "b35fc758-b21a-f5d3-e968-3572f1bb9ad4" - }, - { - "name": "Custom", - "icon": "more_horiz", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "custom_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "10a5a4b6-71fb-f51c-2836-bb360938127f" - } - ] - }, - "borderRadius": "5px", - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "margin": "16px 16px 0 0", - "enableDataExport": false - }, - "row": 0, - "col": 0, - "id": "56274c38-33fc-fe9d-aa39-ff3e1215276b" - }, - "38063fe5-1bd8-695b-a40b-92c1aed19831": { - "typeFullFqn": "system.gateway_widgets.gateway_status", - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": "function", - "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", - "dataKeys": [ - { - "name": "active", - "type": "attribute", - "label": "active", - "color": "#2196f3", - "settings": {}, - "_hash": 0.07932656697286933 - }, - { - "name": "lastDisconnectTime", - "type": "attribute", - "label": "lastDisconnectTime", - "color": "#4caf50", - "settings": {}, - "_hash": 0.06662431625551446 - }, - { - "name": "lastConnectTime", - "type": "attribute", - "label": "lastConnectTime", - "color": "#f44336", - "settings": {}, - "_hash": 0.28485659401606434 - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733240126084, - "endTimeMs": 1733326526084 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "", - "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, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "enableFullscreen": false, - "margin": "16px 16px 16px 0", - "borderRadius": "5px", - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "enableDataExport": false - }, - "row": 0, - "col": 0, - "id": "38063fe5-1bd8-695b-a40b-92c1aed19831" - }, - "e48e7a66-a30e-2494-b0c0-dd3249944286": { - "typeFullFqn": "system.gateway_widgets.gateway_status", - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": "function", - "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", - "dataKeys": [ - { - "name": "active", - "type": "attribute", - "label": "active", - "color": "#2196f3", - "settings": {}, - "_hash": 0.07932656697286933 - }, - { - "name": "lastDisconnectTime", - "type": "attribute", - "label": "lastDisconnectTime", - "color": "#4caf50", - "settings": {}, - "_hash": 0.06662431625551446 - }, - { - "name": "lastConnectTime", - "type": "attribute", - "label": "lastConnectTime", - "color": "#f44336", - "settings": {}, - "_hash": 0.28485659401606434 - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1733240126084, - "endTimeMs": 1733326526084 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "", - "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, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "enableFullscreen": false, - "margin": "16px 16px 16px 0", - "borderRadius": "5px", - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "enableDataExport": false - }, - "row": 0, - "col": 0, - "id": "e48e7a66-a30e-2494-b0c0-dd3249944286" - }, - "3a1f2fe6-7da9-15aa-777d-383e0ac01520": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": "", - "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", - "dataKeys": [], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1732109250067, - "endTimeMs": 1732195650067 - }, - "quickInterval": "CURRENT_DAY", - "hideInterval": false, - "hideLastInterval": false, - "hideFixedInterval": false, - "hideQuickInterval": false - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "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}
`;", - "markdownTextFunction": "const states = ['storage_statistics', 'machine_statistics', 'custom_statistics'];\nconst currentState = ctx.stateController.getStateId();\nlet buttonsHtml = \"\";\nconst buttonName = \nctx.actionsApi.getActionDescriptors('elementClick').forEach((btn, index)=>{\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 line-height: 36px;\r\n}" - }, - "title": "Markdown/HTML Card", - "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, - "actions": { - "elementClick": [ - { - "name": "Storage", - "icon": "more_horiz", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "storage_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "fc4c338d-5799-f7dc-9ecb-448e65f8045d" - }, - { - "name": "Machine", - "icon": "more_horiz", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "machine_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "b35fc758-b21a-f5d3-e968-3572f1bb9ad4" - }, - { - "name": "Custom", - "icon": "more_horiz", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "updateDashboardState", - "targetDashboardStateId": "custom_statistics", - "setEntityId": true, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "fcff099d-8164-08fd-7ffa-e1565772f368" - } - ] + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 }, - "borderRadius": "5px", + "showLegend": false, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "", - "margin": "16px 16px 0 0", "enableDataExport": false }, "row": 0, "col": 0, - "id": "3a1f2fe6-7da9-15aa-777d-383e0ac01520" + "id": "867613d5-9440-fac1-9156-cd8773df5c97" }, "7a34986a-ec7f-4c64-84f9-338cf1626722": { "typeFullFqn": "system.gateway_widgets.gateway_custom_statistics", @@ -8436,11 +6845,6 @@ } } ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, "showTitle": false, "backgroundColor": "#fff", "color": "rgba(0, 0, 0, 0.87)", @@ -8485,7 +6889,6 @@ }, "mobileHeight": null, "useDashboardTimewindow": true, - "displayTimewindow": true, "showTitleIcon": false, "titleTooltip": "", "margin": "16px", @@ -8499,6 +6902,146 @@ "row": 0, "col": 0, "id": "7a34986a-ec7f-4c64-84f9-338cf1626722" + }, + "699b8980-7180-3600-ec29-6af50325a2e1": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "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" + }, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "774c2598-a4fe-e813-87aa-bf00b78043a3", + "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" + ] + } + } + ], + "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 + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "89e0155f-c2c5-8d17-324c-280f8e11ffab" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "699b8980-7180-3600-ec29-6af50325a2e1", + "typeFullFqn": "system.cards.entities_table" } }, "states": { @@ -8886,7 +7429,7 @@ "mobileOrder": 2, "mobileHeight": null }, - "56274c38-33fc-fe9d-aa39-ff3e1215276b": { + "b4194e1e-97af-ed0b-a6a4-70011c71c579": { "sizeX": 5, "sizeY": 12, "resizable": true, @@ -8895,7 +7438,7 @@ "mobileOrder": 1, "mobileHeight": 3 }, - "38063fe5-1bd8-695b-a40b-92c1aed19831": { + "bc199c75-00da-ca8a-dd0d-16da97a9fa53": { "sizeX": 5, "sizeY": 2, "row": 12, @@ -9521,31 +8064,40 @@ } } }, + "gateway_devices_knx": { + "name": "gateway_devices_knx", + "root": false, + "layouts": { + "main": { + "widgets": { + "699b8980-7180-3600-ec29-6af50325a2e1": { + "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, + "layoutType": "default" + } + } + } + }, "custom_statistics": { "name": "Custom statistics", "root": false, "layouts": { "main": { "widgets": { - "e48e7a66-a30e-2494-b0c0-dd3249944286": { - "sizeX": 4, - "sizeY": 2, - "preserveAspectRatio": false, - "resizable": true, - "row": 12, - "col": 20, - "mobileOrder": 3, - "mobileHeight": 2 - }, - "3a1f2fe6-7da9-15aa-777d-383e0ac01520": { - "sizeX": 4, - "sizeY": 12, - "resizable": true, - "row": 0, - "col": 20, - "mobileOrder": 1, - "mobileHeight": 3 - }, "7a34986a-ec7f-4c64-84f9-338cf1626722": { "sizeX": 20, "sizeY": 14, @@ -9554,6 +8106,21 @@ "col": 0, "resizable": true, "mobileOrder": 2 + }, + "b4194e1e-97af-ed0b-a6a4-70011c71c579": { + "sizeX": 4, + "sizeY": 12, + "resizable": true, + "row": 0, + "col": 20 + }, + "bc199c75-00da-ca8a-dd0d-16da97a9fa53": { + "sizeX": 4, + "sizeY": 2, + "preserveAspectRatio": false, + "resizable": true, + "row": 12, + "col": 20 } }, "gridSettings": { @@ -9573,6 +8140,32 @@ } } } + }, + "gateway_status": { + "name": "gateway_status", + "root": false, + "layouts": { + "main": { + "widgets": { + "bc199c75-00da-ca8a-dd0d-16da97a9fa53": { + "sizeX": 5, + "sizeY": 3, + "preserveAspectRatio": false, + "resizable": true, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "layoutType": "default", + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 10, + "outerMargin": true, + "backgroundSizeMode": "100%" + } + } + } } }, "entityAliases": { @@ -9992,6 +8585,39 @@ ], "editable": true }, + "774c2598-a4fe-e813-87aa-bf00b78043a3": { + "id": "774c2598-a4fe-e813-87aa-bf00b78043a3", + "filter": "gateway_devices_knx", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "knx", + "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", diff --git a/application/src/main/data/resources/js_extensions/gateway-management-extension.js b/application/src/main/data/resources/js_extensions/gateway-management-extension.js new file mode 100644 index 0000000000..1c76094959 --- /dev/null +++ b/application/src/main/data/resources/js_extensions/gateway-management-extension.js @@ -0,0 +1,17 @@ +System.register(["@angular/core","@angular/material/sort","@angular/material/table","@angular/material/paginator","@shared/public-api","@core/public-api","@angular/common","@ngx-translate/core","@angular/forms","rxjs","rxjs/operators","@angular/material/dialog","tslib","@angular/material/tooltip","@angular/cdk/collections","@ngrx/store","@angular/router","@home/components/public-api","@angular/platform-browser","@home/components/widget/widget.component","@shared/components/dialog/json-object-edit-dialog.component","@shared/import-export/import-export.service","@shared/components/popover.component","@shared/components/popover.service","@shared/decorators/coercion","@angular/material/core"],(function(e){"use strict";var t,n,i,a,r,o,s,l,p,c,d,u,m,h,g,f,y,v,x,b,w,S,C,_,T,I,E,M,k,P,D,O,A,F,R,B,N,L,V,q,G,z,U,j,H,W,$,K,Y,X,Z,Q,J,ee,te,ne,ie,ae,re,oe,se,le,pe,ce,de,ue,me,he,ge,fe,ye,ve,xe,be,we,Se,Ce,_e,Te,Ie,Ee,Me,ke,Pe,De,Oe,Ae,Fe,Re,Be,Ne,Le,Ve,qe,Ge,ze,Ue,je,He,We,$e,Ke,Ye,Xe,Ze,Qe,Je,et,tt,nt,it,at,rt,ot,st;return{setters:[function(e){t=e,n=e.assertInInjectionContext,i=e.inject,a=e.DestroyRef,e.ɵRuntimeError,e.ɵgetOutputDestroyRef,r=e.Injector,o=e.effect,s=e.untracked,e.assertNotInReactiveContext,e.signal,e.computed,l=e.input,p=e.output,c=e.forwardRef,d=e.ChangeDetectionStrategy,u=e.EventEmitter,m=e.booleanAttribute,h=e.ChangeDetectorRef,g=e.SecurityContext,f=e.KeyValueDiffers,y=e.ɵNG_COMP_DEF},function(e){v=e.MatSort},function(e){x=e.MatTableDataSource},function(e){b=e.MatPaginator},function(e){w=e.Direction,S=e.PageLink,C=e.DataKeyType,_=e.SharedModule,T=e,I=e.coerceBoolean,E=e.emptyPageData,M=e.isClientSideTelemetryType,k=e.TelemetrySubscriber,P=e.coerceNumber,D=e.AttributeScope,O=e.helpBaseUrl,A=e.DialogComponent,F=e.defaultLegendConfig,R=e.widgetType,B=e.NULL_UUID,N=e.DatasourceType,L=e.EntityType,V=e.ContentType,q=e.PageComponent,G=e.TbTableDatasource,z=e.HOUR,U=e.DeviceCredentialsType},function(e){j=e.mergeDeep,H=e.isLiteralObject,W=e.isEqual,$=e.WINDOW,K=e,Y=e.deleteNullProperties,X=e.DialogService,Z=e.isNumber,Q=e.isString,J=e.isDefinedAndNotNull,ee=e.formatValue,te=e.deepClone,ne=e.isUndefinedOrNull,ie=e.isObject,ae=e.generateSecret,re=e.camelCase,oe=e.deepTrim},function(e){se=e.CommonModule},function(e){le=e,pe=e.TranslateService,ce=e.TranslateModule},function(e){de=e,ue=e.NG_VALUE_ACCESSOR,me=e.Validators,he=e.NG_VALIDATORS,ge=e.FormBuilder,fe=e.FormControl},function(e){ye=e.Observable,ve=e.ReplaySubject,xe=e.shareReplay,be=e.combineLatest,we=e.Subject,Se=e.fromEvent,Ce=e.BehaviorSubject,_e=e.of,Te=e.zip,Ie=e.forkJoin,Ee=e.merge},function(e){Me=e.takeUntil,ke=e.map,Pe=e.distinctUntilChanged,De=e.debounceTime,Oe=e.filter,Ae=e.tap,Fe=e.catchError,Re=e.publishReplay,Be=e.refCount,Ne=e.take,Le=e.takeWhile,Ve=e.switchMap,qe=e.startWith,Ge=e.mergeMap},function(e){ze=e.MatDialog,Ue=e.MAT_DIALOG_DATA,je=e},function(e){He=e.__decorate,We=e.__extends},function(e){$e=e,Ke=e.MatTooltip},function(e){Ye=e.SelectionModel},function(e){Xe=e},function(e){Ze=e},function(e){Qe=e.calculateAxisSize,Je=e.measureAxisNameSize},function(e){et=e},function(e){tt=e},function(e){nt=e.JsonObjectEditDialogComponent},function(e){it=e},function(e){at=e},function(e){rt=e},function(e){ot=e.coerceBoolean},function(e){st=e.ErrorStateMatcher}],execute:function(){e("getDefaultConfig",Xt);const lt=e("jsonRequired",(e=>e.value?null:{required:!0}));var pt,ct,dt;e("GatewayLogLevel",pt),function(e){e.NONE="NONE",e.CRITICAL="CRITICAL",e.ERROR="ERROR",e.WARNING="WARNING",e.INFO="INFO",e.DEBUG="DEBUG",e.TRACE="TRACE"}(pt||e("GatewayLogLevel",pt={})),e("GatewayVersion",ct),function(e){e.v3_7_3="3.7.3",e.v3_7_2="3.7.2",e.v3_7_0="3.7",e.v3_6_3="3.6.3",e.v3_6_2="3.6.2",e.v3_6_0="3.6",e.v3_5_2="3.5.2",e.Legacy="legacy"}(ct||e("GatewayVersion",ct={})),e("ConnectorType",dt),function(e){e.MQTT="mqtt",e.MODBUS="modbus",e.GRPC="grpc",e.OPCUA="opcua",e.BLE="ble",e.REQUEST="request",e.CAN="can",e.BACNET="bacnet",e.ODBC="odbc",e.REST="rest",e.SNMP="snmp",e.FTP="ftp",e.SOCKET="socket",e.XMPP="xmpp",e.OCPP="ocpp",e.CUSTOM="custom",e.KNX="knx"}(dt||e("ConnectorType",dt={}));const ut=e("GatewayConnectorDefaultTypesTranslatesMap",new Map([[dt.MQTT,"MQTT"],[dt.MODBUS,"MODBUS"],[dt.GRPC,"GRPC"],[dt.OPCUA,"OPCUA"],[dt.BLE,"BLE"],[dt.REQUEST,"REQUEST"],[dt.CAN,"CAN"],[dt.BACNET,"BACNET"],[dt.ODBC,"ODBC"],[dt.REST,"REST"],[dt.SNMP,"SNMP"],[dt.FTP,"FTP"],[dt.SOCKET,"SOCKET"],[dt.XMPP,"XMPP"],[dt.OCPP,"OCPP"],[dt.CUSTOM,"CUSTOM"],[dt.KNX,"KNX"]])),mt=e("ConnectorsTypesByVersion",new Map([[ct.v3_7_0,Object.values(dt)],[ct.Legacy,Object.values(dt).filter((e=>e!==dt.KNX))]]));var ht,gt;e("SocketEncoding",ht),function(e){e.UTF8="utf-8",e.HEX="hex",e.UTF16="utf-16",e.UTF32="utf-32",e.UTF16BE="utf-16-be",e.UTF16LE="utf-16-le",e.UTF32BE="utf-32-be",e.UTF32LE="utf-32-le"}(ht||e("SocketEncoding",ht={})),e("HTTPMethods",gt),function(e){e.DELETE="DELETE",e.GET="GET",e.HEAD="HEAD",e.OPTIONS="OPTIONS",e.PATCH="PATCH",e.POST="POST",e.PUT="PUT"}(gt||e("HTTPMethods",gt={}));var ft,yt={gateway:{active:"Active",address:"Address","address-required":"Address required","add-entry":"Add configuration","add-attribute":"Add attribute","add-attribute-update":"Add attribute update","add-attribute-request":"Add attribute request","add-key":"Add key","add-timeseries":"Add time series","add-mapping":"Add mapping","add-slave":"Add Slave",arguments:"Arguments","add-rpc-method":"Add method","add-rpc-request":"Add request","add-value":"Add argument","advanced-settings":"Advanced settings",application:"Application",bacnet:{"device-timeout":"Discovery timeout","network-number":"Network number","alt-responses-address":"Alternative responses address","apdu-length":"APDU Length","object-name":"Object Name","object-type":{"analog-input":"Analog Input","analog-output":"Analog Output","analog-value":"Analog Value","binary-input":"Binary Input","binary-output":"Binary Output","binary-value":"Binary Value"},"request-type":{label:"Request Type",write:"Write Property",read:"Read Property"},"property-id":{"present-value":"Present Value","status-flags":"Status Flags","cov-increment":"COV Increment","event-state":"Event State","out-of-service":"Out of Service",polarity:"Polarity","priority-array":"Priority Array","relinquish-default":"Relinquish Default","current-command-priority":"Current Command Priority","event-message-texts":"Event Message Texts","event-message-texts-config":"Event Message Texts Config","event-algorithm-inhibit-reference":"Event Algorithm Inhibit Reference","time-delay-normal":"Time Delay Normal"},segmentation:{label:"Segmentation",no:"None",both:"Both",transmit:"Transmit",receive:"Receive"}},baudrate:"Baudrate","buffer-size":"Buffer size","buffer-size-required":"Buffer size is required","buffer-size-range":"Buffer size should be greater than 0",bytesize:"Bytesize",boolean:"Boolean",bit:"Bit","bit-target-type":"Bit target type","delete-value":"Delete value","delete-argument":"Delete argument","delete-rpc-method":"Delete method","delete-rpc-request":"Delete request","delete-attribute-update":"Delete attribute update","delete-attribute-request":"Delete attribute request",advanced:"Advanced","add-device":"Add device","address-filter":"Address filter","address-filter-required":"Address filter is required","advanced-connection-settings":"Advanced connection settings","advanced-configuration-settings":"Advanced configuration settings",attributes:"Attributes","attribute-updates":"Attribute updates","attribute-on-platform":"Attribute on platform","attribute-requests":"Attribute requests","attribute-filter":"Attribute filter","attribute-filter-hint":"Filter for incoming attribute name from platform, supports regular expression.","attribute-filter-required":"Attribute filter required.","attribute-name-expression":"Attribute name expression","attribute-name-expression-required":"Attribute name expression required.","attribute-name-expression-hint":"Hint for Attribute name expression",basic:"Basic","byte-order":"Byte order","word-order":"Word order",broker:{connection:"Connection to broker",name:"Broker name","name-required":"Broker name required.","security-types":{anonymous:"Anonymous",basic:"Basic",certificates:"Certificates"}},certificate:"Certificate","CA-certificate-path":"Path to CA certificate file","path-to-CA-cert-required":"Path to CA certificate file is required.","change-connector-title":"Confirm connector change","change-connector-text":"Switching connectors will discard any unsaved changes. Continue?","checking-device-activity":"Checking device activity",command:"Docker commands","command-copied-message":"Docker command has been copied to clipboard",configuration:"Configuration","add-connector":"Add connector","delete-connector":"Delete connector","connector-add":"Add new connector","connector-enabled":"Enable connector","connector-name":"Connector name",connector:"Connector",converter:"Converter","connector-name-required":"Connector name is required.","connector-type":"Connector type","connector-type-required":"Connector type is required.",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","connection-timeout":"Connection timeout (s)","connect-attempt-time":"Connect attempt time (ms)","connect-attempt-count":"Connect attempt count","copy-username":"Copy username","copy-password":"Copy password","copy-client-id":"Copy client ID","connector-created":"Connector created","connector-updated":"Connector updated","create-new-one":"Create new one!","rpc-command-save-template":"Save Template","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 Docker compose with credentials for selected device","install-docker-compose":"Use the instructions to download, install and setup docker compose",integer:"Integer",inactive:"Inactive",device:"Device",devices:"Devices","device-profile":"Device profile","device-info-settings":"Device info settings","device-info":{"entity-field":"Entity field",source:"Source",expression:"Value / Expression","expression-hint":"Show help",name:"Name","profile-name":"Profile name","device-name-expression":"Device name expression","device-name-expression-required":"Device name expression is required.","device-profile-expression-required":"Device profile expression is required."},"device-name-filter":"Device name filter","device-name-filter-hint":"This field supports regular expressions to filter incoming data by device name.","device-name-filter-required":"Device name filter is required.",details:"Details","delete-mapping-title":"Are you sure you want to delete the mapping related with '{{name}}'?","delete-mapping-description":"Be careful, after the confirmation mapping configuration and all related data will become unrecoverable.","delete-slave-title":"Are you sure you want to delete the server '{{name}}'?","delete-slave-description":"Be careful, after the confirmation server configuration and all related data will become unrecoverable.","delete-device-title":"Are you sure you want to delete the device '{{name}}'?","delete-device-description":"Be careful, after the confirmation the device configuration and all related data will become unrecoverable.",divider:"Divider","download-configuration-file":"Download configuration file","download-docker-compose":"Download docker-compose.yml for your gateway","enable-remote-logging":"Enable remote logging","ellipsis-chips-text":"+ {{count}} more","launch-gateway":"Launch gateway","launch-docker-compose":"Start the gateway using the following command in the terminal from folder with docker-compose.yml file","logs-configuration":"Logs configuration","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","connector-duplicate-name":"Connector with such name already exists.","connection-type":"Connection type","connector-side":"Connector side","payload-type":"Payload type","platform-side":"Platform side",JSON:"JSON","JSON-hint":"Converter for this payload type processes MQTT messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and time series from the message. And regular expressions to get device details from topics.",byte:"Byte",bytes:"Bytes","bytes-hint":"Converter for this payload type designed for binary MQTT payloads, this converter directly interprets binary data to retrieve device names and device profile names, along with attributes and time series, using specific byte positions for data extraction.",custom:"Custom","custom-hint":"This option allows you to use a custom converter for specific data tasks. You need to add your custom converter to the extension folder and enter its class name in the UI settings. Any keys you provide will be sent as configuration to your custom converter.","client-cert-path":"Path to client certificate file","path-to-client-cert-required":"Path to client certificate file is required.","client-id":"Client ID","data-conversion":"Data conversion","data-mapping":"Data mapping","data-mapping-hint":"Data mapping provides the capability to parse and convert the data received from a MQTT client in incoming messages into specific attributes and time series data keys.","opcua-data-mapping-hint":"Data mapping provides the capability to parse and convert the data received from a OPCUA server into specific data keys.",delete:"Delete configuration","delete-attribute":"Delete attribute","delete-key":"Delete key","delete-timeseries":"Delete time series",default:"Default","device-node":"Device node","device-node-required":"Device node required.","device-node-hint":"Path or identifier for device node on OPC UA server. Relative paths from it for attributes and time series can be used.","device-name":"Device name","device-profile-label":"Device profile","device-name-required":"Device name required","device-profile-required":"Device profile required","download-tip":"Download configuration file","drop-file":"Drop file here or",enable:"Enable",encoding:"Encoding","enable-subscription":"Enable subscription",extension:"Extension","extension-hint":"Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.","extension-required":"Extension is required.","extension-configuration":"Extension configuration","extension-configuration-hint":"Configuration for converter. These arguments will be passed as a config to convert function.","fill-connector-defaults":"Fill configuration with default values","fill-connector-defaults-hint":"This property allows to fill connector configuration with default values on it's creation.","from-device-request-settings":"Input request parsing","from-device-request-settings-hint":"These fields support JSONPath expressions to extract a name from incoming message.","function-code":"Function code","function-codes":{"read-coils":"01 - Read Coils","read-discrete-inputs":"02 - Read Discrete Inputs","read-multiple-holding-registers":"03 - Read Multiple Holding Registers","read-input-registers":"04 - Read Input Registers","write-single-coil":"05 - Write Single Coil","write-single-holding-register":"06 - Write Single Holding Register","write-multiple-coils":"15 - Write Multiple Coils","write-multiple-holding-registers":"16 - Write Multiple Holding Registers"},"to-device-response-settings":"Output request processing","to-device-response-settings-hint":"For these fields you can use the following variables and they will be replaced with actual values: ${deviceName}, ${attributeKey}, ${attributeValue}",gateway:"Gateway","gateway-exists":"Device with same name is already exists.","gateway-status":"Gateway status","gateway-name":"Gateway name","gateway-name-required":"Gateway name is required.","gateway-saved":"Gateway configuration successfully saved.","generate-client-id":"Generate Client ID",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",info:"Info",identity:"Identity","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","unit-id":"Unit ID",host:"Host","host-required":"Host is required.",holding_registers:"Holding registers",coils_initializer:"Coils initializer",input_registers:"Input registers",discrete_inputs:"Discrete inputs","json-parse":"Not valid JSON.","json-required":"Field cannot be empty.","JSONPath-hint":"Supports constants and JSONPath expressions to extract data.",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",columns:{general:"General",service:"Service",connection:"Connection",storage:"Storage",extension:"Extension"},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"},"max-number-of-workers":"Max number of workers","max-number-of-workers-hint":"Maximal number of workers threads for converters \n(The amount of workers changes dynamically, depending on load) \nRecommended amount 50-150.","max-number-of-workers-required":"Max number of workers is required.","max-messages-queue-for-worker":"Max messages queue per worker","max-messages-queue-for-worker-hint":"Maximal messages count that will be in the queue \nfor each converter worker.","max-messages-queue-for-worker-required":"Max messages queue per worker is required.",method:"Method","method-name":"Method name","method-required":"Method name is 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 10","min-pack-send-delay-pattern":"Min pack send delay is not valid",multiplier:"Multiplier",mode:"Mode","model-name":"Model name",modifier:"Modifier","modifier-invalid":"Modifier is not valid","mqtt-version":"MQTT version",name:"Name","name-required":"Name is required.","network-mask":"Network mask","no-attributes":"No attributes","no-attribute-updates":"No attribute updates","no-attribute-requests":"No attribute requests","no-connectors":"No connectors","no-data":"No configurations","no-gateway-found":"No gateway found.","no-gateway-matching":" '{{item}}' not found.","no-timeseries":"No time series","no-keys":"No keys","no-value":"No arguments","no-rpc-methods":"No RPC methods","no-rpc-requests":"No RPC requests","path-hint":"The path is local to the gateway file system","path-logs":"Path to log files","path-logs-required":"Path is required.",password:"Password","password-required":"Password is required.","permit-without-calls":"Keep alive permit without calls","property-id":"Property ID","poll-period":"Poll period (ms)","poll-period-error":"Poll period should be at least {{min}} (ms).",port:"Port","port-required":"Port is required.","port-limits-error":"Port should be number from {{min}} to {{max}}.","private-key-path":"Path to private key file","path-to-private-key-required":"Path to private key file is required.",parity:"Parity","product-code":"Product code","product-name":"Product name",raw:"Raw",retain:"Retain","retain-hint":"This flag tells the broker to store the message for a topic\nand ensures any new client subscribing to that topic\nwill receive the stored message.",remote:"Remote configuration","remote-logging-level":"Logging level","remove-entry":"Remove configuration","remote-shell":"Remote shell","remote-configuration":"Remote Configuration","request-expression":"Request expression","request-expression-required":"Request expression is required",retries:"Retries","retries-on-empty":"Retries on empty","retries-on-invalid":"Retries on invalid",response:"Response",rest:{"add-http-header":"Add HTTP header","no-http-headers":"No HTTP headers","delete-http-header":"Delete HTTP header","ssl-verify":"SSL verify",endpoint:"Endpoint","http-methods":"HTTP methods","http-method":"HTTP method","on-error":"On error","on-success":"On success",tries:"Tries","http-headers":"HTTP headers","allow-redirects":"Allow redirects","request-url-expression":"Request URL expression","response-attribute":"Response attribute","response-expected":"Expected","response-type":{default:"Default",const:"Constant",advanced:"Advanced"}},rpc:{rpc:"RPC",title:"{{type}} Connector RPC parameters","templates-title":"Connector RPC Templates",methodFilter:"Method filter","method-name":"Method name",requestTopicExpression:"Request topic expression",responseTopicExpression:"Response topic expression",responseTimeout:"Response timeout",valueExpression:"Value expression",tag:"Tag",type:"Type",functionCode:"Function Code",objectsCount:"Objects Count",address:"Address",method:"Method",requestType:"Request Type",requestTimeout:"Request Timeout",objectType:"Object type",identifier:"Identifier",propertyId:"Property ID",methodRPC:"Method RPC name",withResponse:"With Response",characteristicUUID:"Characteristic UUID",methodProcessing:"Method Processing",nodeID:"Node ID",isExtendedID:"Is Extended ID",isFD:"Is FD",bitrateSwitch:"Bitrate Switch",dataInHEX:"Data In HEX",dataLength:"Data Length",dataByteorder:"Data Byte Order",dataBefore:"Data Before",dataAfter:"Data After",dataExpression:"Data Expression",oid:"OID","add-oid":"Add OID","add-header":"Add header","add-security":"Add security",remove:"Remove",requestFilter:"Request Filter",requestUrlExpression:"Request URL Expression",httpMethod:"HTTP Method",timeout:"Timeout",tries:"Tries",httpHeaders:"HTTP Headers","header-name":"Header name",hint:{"modbus-response-reading":"RPC response will return all subtracted values from all connected devices when the reading functions are selected.","modbus-writing-functions":"RPC will write a filled value to all connected devices when the writing functions are selected.","opc-method":"A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)."},"security-name":"Security name",value:"Value",security:"Security",responseValueExpression:"Response Value Expression",requestValueExpression:"Request Value Expression",arguments:"Arguments","add-argument":"Add argument","write-property":"Write property","read-property":"Read property","analog-output":"Analog output","analog-input":"Analog input","binary-output":"Binary output","binary-input":"Binary input","binary-value":"Binary value","analog-value":"Analog value",write:"Write",read:"Read",scan:"Scan",oids:"OIDS",set:"Set",multiset:"Multiset",get:"Get","bulk-walk":"Bulk walk",table:"Table","multi-get":"Multiget","get-next":"Get next","bulk-get":"Bulk get",walk:"Walk","save-template":"Save template","template-name":"Template name","template-name-required":"Template name is required.","template-name-duplicate":"Template with such name already exists, it will be updated.",command:"Command",params:"Params","json-value-invalid":"JSON value has an invalid format"},"rpc-methods":"RPC methods","rpc-requests":"RPC requests",request:{"connect-request":"Connect request","disconnect-request":"Disconnect request","attribute-request":"Attribute request","attribute-update":"Attribute update","rpc-connection":"RPC command"},"request-type":"Request type","request-timeout":"Request timeout (ms)","requests-mapping":"Requests mapping","requests-mapping-hint":"MQTT Connector requests allows you to connect, disconnect, process attribute requests from the device, handle attribute updates on the server and RPC processing configuration.","request-topic-expression":"Request topic expression","request-client-certificate":"Request client certificate","request-topic-expression-required":"Request topic expression is required.","response-timeout":"Response timeout","response-timeout-required":"Response timeout is required.","response-timeout-limits-error":"Timeout must be more then {{min}} ms.","response-topic-Qos":"Response topic QoS","response-topic-Qos-hint":"MQTT Quality of Service (QoS) is an agreement between the message sender and receiver that defines the level of delivery guarantee for a specific message.","response-topic-expression":"Response topic expression","response-topic-expression-required":"Response topic expression is required.","response-value-expression":"Response value expression","response-value-expression-required":"Response value expression is required.","vendor-name":"Vendor name","vendor-url":"Vendor URL",value:"Value",values:"Values","value-required":"Value is required.","value-expression":"Value expression","value-expression-required":"Value expression is required.","with-response":"With response","without-response":"Without response",other:"Other",socket:"Socket","save-tip":"Save configuration file","scan-period":"Scan period (ms)","scan-period-error":"Scan period should be at least {{min}} (ms).","sub-check-period":"Subscription check period (ms)","sub-check-period-error":"Subscription check period should be at least {{min}} (ms).",security:"Security","security-policy":"Security policy","security-type":"Security type","security-types":{"access-token":"Access Token","username-password":"Username and Password",tls:"TLS","tls-access-token":"TLS + Access Token","tls-private-key":"TLS + Private Key"},"select-connector":"Select connector to display config","send-change-data":"Send data only on change","send-data-to-platform":"Send data to platform","send-data-on-change":"Send data only on change","send-change-data-hint":"The values will be saved to the database only if they are different from the corresponding values in the previous converted message. This functionality applies to both attributes and time series in the converter output.",server:"Server","server-hostname":"Server hostname","server-slave":"Server (Slave)","servers-slaves":"Servers (Slaves)","server-port":"Server port","server-url":"Server endpoint url","server-connection":"Server Connection","server-config":"Server configuration","server-slave-config":"Server (Slave) configuration","server-url-required":"Server endpoint url is required.",stopbits:"Stopbits",strict:"Strict",set:"Set","show-map":"Show map",statistics:{entry:"Statistic entry","custom-send-period":"Custom send period (in sec)","custom-send-period-pattern":"Custom send period is not valid","custom-send-period-min":"Custom send period can not be less then 60","custom-send-period-required":"Custom send period is required","create-command":"Create command",attributes:"Attributes","name-already-exists":"Attribute name already exists.",telemetry:"Telemetry","storage-message-count":"Storage message count","messages-from-platform":"Messages from platform","pushed-datapoints":"Pushed datapoints","messages-pulled-from-storage":"Messages pulled from storage","messages-pushed-to-platform":"Messages pushed to platform","messages-sent-to-platform":"Messages sent to platform","process-cpu-usage":"Gateway process CPU usage",memory:"Gateway process memory usage","machine-resources":"Machine resources","free-disk":"Free disk",statistic:"Statistic",statistics:"Statistics","general-statistics":"General statistics","custom-statistics":"Custom statistics","copy-message":"Copy message","statistic-commands-empty":'No configured statistic keys found. You can configure them in "Statistics" tab in general configuration.',"statistics-button":"Go to configuration",commands:"Commands",name:"Time series name","time-series-name-already-exists":"Time series name already exists.","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)",messages:"Messages","max-payload-size-bytes":"Max payload size in bytes","max-payload-size-bytes-required":"Max payload size in bytes is required","max-payload-size-bytes-min":"Max payload size in bytes can not be less then 100","max-payload-size-bytes-pattern":"Max payload size in bytes is not valid","min-pack-size-to-send":"Min packet size to send","min-pack-size-to-send-required":"Min packet size to send is required","min-pack-size-to-send-min":"Min packet size to send can not be less then 100","min-pack-size-to-send-pattern":"Min packet size to send is not valid","no-config-commands-found":"No configuration commands found","delete-command":"Delete command '{{command}}'?","delete-command-data":"All command data will be deleted.","edit-command":"Edit command","change-command-title":"Discard command change","change-command-text":"Cancelling command edit will discard any unsaved changes. Continue?","no-command-found":"No command found","no-commands-matching":"No command matching '{{command}}' were found.","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","install-cmd":"Install command",add:"Add command",timeout:"Timeout (in sec)","timeout-required":"Timeout is required","timeout-min":"Timeout can not be less then 1","timeout-pattern":"Timeout is not valid","time-series-name-required":"Time series name is required","time-series-name-pattern":"Time series name is not valid",command:"Command","command-required":"Command is required","command-pattern":"Command is not valid",remove:"Remove command"},storage:"Storage","storage-max-files":"Maximum number of files","storage-max-files-min":"Minimum number is 1.","storage-max-files-pattern":"Number is not valid.","storage-max-files-required":"Number is required.","storage-max-records":"Maximum records in storage","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-storage":"Max read record count in storage","storage-max-read-record-count":"Max read record count","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-data-folder-path-pattern":"Data folder path path path must start with './' and end with '/', without dots in folder names.","storage-pack-size":"Maximum event pack size","storage-pack-size-min":"Minimum number is 1.","storage-pack-size-pattern":"Number is not valid.","storage-pack-size-required":"Maximum event pack size is required.","storage-path":"Storage path","storage-path-required":"Storage path is required.","storage-path-pattern":"Storage path path must start with './' and end with '/', without dots in folder names.","storage-type":"Storage type","storage-types":{"file-storage":"File storage","memory-storage":"Memory storage",sqlite:"SQLITE"},suffix:{s:"s",ms:"ms"},"report-strategy":{label:"Report strategy","on-change":"On value change","on-report-period":"On report period","on-change-or-report-period":"On value change or report period","report-period":"Report period","on-received":"On received"},"source-type":{msg:"Message",topic:"Topic",const:"Constant",identifier:"Identifier",path:"Path",expression:"Expression",request:"From request"},"workers-settings":"Workers settings",thingsboard:"ThingsBoard",general:"General",timeseries:"Time series",key:"Key",keys:"Keys","key-required":"Key is required.","thingsboard-host":"Platform host","thingsboard-host-required":"Host is required.","thingsboard-port":"Platform port","thingsboard-port-max":"Maximum port number is 65535.","thingsboard-port-min":"Minimum port number is 1.","thingsboard-port-pattern":"Port is not valid.","thingsboard-port-required":"Port is required.",tidy:"Tidy","tidy-tip":"Tidy config JSON",timeout:"Timeout","timeout-error":"Timeout should be at least {{min}} (ms).","title-connectors-json":"Connector {{typeName}} configuration",type:"Type","topic-filter":"Topic filter","topic-required":"Topic filter is required.","tls-path-ca-certificate":"Path to CA certificate on gateway","tls-path-client-certificate":"Path to client certificate on gateway","tls-connection":"TLS Connection","master-connections":"Master Connections","method-filter":"Method filter","method-filter-hint":"Regular expression to filter incoming RPC method from platform.","method-filter-required":"Method filter is required.","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.","size-limit":"Size limit","size-limit-hint":"Maximum size of each SQLite file in megabytes. When exceeded, triggers rotation to a new DB file.","size-limit-required":"Size limit is required.","size-limit-min":"Min size limit is 1.","size-limit-pattern":"Number is not valid.","max-db-amount":"Maximum DB amount","max-db-amount-hint":"Maximum number of rotated DB files to keep on disk. When reached, further writes are dropped until cleanup.","max-db-amount-required":"Maximum DB amount is required.","max-db-amount-min":"Min number is 1.","max-db-amount-pattern":"Number is not valid.","oversize-check-period":"Oversize check period","oversize-check-period-hint":"How frequently (in minutes) to check the current DB’s file size against size_limit.","oversize-check-period-required":"Oversize check period is required.","oversize-check-period-min":"Min number is 1.","oversize-check-period-pattern":"Number is not valid.","writing-batch-size":"Writing batch size","writing-batch-size-hint":"Maximum number of messages collected before writing to the DB during single write request.","writing-batch-size-required":"Writing batch size is required.","writing-batch-size-min":"Min number is 1.","writing-batch-size-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",qos:{"at-most-once":"0 - At most once","at-least-once":"1 - At least once","exactly-once":"2 - Exactly once"},"objects-count":"Objects count","object-id":"Object ID","objects-count-required":"Objects count is required","wait-after-failed-attempts":"Wait after failed attempts (ms)","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",username:"Username","username-required":"Username is required.","unit-id-required":"Unit ID is required.","vendor-id":"Vendor ID","write-coil":"Write Coil","write-coils":"Write Coils","write-register":"Write Register","write-registers":"Write Registers","general-configuration-widget":{"general-configuration":"General configuration","connectors-configuration":"Connectors configuration",logs:"Logs",statistics:"Statistics","remote-shell":"Remote Shell",rpc:"RPC"},"statistics-nav-widget":{"storage-statistics":"Storage","machine-statistics":"Machine","custom-statistics":"Custom"},"status-widget":{status:"Status","gateway-name":"Gateway name","gateway-version":"Gateway version","gateway-type":"Gateway type",devices:"Devices","active-inactive":"(Active | Inactive)",connectors:"Connectors","enabled-disabled":"(Enabled | Disabled)",errors:"Errors"},hints:{"enable-general-statistics":"Enables gateway statistics (machine, storage, connectors).","enable-custom-statistics":"Enables collecting statistics using custom commands.","buffer-size":"Buffer size for received data blocks.",encoding:"Encoding used for writing received string data to storage.",method:"Name for method on a platform.","modbus-master":"Configuration sections for connecting to Modbus servers and reading data from them.","modbus-server":"Configuration section for the Modbus server, storing data and sending updates to the platform when changes occur or at fixed intervals.","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 platform server",port:"Port of MQTT service on platform server",token:"Access token for the gateway from platform server","client-id":"MQTT client id for the gateway form platform server",username:"MQTT username for the gateway form platform server",password:"MQTT password for the gateway form platform server","ca-cert":"Path to CA certificate file","date-form":"Date format in log message","object-id-required":"Object ID is required","vendor-id-required":"Vendor ID is required","data-folder":"Path to the folder that will contain 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 number of files that will be created","max-read-count":"Number of messages to retrieve from the storage and send to platform","max-records":"Maximum count of records that will be stored in one file","read-record-count":"Number of messages to retrieve from the storage and send to platform","max-records-count":"Maximum number of data entries in storage before sending to platform","ttl-check-hour":"How often will the Gateway check data for obsolescence","ttl-messages-day":"Maximum number of days that the storage will retain data","username-required-with-password":"Username required if password is specified",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.","path-in-os":"Path in gateway os.",memory:"Your data will be stored in the in-memory queue, it is a fastest but no persistence guarantee.",file:"Your data will be stored in separated files and will be saved even after the gateway restart.",sqlite:"Your data will be stored in file based database. And will be saved even after the gateway restart.","opc-timeout":"Timeout in milliseconds for connecting to OPC-UA server.","security-policy":"Security Policy defines the security mechanisms to be applied.","install-cmd":"Packages that will be installed for command executing.","scan-period":"Period in milliseconds to rescan the server.","sub-check-period":"Period to check the subscriptions in the OPC-UA server.","enable-subscription":"If true - the gateway will subscribe to interesting nodes and wait for data update and if false - the gateway will rescan OPC-UA server every scanPeriodInMillis.","show-map":"Show nodes on scanning.","method-name":"Name of method on OPC-UA server.",arguments:"Arguments for the method (will be overwritten by arguments from the RPC request).","min-pack-size-to-send":"Minimum package size for sending.","max-payload-size-bytes":"Maximum package size in bytes","poll-period":"Period in milliseconds to read data from nodes.","poll-period-required":"Poll period is required.","report-period-required":"Report period is required.","report-period-range":"Report period must be greater than 100.","timeout-pattern":"Timeout is not valid",rest:{"endpoint-required":"Endpoint is required.","endpoint-pattern":"Endpoint is not valid.","device-name-filter-required":"Device name filter is required.","device-name-filter-pattern":"Device name filter is not valid.","request-url-expression-required":"Request URL expression is required.","request-url-expression-pattern":"Request URL expression is not valid.","value-expression-required":"Value expression is required.","value-expression-pattern":"Value expression is not valid.","method-filter-required":"Method filter is required.","method-filter-pattern":"Method filter is not valid.","scope-type":"Attribute scope on platform.","tries-min":"Request tries number should be greater than 0.","tries-pattern":"Request tries is invalid.","http-methods-required":"HTTP methods are required.",host:"Domain or IP address of the server.",port:"Port on the server for listening clients connection.","ssl-verify":"Enable SSL certificate verification.",cert:"Path to the certificate file.",key:"Path to the private key file",endpoint:"Endpoint path on the server.","http-methods":"HTTP methods allowed for endpoint.","http-method":"Request to external URL HTTP method.",JSON:"Converter for this payload type processes REST messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and time series from the request body.",extension:"Custom converter classname. File with this class should be in extension/rest folder.","response-attribute-required":"Response attribute is required.","response-attribute-pattern":"Response attribute is not valid.","update-ssl":"Verify SSL certificate on the external server.","attribute-filter":"Filter for incoming attribute name from platform, support regular expression.","value-expression":"Used to create request body for request to external server. Supports ${deviceName}, ${deviceType}, ${attributeName}, ${attributeValue} variables.","attribute-url-expression":"Used to create request URL for request to external server. Supports ${deviceName}, ${deviceType}, ${attributeName}, ${attributeValue} variables.","method-filter":"Filter for RPC requests from the platform based on the method name. Supports regular expressions.","attribute-name-expression-required":"Attribute name expression is required.","attribute-name-expression-pattern":"Attribute name expression is not valid.","attribute-filter-required":"Attribute filter is required.","attribute-filter-pattern":"Attribute filter is not valid."},socket:{"attribute-on-platform-required":"Attribute on platform is required","attribute-requests-type":"The type of requested attribute can be “shared” or “client.“","with-response":"Boolean flag that specifies whether to send a response back to platform.","key-telemetry":"Name for telemetry on platform.","key-attribute":"Name for attribute on platform."},modbus:{"bit-target-type":"The response type can be either an integer (1/0) or a boolean (True/False).",bit:"Specify the index of the bit to read from the array, or leave it blank to read the entire array.","max-bit":"The bit value must not exceed the objects count.","framer-type":"Type of a framer (Socket, RTU, or ASCII), if needed.",host:"Hostname or IP address of Modbus server.",port:"Modbus server port for connection.","unit-id":"Modbus slave ID.","connection-timeout":"Connection timeout (in seconds) for the Modbus server.","byte-order":"Byte order for reading data.","word-order":"Word order when reading multiple registers.",retries:"Retrying sending data to the master.","retries-on-empty":"Retry sending data to the master if the data is empty.","retries-on-invalid":"Retry sending data to the master if it fails.","poll-period":"Period in milliseconds to check attributes and telemetry on the slave.","connect-attempt-time":"A waiting period in milliseconds before establishing a connection to the master.","connect-attempt-count":"The number of connection attempts made through the gateway.","wait-after-failed-attempts":"A waiting period in milliseconds before attempting to send data to the master.","serial-port":"Serial port for connection.",baudrate:"Baud rate for the serial device.",stopbits:"The number of stop bits sent after each character in a message to indicate the end of the byte.",bytesize:"The number of bits in a byte of serial data. This can be one of 5, 6, 7, or 8.",parity:"The type of checksum used to verify data integrity. Options: (E)ven, (O)dd, (N)one.",strict:"Use inter-character timeout for baudrates ≤ 19200.","objects-count":"Depends on the selected type.",address:"Register address to verify.",key:"Key to be used as the attribute key for the platform instance.",method:"Method name to be used for the platform instance.","data-keys":"For more information about function codes and data types click on help icon",modifier:"The retrieved value will be adjusted (by multiplying or dividing it) based on the specified modifier value."},bacnet:{"object-id":"The gateway object identifier in the BACnet network.","vendor-id":"The gateway vendor identifier in the BACnet network","apdu-length":"Maximal length of the APDU.",segmentation:"Segmentation type for transmitting large BACnet messages.","key-object-id":"Object id in the BACnet device.","property-id":"Property id in the BACnet device.","request-type":"“writeProperty” to write data and “readProperty” to read data.","request-timeout":"Timeout to wait the response from the BACnet device, milliseconds.","device-timeout":"Period of time when the connector will try to discover BACnet devices.","network-number":"Identifier of the network segment."}}}},vt={gateway:{"add-entry":"إضافة تكوين",advanced:"متقدم","checking-device-activity":"فحص نشاط الجهاز",command:"أوامر Docker","command-copied-message":"تم نسخ أمر Docker إلى الحافظة",configuration:"التكوين","connector-add":"إضافة موصل جديد","connector-enabled":"تمكين الموصل","connector-name":"اسم الموصل","connector-name-required":"اسم الموصل مطلوب.","connector-type":"نوع الموصل","connector-type-required":"نوع الموصل مطلوب.",connectors:"الموصلات","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":"تكوين عام","docker-label":"استخدم التعليمات التالية لتشغيل IoT Gateway في Docker compose مع بيانات اعتماد للجهاز المحدد","install-docker-compose":"استخدم التعليمات لتنزيل وتثبيت وإعداد docker compose","download-configuration-file":"تنزيل ملف التكوين","download-docker-compose":"تنزيل docker-compose.yml لبوابتك","launch-gateway":"تشغيل البوابة","launch-docker-compose":"بدء تشغيل البوابة باستخدام الأمر التالي في الطرفية من المجلد الذي يحتوي على ملف docker-compose.yml","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":"الحد الأدنى للوقت بين البينغات (بالمللي ثانية)","grpc-min-time-between-pings-required":"الحد الأدنى للوقت بين البينغات مطلوب","grpc-min-time-between-pings-min":"الحد الأدنى للوقت بين البينغات لا يمكن أن يكون أقل من 1","grpc-min-time-between-pings-pattern":"الحد الأدنى للوقت بين البينغات غير صالح","grpc-min-ping-interval-without-data":"الحد الأدنى لفاصل البينغ بدون بيانات (بالمللي ثانية)","grpc-min-ping-interval-without-data-required":"الحد الأدنى لفاصل البينغ بدون بيانات مطلوب","grpc-min-ping-interval-without-data-min":"الحد الأدنى لفاصل البينغ بدون بيانات لا يمكن أن يكون أقل من 1","grpc-min-ping-interval-without-data-pattern":"الحد الأدنى لفاصل البينغ بدون بيانات غير صالح","grpc-max-pings-without-data":"الحد الأقصى لعدد البينغات بدون بيانات","grpc-max-pings-without-data-required":"الحد الأقصى لعدد البينغات بدون بيانات مطلوب","grpc-max-pings-without-data-min":"الحد الأقصى لعدد البينغات بدون بيانات لا يمكن أن يكون أقل من 1","grpc-max-pings-without-data-pattern":"الحد الأقصى لعدد البينغات بدون بيانات غير صالح","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":"قشرة عن بُعد","remote-configuration":"التكوين عن بُعد",other:"آخر","save-tip":"حفظ ملف التكوين","security-type":"نوع الأمان","security-types":{"access-token":"رمز الوصول","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-required":"المهلة مطلوبة","timeout-min":"لا يمكن أن تكون المهلة أقل من 1","timeout-pattern":"المهلة غير صالحة","attribute-name":"اسم السمة","attribute-name-required":"اسم السمة مطلوب",command:"الأمر","command-required":"الأمر مطلوب","command-pattern":"الأمر غير صالح",remove:"إزالة الأمر"},storage:"التخزين","storage-max-file-records":"السجلات القصوى في الملف","storage-max-files":"الحد الأقصى لعدد الملفات","storage-max-files-min":"الحد الأدنى هو 1.","storage-max-files-pattern":"العدد غير صالح.","storage-max-files-required":"العدد مطلوب.","storage-max-records":"السجلات القصوى في التخزين","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":"العدد غير صالح.","storage-pack-size-required":"الحجم الأقصى لحزمة الحدث مطلوب.","storage-path":"مسار التخزين","storage-path-required":"مسار التخزين مطلوب.","storage-type":"نوع التخزين","storage-types":{"file-storage":"تخزين الملفات","memory-storage":"تخزين الذاكرة",sqlite:"SQLITE"},thingsboard:"ثينغزبورد",general:"عام","thingsboard-host":"مضيف ثينغزبورد","thingsboard-host-required":"المضيف مطلوب.","thingsboard-port":"منفذ ثينغزبورد","thingsboard-port-max":"الحد الأقصى لرقم المنفذ هو 65535.","thingsboard-port-min":"الحد الأدنى لرقم المنفذ هو 1.","thingsboard-port-pattern":"المنفذ غير صالح.","thingsboard-port-required":"المنفذ مطلوب.",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",hints:{"remote-configuration":"يمكنك تمكين التكوين وإدارة البوابة عن بُعد","remote-shell":"يمكنك تمكين التحكم البعيد في نظام التشغيل مع البوابة من عنصر واجهة المستخدم قشرة عن بُعد",host:"اسم المضيف أو عنوان IP لخادم ثينغزبورد",port:"منفذ خدمة MQTT على خادم ثينغزبورد",token:"رمز الوصول للبوابة من خادم ثينغزبورد","client-id":"معرف عميل MQTT للبوابة من خادم ثينغزبورد",username:"اسم المستخدم MQTT للبوابة من خادم ثينغزبورد",password:"كلمة المرور MQTT للبوابة من خادم ثينغزبورد","ca-cert":"المسار إلى ملف شهادة CA","date-form":"تنسيق التاريخ في رسالة السجل","data-folder":"المسار إلى المجلد الذي سيحتوي على البيانات (نسبي أو مطلق)","log-format":"تنسيق رسالة السجل","remote-log":"يمكنك تمكين التسجيل البعيد وقراءة السجلات من البوابة","backup-count":"إذا كان عدد النسخ الاحتياطية > 0، عند عملية تدوير، لا يتم الاحتفاظ بأكثر من عدد النسخ الاحتياطية المحددة - يتم حذف الأقدم",storage:"يوفر تكوينًا لحفظ البيانات الواردة قبل إرسالها إلى المنصة","max-file-count":"العدد الأقصى لعدد الملفات التي سيتم إنشاؤها","max-read-count":"عدد الرسائل للحصول عليها من التخزين وإرسالها إلى ثينغزبورد","max-records":"العدد الأقصى للسجلات التي ستخزن في ملف واحد","read-record-count":"عدد الرسائل للحصول عليها من التخزين وإرسالها إلى ثينغزبورد","max-records-count":"العدد الأقصى للبيانات في التخزين قبل إرسالها إلى ثينغزبورد","ttl-check-hour":"كم مرة سيتحقق البوابة من البيانات القديمة","ttl-messages-day":"الحد الأقصى لعدد الأيام التي ستحتفظ فيها التخزين بالبيانات",commands:"الأوامر لجمع الإحصائيات الإضافية",attribute:"مفتاح تلقي الإحصائيات",timeout:"مهلة زمنية لتنفيذ الأمر",command:"سيتم استخدام نتيجة تنفيذ الأمر كقيمة لتلقي الإحصائيات","check-device-activity":"يمكنك تمكين مراقبة نشاط كل جهاز متصل","inactivity-timeout":"الوقت بعد الذي ستفصل البوابة الجهاز","inactivity-period":"تكرار فحص نشاط الجهاز","minimal-pack-delay":"التأخير بين إرسال حزم الرسائل (يؤدي تقليل هذا الإعداد إلى زيادة استخدام وحدة المعالجة المركزية)",qos:"جودة الخدمة في رسائل MQTT (0 - على الأكثر مرة واحدة، 1 - على الأقل مرة واحدة)","server-port":"منفذ الشبكة الذي سيستمع فيه خادم GRPC للاستفسارات الواردة.","grpc-keep-alive-timeout":"الحد الأقصى للوقت الذي يجب أن ينتظره الخادم لاستجابة رسالة الحفاظ على الاتصال قبل اعتبار الاتصال ميتًا.","grpc-keep-alive":"المدة بين رسائل حفظ الاتصال المتعاقبة عند عدم وجود استدعاء RPC نشط.","grpc-min-time-between-pings":"الحد الأدنى للوقت الذي يجب فيه أن ينتظر الخادم بين إرسال رسائل حفظ الاتصال","grpc-max-pings-without-data":"الحد الأقصى لعدد رسائل حفظ الاتصال التي يمكن للخادم إرسالها دون تلقي أي بيانات قبل اعتبار الاتصال ميتًا.","grpc-min-ping-interval-without-data":"الحد الأدنى للوقت الذي يجب فيه أن ينتظر الخادم بين إرسال رسائل حفظ الاتصال عند عدم إرسال أو استلام بيانات.","permit-without-calls":"السماح للخادم بإبقاء اتصال GRPC حيًا حتى عندما لا تكون هناك استدعاءات RPC نشطة."}}},xt={gateway:{"add-entry":"Afegir configuració","connector-add":"Afegir conector","connector-enabled":"Activar conector","connector-name":"Nom conector","connector-name-required":"Cal nom conector.","connector-type":"Tipus conector","connector-type-required":"Cal tipus conector.",connectors:"Configuració de conectors","create-new-gateway":"Crear un gateway nou","create-new-gateway-text":"Crear un nou gateway amb el nom: '{{gatewayName}}'?",delete:"Esborrar configuració","download-tip":"Descarregar fitxer de configuració",gateway:"Gateway","gateway-exists":"Ja existeix un dispositiu amb el mateix nom.","gateway-name":"Nom de Gateway","gateway-name-required":"Cal un nom de gateway.","gateway-saved":"Configuració de gateway gravada satisfactòriament.","json-parse":"JSON no vàlid.","json-required":"El camp no pot ser buit.","no-connectors":"No hi ha conectors","no-data":"No hi ha configuracions","no-gateway-found":"No s'ha trobat cap gateway.","no-gateway-matching":" '{{item}}' no trobat.","path-logs":"Ruta als fitxers de log","path-logs-required":"Cal ruta.",remote:"Configuració remota","remote-logging-level":"Nivel de logging","remove-entry":"Esborrar configuració","save-tip":"Gravar fitxer de configuració","security-type":"Tipus de seguretat","security-types":{"access-token":"Token d'accés",tls:"TLS"},storage:"Grabació","storage-max-file-records":"Número màxim de registres en fitxer","storage-max-files":"Número màxim de fitxers","storage-max-files-min":"El número mínim és 1.","storage-max-files-pattern":"Número no vàlid.","storage-max-files-required":"Cal número.","storage-max-records":"Màxim de registres en el magatzem","storage-max-records-min":"El número mínim és 1.","storage-max-records-pattern":"Número no vàlid.","storage-max-records-required":"Cal número.","storage-pack-size":"Mida màxim de esdeveniments","storage-pack-size-min":"El número mínim és 1.","storage-pack-size-pattern":"Número no vàlid.","storage-pack-size-required":"Cal número.","storage-path":"Ruta de magatzem","storage-path-required":"Cal ruta de magatzem.","storage-type":"Tipus de magatzem","storage-types":{"file-storage":"Magatzem fitxer","memory-storage":"Magatzem en memoria"},thingsboard:"ThingsBoard","thingsboard-host":"Host ThingsBoard","thingsboard-host-required":"Cal Host.","thingsboard-port":"Port ThingsBoard","thingsboard-port-max":"El port màxim és 65535.","thingsboard-port-min":"El port mínim és 1.","thingsboard-port-pattern":"Port no vàlid.","thingsboard-port-required":"Cal port.",tidy:"Endreçat","tidy-tip":"Endreçat JSON","title-connectors-json":"Configuració conector {{typeName}}","tls-path-ca-certificate":"Ruta al certificat CA al gateway","tls-path-client-certificate":"Ruta al certificat client al gateway","tls-path-private-key":"Ruta a la clau privada al gateway","toggle-fullscreen":"Pantalla completa fullscreen","transformer-json-config":"Configuració JSON*","update-config":"Afegir/actualizar configuració JSON"}},bt={gateway:{"add-entry":"Přidat konfiguraci","connector-add":"Přidat nový konektor","connector-enabled":"Povolit konektor","connector-name":"Název konektoru","connector-name-required":"Název konektoru je povinný.","connector-type":"Typ konektoru","connector-type-required":"Typ konektoru je povinný.",connectors:"Konfigurace konektoru","create-new-gateway":"Vytvořit novou bránu","create-new-gateway-text":"Jste si jisti, že chcete vytvořit novou bránu s názvem: '{{gatewayName}}'?",delete:"Smazat konfiguraci","download-tip":"Stáhnout soubor konfigurace",gateway:"Brána","gateway-exists":"Zařízení se shodným názvem již existuje.","gateway-name":"Název brány","gateway-name-required":"Název brány je povinný.","gateway-saved":"Konfigurace brány byla úspěšně uložena.","json-parse":"Neplatný JSON.","json-required":"Pole nemůže být prázdné.","no-connectors":"Žádné konektory","no-data":"Žádné konfigurace","no-gateway-found":"Žádné brány nebyly nalezeny.","no-gateway-matching":" '{{item}}' nenalezena.","path-logs":"Cesta k souborům logu","path-logs-required":"Cesta je povinná.",remote:"Vzdálená konfigurace","remote-logging-level":"Úroveň logování","remove-entry":"Odstranit konfiguraci","save-tip":"Uložit soubor konfigurace","security-type":"Typ zabezpečení","security-types":{"access-token":"Přístupový token",tls:"TLS"},storage:"Úložiště","storage-max-file-records":"Maximální počet záznamů v souboru","storage-max-files":"Maximální počet souborů","storage-max-files-min":"Minimální počet je 1.","storage-max-files-pattern":"Počet není platný.","storage-max-files-required":"Počet je povinný.","storage-max-records":"Maximální počet záznamů v úložišti","storage-max-records-min":"Minimální počet záznamů je 1.","storage-max-records-pattern":"Počet není platný.","storage-max-records-required":"Maximální počet záznamů je povinný.","storage-pack-size":"Maximální velikost souboru událostí","storage-pack-size-min":"Minimální počet je 1.","storage-pack-size-pattern":"Počet není platný.","storage-pack-size-required":"Maximální velikost souboru událostí je povinná.","storage-path":"Cesta k úložišti","storage-path-required":"Cesta k úložišti je povinná.","storage-type":"Typ úložiště","storage-types":{"file-storage":"Soubor","memory-storage":"Paměť"},thingsboard:"ThingsBoard","thingsboard-host":"Host ThingsBoard","thingsboard-host-required":"Host je povinný.","thingsboard-port":"Port ThingsBoard","thingsboard-port-max":"Maximální číslo portu je 65535.","thingsboard-port-min":"Minimální číslo portu je 1.","thingsboard-port-pattern":"Port není platný.","thingsboard-port-required":"Port je povinný.",tidy:"Uspořádat","tidy-tip":"Uspořádat JSON konfiguraci","title-connectors-json":"Konfigurace {{typeName}} konektoru","tls-path-ca-certificate":"Cesta k certifikátu CA brány","tls-path-client-certificate":"Cesta k certifikátu klienta brány","tls-path-private-key":"Cesta k privátnímu klíči brány","toggle-fullscreen":"Přepnout do režimu celé obrazovky","transformer-json-config":"JSON* konfigurace","update-config":"Přidat/editovat JSON konfiguraci"}},wt={gateway:{"add-entry":"Tilføj konfiguration","connector-add":"Tilføj ny stikforbindelse","connector-enabled":"Aktivér stikforbindelse","connector-name":"Navn på stikforbindelse","connector-name-required":"Navn på stikforbindelse er påkrævet.","connector-type":"Stikforbindelsestype","connector-type-required":"Stikforbindelsestype er påkrævet.",connectors:"Konfiguration af stikforbindelser","create-new-gateway":"Opret en ny gateway","create-new-gateway-text":"",delete:"Slet konfiguration","download-tip":"Download konfigurationsfil",gateway:"Gateway","gateway-exists":"Enhed med samme navn findes allerede.","gateway-name":"Gateway-navn","gateway-name-required":"Gateway-navn er påkrævet.","gateway-saved":"Gateway-konfigurationen blev gemt.","json-parse":"Ikke gyldig JSON.","json-required":"Feltet må ikke være tomt.","no-connectors":"Ingen stikforbindelser","no-data":"Ingen konfigurationer","no-gateway-found":"Ingen gateway fundet.","no-gateway-matching":"","path-logs":"Sti til logfiler","path-logs-required":"Sti er påkrævet.",remote:"Fjernkonfiguration","remote-logging-level":"Logføringsniveau","remove-entry":"Fjern konfiguration","save-tip":"Gem konfigurationsfil","security-type":"Sikkerhedstype","security-types":{"access-token":"Adgangstoken",tls:"TLS"},storage:"Lagring","storage-max-file-records":"Maks. antal poster i fil","storage-max-files":"Maks. antal filer","storage-max-files-min":"Min. antal er 1.","storage-max-files-pattern":"Antal er ikke gyldigt.","storage-max-files-required":"Antal er påkrævet.","storage-max-records":"Maks. antal poster i lagring","storage-max-records-min":"Min. antal poster er 1.","storage-max-records-pattern":"Antal er ikke gyldigt.","storage-max-records-required":"Maks. antal poster er påkrævet.","storage-pack-size":"Maks. antal pakkestørrelse for begivenhed","storage-pack-size-min":"Min. antal er 1.","storage-pack-size-pattern":"Antal er ikke gyldigt.","storage-pack-size-required":"Maks. antal pakkestørrelse for begivenhed er påkrævet.","storage-path":"Lagringssti","storage-path-required":"Lagringssti er påkrævet.","storage-type":"Lagringstype","storage-types":{"file-storage":"Lagring af filter","memory-storage":"Lagring af hukommelse"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard-vært","thingsboard-host-required":"Vært er påkrævet.","thingsboard-port":"ThingsBoard-port","thingsboard-port-max":"Maks. portnummer er 65535.","thingsboard-port-min":"Min. portnummer er 1.","thingsboard-port-pattern":"Port er ikke gyldig.","thingsboard-port-required":"Port er påkrævet.",tidy:"Tidy","tidy-tip":"Tidy konfig. JSON","title-connectors-json":"","tls-path-ca-certificate":"Sti til CA-certifikat på gateway","tls-path-client-certificate":"Sti til klientcertifikat på gateway","tls-path-private-key":"Sti til privat nøgle på gateway","toggle-fullscreen":"Skift til fuld skærm","transformer-json-config":"Konfiguration JSON*","update-config":"Tilføj/opdater konfiguration JSON"}},St={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:"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":"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-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","storage-max-files":"Número máximo de ficheros","storage-max-files-min":"El número mínimo es 1.","storage-max-files-pattern":"Número no válido.","storage-max-files-required":"Se requiere número.","storage-max-records":"Máximo de registros en el almacén","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.","storage-pack-size-required":"Se requiere número.","storage-path":"Ruta de almacén","storage-path-required":"Se requiere ruta de almacén.","storage-type":"Tipo de almacén","storage-types":{"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","thingsboard-port-max":"El puerto máximo es 65535.","thingsboard-port-min":"El puerto mínimo es 1.","thingsboard-port-pattern":"Puerto no válido.","thingsboard-port-required":"Se requiere puerto.",tidy:"Tidy","tidy-tip":"Tidy JSON","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",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."}}},Ct={gateway:{"add-entry":"설정 추가","connector-add":"새로운 연결자 추가","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","create-new-gateway":"Create a new gateway","create-new-gateway-text":"Are you sure you want create a new gateway with name: '{{gatewayName}}'?",delete:"Delete configuration","download-tip":"Download configuration file",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.","json-parse":"Not valid JSON.","json-required":"Field cannot be empty.","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.",remote:"Remote configuration","remote-logging-level":"Logging level","remove-entry":"Remove configuration","save-tip":"Save configuration file","security-type":"Security type","security-types":{"access-token":"Access Token",tls:"TLS"},storage:"Storage","storage-max-file-records":"Maximum records in file","storage-max-files":"Maximum number of files","storage-max-files-min":"Minimum number is 1.","storage-max-files-pattern":"Number is not valid.","storage-max-files-required":"Number is required.","storage-max-records":"Maximum records in storage","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-pack-size":"Maximum event pack size","storage-pack-size-min":"Minimum number is 1.","storage-pack-size-pattern":"Number is not valid.","storage-pack-size-required":"Maximum event pack size is required.","storage-path":"Storage path","storage-path-required":"Storage path is required.","storage-type":"Storage type","storage-types":{"file-storage":"File storage","memory-storage":"Memory storage"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard host","thingsboard-host-required":"Host is required.","thingsboard-port":"ThingsBoard port","thingsboard-port-max":"Maximum port number is 65535.","thingsboard-port-min":"Minimum port number is 1.","thingsboard-port-pattern":"Port is not valid.","thingsboard-port-required":"Port is required.",tidy:"Tidy","tidy-tip":"Tidy config JSON","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","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"}},_t={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","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":"Result","rpc-command-edit-params":"Edit parameters","gateway-configuration":"General Configuration","docker-label":"In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.","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","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-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",remove:"Remove command"},storage:"Storage","storage-max-file-records":"Maximum records in file","storage-max-files":"Maximum number of files","storage-max-files-min":"Minimum number is 1.","storage-max-files-pattern":"Number is not valid.","storage-max-files-required":"Number is required.","storage-max-records":"Maximum records in storage","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.","storage-pack-size-required":"Maximum event pack size is required.","storage-path":"Storage path","storage-path-required":"Storage path is required.","storage-type":"Storage type","storage-types":{"file-storage":"File 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","thingsboard-port-max":"Maximum port number is 65535.","thingsboard-port-min":"Minimum port number is 1.","thingsboard-port-pattern":"Port is not valid.","thingsboard-port-required":"Port is required.",tidy:"Tidy","tidy-tip":"Tidy config JSON","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",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."}}},Tt={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"}},It={gateway:{"add-entry":"Dodaj konfigurację",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":"Dodaj nowe złącze","connector-enabled":"Włącz złącze","connector-name":"Nazwa złącza","connector-name-required":"Nazwa złącza jest wymagana.","connector-type":"Typ złącza","connector-type-required":"Typ złącza jest wymagany.",connectors:"Konfiguracja złączy","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":"Result","rpc-command-edit-params":"Edit parameters","gateway-configuration":"General Configuration","docker-label":"In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.","create-new-gateway":"Utwórz nowy gateway","create-new-gateway-text":"Czy na pewno chcesz utworzyć nowy gateway o nazwie: '{{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:"Usuń konfigurację","download-tip":"Pobierz plik konfiguracyjny","drop-file":"Drop file here or",gateway:"Wejście","gateway-exists":"Urządzenie o tej samej nazwie już istnieje.","gateway-name":"Nazwa Gateway","gateway-name-required":"Nazwa Gateway'a jest wymagana.","gateway-saved":"Konfiguracja Gatewey'a została pomyślnie zapisana.",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":"Nieprawidłowy JSON.","json-required":"Pole nie może być puste.",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":"Brak złączy","no-data":"Brak konfiguracji","no-gateway-found":"Nie znaleziono gateway'a.","no-gateway-matching":" '{{item}}' nie znaleziono.","path-logs":"Ścieżka do plików dziennika","path-logs-required":"Ścieżka jest wymagana.","permit-without-calls":"Keep alive permit without calls",remote:"Zdalna konfiguracja","remote-logging-level":"Poziom logowania","remove-entry":"Usuń konfigurację","remote-shell":"Remote shell","remote-configuration":"Remote Configuration",other:"Other","save-tip":"Zapisz plik konfiguracyjny","security-type":"Rodzaj zabezpieczenia","security-types":{"access-token":"Token dostępu","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-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",remove:"Remove command"},storage:"Składowanie","storage-max-file-records":"Maksymalna liczba rekordów w pliku","storage-max-files":"Maksymalna liczba plików","storage-max-files-min":"Minimalna liczba to 1.","storage-max-files-pattern":"Numer jest nieprawidłowy.","storage-max-files-required":"Numer jest wymagany.","storage-max-records":"Maksymalna liczba rekordów w pamięci","storage-max-records-min":"Minimalna liczba rekordów to 1.","storage-max-records-pattern":"Numer jest nieprawidłowy.","storage-max-records-required":"Maksymalna liczba rekordów jest wymagana.","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":"Maksymalny rozmiar pakietu wydarzeń","storage-pack-size-min":"Minimalna liczba to 1.","storage-pack-size-pattern":"Numer jest nieprawidłowy.","storage-pack-size-required":"Maksymalny rozmiar pakietu wydarzeń jest wymagany.","storage-path":"Ścieżka przechowywania","storage-path-required":"Ścieżka do przechowywania jest wymagana.","storage-type":"Typ składowania","storage-types":{"file-storage":"Nośnik danych","memory-storage":"Przechowywanie pamięci",sqlite:"SQLITE"},thingsboard:"ThingsBoard",general:"General","thingsboard-host":"Gospodarz ThingsBoard","thingsboard-host-required":"Host jest wymagany.","thingsboard-port":"Port ThingsBoard","thingsboard-port-max":"Maksymalny numer portu to 65535.","thingsboard-port-min":"Minimalny numer portu to 1.","thingsboard-port-pattern":"Port jest nieprawidłowy.","thingsboard-port-required":"Port jest wymagany.",tidy:"Uporządkuj","tidy-tip":"Uporządkowana konfiguracja JSON","title-connectors-json":"Złącze {{typeName}} konfiguracja","tls-path-ca-certificate":"Ścieżka do certyfikatu CA na gateway","tls-path-client-certificate":"Ścieżka do certyfikatu klienta na 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":"Ścieżka do klucza prywatnego na bramce","toggle-fullscreen":"Przełącz tryb pełnoekranowy","transformer-json-config":"Konfiguracja JSON*","update-config":"Dodaj/zaktualizuj konfigurację 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."}}},Et={gateway:{"add-entry":"Adicionar configuração","connector-add":"Adicionar novo conector","connector-enabled":"Habilitar conector","connector-name":"Nome do conector","connector-name-required":"O nome do conector é obrigatório.","connector-type":"Tipo de conector","connector-type-required":"O tipo de conector é obrigatório.",connectors:"Configuração de conectores","create-new-gateway":"Criar um novo gateway","create-new-gateway-text":"Tem certeza de que deseja criar um novo gateway com o nome: '{{gatewayName}}'?",delete:"Excluir configuração","download-tip":"Download de arquivo de configuração",gateway:"Gateway","gateway-exists":"Já existe um dispositivo com o mesmo nome.","gateway-name":"Nome do gateway","gateway-name-required":"O nome do gateway é obrigatório.","gateway-saved":"A configuração do gateway foi salva corretamente.","json-parse":"JSON inválido.","json-required":"O campo não pode estar em branco.","no-connectors":"Sem conectores","no-data":"Sem configurações","no-gateway-found":"Nenhum gateway encontrado.","no-gateway-matching":" '{{item}}' não encontrado.","path-logs":"Caminho para arquivos de log","path-logs-required":"O caminho é obrigatório",remote:"Configuração remota","remote-logging-level":"Nível de registro em log","remove-entry":"Remover configuração","save-tip":"Salvar arquivo de configuração","security-type":"Tipo de segurança","security-types":{"access-token":"Token de Acesso",tls:"TLS"},storage:"Armazenamento","storage-max-file-records":"Número máximo de registros em arquivo","storage-max-files":"Número máximo de arquivos","storage-max-files-min":"O número mínimo é 1.","storage-max-files-pattern":"O número não é válido.","storage-max-files-required":"O número é obrigatório.","storage-max-records":"Número máximo de registros em armazenamento","storage-max-records-min":"O número mínimo de registros é 1.","storage-max-records-pattern":"O número não é válido.","storage-max-records-required":"O número máximo de registros é obrigatório.","storage-pack-size":"Tamanho máximo de pacote de eventos","storage-pack-size-min":"O número mínimo é 1.","storage-pack-size-pattern":"O número não é válido.","storage-pack-size-required":"O tamanho máximo de pacote de eventos é obrigatório.","storage-path":"Caminho de armazenamento","storage-path-required":"O caminho de armazenamento é obrigatório.","storage-type":"Tipo de armazenamento","storage-types":{"file-storage":"Armazenamento de arquivo","memory-storage":"Armazenamento de memória"},thingsboard:"ThingsBoard","thingsboard-host":"Host ThingsBoard","thingsboard-host-required":"O host é obrigatório.","thingsboard-port":"Porta ThingsBoard","thingsboard-port-max":"O número máximo de portas é 65535.","thingsboard-port-min":"O número mínimo de portas é 1.","thingsboard-port-pattern":"A porta não é válida.","thingsboard-port-required":"A porta é obrigatória.",tidy:"Tidy","tidy-tip":"Config Tidy JSON","title-connectors-json":"Configuração do conector {{typeName}}","tls-path-ca-certificate":"Caminho para certificado de Autoridade de Certificação no gateway","tls-path-client-certificate":"Caminho para certificado de cliente no gateway","tls-path-private-key":"Caminho para chave privada no gateway","toggle-fullscreen":"Alternar tela inteira","transformer-json-config":"Configuração JSON*","update-config":"Adicionar/atualizar configuração de JSON"}},Mt={gateway:{"add-entry":"Dodaj konfiguracijo","connector-add":"Dodaj nov priključek","connector-enabled":"Omogoči priključek","connector-name":"Ime priključka","connector-name-required":"Ime priključka je obvezno.","connector-type":"Vrsta priključka","connector-type-required":"Zahteva se vrsta priključka.",connectors:"Konfiguracija priključkov","create-new-gateway":"Ustvari nov prehod","create-new-gateway-text":"Ali ste prepričani, da želite ustvariti nov prehod z imenom: '{{gatewayName}}'?",delete:"Izbriši konfiguracijo","download-tip":"Prenos konfiguracijske datoteke",gateway:"Prehod","gateway-exists":"Naprava z istim imenom že obstaja.","gateway-name":"Ime prehoda","gateway-name-required":"Ime prehoda je obvezno.","gateway-saved":"Konfiguracija prehoda je uspešno shranjena.","json-parse":"Neveljaven JSON.","json-required":"Polje ne sme biti prazno.","no-connectors":"Ni priključkov","no-data":"Brez konfiguracij","no-gateway-found":"Prehod ni najden.","no-gateway-matching":" '{{item}}' ni mogoče najti.","path-logs":"Pot do dnevniških datotek","path-logs-required":"Pot je obvezna.",remote:"Oddaljena konfiguracija","remote-logging-level":"Raven beleženja","remove-entry":"Odstrani konfiguracijo","save-tip":"Shrani konfiguracijsko datoteko","security-type":"Vrsta zaščite","security-types":{"access-token":"Dostopni žeton",tls:"TLS"},storage:"Shramba","storage-max-file-records":"Največ zapisov v datoteki","storage-max-files":"Največje število datotek","storage-max-files-min":"Najmanjše število je 1.","storage-max-files-pattern":"Številka ni veljavna.","storage-max-files-required":"Številka je obvezna.","storage-max-records":"Največ zapisov v pomnilniku","storage-max-records-min":"Najmanjše število zapisov je 1.","storage-max-records-pattern":"Številka ni veljavna.","storage-max-records-required":"Zahtevan je največ zapisov.","storage-pack-size":"Največja velikost paketa dogodkov","storage-pack-size-min":"Najmanjše število je 1.","storage-pack-size-pattern":"Številka ni veljavna.","storage-pack-size-required":"Zahtevana je največja velikost paketa dogodkov.","storage-path":"Pot pomnilnika","storage-path-required":"Zahtevana je pot do pomnilnika.","storage-type":"Vrsta pomnilnika","storage-types":{"file-storage":"Shramba datotek","memory-storage":"Spomin pomnilnika"},thingsboard:"ThingsBoard","thingsboard-host":"Gostitelj ThingsBoard","thingsboard-host-required":"Potreben je gostitelj.","thingsboard-port":"Vrata ThingsBoard","thingsboard-port-max":"Največja številka vrat je 65535.","thingsboard-port-min":"Najmanjša številka vrat je 1.","thingsboard-port-pattern":"Vrata niso veljavna.","thingsboard-port-required":"Potrebna so vrata.",tidy:"Urejeno","tidy-tip":"Urejena konfiguracija JSON","title-connectors-json":"Konfiguracija konektorja {{typeName}}","tls-path-ca-certificate":"Pot do potrdila CA na prehodu","tls-path-client-certificate":"Pot do potrdila stranke na prehodu","tls-path-private-key":"Pot do zasebnega ključa na prehodu","toggle-fullscreen":"Preklop na celozaslonski način","transformer-json-config":"Konfiguracija JSON *","update-config":"Dodaj / posodobi konfiguracijo JSON"}},kt={gateway:{"add-entry":"Yapılandırma ekle","connector-add":"Yeni bağlayıcı ekle","connector-enabled":"Bağlayıcıyı etkinleştir","connector-name":"Bağlayıcı adı","connector-name-required":"Bağlayıcı adı gerekli.","connector-type":"Bağlayıcı tipi","connector-type-required":"Bağlayıcı türü gerekli.",connectors:"Bağlayıcıların yapılandırması","create-new-gateway":"Yeni bir ağ geçidi oluştur","create-new-gateway-text":"'{{gatewayName}}' adında yeni bir ağ geçidi oluşturmak istediğinizden emin misiniz?",delete:"Yapılandırmayı sil","download-tip":"Yapılandırma dosyasını indirin",gateway:"Ağ geçidi","gateway-exists":"Aynı ada sahip cihaz zaten var.","gateway-name":"Ağ geçidi adı","gateway-name-required":"Ağ geçidi adı gerekli.","gateway-saved":"Ağ geçidi yapılandırması başarıyla kaydedildi.","json-parse":"Geçerli bir JSON değil.","json-required":"Alan boş olamaz.","no-connectors":"Bağlayıcı yok","no-data":"Yapılandırma yok","no-gateway-found":"Ağ geçidi bulunamadı.","no-gateway-matching":" '{{item}}' bulunamadı.","path-logs":"Log dosyaları yolu","path-logs-required":"Log dosyaları dizini gerekli.",remote:"Uzaktan yapılandırma","remote-logging-level":"Loglama seviyesi","remove-entry":"Yapılandırmayı kaldır","save-tip":"Yapılandırma dosyasını kaydet","security-type":"Güvenlik türü","security-types":{"access-token":"Access Token",tls:"TLS"},storage:"Depolama","storage-max-file-records":"Dosyadaki maksimum kayıt","storage-max-files":"Maksimum dosya sayısı","storage-max-files-min":"Minimum sayı 1'dir.","storage-max-files-pattern":"Sayı geçerli değil.","storage-max-files-required":"Sayı gerekli.","storage-max-records":"Depodaki maksimum kayıt","storage-max-records-min":"Minimum kayıt sayısı 1'dir.","storage-max-records-pattern":"Sayı geçerli değil.","storage-max-records-required":"Maksimum kayıt gerekli.","storage-pack-size":"Maksimum etkinlik paketi boyutu","storage-pack-size-min":"Minimum sayı 1'dir.","storage-pack-size-pattern":"Sayı geçerli değil.","storage-pack-size-required":"Maksimum etkinlik paketi boyutu gerekli.","storage-path":"Depolama yolu","storage-path-required":"Depolama yolu gerekli.","storage-type":"Depolama türü","storage-types":{"file-storage":"Dosya depolama","memory-storage":"Bellek depolama"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard host","thingsboard-host-required":"Host gerekli.","thingsboard-port":"ThingsBoard port","thingsboard-port-max":"Maksimum port numarası 65535.","thingsboard-port-min":"Minimum port numarası 1'dir.","thingsboard-port-pattern":"Port geçerli değil.","thingsboard-port-required":"Port gerekli.",tidy:"Tidy","tidy-tip":"Tidy config JSON","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","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"}},Pt={gateway:{"add-entry":"添加配置",advanced:"高级","checking-device-activity":"检查设备活动",command:"Docker命令","command-copied-message":"Docker命令已复制到剪贴板",configuration:"配置","connector-add":"添加连接器","connector-enabled":"启用连接器","connector-name":"连接器名称","connector-name-required":"连接器名称必填。","connector-type":"连接器类型","connector-type-required":"连接器类型必填。",connectors:"连接器配置","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":"访问令牌","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-required":"超时时间是必需的","timeout-min":"超时时间不能小于1","timeout-pattern":"超时时间无效","attribute-name":"属性名称","attribute-name-required":"属性名称是必需的",command:"命令","command-required":"命令是必需的","command-pattern":"命令无效",remove:"删除命令"},storage:"存储","storage-max-file-records":"文件中的最大记录数","storage-max-files":"最大文件数","storage-max-files-min":"最小值为1。","storage-max-files-pattern":"数字无效。","storage-max-files-required":"数字是必需的。","storage-max-records":"存储中的最大记录数","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":"数字无效。","storage-pack-size-required":"最大事件包大小必填。","storage-path":"存储路径","storage-path-required":"存储路径必填。","storage-type":"存储类型","storage-types":{"file-storage":"文件存储","memory-storage":"内存存储",sqlite:"SQLITE"},thingsboard:"ThingsBoard",general:"常规","thingsboard-host":"ThingsBoard主机","thingsboard-host-required":"主机必填。","thingsboard-port":"ThingsBoard端口","thingsboard-port-max":"最大端口号为65535。","thingsboard-port-min":"最小端口号为1。","thingsboard-port-pattern":"端口无效。","thingsboard-port-required":"端口必填。",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",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连接活动"},"docker-label":"使用以下指令在 Docker compose 中运行 IoT 网关,并为选定的设备提供凭据","install-docker-compose":"使用以下说明下载、安装和设置 Docker Compose","download-configuration-file":"下载配置文件","download-docker-compose":"下载您的网关的 docker-compose.yml 文件","launch-gateway":"启动网关","launch-docker-compose":"在包含 docker-compose.yml 文件的文件夹中,使用以下命令在终端中启动网关"}},Dt={gateway:{"add-entry":"增加配置","connector-add":"增加新連接器","connector-enabled":"啟用連接器","connector-name":"連接器名稱","connector-name-required":"需要連接器名稱。","connector-type":"連接器類型","connector-type-required":"需要連接器類型。",connectors:"連接器配置","create-new-gateway":"建立新閘道","create-new-gateway-text":"您確定要建立一個名稱為:'{{gatewayName}}'的新閘道嗎?",delete:"刪除配置","download-tip":"下載配置文件",gateway:"閘道","gateway-exists":"同名設備已存在。","gateway-name":"閘道名稱","gateway-name-required":"需要閘道名稱。","gateway-saved":"閘道配置已成功保存。","json-parse":"無效的JSON","json-required":"欄位不能為空。","no-connectors":"無連接器","no-data":"無配置","no-gateway-found":"未找到閘道。","no-gateway-matching":" 未找到'{{item}}'。","path-logs":"日誌文件的路徑","path-logs-required":"需要路徑。",remote:"移除配置","remote-logging-level":"日誌記錄級別","remove-entry":"移除配置","save-tip":"保存配置文件","security-type":"安全類型","security-types":{"access-token":"訪問Token",tls:"TLS"},storage:"貯存","storage-max-file-records":"文件中的最大紀錄","storage-max-files":"最大文件數","storage-max-files-min":"最小數量為1。","storage-max-files-pattern":"號碼無效。","storage-max-files-required":"需要號碼。","storage-max-records":"存儲中的最大紀錄","storage-max-records-min":"最小紀錄數為1。","storage-max-records-pattern":"號碼無效。","storage-max-records-required":"需要最大紀錄數","storage-pack-size":"最大事件包大小","storage-pack-size-min":"最小數量為1。","storage-pack-size-pattern":"號碼無效.","storage-pack-size-required":"需要最大事件包大小","storage-path":"存儲路徑","storage-path-required":"需要存儲路徑。","storage-type":"存儲類型","storage-types":{"file-storage":"文件存儲","memory-storage":"記憶體存儲"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard主機","thingsboard-host-required":"需要主機。","thingsboard-port":"ThingsBoard連接埠","thingsboard-port-max":"最大埠號為 65535。","thingsboard-port-min":"最小埠號為1。","thingsboard-port-pattern":"連接埠無效。","thingsboard-port-required":"需要連接埠。",tidy:"整理","tidy-tip":"整理配置JSON","title-connectors-json":"連接器{{typeName}}配置","tls-path-ca-certificate":"閘道上CA證書的路徑","tls-path-client-certificate":"閘道上用戶端憑據的路徑","tls-path-private-key":"閘道上的私鑰路徑","toggle-fullscreen":"切換全螢幕","transformer-json-config":"配置JSON*","update-config":"增加/更新配置JSON"}};e("AvailableLanguages",ft),function(e){e.English="en_US",e.Arabic="ar_AE",e.Catalan="ca_ES",e.Czech="cs_CZ",e.Danish="da_DK",e.Spanish="es_ES",e.Korean="ko_KR",e.Lithuanian="lt_LT",e.Dutch="nl_BE",e.Polish="pl_PL",e.PortugueseBrazil="pt_BR",e.Slovenian="sl_SI",e.Turkish="tr_TR",e.ChineseSimplified="zh_CN",e.ChineseTraditional="zh_TW"}(ft||e("AvailableLanguages",ft={}));const Ot=new Map([[ft.English,yt],[ft.Arabic,vt],[ft.Catalan,xt],[ft.Czech,bt],[ft.Danish,wt],[ft.Spanish,St],[ft.Korean,Ct],[ft.Lithuanian,_t],[ft.Dutch,Tt],[ft.Polish,It],[ft.PortugueseBrazil,Et],[ft.Slovenian,Mt],[ft.Turkish,kt],[ft.ChineseSimplified,Pt],[ft.ChineseTraditional,Dt]]),At=e("addGatewayLocale",(e=>{const t=ft.English,n=e.currentLang??t,i=e.translations[n]?.gateway?n:t,a=Ot.get(n)?.gateway?n:t,r=[a===t?void 0:Ot.get(t)?.gateway,Ot.get(a)?.gateway,e.translations[i]?.gateway].filter(Ft);if(!r.length)return;const o=1===r.length?r[0]:j({},...r);e.setTranslation(i,{gateway:o},!0)})),Ft=e=>H(e)&&0!==Object.keys(e).length;var Rt={3.6:{socket:{type:"TCP",address:"127.0.0.1",port:5e4,bufferSize:1024},devices:[{address:"*:*",deviceName:"Device Example",deviceType:"default",encoding:"utf-8",telemetry:[{key:"temp",byteFrom:0,byteTo:-1},{key:"hum",byteFrom:0,byteTo:2}],attributes:[{key:"name",byteFrom:0,byteTo:-1},{key:"num",byteFrom:2,byteTo:4}],attributeRequests:[{type:"shared",requestExpressionSource:"constant",attributeNameExpressionSource:"constant",requestExpression:"${[0:3]==atr}",attributeNameExpression:"[3:]"}],attributeUpdates:[{encoding:"utf-16",attributeOnThingsBoard:"sharedName"}],serverSideRpc:[{methodRPC:"rpcMethod1",withResponse:!0,encoding:"utf-8"}]}]},legacy:{type:"TCP",address:"127.0.0.1",port:5e4,bufferSize:1024,devices:[{address:"*:*",deviceName:"Device Example",deviceType:"default",encoding:"utf-8",telemetry:[{key:"temp",byteFrom:0,byteTo:-1},{key:"hum",byteFrom:0,byteTo:2}],attributes:[{key:"name",byteFrom:0,byteTo:-1},{key:"num",byteFrom:2,byteTo:4}],attributeRequests:[{type:"shared",requestExpression:"${[0:3]==atr}",attributeNameExpression:"[3:]"}],attributeUpdates:[{encoding:"utf-16",attributeOnThingsBoard:"sharedName"}],serverSideRpc:[{methodRPC:"rpcMethod1",withResponse:!0,methodProcessing:"write",encoding:"utf-8"}]}]}},Bt={"3.5.2":{broker:{host:"127.0.0.1",port:1883,clientId:"ThingsBoard_gateway",version:5,maxMessageNumberPerWorker:10,maxNumberOfWorkers:100,sendDataOnlyOnChange:!1,security:{type:"anonymous"}},mapping:[{topicFilter:"sensor/data",subscriptionQos:1,converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}",deviceProfileExpressionSource:"message",deviceProfileExpression:"${sensorType}"},sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"${sensorModel}",value:"on"}],timeseries:[{type:"string",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"},{type:"string",key:"combine",value:"${hum}:${temp}"}]}},{topicFilter:"sensor/+/data",subscriptionQos:1,converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"topic",deviceNameExpression:"(?<=sensor/)(.*?)(?=/data)",deviceProfileExpressionSource:"constant",deviceProfileExpression:"Thermometer"},sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"string",key:"humidity",value:"${hum}"}]}},{topicFilter:"sensor/raw_data",subscriptionQos:1,converter:{type:"bytes",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"[0:4]",deviceProfileExpressionSource:"constant",deviceProfileExpression:"default"},sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"raw",key:"rawData",value:"[:]"}],timeseries:[{type:"raw",key:"temp",value:"[4:]"}]}},{topicFilter:"custom/sensors/+",subscriptionQos:1,converter:{type:"custom",extension:"CustomMqttUplinkConverter",cached:!0,extensionConfig:{temperature:2,humidity:2,batteryLevel:1}}}],requestsMapping:{connectRequests:[{topicFilter:"sensor/connect",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}",deviceProfileExpressionSource:"constant",deviceProfileExpression:"Thermometer"}},{topicFilter:"sensor/+/connect",deviceInfo:{deviceNameExpressionSource:"topic",deviceNameExpression:"(?<=sensor/)(.*?)(?=/connect)",deviceProfileExpressionSource:"constant",deviceProfileExpression:"Thermometer"}}],disconnectRequests:[{topicFilter:"sensor/disconnect",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}"}},{topicFilter:"sensor/+/disconnect",deviceInfo:{deviceNameExpressionSource:"topic",deviceNameExpression:"(?<=sensor/)(.*?)(?=/connect)"}}],attributeRequests:[{retain:!1,topicFilter:"v1/devices/me/attributes/request",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}"},attributeNameExpressionSource:"message",attributeNameExpression:"${versionAttribute}, ${pduAttribute}",topicExpression:"devices/${deviceName}/attrs",valueExpression:"${attributeKey}: ${attributeValue}"}],attributeUpdates:[{retain:!0,deviceNameFilter:".*",attributeFilter:"firmwareVersion",topicExpression:"sensor/${deviceName}/${attributeKey}",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{type:"twoWay",deviceNameFilter:".*",methodFilter:"echo",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",responseTopicExpression:"sensor/${deviceName}/response/${methodName}/${requestId}",responseTopicQoS:1,responseTimeout:1e4,valueExpression:"${params}"},{type:"oneWay",deviceNameFilter:".*",methodFilter:"no-reply",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",valueExpression:"${params}"}]}},legacy:{broker:{name:"Default Local Broker",host:"127.0.0.1",port:1883,clientId:"ThingsBoard_gateway",version:5,maxMessageNumberPerWorker:10,maxNumberOfWorkers:100,sendDataOnlyOnChange:!1,security:{type:"basic",username:"user",password:"password"}},mapping:[{topicFilter:"sensor/data",converter:{type:"json",deviceNameJsonExpression:"${serialNumber}",deviceTypeJsonExpression:"${sensorType}",sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"${sensorModel}",value:"on"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"},{type:"string",key:"combine",value:"${hum}:${temp}"}]}},{topicFilter:"sensor/+/data",converter:{type:"json",deviceNameTopicExpression:"(?<=sensor/)(.*?)(?=/data)",deviceTypeTopicExpression:"Thermometer",sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{topicFilter:"sensor/raw_data",converter:{type:"bytes",deviceNameExpression:"[0:4]",deviceTypeExpression:"default",sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"raw",key:"rawData",value:"[:]"}],timeseries:[{type:"raw",key:"temp",value:"[4:]"}]}},{topicFilter:"custom/sensors/+",converter:{type:"custom",extension:"CustomMqttUplinkConverter",cached:!0,"extension-config":{temperatureBytes:2,humidityBytes:2,batteryLevelBytes:1}}}],connectRequests:[{topicFilter:"sensor/connect",deviceNameJsonExpression:"${serialNumber}"},{topicFilter:"sensor/+/connect",deviceNameTopicExpression:"(?<=sensor/)(.*?)(?=/connect)"}],disconnectRequests:[{topicFilter:"sensor/disconnect",deviceNameJsonExpression:"${serialNumber}"},{topicFilter:"sensor/+/disconnect",deviceNameTopicExpression:"(?<=sensor/)(.*?)(?=/disconnect)"}],attributeRequests:[{retain:!1,topicFilter:"v1/devices/me/attributes/request",deviceNameJsonExpression:"${serialNumber}",attributeNameJsonExpression:"${versionAttribute}, ${pduAttribute}",topicExpression:"devices/${deviceName}/attrs",valueExpression:"${attributeKey}: ${attributeValue}"}],attributeUpdates:[{retain:!0,deviceNameFilter:".*",attributeFilter:"firmwareVersion",topicExpression:"sensor/${deviceName}/${attributeKey}",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",responseTopicExpression:"sensor/${deviceName}/response/${methodName}/${requestId}",responseTimeout:1e4,valueExpression:"${params}"},{deviceNameFilter:".*",methodFilter:"no-reply",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",valueExpression:"${params}"}]}},Nt={"3.5.2":{master:{slaves:[{host:"127.0.0.1",port:5021,type:"tcp",method:"socket",timeout:35,byteOrder:"LITTLE",wordOrder:"LITTLE",retries:3,retryOnEmpty:!0,retryOnInvalid:!0,pollPeriod:5e3,unitId:1,deviceName:"Temp Sensor",deviceType:"default",connectAttemptTimeMs:5e3,connectAttemptCount:5,waitAfterFailedAttemptsMs:3e5,reportStrategy:{type:"ON_REPORT_PERIOD",reportPeriod:3e4},attributes:[{tag:"string_read",type:"string",functionCode:4,objectsCount:4,address:1,reportStrategy:{type:"ON_REPORT_PERIOD",reportPeriod:15e3}},{tag:"bits_read",type:"bits",functionCode:4,objectsCount:1,address:5},{tag:"8int_read",type:"8int",functionCode:4,objectsCount:1,address:6},{tag:"16int_read",type:"16int",functionCode:4,objectsCount:1,address:7},{tag:"32int_read_divider",type:"32int",functionCode:4,objectsCount:2,address:8,divider:10},{tag:"8int_read_multiplier",type:"8int",functionCode:4,objectsCount:1,address:10,multiplier:10},{tag:"32int_read",type:"32int",functionCode:4,objectsCount:2,address:11},{tag:"64int_read",type:"64int",functionCode:4,objectsCount:4,address:13}],timeseries:[{tag:"8uint_read",type:"8uint",functionCode:4,objectsCount:1,address:17,reportStrategy:{type:"ON_REPORT_PERIOD",reportPeriod:15e3}},{tag:"16uint_read",type:"16uint",functionCode:4,objectsCount:2,address:18},{tag:"32uint_read",type:"32uint",functionCode:4,objectsCount:4,address:20},{tag:"64uint_read",type:"64uint",functionCode:4,objectsCount:1,address:24},{tag:"16float_read",type:"16float",functionCode:4,objectsCount:1,address:25},{tag:"32float_read",type:"32float",functionCode:4,objectsCount:2,address:26},{tag:"64float_read",type:"64float",functionCode:4,objectsCount:4,address:28}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31},{tag:"getValue",type:"bits",functionCode:1,objectsCount:1,address:31},{tag:"setCPUFanSpeed",type:"32int",functionCode:16,objectsCount:2,address:33},{tag:"getCPULoad",type:"32int",functionCode:4,objectsCount:2,address:35}]}]},slave:{type:"tcp",host:"127.0.0.1",port:5026,method:"socket",deviceName:"Modbus Slave Example",deviceType:"default",pollPeriod:5e3,sendDataToThingsBoard:!1,byteOrder:"LITTLE",wordOrder:"LITTLE",unitId:0,values:{holding_registers:{attributes:[{address:1,type:"string",tag:"sm",objectsCount:1,value:"ON"}],timeseries:[{address:2,type:"8int",tag:"smm",objectsCount:1,value:"12334"}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29,value:1243}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31,value:1}]},coils_initializer:{attributes:[{address:5,type:"8int",tag:"coil",objectsCount:1,value:0}],timeseries:[],attributeUpdates:[],rpc:[]}}}},legacy:{master:{slaves:[{host:"127.0.0.1",port:5021,type:"tcp",method:"socket",timeout:35,byteOrder:"LITTLE",wordOrder:"LITTLE",retries:3,retryOnEmpty:!0,retryOnInvalid:!0,pollPeriod:5e3,unitId:1,deviceName:"Temp Sensor",deviceType:"default",sendDataOnlyOnChange:!0,connectAttemptTimeMs:5e3,connectAttemptCount:5,waitAfterFailedAttemptsMs:3e5,attributes:[{tag:"string_read",type:"string",functionCode:4,objectsCount:4,address:1},{tag:"bits_read",type:"bits",functionCode:4,objectsCount:1,address:5},{tag:"16int_read",type:"16int",functionCode:4,objectsCount:1,address:7},{tag:"32int_read_divider",type:"32int",functionCode:4,objectsCount:2,address:8,divider:10},{tag:"32int_read",type:"32int",functionCode:4,objectsCount:2,address:11},{tag:"64int_read",type:"64int",functionCode:4,objectsCount:4,address:13}],timeseries:[{tag:"16uint_read",type:"16uint",functionCode:4,objectsCount:2,address:18},{tag:"32uint_read",type:"32uint",functionCode:4,objectsCount:4,address:20},{tag:"64uint_read",type:"64uint",functionCode:4,objectsCount:1,address:24},{tag:"16float_read",type:"16float",functionCode:4,objectsCount:1,address:25},{tag:"32float_read",type:"32float",functionCode:4,objectsCount:2,address:26},{tag:"64float_read",type:"64float",functionCode:4,objectsCount:4,address:28}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31},{tag:"getValue",type:"bits",functionCode:1,objectsCount:1,address:31},{tag:"setCPUFanSpeed",type:"32int",functionCode:16,objectsCount:2,address:33},{tag:"getCPULoad",type:"32int",functionCode:4,objectsCount:2,address:35}]}]},slave:{type:"tcp",host:"127.0.0.1",port:5026,method:"socket",deviceName:"Modbus Slave Example",deviceType:"default",pollPeriod:5e3,sendDataToThingsBoard:!1,byteOrder:"LITTLE",wordOrder:"LITTLE",unitId:0,values:{holding_registers:[{attributes:[{address:1,type:"string",tag:"sm",objectsCount:1,value:"ON"}],timeseries:[{address:2,type:"int",tag:"smm",objectsCount:1,value:"12334"}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29,value:1243}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31,value:1}]}],coils_initializer:[{attributes:[{address:5,type:"string",tag:"sm",objectsCount:1,value:"12"}],timeseries:[],attributeUpdates:[],rpc:[]}]}}}},Lt={"3.5.2":{server:{url:"localhost:4840/freeopcua/server/",timeoutInMillis:5e3,scanPeriodInMillis:36e5,pollPeriodInMillis:5e3,enableSubscriptions:!0,subCheckPeriodInMillis:100,showMap:!1,security:"Basic128Rsa15",identity:{type:"anonymous"}},mapping:[{deviceNodePattern:"Root\\.Objects\\.Device1",deviceNodeSource:"path",deviceInfo:{deviceNameExpression:"Device ${Root\\.Objects\\.Device1\\.serialNumber}",deviceNameExpressionSource:"path",deviceProfileExpression:"Device",deviceProfileExpressionSource:"constant"},attributes:[{key:"temperature °C",type:"path",value:"${ns=2;i=5}"}],timeseries:[{key:"humidity",type:"path",value:"${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"},{key:"batteryLevel",type:"path",value:"${Battery\\.batteryLevel}"}],rpc_methods:[{method:"multiply",arguments:[{type:"integer",value:2},{type:"integer",value:4}]}],attributes_updates:[{key:"deviceName",type:"path",value:"Root\\.Objects\\.Device1\\.serialNumber"}]}]},legacy:{server:{name:"OPC-UA Default Server",url:"localhost:4840/freeopcua/server/",timeoutInMillis:5e3,scanPeriodInMillis:5e3,disableSubscriptions:!1,subCheckPeriodInMillis:100,showMap:!1,security:"Basic128Rsa15",identity:{type:"anonymous"},mapping:[{deviceNodePattern:"Root\\.Objects\\.Device1",deviceNamePattern:"Device ${Root\\.Objects\\.Device1\\.serialNumber}",attributes:[{key:"temperature °C",path:"${ns=2;i=5}"}],timeseries:[{key:"humidity",path:"${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"},{key:"batteryLevel",path:"${Battery\\.batteryLevel}"}],rpc_methods:[{method:"multiply",arguments:[2,4]}],attributes_updates:[{attributeOnThingsBoard:"deviceName",attributeOnDevice:"Root\\.Objects\\.Device1\\.serialNumber"}]}]}}},Vt={passiveScanMode:!0,showMap:!1,scanner:{timeout:1e4,deviceName:"Device name"},devices:[{name:"Temperature and humidity sensor",MACAddress:"4C:65:A8:DF:85:C0",pollPeriod:5e5,showMap:!1,timeout:1e4,connectRetry:5,connectRetryInSeconds:0,waitAfterConnectRetries:10,telemetry:[{key:"temperature",method:"notify",characteristicUUID:"226CAA55-6476-4566-7562-66734470666D",valueExpression:"[2]"},{key:"humidity",method:"notify",characteristicUUID:"226CAA55-6476-4566-7562-66734470666D",valueExpression:"[0]"}],attributes:[{key:"name",method:"read",characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",valueExpression:"[0:2]cm [2:]A"},{key:"values",method:"read",characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",valueExpression:"All values: [:]"}],attributeUpdates:[{attributeOnThingsBoard:"sharedName",characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB"}],serverSideRpc:[{methodRPC:"rpcMethod1",withResponse:!0,characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",methodProcessing:"read"},{methodRPC:"rpcMethod2",withResponse:!0,characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",methodProcessing:"write"},{methodRPC:"rpcMethod3",withResponse:!0,methodProcessing:"scan"}]}]},qt={host:"http://127.0.0.1:5000",SSLVerify:!0,security:{type:"basic",username:"user",password:"password"},mapping:[{url:"getdata",httpMethod:"GET",httpHeaders:{ACCEPT:"application/json"},content:{name:"morpheus",job:"leader"},allowRedirects:!0,timeout:.5,scanPeriod:5,converter:{type:"json",deviceNameJsonExpression:"SD8500",deviceTypeJsonExpression:"SD",attributes:[{key:"serialNumber",type:"string",value:"${serial}"}],telemetry:[{key:"Maintainer",type:"string",value:"${Developer}"},{key:"combine",type:"string",value:"${Developer}:${hum}"}]}},{url:"get_info",httpMethod:"GET",httpHeaders:{ACCEPT:"application/json"},allowRedirects:!0,timeout:.5,scanPeriod:100,converter:{type:"custom",deviceNameJsonExpression:"SD8500",deviceTypeJsonExpression:"SD",extension:"CustomRequestUplinkConverter","extension-config":[{key:"Totaliser",type:"float",fromByte:0,toByte:4,byteorder:"big",signed:!0,multiplier:1},{key:"Flow",type:"int",fromByte:4,toByte:6,byteorder:"big",signed:!0,multiplier:.01},{key:"Temperature",type:"int",fromByte:8,toByte:10,byteorder:"big",signed:!0,multiplier:.01},{key:"Pressure",type:"int",fromByte:12,toByte:14,byteorder:"big",signed:!0,multiplier:.01},{key:"deviceStatus",type:"int",byteAddress:15,fromBit:4,toBit:8,byteorder:"big",signed:!1},{key:"OUT2",type:"int",byteAddress:15,fromBit:1,toBit:2,byteorder:"big"},{key:"OUT1",type:"int",byteAddress:15,fromBit:0,toBit:1,byteorder:"big"}]}}],attributeUpdates:[{httpMethod:"POST",httpHeaders:{"CONTENT-TYPE":"application/json"},timeout:.5,tries:3,allowRedirects:!0,deviceNameFilter:"SD.*",attributeFilter:"send_data",requestUrlExpression:"sensor/${deviceName}/${attributeKey}",requestValueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",responseTimeout:1,httpMethod:"GET",requestValueExpression:"${params}",responseValueExpression:"${temp}",timeout:.5,tries:3,httpHeaders:{"Content-Type":"application/json"}},{deviceNameFilter:".*",methodFilter:"no-reply",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",httpMethod:"POST",requestValueExpression:"${params}",httpHeaders:{"Content-Type":"application/json"}}]},Gt={interface:"socketcan",channel:"vcan0",backend:{fd:!0},reconnectPeriod:5,devices:[{name:"Car",sendDataOnlyOnChange:!1,enableUnknownRpc:!0,strictEval:!1,attributes:[{key:"isDriverDoorOpened",nodeId:41,command:"2:2:big:8717",value:"4:1:int",expression:"bool(value & 0b00000100)",polling:{type:"once",dataInHex:"AB CD AB CD"}}],timeseries:[{key:"rpm",nodeId:1918,isExtendedId:!0,command:"2:2:big:48059",value:"4:2:big:int",expression:"value / 4",polling:{type:"always",period:5,dataInHex:"aaaa bbbb aaaa bbbb"}},{key:"milliage",nodeId:1918,isExtendedId:!0,value:"4:2:little:int",expression:"value * 10",polling:{type:"always",period:30,dataInHex:"aa bb cc dd ee ff aa bb"}}],attributeUpdates:[{attributeOnThingsBoard:"softwareVersion",nodeId:64,isExtendedId:!0,dataLength:4,dataExpression:"value + 5",dataByteorder:"little"}],serverSideRpc:[{method:"sendSameData",nodeId:4,isExtendedId:!0,isFd:!0,bitrateSwitch:!0,dataInHex:"aa bb cc dd ee ff aa bb aa bb cc d ee ff"},{method:"setLightLevel",nodeId:5,dataLength:2,dataByteorder:"little",dataBefore:"00AA"},{method:"setSpeed",nodeId:16,dataAfter:"0102",dataExpression:"userSpeed if maxAllowedSpeed > userSpeed else maxAllowedSpeed"}]}]},zt={"3.6.2":{application:{objectName:"TB_gateway",host:"0.0.0.0",port:47808,objectIdentifier:599,maxApduLengthAccepted:1476,segmentationSupported:"segmentedBoth",vendorIdentifier:15},devices:[{deviceInfo:{deviceNameExpression:"BACnet Device ${objectName}",deviceProfileExpression:"default",deviceNameExpressionSource:"expression",deviceProfileExpressionSource:"constant"},host:"192.168.2.110",port:"47808",pollPeriod:1e4,attributes:[{key:"temperature",objectType:"analogOutput",objectId:1,propertyId:"presentValue"}],timeseries:[{key:"state",objectType:"binaryValue",objectId:1,propertyId:"presentValue"}],attributeUpdates:[{key:"brightness",objectType:"analogOutput",objectId:1,propertyId:"presentValue"}],serverSideRpc:[{method:"set_state",requestType:"writeProperty",requestTimeout:1e4,objectType:"binaryOutput",objectId:1,propertyId:"presentValue"},{method:"get_state",requestType:"readProperty",requestTimeout:1e4,objectType:"binaryOutput",objectId:1,propertyId:"presentValue"}]}]},legacy:{general:{objectName:"TB_gateway",address:"0.0.0.0:47808",objectIdentifier:599,maxApduLengthAccepted:1476,segmentationSupported:"segmentedBoth",vendorIdentifier:15},devices:[{deviceName:"BACnet Device ${objectName}",deviceType:"default",address:"192.168.2.110:47808",pollPeriod:1e4,attributes:[{key:"temperature",type:"string",objectId:"analogOutput:1",propertyId:"presentValue"}],timeseries:[{key:"state",type:"bool",objectId:"binaryValue:1",propertyId:"presentValue"}],attributeUpdates:[{key:"brightness",requestType:"writeProperty",objectId:"analogOutput:1",propertyId:"presentValue"}],serverSideRpc:[{method:"set_state",requestType:"writeProperty",requestTimeout:1e4,objectId:"binaryOutput:1",propertyId:"presentValue"},{method:"get_state",requestType:"readProperty",requestTimeout:1e4,objectId:"binaryOutput:1",propertyId:"presentValue"}]}]}},Ut={connection:{str:"Driver={PostgreSQL};Server=localhost;Port=5432;Database=thingsboard;Uid=postgres;Pwd=postgres;",attributes:{autocommit:!0,timeout:0},encoding:"utf-8",decoding:{char:"utf-8",wchar:"utf-8",metadata:"utf-16le"},reconnect:!0,reconnectPeriod:60},pyodbc:{pooling:!1},polling:{query:"SELECT bool_v, str_v, dbl_v, long_v, entity_id, ts FROM ts_kv WHERE ts > ? ORDER BY ts ASC LIMIT 10",period:10,iterator:{column:"ts",query:"SELECT MIN(ts) - 1 FROM ts_kv",persistent:!1}},mapping:{device:{type:"postgres",name:"'ODBC ' + entity_id"},sendDataOnlyOnChange:!1,attributes:"*",timeseries:[{name:"value",value:"[i for i in [str_v, long_v, dbl_v,bool_v] if i is not None][0]"}]},serverSideRpc:{enableUnknownRpc:!1,overrideRpcConfig:!0,methods:["procedureOne",{name:"procedureTwo",args:["One",2,3]}]}},jt={"3.7.2":{server:{host:"127.0.0.1",port:"5000",SSL:!1,security:{cert:"~/ssl/cert.pem",key:"~/ssl/key.pem"}},mapping:[{endpoint:"/my_devices",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"request",deviceNameExpression:"${sensorName}",deviceProfileExpressionSource:"request",deviceProfileExpression:"${sensorType}"},attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"certificateNumber",value:"${certificateNumber}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon1",HTTPMethods:["GET","POST"],security:{type:"anonymous"},converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"constant",deviceNameExpression:"Device 2",deviceProfileExpressionSource:"constant",deviceProfileExpression:"default"},attributes:[{type:"string",key:"model",value:"Model2"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon2",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"custom",deviceInfo:{deviceNameExpressionSource:"constant",deviceNameExpression:"SuperAnonDevice",deviceProfileExpressionSource:"constant",deviceProfileExpression:"default"},extension:"CustomRestUplinkConverter",extensionConfig:{key:"Totaliser",datatype:"float",fromByte:0,toByte:4,byteorder:"big",signed:!0,multiplier:1}}}],requestsMapping:{attributeRequests:[{endpoint:"/sharedAttributes",type:"shared",HTTPMethods:["POST"],security:{type:"anonymous"},timeout:10,deviceNameExpression:"${deviceName}",attributeNameExpression:"${attribute}${attribute1}"}],attributeUpdates:[{HTTPMethod:"POST",SSLVerify:!1,httpHeaders:{"CONTENT-TYPE":"application/json"},security:{type:"anonymous"},timeout:.5,tries:3,allowRedirects:!0,deviceNameFilter:"SN.*",attributeFilter:".*",requestUrlExpression:"http://127.0.0.1:5001/",valueExpression:'{"deviceName":"${deviceName}","${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestUrlExpression:"http://127.0.0.1:5001/${deviceName}",responseTimeout:1,HTTPMethod:"GET",valueExpression:"${params}",timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:"SN.*",methodFilter:"post_attributes",requestUrlExpression:"http://127.0.0.1:5000/my_devices",responseTimeout:1,HTTPMethod:"POST",valueExpression:'{"sensorName":"${deviceName}", "sensorModel":"${params.sensorModel}", "certificateNumber":"${params.certificateNumber}", "temp":"${params.temp}", "hum":"${params.hum}"}',timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:".*",methodFilter:"no-reply",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",HTTPMethod:"POST",valueExpression:"${params}",httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}}]}},legacy:{host:"127.0.0.1",port:"5000",SSL:!1,security:{cert:"~/ssl/cert.pem",key:"~/ssl/key.pem"},mapping:[{endpoint:"/my_devices",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"json",deviceNameExpression:"${sensorName}",deviceTypeExpression:"${sensorType}",attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"certificateNumber",value:"${certificateNumber}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon1",HTTPMethods:["GET","POST"],security:{type:"anonymous"},converter:{type:"json",deviceNameExpression:"Device 2",deviceTypeExpression:"default",attributes:[{type:"string",key:"model",value:"Model2"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon2",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"custom",deviceNameExpression:"SuperAnonDevice",deviceTypeExpression:"default",extension:"CustomRestUplinkConverter","extension-config":{key:"Totaliser",datatype:"float",fromByte:0,toByte:4,byteorder:"big",signed:!0,multiplier:1}}}],attributeRequests:[{endpoint:"/sharedAttributes",type:"shared",HTTPMethods:["POST"],security:{type:"anonymous"},timeout:10,deviceNameExpression:"${deviceName}",attributeNameExpression:"${attribute}${attribute1}"}],attributeUpdates:[{HTTPMethod:"POST",SSLVerify:!1,httpHeaders:{"CONTENT-TYPE":"application/json"},security:{type:"anonymous"},timeout:.5,tries:3,allowRedirects:!0,deviceNameFilter:"SN.*",attributeFilter:".*",requestUrlExpression:"http://127.0.0.1:5001/",valueExpression:'{"deviceName":"${deviceName}","${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestUrlExpression:"http://127.0.0.1:5001/${deviceName}",responseTimeout:1,HTTPMethod:"GET",valueExpression:"${params}",timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:"SN.*",methodFilter:"post_attributes",requestUrlExpression:"http://127.0.0.1:5000/my_devices",responseTimeout:1,HTTPMethod:"POST",valueExpression:'{"sensorName":"${deviceName}", "sensorModel":"${params.sensorModel}", "certificateNumber":"${params.certificateNumber}", "temp":"${params.temp}", "hum":"${params.hum}"}',timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:".*",methodFilter:"no-reply",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",HTTPMethod:"POST",valueExpression:"${params}",httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}}]}},Ht={devices:[{deviceName:"SNMP router",deviceType:"snmp",ip:"snmp.live.gambitcommunications.com",port:161,pollPeriod:5e3,community:"public",attributes:[{key:"ReceivedFromGet",method:"get",oid:"1.3.6.1.2.1.1.1.0",timeout:6},{key:"ReceivedFromMultiGet",method:"multiget",oid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"],timeout:6},{key:"ReceivedFromGetNext",method:"getnext",oid:"1.3.6.1.2.1.1.1.0",timeout:6},{key:"ReceivedFromMultiWalk",method:"multiwalk",oid:["1.3.6.1.2.1.1.1.0","1.3.6.0.1.2.1"]},{key:"ReceivedFromBulkWalk",method:"bulkwalk",oid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"]},{key:"ReceivedFromBulkGet",method:"bulkget",scalarOid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"],repeatingOid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"],maxListSize:10}],telemetry:[{key:"ReceivedFromWalk",community:"private",method:"walk",oid:"1.3.6.1.2.1.1.1.0"},{key:"ReceivedFromTable",method:"table",oid:"1.3.6.1.2.1.1"}],attributeUpdateRequests:[{attributeFilter:"dataToSet",method:"set",oid:"1.3.6.1.2.1.1.1.0"},{attributeFilter:"dataToMultiSet",method:"multiset",mappings:{"1.2.3":"10","2.3.4":"${attribute}"}}],serverSideRpcRequests:[{requestFilter:"setData",method:"set",oid:"1.3.6.1.2.1.1.1.0"},{requestFilter:"multiSetData",method:"multiset"},{requestFilter:"getData",method:"get",oid:"1.3.6.1.2.1.1.1.0"},{requestFilter:"runBulkWalk",method:"bulkwalk",oid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"]}]},{deviceName:"SNMP router",deviceType:"snmp",ip:"127.0.0.1",pollPeriod:5e3,community:"public",converter:"CustomSNMPConverter",attributes:[{key:"ReceivedFromGetWithCustomConverter",method:"get",oid:"1.3.6.1.2.1.1.1.0"}],telemetry:[{key:"ReceivedFromTableWithCustomConverter",method:"table",oid:"1.3.6.1.2.1.1.1.0"}]}]},Wt={host:"0.0.0.0",port:21,TLSSupport:!1,security:{type:"basic",username:"admin",password:"admin"},paths:[{devicePatternName:"asd",devicePatternType:"Device",delimiter:",",path:"fol/*_hello*.txt",readMode:"FULL",maxFileSize:5,pollPeriod:500,txtFileDataView:"SLICED",withSortingFiles:!0,attributes:[{key:"temp",value:"[1:]"},{key:"tmp",value:"[0:1]"}],timeseries:[{type:"int",key:"[0:1]",value:"[0:1]"},{type:"int",key:"temp",value:"[1:]"}]}],attributeUpdates:[{path:"fol/hello.json",deviceNameFilter:".*",writingMode:"WRITE",valueExpression:"{'${attributeKey}':'${attributeValue}'}"}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"read",valueExpression:"${params}"},{deviceNameFilter:".*",methodFilter:"write",valueExpression:"${params}"}]},$t={server:{jid:"gateway@localhost",password:"password",host:"localhost",port:5222,use_ssl:!1,disable_starttls:!1,force_starttls:!0,timeout:1e4,plugins:["xep_0030","xep_0323","xep_0325"]},devices:[{jid:"device@localhost/TMP_1101",deviceNameExpression:"${serialNumber}",deviceTypeExpression:"default",attributes:[{key:"temperature",value:"${temp}"}],timeseries:[{key:"humidity",value:"${hum}"},{key:"combination",value:"${temp}:${hum}"}],attributeUpdates:[{attributeOnThingsBoard:"shared",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{methodRPC:"rpc1",withResponse:!0,valueExpression:"${params}"}]}]},Kt={centralSystem:{name:"Central System",host:"127.0.0.1",port:9e3,connection:{type:"insecure"},security:[{type:"token",tokens:["Bearer ACCESS_TOKEN"]},{type:"basic",credentials:[{username:"admin",password:"admin"}]}]},chargePoints:[{idRegexpPattern:"bidon/hello/CP_1",deviceNameExpression:"${Vendor} ${Model}",deviceTypeExpression:"default",attributes:[{messageTypeFilter:"MeterValues,",key:"temp1",value:"${meter_value[:].sampled_value[:].value}"},{messageTypeFilter:"MeterValues,",key:"vendorId",value:"${connector_id}"}],timeseries:[{messageTypeFilter:"DataTransfer,",key:"temp",value:"${data.temp}"}],attributeUpdates:[{attributeOnThingsBoard:"shared",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{methodRPC:"rpc1",withResponse:!0,valueExpression:"${params}"}]}]};const Yt=e("connectorConfigs",{[dt.MQTT]:Bt,[dt.MODBUS]:Nt,[dt.OPCUA]:Lt,[dt.BLE]:Vt,[dt.REQUEST]:qt,[dt.CAN]:Gt,[dt.BACNET]:zt,[dt.ODBC]:Ut,[dt.REST]:jt,[dt.SNMP]:Ht,[dt.FTP]:Wt,[dt.SOCKET]:Rt,[dt.XMPP]:$t,[dt.OCPP]:Kt,[dt.KNX]:{3.7:{logLevel:"INFO",client:{type:"AUTOMATIC",addressFormat:"LONG",gatewayIP:"127.0.0.1",gatewayPort:3671,autoReconnect:!0,autoReconnectWait:3,gatewaysScanner:{enabled:!0,scanPeriod:3,stopOnFound:!1}},devices:[{deviceInfo:{deviceNameDataType:"string",deviceNameExpressionSource:"expression",deviceNameExpression:"Device ${1/0/5}",deviceProfileDataType:"none",deviceProfileExpressionSource:"constant",deviceProfileNameExpression:"default"},pollPeriod:5e3,attributes:[{type:"temperature",key:"temperature",groupAddress:"1/0/6"}],timeseries:[{type:"humidity",key:"humidity",groupAddress:"1/0/7"}]}],attributeUpdates:[{deviceNameFilter:".*",dataType:"precent_U8",groupAddress:"1/0/9",key:"brightness"}],serverSideRpc:[{requestType:"read",deviceNameFilter:".*",method:"get_name",dataType:"string",groupAddress:"1/0/5"},{requestType:"write",deviceNameFilter:".*",method:"set_name",dataType:"string",groupAddress:"1/0/5"}]}}});function Xt(e){const t=Yt[e];if(!t)throw new Error("No default config found");return t}var Zt;e("ModbusDataType",Zt),function(e){e.STRING="string",e.BYTES="bytes",e.BITS="bits",e.INT8="8int",e.UINT8="8uint",e.INT16="16int",e.UINT16="16uint",e.FLOAT16="16float",e.INT32="32int",e.UINT32="32uint",e.FLOAT32="32float",e.INT64="64int",e.UINT64="64uint",e.FLOAT64="64float"}(Zt||e("ModbusDataType",Zt={}));const Qt=e("ModbusEditableDataTypes",[Zt.BYTES,Zt.BITS,Zt.STRING]);var Jt,en;e("ModbusObjectCountByDataType",Jt),function(e){e[e["8int"]=1]="8int",e[e["8uint"]=1]="8uint",e[e["16int"]=1]="16int",e[e["16uint"]=1]="16uint",e[e["16float"]=1]="16float",e[e["32int"]=2]="32int",e[e["32uint"]=2]="32uint",e[e["32float"]=2]="32float",e[e["64int"]=4]="64int",e[e["64uint"]=4]="64uint",e[e["64float"]=4]="64float"}(Jt||e("ModbusObjectCountByDataType",Jt={})),e("MappingValueType",en),function(e){e.STRING="string",e.INTEGER="integer",e.DOUBLE="double",e.BOOLEAN="boolean"}(en||e("MappingValueType",en={}));const tn=e("mappingValueTypesMap",new Map([[en.STRING,{name:"value.string",icon:"mdi:format-text"}],[en.INTEGER,{name:"value.integer",icon:"mdi:numeric"}],[en.DOUBLE,{name:"value.double",icon:"mdi:numeric"}],[en.BOOLEAN,{name:"value.boolean",icon:"mdi:checkbox-marked-outline"}]])),nn=e("ModbusFunctionCodeTranslationsMap",new Map([[1,"gateway.function-codes.read-coils"],[2,"gateway.function-codes.read-discrete-inputs"],[3,"gateway.function-codes.read-multiple-holding-registers"],[4,"gateway.function-codes.read-input-registers"],[5,"gateway.function-codes.write-single-coil"],[6,"gateway.function-codes.write-single-holding-register"],[15,"gateway.function-codes.write-multiple-coils"],[16,"gateway.function-codes.write-multiple-holding-registers"]]));var an,rn,on;e("ConfigurationModes",an),function(e){e.BASIC="basic",e.ADVANCED="advanced"}(an||e("ConfigurationModes",an={})),e("ReportStrategyType",rn),function(e){e.OnChange="ON_CHANGE",e.OnReportPeriod="ON_REPORT_PERIOD",e.OnChangeOrReportPeriod="ON_CHANGE_OR_REPORT_PERIOD",e.OnReceived="ON_RECEIVED"}(rn||e("ReportStrategyType",rn={})),e("ReportStrategyDefaultValue",on),function(e){e[e.Gateway=6e4]="Gateway",e[e.Connector=6e4]="Connector",e[e.Device=3e4]="Device",e[e.Key=15e3]="Key"}(on||e("ReportStrategyDefaultValue",on={}));const sn=e("ReportStrategyTypeTranslationsMap",new Map([[rn.OnChange,"gateway.report-strategy.on-change"],[rn.OnReportPeriod,"gateway.report-strategy.on-report-period"],[rn.OnChangeOrReportPeriod,"gateway.report-strategy.on-change-or-report-period"],[rn.OnReceived,"gateway.report-strategy.on-received"]])),ln=e("numberInputPattern",new RegExp(/^\d{1,15}(\.\d{1,15})?$/)),pn=e("noLeadTrailSpacesRegex",/^\S+(?: \S+)*$/),cn=e("integerRegex",/^[-+]?\d+$/),dn=e("nonZeroFloat",/^-?(?!0(\.0+)?$)\d+(\.\d+)?$/),un=e("directoryRegex",new RegExp(/^\.\/[^.\/ ]+(?: [^.\/ ]+)*(?:\/[^.\/ ]+(?: [^.\/ ]+)*)*\/$/));var mn;!function(e){e.EXCEPTION="EXCEPTION"}(mn||(mn={}));const hn={...pt,...mn},gn=()=>[10,20,30];function fn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"a",19),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).$implicit,i=t.ɵɵnextContext();return t.ɵɵresetView(i.onTabChanged(n))})),t.ɵɵtext(1),t.ɵɵelementEnd()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("active",i.activeLink.name===e.name),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}function yn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",20),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.created-time")))}function vn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtext(1),t.ɵɵpipe(2,"date"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,1,e.ts,"yyyy-MM-dd HH:mm:ss")," ")}}function xn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.level")))}function bn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell")(1,"span"),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassMap(i.statusClass(e.status)),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.status)}}function wn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.message")))}function Sn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵclassMap(i.statusClassMsg(e.status)),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.message," ")}}function Cn(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",23)}function _n(e,n){1&e&&t.ɵɵelement(0,"mat-row",23)}class Tn{constructor(e){this.translate=e,this.displayedColumns=["ts","status","message"],this.gatewayLogLinks=[{name:this.translate.instant("gateway.logs.columns.general"),key:"LOGS"},{name:this.translate.instant("gateway.logs.columns.service"),key:"SERVICE_LOGS"},{name:this.translate.instant("gateway.logs.columns.connection"),key:"CONNECTION_LOGS"},{name:this.translate.instant("gateway.logs.columns.storage"),key:"STORAGE_LOGS"},{key:"EXTENSIONS_LOGS",name:this.translate.instant("gateway.logs.columns.extension")}];const t={property:"ts",direction:w.DESC};this.pageLink=new S(10,0,null,t),this.dataSource=new x([])}ngOnInit(){this.updateWidgetTitle()}ngAfterViewInit(){if(this.dataSource.sort=this.sort,this.dataSource.paginator=this.paginator,this.ctx.defaultSubscription.onTimewindowChangeFunction=e=>(this.ctx.defaultSubscription.options.timeWindowConfig=e,this.ctx.defaultSubscription.updateDataSubscriptions(),e),this.ctx.settings.isConnectorLog&&this.ctx.settings.connectorLogState){const e=this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState];this.logLinks=[{key:`${e.key}_LOGS`,name:this.translate.instant("gateway.connector"),filterFn:e=>!e.message.includes("_converter.py")},{key:`${e.key}_converter_LOGS`,name:this.translate.instant("gateway.converter"),filterFn:e=>e.message.includes("_converter.py")}]}else this.logLinks=this.gatewayLogLinks;this.activeLink=this.logLinks[0],this.changeSubscription()}updateWidgetTitle(){if(this.ctx.settings.isConnectorLog&&this.ctx.settings.connectorLogState){const e=this.ctx.widgetConfig.title,t="${connectorName}";if(e.includes(t)){const n=this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState];this.ctx.widgetTitle=e.replace(t,n.key)}}}updateData(){if(this.ctx.defaultSubscription.data.length&&this.ctx.defaultSubscription.data[0]){let e=this.ctx.defaultSubscription.data[0].data.map((e=>{const t={ts:e[0],key:this.activeLink.key,message:e[1],status:"INVALID LOG FORMAT"};try{t.message=/\[(.*)/.exec(e[1])[0]}catch(n){t.message=e[1]}try{t.status=e[1].match(/\|(\w+)\|/)[1]}catch(e){t.status="INVALID LOG FORMAT"}return t}));this.activeLink.filterFn&&(e=e.filter((e=>this.activeLink.filterFn(e)))),this.dataSource.data=e}}onTabChanged(e){this.activeLink=e,this.changeSubscription()}statusClass(e){switch(e){case hn.DEBUG:return"status status-debug";case hn.WARNING:return"status status-warning";case hn.ERROR:case hn.EXCEPTION:return"status status-error";default:return"status status-info"}}statusClassMsg(e){if(e===hn.EXCEPTION)return"msg-status-exception"}trackByLogTs(e,t){return t.ts}changeSubscription(){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:C.timeseries,settings:{}}],this.ctx.defaultSubscription.unsubscribe(),this.ctx.defaultSubscription.updateDataSubscriptions(),this.ctx.defaultSubscription.callbacks.onDataUpdated=()=>{this.updateData()})}static{this.ɵfac=function(e){return new(e||Tn)(t.ɵɵdirectiveInject(le.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Tn,selectors:[["tb-gateway-logs"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(v,5),t.ɵɵviewQuery(b,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.sort=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.paginator=e.first)}},inputs:{ctx:"ctx",dialogRef:"dialogRef"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:23,vars:21,consts:[["tabPanel",""],[1,"flex","h-full","flex-col"],["mat-tab-nav-bar","",3,"tabPanel"],["mat-tab-link","",3,"active","click",4,"ngFor","ngForOf"],[1,"flex-1","overflow-auto"],["mat-table","","matSort","","matSortDisableClear","",3,"dataSource","trackBy","matSortActive","matSortDirection"],["matColumnDef","ts"],["mat-sort-header","","style","width: 20%",4,"matHeaderCellDef"],[4,"matCellDef"],["matColumnDef","status"],["mat-sort-header","","style","width: 10%",4,"matHeaderCellDef"],["matColumnDef","message"],["mat-sort-header","","style","width: 70%",4,"matHeaderCellDef"],[3,"class",4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],["class","mat-row-select",4,"matRowDef","matRowDefColumns"],[1,"no-data-found","flex-1","items-center","justify-center"],[1,"flex-1"],["showFirstLastButtons","",3,"length","pageIndex","pageSize","pageSizeOptions"],["mat-tab-link","",3,"click","active"],["mat-sort-header","",2,"width","20%"],["mat-sort-header","",2,"width","10%"],["mat-sort-header","",2,"width","70%"],[1,"mat-row-select"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"nav",2),t.ɵɵtemplate(2,fn,2,2,"a",3),t.ɵɵelementEnd(),t.ɵɵelement(3,"mat-tab-nav-panel",null,0),t.ɵɵelementStart(5,"div",4)(6,"table",5),t.ɵɵelementContainerStart(7,6),t.ɵɵtemplate(8,yn,3,3,"mat-header-cell",7)(9,vn,3,4,"mat-cell",8),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(10,9),t.ɵɵtemplate(11,xn,3,3,"mat-header-cell",10)(12,bn,3,3,"mat-cell",8),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(13,11),t.ɵɵtemplate(14,wn,3,3,"mat-header-cell",12)(15,Sn,2,3,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(16,Cn,1,0,"mat-header-row",14)(17,_n,1,0,"mat-row",15),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"span",16),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(21,"span",17),t.ɵɵelementEnd(),t.ɵɵelement(22,"mat-paginator",18),t.ɵɵelementEnd()),2&e){const e=t.ɵɵreference(4);t.ɵɵadvance(),t.ɵɵproperty("tabPanel",e),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.logLinks),t.ɵɵadvance(4),t.ɵɵproperty("dataSource",n.dataSource)("trackBy",n.trackByLogTs)("matSortActive",n.pageLink.sortOrder.property)("matSortDirection",n.pageLink.sortDirection()),t.ɵɵadvance(10),t.ɵɵproperty("matHeaderRowDef",n.displayedColumns)("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",n.displayedColumns),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",0!==n.dataSource.data.length),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,18,"attribute.no-telemetry-text")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",0===n.dataSource.data.length),t.ɵɵadvance(),t.ɵɵproperty("length",n.dataSource.data.length)("pageIndex",n.pageLink.page)("pageSize",n.pageLink.pageSize)("pageSizeOptions",t.ɵɵpureFunction0(20,gn))}},dependencies:t.ɵɵgetComponentDepsFactory(Tn,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex;flex-direction:column;overflow-x:auto;padding:0}[_nghost-%COMP%] .status[_ngcontent-%COMP%]{border-radius:20px;font-weight:500;padding:5px 15px}[_nghost-%COMP%] .status-debug[_ngcontent-%COMP%]{color:green;background:#0080001a}[_nghost-%COMP%] .status-warning[_ngcontent-%COMP%]{color:orange;background:#ffa5001a}[_nghost-%COMP%] .status-error[_ngcontent-%COMP%]{color:red;background:#ff00001a}[_nghost-%COMP%] .status-info[_ngcontent-%COMP%]{color:#00f;background:#0000801a}[_nghost-%COMP%] .msg-status-exception[_ngcontent-%COMP%]{color:red}']})}} + /** + * @license Angular v18.2.6 + * (c) 2010-2024 Google LLC. https://angular.io/ + * License: MIT + */ + function In(e){e||(n(In),e=i(a));const t=new ye((t=>e.onDestroy(t.next.bind(t))));return e=>e.pipe(Me(t))}e("GatewayLogsComponent",Tn);function En(e,t){!t?.injector&&n(En);const l=t?.injector??i(r),p=new ve(1),c=o((()=>{let t;try{t=e()}catch(e){return void s((()=>p.error(e)))}s((()=>p.next(t)))}),{injector:l,manualCleanup:!0});return l.get(a).onDestroy((()=>{c.destroy(),p.complete()})),p.asObservable()}const Mn=["commandInput"],kn=(e,t)=>t.attributeOnGateway,Pn=e=>({command:e});function Dn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",10),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.clear())})),t.ɵɵelementStart(2,"mat-icon",11),t.ɵɵtext(3,"close"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"action.clear"))}function On(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",12),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onCreateNewClick(n))})),t.ɵɵelementStart(1,"span",13),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"notification.create-new")))}function An(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵelement(1,"span",14),t.ɵɵpipe(2,"async"),t.ɵɵpipe(3,"highlight"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(3,4,e.attributeOnGateway,t.ɵɵpipeBind1(2,2,i.searchText$)),t.ɵɵsanitizeHtml)}}function Fn(e,n){if(1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"async"),t.ɵɵpipe(2,"translate")),2&e){const e=t.ɵɵnextContext(2);t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,3,"gateway.statistics.no-commands-matching",t.ɵɵpureFunction1(6,Pn,e.truncate.transform(t.ɵɵpipeBind1(1,1,e.searchText$),!0,6,"...")))," ")}}function Rn(e,n){1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(1,1,"gateway.statistics.no-command-found")," ")}function Bn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-option",9)(1,"div",15),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(2,"span"),t.ɵɵtemplate(3,Fn,3,8),t.ɵɵpipe(4,"async"),t.ɵɵtemplate(5,Rn,2,3),t.ɵɵelementStart(6,"a",16),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onCreateNewClick(n))})),t.ɵɵtext(7,"gateway.create-new-one"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("value",null),t.ɵɵadvance(3),t.ɵɵconditional(t.ɵɵpipeBind1(4,2,e.searchText$)?3:5)}}class Nn{constructor(e,t){this.truncate=e,this.fb=t,this.commands=l(),this.onCreateNewClicked=p(),this.selectStatisticsCommandControl=this.fb.control({}),this.searchText$=this.selectStatisticsCommandControl.valueChanges.pipe(ke((e=>e?"string"==typeof e?e:e?.attributeOnGateway:"")),Pe(),xe(1)),this.filteredCommands$=be([this.searchText$,En(this.commands)]).pipe(De(150),ke((([e,t])=>{const n=t.find((t=>t.attributeOnGateway===e))??null,i=this.selectStatisticsCommandControl.value;return"string"==typeof i&&n?.attributeOnGateway!==e||this.selectStatisticsCommandControl.patchValue(n,{emitEvent:!W(n,i)}),t.filter((t=>t.attributeOnGateway.toLowerCase().includes(e?.toLowerCase()??"")))})),xe(1)),this.onChanges=e=>{},this.selectStatisticsCommandControl.valueChanges.pipe(In()).subscribe((e=>this.onChanges(e)))}registerOnChange(e){this.onChanges=e}registerOnTouched(e){}writeValue(e){this.selectStatisticsCommandControl.patchValue(e)}displayCommandFn(e){return e?e.attributeOnGateway:null}clear(){this.selectStatisticsCommandControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.commandInput.nativeElement.blur(),this.commandInput.nativeElement.focus()}),0)}onCreateNewClick(e){e.stopPropagation(),this.onCreateNewClicked.emit()}static{this.ɵfac=function(e){return new(e||Nn)(t.ɵɵdirectiveInject(T.TruncatePipe),t.ɵɵdirectiveInject(de.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Nn,selectors:[["tb-statistics-commands-autocomplete"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(Mn,7),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.commandInput=e.first)}},inputs:{commands:[1,"commands"]},outputs:{onCreateNewClicked:"onCreateNewClicked"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>Nn)),multi:!0}]),t.ɵɵStandaloneFeature],decls:14,vars:10,consts:[["commandInput",""],["commandAutocomplete","matAutocomplete"],["appearance","outline",1,"mat-block"],["translate",""],["matInput","","type","text",3,"formControl","matAutocomplete"],["type","button","matTooltipPosition","above","matSuffix","","mat-icon-button","","aria-label","Clear",1,"action-button",3,"matTooltip"],["mat-button","","color","primary","matSuffix","",1,"mr-2"],[1,"tb-autocomplete",3,"displayWith"],[3,"value"],[1,"tb-not-found",3,"value"],["type","button","matTooltipPosition","above","matSuffix","","mat-icon-button","","aria-label","Clear",1,"action-button",3,"click","matTooltip"],[1,"material-icons"],["mat-button","","color","primary","matSuffix","",1,"mr-2",3,"click"],[1,"whitespace-nowrap"],[3,"innerHTML"],[1,"tb-not-found-content",3,"click"],["translate","",3,"click"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",2)(1,"mat-label",3),t.ɵɵtext(2,"gateway.statistics.name"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",4,0),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,Dn,4,3,"button",5)(7,On,4,3,"button",6),t.ɵɵelementStart(8,"mat-autocomplete",7,1),t.ɵɵrepeaterCreate(10,An,4,7,"mat-option",8,kn,!1,Bn,8,4,"mat-option",9),t.ɵɵpipe(13,"async"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵreference(9);t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.selectStatisticsCommandControl)("matAutocomplete",e),t.ɵɵattribute("aria-label",t.ɵɵpipeBind1(5,6,"gateway.statistics.command")),t.ɵɵadvance(3),t.ɵɵconditional(n.selectStatisticsCommandControl.value?6:7),t.ɵɵadvance(2),t.ɵɵproperty("displayWith",n.displayCommandFn),t.ɵɵadvance(2),t.ɵɵrepeater(t.ɵɵpipeBind1(13,8,n.filteredCommands$))}},dependencies:t.ɵɵgetComponentDepsFactory(Nn,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%] a[_ngcontent-%COMP%]{border-bottom:none;color:inherit}[_nghost-%COMP%] .action-button[_ngcontent-%COMP%]{opacity:.7}']})}}const Ln=()=>["createdTime","message"];function Vn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",12),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.created-time")))}function qn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",13),t.ɵɵtext(1),t.ɵɵpipe(2,"date"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(2,1,e[0],"yyyy-MM-dd HH:mm:ss"))}}function Gn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",14),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"widgets.gateway.message")," "))}function zn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell")(1,"div",15),t.ɵɵtext(2),t.ɵɵelement(3,"tb-copy-button",16),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",e[1]," "),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(4,3,"gateway.statistics.copy-message")),t.ɵɵproperty("copyText",e[1])}}function Un(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",17)}function jn(e,n){1&e&&t.ɵɵelement(0,"mat-row",17)}class Hn{constructor(){this.data=l([]),this.defaultPageSizes=[10,20,30],this.defaultSortOrder={property:"0",direction:w.DESC},this.pageLink=new S(this.defaultPageSizes[0],0,null,this.defaultSortOrder),this.dataSource=new x([]),o((()=>{this.dataSource.data=this.data()}))}ngAfterViewInit(){this.dataSource.sort=this.sort,this.dataSource.paginator=this.paginator,this.dataSource.sortingDataAccessor=e=>e[Number(this.sort?.active)||0]}static{this.ɵfac=function(e){return new(e||Hn)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Hn,selectors:[["tb-custom-statistics-table"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(v,5),t.ɵɵviewQuery(b,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.sort=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.paginator=e.first)}},inputs:{data:[1,"data"]},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:12,vars:13,consts:[[1,"flex","h-full","flex-col"],[1,"flex-1","overflow-auto"],["mat-table","","matSort","","matSortDisableClear","",3,"dataSource","matSortActive","matSortDirection"],["matColumnDef","createdTime"],["mat-sort-header","","class","w-1/5",4,"matHeaderCellDef"],["class","!w-1/5",4,"matCellDef"],["matColumnDef","message"],["mat-sort-header","",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],["class","mat-row-select",4,"matRowDef","matRowDefColumns"],["showFirstLastButtons","",3,"length","pageSize","pageSizeOptions"],["mat-sort-header","",1,"w-1/5"],[1,"!w-1/5"],["mat-sort-header",""],[1,"flex","items-center","justify-between"],["tooltipPosition","above","icon","content_copy",1,"copy-content",3,"copyText","tooltipText"],[1,"mat-row-select"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"table",2),t.ɵɵelementContainerStart(3,3),t.ɵɵtemplate(4,Vn,3,3,"mat-header-cell",4)(5,qn,3,4,"mat-cell",5),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(6,6),t.ɵɵtemplate(7,Gn,3,3,"mat-header-cell",7)(8,zn,5,5,"mat-cell",8),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(9,Un,1,0,"mat-header-row",9)(10,jn,1,0,"mat-row",10),t.ɵɵelementEnd()(),t.ɵɵelement(11,"mat-paginator",11),t.ɵɵelementEnd()),2&e){let e;t.ɵɵadvance(2),t.ɵɵproperty("dataSource",n.dataSource)("matSortActive",n.pageLink.sortOrder.property)("matSortDirection",n.pageLink.sortDirection()),t.ɵɵadvance(7),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(11,Ln))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(12,Ln)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",!n.dataSource.data.length),t.ɵɵproperty("length",null!==(e=null==n.dataSource||null==n.dataSource.data?null:n.dataSource.data.length)&&void 0!==e?e:0)("pageSize",n.defaultPageSizes[0])("pageSizeOptions",n.defaultPageSizes)}},dependencies:t.ɵɵgetComponentDepsFactory(Hn,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .copy-content .mat-icon{padding:4px;font-size:18px}']})}}class Wn{constructor(e,t,n){this.elementRef=e,this.renderer=t,this.tooltip=n,this.tooltipEnabled=!0,this.position="above",this.destroy$=new we}ngOnInit(){this.observeMouseEvents(),this.applyTruncationStyles()}ngAfterViewInit(){this.tooltip.position=this.position}ngOnDestroy(){this.tooltip._isTooltipVisible()&&this.hideTooltip(),this.destroy$.next(),this.destroy$.complete()}observeMouseEvents(){Se(this.elementRef.nativeElement,"mouseenter").pipe(Oe((()=>this.tooltipEnabled)),Oe((()=>this.isOverflown(this.elementRef.nativeElement))),Ae((()=>this.showTooltip())),Me(this.destroy$)).subscribe(),Se(this.elementRef.nativeElement,"mouseleave").pipe(Oe((()=>this.tooltipEnabled)),Oe((()=>this.tooltip._isTooltipVisible())),Ae((()=>this.hideTooltip())),Me(this.destroy$)).subscribe()}applyTruncationStyles(){this.renderer.setStyle(this.elementRef.nativeElement,"white-space","nowrap"),this.renderer.setStyle(this.elementRef.nativeElement,"overflow","hidden"),this.renderer.setStyle(this.elementRef.nativeElement,"text-overflow","ellipsis")}isOverflown(e){return e.clientWidth{this.adjustChips()}),0))}constructor(e,t,n,i){this.el=e,this.renderer=t,this.translate=n,this.window=i,this.destroy$=new we,this.renderer.setStyle(this.el.nativeElement,"max-height","48px"),this.renderer.setStyle(this.el.nativeElement,"overflow","auto"),Se(i,"resize").pipe(Me(this.destroy$)).subscribe((()=>{this.adjustChips()})),this.observeIntersection()}observeIntersection(){this.intersectionObserver=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.adjustChips()}))})),this.intersectionObserver.observe(this.el.nativeElement)}adjustChips(){const e=this.el.nativeElement,t=this.el.nativeElement.querySelector(".ellipsis-chip"),n=parseFloat(this.window.getComputedStyle(t).marginLeft)||0,i=e.querySelectorAll("mat-chip:not(.ellipsis-chip)");if(this.chipsValue.length>1){const a=this.el.nativeElement.querySelector(".ellipsis-text");this.renderer.setStyle(t,"display","inline-flex"),a.innerHTML=this.translate.instant("gateway.ellipsis-chips-text",{count:this.chipsValue.length});const r=e.offsetWidth-(t.offsetWidth+n);let o=0,s=0;i.forEach((e=>{this.renderer.setStyle(e,"display","inline-flex");const t=e.querySelector(".mdc-evolution-chip__text-label");this.applyMaxChipTextWidth(t,r/3),o+(e.offsetWidth+n)<=r&&s_e(E())))).subscribe((e=>{this.attributesSubject.next(e.data),this.pageDataSubject.next(e),a.next(e)})),a}fetchAttributes(e,t,n){return this.getAllAttributes(e,t).pipe(ke((e=>{const t=e.filter((e=>0!==e.lastUpdateTs));return n.filterData(t)})))}getAllAttributes(e,t){if(!this.allAttributes){let n;M.get(t)?(this.telemetrySubscriber=k.createEntityAttributesSubscription(this.telemetryWsService,e,t,this.zone),this.telemetrySubscriber.subscribe(),n=this.telemetrySubscriber.attributeData$()):n=this.attributeService.getEntityAttributes(e,t),this.allAttributes=n.pipe(Re(1),Be())}return this.allAttributes}isAllSelected(){const e=this.selection.selected.length;return this.attributesSubject.pipe(ke((t=>e===t.length)))}isEmpty(){return this.attributesSubject.pipe(ke((e=>!e.length)))}total(){return this.pageDataSubject.pipe(ke((e=>e.totalElements)))}masterToggle(){this.attributesSubject.pipe(Ae((e=>{this.selection.selected.length===e.length?this.selection.clear():e.forEach((e=>{this.selection.select(e)}))})),Ne(1)).subscribe()}}e("AttributeDatasource",Kn);const Yn=()=>({maxWidth:"970px"});function Xn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-expansion-panel",4)(1,"mat-expansion-panel-header",5)(2,"mat-panel-title")(3,"mat-slide-toggle",6),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(4,"mat-label"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementContainer(7,7),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(),n=t.ɵɵreference(5);t.ɵɵproperty("expanded",e.showStrategyControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showStrategyControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,4,"gateway.report-strategy.label")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n)}}function Zn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",8),t.ɵɵtext(1,"gateway.report-strategy.label"),t.ɵɵelementEnd(),t.ɵɵelementContainer(2,7)),2&e){t.ɵɵnextContext();const e=t.ɵɵreference(5);t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",e)}}function Qn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",16),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.ReportTypeTranslateMap.get(e)))}}function Jn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",19),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.reportStrategyFormGroup.get("reportPeriod").hasError("min")?"gateway.hints.report-period-range":"gateway.hints.report-period-required"))}}function ei(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",17),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",18),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,Jn,3,3,"mat-icon",19),t.ɵɵelementStart(8,"span",20),t.ɵɵtext(9,"gateway.suffix.ms"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"gateway.report-strategy.report-period")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.reportStrategyFormGroup.get("reportPeriod").hasError("required")||e.reportStrategyFormGroup.get("reportPeriod").hasError("min")&&e.reportStrategyFormGroup.get("reportPeriod").touched?7:-1)}}function ti(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelement(4,"div",11),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",12)(6,"mat-select",13),t.ɵɵtemplate(7,Qn,3,4,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵtemplate(8,ei,10,7,"div",15)),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"gateway.type")," "),t.ɵɵadvance(2),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/report-strategy_fn")("tb-help-popup-style",t.ɵɵpureFunction0(7,Yn)),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.reportStrategyTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.reportStrategyFormGroup.get("type").value!==e.ReportStrategyType.OnChange&&e.reportStrategyFormGroup.get("type").value!==e.ReportStrategyType.OnReceived)}}class ni{constructor(e){this.fb=e,this.isExpansionMode=!1,this.defaultValue=on.Key,this.isConfig=!1,this.reportStrategyTypes=Object.values(rn),this.ReportTypeTranslateMap=sn,this.ReportStrategyType=rn,this.destroy$=new we,this.showStrategyControl=this.fb.control(!1),this.reportStrategyFormGroup=this.fb.group({type:[{value:rn.OnReportPeriod,disabled:!0},[]],reportPeriod:[{value:this.defaultValue,disabled:!0},[me.required,me.min(100)]]}),this.observeStrategyFormChange(),this.observeStrategyToggle()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){const t=this.isDisabledConfig(e);if(this.isExpansionMode&&this.showStrategyControl.setValue(!t&&!!e,{emitEvent:!1}),t||!e)return this.reportStrategyFormGroup.disable({emitEvent:!1}),void this.reportStrategyFormGroup.setValue(this.getDefaultConfig(),{emitEvent:!1});this.reportStrategyFormGroup.enable({emitEvent:!1});const n=this.getDefaultConfig(),{type:i=n.type,reportPeriod:a=n.reportPeriod}=e??{};this.reportStrategyFormGroup.setValue({type:i,reportPeriod:a},{emitEvent:!1}),this.onTypeChange(i)}validate(){return this.reportStrategyFormGroup.valid||this.reportStrategyFormGroup.disabled?null:{reportStrategyForm:{valid:!1}}}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}observeStrategyFormChange(){this.reportStrategyFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()})),this.reportStrategyFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.onTypeChange(e)))}observeStrategyToggle(){this.showStrategyControl.valueChanges.pipe(Me(this.destroy$),Oe((()=>this.isExpansionMode))).subscribe((e=>{if(e)this.reportStrategyFormGroup.enable({emitEvent:!1}),this.onTypeChange(this.reportStrategyFormGroup.get("type").value),this.reportStrategyFormGroup.updateValueAndValidity({emitEvent:!1}),this.onChange(this.reportStrategyFormGroup.value);else{this.reportStrategyFormGroup.disable({emitEvent:!1});const e=this.isConfig?{type:"DISABLED"}:null;this.onChange(e)}}))}onTypeChange(e){const t=this.reportStrategyFormGroup.get("reportPeriod");e===rn.OnChange||e===rn.OnReceived?t.disable({emitEvent:!1}):this.isExpansionMode&&!this.showStrategyControl.value||(t.enable({emitEvent:!1}),this.isConfig&&t.setValue(on.Gateway,{emitEvent:!1}))}getDefaultConfig(){return{type:this.isConfig?rn.OnReceived:rn.OnReportPeriod,reportPeriod:this.isConfig?null:this.defaultValue}}isDisabledConfig(e){return this.isConfig&&("DISABLED"===e?.type||"DISABLED"===e?.type)}static{this.ɵfac=function(e){return new(e||ni)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ni,selectors:[["tb-report-strategy"]],inputs:{isExpansionMode:"isExpansionMode",defaultValue:"defaultValue",isConfig:"isConfig"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>ni)),multi:!0},{provide:he,useExisting:c((()=>ni)),multi:!0}]),t.ɵɵStandaloneFeature],decls:6,vars:3,consts:[["defaultMode",""],["strategyFields",""],[3,"formGroup"],["class","tb-settings",3,"expanded",4,"ngIf","ngIfElse"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide",3,"click","formControl"],[3,"ngTemplateOutlet"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","flex","items-center","gap-2"],["matSuffix","","tb-help-popup-placement","right",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],[3,"value"],["tbTruncateWithTooltip","",1,"fixed-title-width","tb-required"],["matInput","","type","number","min","100","name","value","formControlName","reportPeriod",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["translate","","matSuffix","",1,"block","pr-2"]],template:function(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,2),t.ɵɵtemplate(1,Xn,8,6,"mat-expansion-panel",3)(2,Zn,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor)(4,ti,9,8,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(3);t.ɵɵproperty("formGroup",n.reportStrategyFormGroup),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isExpansionMode)("ngIfElse",e)}},dependencies:t.ɵɵgetComponentDepsFactory(ni,[se,_,Wn]),encapsulation:2,changeDetection:d.OnPush})}}e("ReportStrategyComponent",ni),He([I()],ni.prototype,"isExpansionMode",void 0),He([P()],ni.prototype,"defaultValue",void 0),He([I()],ni.prototype,"isConfig",void 0);class ii{constructor(e,t){this.attributeService=e,this.cd=t,this.isGatewayActive=!1}ngAfterViewInit(){this.ctx.$scope.gatewayStatus=this,this.loadGatewayState()}loadGatewayState(){this.attributeService.getEntityAttributes(this.deviceId,D.SERVER_SCOPE,["active","lastDisconnectTime","lastConnectTime"]).subscribe((e=>{const t=e.find((e=>"active"===e.key)).value,n=e.find((e=>"lastDisconnectTime"===e.key))?.value,i=e.find((e=>"lastConnectTime"===e.key))?.value;this.isGatewayActive=this.getGatewayStatus(t,i,n),this.cd.detectChanges()}))}getGatewayStatus(e,t,n){return!!e&&(!n||t>n)}onDataUpdated(){const e=this.ctx.defaultSubscription.data,t=e.find((e=>"active"===e.dataKey.name)).data[0][1],n=e.find((e=>"lastDisconnectTime"===e.dataKey.name)).data[0][1],i=e.find((e=>"lastConnectTime"===e.dataKey.name)).data[0][1];this.isGatewayActive=this.getGatewayStatus(t,i,n),this.cd.detectChanges()}static{this.ɵfac=function(e){return new(e||ii)(t.ɵɵdirectiveInject(K.AttributeService),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ii,selectors:[["tb-gateway-status"]],inputs:{ctx:"ctx",deviceId:"deviceId"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:9,vars:10,consts:[[1,"flex","min-h-10","flex-1","justify-center"],[1,"divider"],[1,"whitespace-nowrap"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-card",0),t.ɵɵelement(1,"div",1),t.ɵɵelementStart(2,"mat-card-header")(3,"mat-card-subtitle",2),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"mat-card-content"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵclassProp("divider-red",!n.isGatewayActive)("divider-green",n.isGatewayActive),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,6,"gateway.gateway-status")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,8,n.isGatewayActive?"gateway.active":"gateway.inactive")))},dependencies:t.ɵɵgetComponentDepsFactory(ii,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex}[_nghost-%COMP%] .divider[_ngcontent-%COMP%]{position:absolute;width:3px;top:12px;border-radius:2px;bottom:4px;left:10px}[_nghost-%COMP%] .divider-green[_ngcontent-%COMP%] .divider[_ngcontent-%COMP%]{border:1px solid rgb(25,128,56);background-color:#198038}[_nghost-%COMP%] .divider-green[_ngcontent-%COMP%] .mat-mdc-card-content[_ngcontent-%COMP%]{color:#198038}[_nghost-%COMP%] .divider-red[_ngcontent-%COMP%] .divider[_ngcontent-%COMP%]{border:1px solid rgb(203,37,48);background-color:#cb2530}[_nghost-%COMP%] .divider-red[_ngcontent-%COMP%] .mat-mdc-card-content[_ngcontent-%COMP%]{color:#cb2530}.mdc-card[_ngcontent-%COMP%]{position:relative;padding-left:10px;box-shadow:none}.mat-mdc-card-subtitle[_ngcontent-%COMP%]{font-weight:400;font-size:12px}.mat-mdc-card-header[_ngcontent-%COMP%]{padding:8px 16px 0}.mat-mdc-card-content[_ngcontent-%COMP%]:last-child{padding-bottom:8px;font-size:16px}'],changeDetection:d.OnPush})}}e("GatewayStatusComponent",ii);class ai{constructor(){this.tooltipText=""}static{this.ɵfac=function(e){return new(e||ai)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ai,selectors:[["tb-error-icon"]],inputs:{tooltipText:"tooltipText"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:2,vars:1,consts:[["matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",0),t.ɵɵtext(1," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",n.tooltipText)},dependencies:t.ɵɵgetComponentDepsFactory(ai,[_,se]),styles:["[_nghost-%COMP%]{display:flex;width:40px;height:40px;padding:10px}mat-icon[_ngcontent-%COMP%]{font-size:20px}"]})}}var ri,oi;e("ErrorTooltipIconComponent",ai),e("MqttConverterType",ri),function(e){e.JSON="json",e.BYTES="bytes",e.CUSTOM="custom"}(ri||e("MqttConverterType",ri={})),e("MQTTSourceType",oi),function(e){e.MSG="message",e.TOPIC="topic",e.CONST="constant"}(oi||e("MQTTSourceType",oi={}));const si=e("MqttVersions",[{name:3.1,value:3},{name:3.11,value:4},{name:5,value:5}]),li=e("QualityTypeTranslationsMap",new Map([[0,"gateway.qos.at-most-once"],[1,"gateway.qos.at-least-once"],[2,"gateway.qos.exactly-once"]])),pi=e("ConvertorTypeTranslationsMap",new Map([[ri.JSON,"gateway.JSON"],[ri.BYTES,"gateway.bytes"],[ri.CUSTOM,"gateway.custom"]]));var ci;e("RequestType",ci),function(e){e.CONNECT_REQUEST="connectRequests",e.DISCONNECT_REQUEST="disconnectRequests",e.ATTRIBUTE_REQUEST="attributeRequests",e.ATTRIBUTE_UPDATE="attributeUpdates",e.SERVER_SIDE_RPC="serverSideRpc"}(ci||e("RequestType",ci={}));const di=e("RequestTypesTranslationsMap",new Map([[ci.CONNECT_REQUEST,"gateway.request.connect-request"],[ci.DISCONNECT_REQUEST,"gateway.request.disconnect-request"],[ci.ATTRIBUTE_REQUEST,"gateway.request.attribute-request"],[ci.ATTRIBUTE_UPDATE,"gateway.request.attribute-update"],[ci.SERVER_SIDE_RPC,"gateway.request.rpc-connection"]])),ui=e("DataConversionTranslationsMap",new Map([[ri.JSON,"gateway.JSON-hint"],[ri.BYTES,"gateway.bytes-hint"],[ri.CUSTOM,"gateway.custom-hint"]]));var mi,hi;e("SocketType",mi),function(e){e.TCP="TCP",e.UDP="UDP"}(mi||e("SocketType",mi={})),e("SocketValueKey",hi),function(e){e.TIMESERIES="telemetry",e.ATTRIBUTES="attributes",e.ATTRIBUTES_REQUESTS="attributeRequests",e.ATTRIBUTES_UPDATES="attributeUpdates",e.RPC_METHODS="serverSideRpc"}(hi||e("SocketValueKey",hi={}));const gi=e("SocketKeysPanelTitleTranslationsMap",new Map([[hi.ATTRIBUTES,"gateway.attributes"],[hi.TIMESERIES,"gateway.timeseries"],[hi.ATTRIBUTES_REQUESTS,"gateway.attribute-requests"],[hi.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[hi.RPC_METHODS,"gateway.rpc-methods"]]));var fi,yi;e("RequestsType",fi),function(e){e.Shared="shared",e.Client="client"}(fi||e("RequestsType",fi={})),e("ExpressionType",yi),function(e){e.Constant="constant",e.Expression="expression"}(yi||e("ExpressionType",yi={}));const vi=e("SocketKeysAddKeyTranslationsMap",new Map([[hi.ATTRIBUTES,"gateway.add-attribute"],[hi.TIMESERIES,"gateway.add-timeseries"],[hi.ATTRIBUTES_REQUESTS,"gateway.add-attribute-request"],[hi.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[hi.RPC_METHODS,"gateway.add-rpc-method"]])),xi=e("SocketKeysDeleteKeyTranslationsMap",new Map([[hi.ATTRIBUTES,"gateway.delete-attribute"],[hi.TIMESERIES,"gateway.delete-timeseries"],[hi.ATTRIBUTES_REQUESTS,"gateway.delete-attribute-request"],[hi.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[hi.RPC_METHODS,"gateway.delete-rpc-method"]])),bi=e("SocketKeysNoKeysTextTranslationsMap",new Map([[hi.ATTRIBUTES,"gateway.no-attributes"],[hi.TIMESERIES,"gateway.no-timeseries"],[hi.ATTRIBUTES_REQUESTS,"gateway.no-attribute-requests"],[hi.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[hi.RPC_METHODS,"gateway.no-rpc-methods"]]));var wi,Si,Ci,_i;e("RestConverterType",wi),function(e){e.JSON="json",e.CUSTOM="custom"}(wi||e("RestConverterType",wi={})),e("RestSourceType",Si),function(e){e.REQUEST="request",e.CONST="constant"}(Si||e("RestSourceType",Si={})),e("ResponseType",Ci),function(e){e.DEFAULT="default",e.CONST="constant",e.ADVANCED="advanced"}(Ci||e("ResponseType",Ci={})),e("ResponseStatus",_i),function(e){e.OK="OK",e.ERROR="Error"}(_i||e("ResponseStatus",_i={}));const Ti=e("ResponseTypeTranslationsMap",new Map([[Ci.DEFAULT,"gateway.rest.response-type.default"],[Ci.CONST,"gateway.rest.response-type.const"],[Ci.ADVANCED,"gateway.rest.response-type.advanced"]]));var Ii;e("RestRequestType",Ii),function(e){e.ATTRIBUTE_REQUEST="attributeRequests",e.ATTRIBUTE_UPDATE="attributeUpdates",e.SERVER_SIDE_RPC="serverSideRpc"}(Ii||e("RestRequestType",Ii={}));const Ei=e("RestRequestTypesTranslationsMap",new Map([[Ii.ATTRIBUTE_REQUEST,"gateway.request.attribute-request"],[Ii.ATTRIBUTE_UPDATE,"gateway.request.attribute-update"],[Ii.SERVER_SIDE_RPC,"gateway.request.rpc-connection"]]));var Mi;e("RestRequestsScopeType",Mi),function(e){e.Shared="shared",e.Client="client"}(Mi||e("RestRequestsScopeType",Mi={}));const ki=e("RestRequestTypeFieldsMap",new Map([[Ii.ATTRIBUTE_REQUEST,["HTTPMethods","endpoint","type","deviceNameExpression","attributeNameExpression"]],[Ii.ATTRIBUTE_UPDATE,["HTTPMethod","SSLVerify","deviceNameFilter","attributeFilter","requestUrlExpression","valueExpression","httpHeaders","tries","allowRedirects"]],[Ii.SERVER_SIDE_RPC,["HTTPMethod","deviceNameFilter","methodFilter","requestUrlExpression","valueExpression","responseTimeout","httpHeaders","tries"]],["all",["requestType","timeout","security"]]])),Pi=e("RestConvertorTypeTranslationsMap",new Map([[wi.JSON,"gateway.JSON"],[wi.CUSTOM,"gateway.custom"]])),Di=e("RestDataConversionTranslationsMap",new Map([[wi.JSON,"gateway.hints.rest.JSON"],[wi.CUSTOM,"gateway.custom-hint"]]));var Oi;e("PortLimits",Oi),function(e){e[e.MIN=1]="MIN",e[e.MAX=65535]="MAX"}(Oi||e("PortLimits",Oi={}));const Ai=e("GatewayConnectorConfigVersionMap",new Map([[dt.REST,ct.v3_7_2],[dt.KNX,ct.v3_7_0],[dt.BACNET,ct.v3_6_2],[dt.SOCKET,ct.v3_6_0],[dt.MQTT,ct.v3_5_2],[dt.OPCUA,ct.v3_5_2],[dt.MODBUS,ct.v3_5_2]]));var Fi,Ri,Bi,Ni;e("OPCUaSourceType",Fi),function(e){e.PATH="path",e.IDENTIFIER="identifier",e.CONST="constant"}(Fi||e("OPCUaSourceType",Fi={})),e("SecurityType",Ri),function(e){e.ANONYMOUS="anonymous",e.BASIC="basic",e.CERTIFICATES="certificates"}(Ri||e("SecurityType",Ri={})),e("ModeType",Bi),function(e){e.NONE="None",e.SIGN="Sign",e.SIGNANDENCRYPT="SignAndEncrypt"}(Bi||e("ModeType",Bi={})),e("MappingType",Ni),function(e){e.DATA="data",e.REQUESTS="requests",e.OPCUA="OPCua"}(Ni||e("MappingType",Ni={}));const Li=e("MappingTypeTranslationsMap",new Map([[Ni.DATA,"gateway.data-mapping"],[Ni.REQUESTS,"gateway.requests-mapping"],[Ni.OPCUA,"gateway.data-mapping"]]));var Vi;e("SecurityPolicy",Vi),function(e){e.BASIC128="Basic128Rsa15",e.BASIC256="Basic256",e.BASIC256SHA="Basic256Sha256"}(Vi||e("SecurityPolicy",Vi={}));const qi=e("SecurityPolicyTypes",[{value:Vi.BASIC128,name:"Basic128RSA15"},{value:Vi.BASIC256,name:"Basic256"},{value:Vi.BASIC256SHA,name:"Basic256SHA256"}]),Gi=e("SecurityTypeTranslationsMap",new Map([[Ri.ANONYMOUS,"gateway.broker.security-types.anonymous"],[Ri.BASIC,"gateway.broker.security-types.basic"],[Ri.CERTIFICATES,"gateway.broker.security-types.certificates"]])),zi=e("SourceTypeTranslationsMap",new Map([[oi.MSG,"gateway.source-type.msg"],[oi.TOPIC,"gateway.source-type.topic"],[oi.CONST,"gateway.source-type.const"],[Fi.PATH,"gateway.source-type.path"],[Fi.IDENTIFIER,"gateway.source-type.identifier"],[Fi.CONST,"gateway.source-type.const"],[yi.Expression,"gateway.source-type.expression"],[Si.REQUEST,"gateway.source-type.request"]]));var Ui;e("MappingKeysType",Ui),function(e){e.ATTRIBUTES="attributes",e.TIMESERIES="timeseries",e.CUSTOM="extensionConfig",e.RPC_METHODS="rpc_methods",e.ATTRIBUTES_UPDATES="attributes_updates"}(Ui||e("MappingKeysType",Ui={}));const ji=e("MappingKeysPanelTitleTranslationsMap",new Map([[Ui.ATTRIBUTES,"gateway.attributes"],[Ui.TIMESERIES,"gateway.timeseries"],[Ui.CUSTOM,"gateway.keys"],[Ui.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[Ui.RPC_METHODS,"gateway.rpc-methods"]])),Hi=e("MappingKeysAddKeyTranslationsMap",new Map([[Ui.ATTRIBUTES,"gateway.add-attribute"],[Ui.TIMESERIES,"gateway.add-timeseries"],[Ui.CUSTOM,"gateway.add-key"],[Ui.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[Ui.RPC_METHODS,"gateway.add-rpc-method"]])),Wi=e("MappingKeysDeleteKeyTranslationsMap",new Map([[Ui.ATTRIBUTES,"gateway.delete-attribute"],[Ui.TIMESERIES,"gateway.delete-timeseries"],[Ui.CUSTOM,"gateway.delete-key"],[Ui.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[Ui.RPC_METHODS,"gateway.delete-rpc-method"]])),$i=e("MappingKeysNoKeysTextTranslationsMap",new Map([[Ui.ATTRIBUTES,"gateway.no-attributes"],[Ui.TIMESERIES,"gateway.no-timeseries"],[Ui.CUSTOM,"gateway.no-keys"],[Ui.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[Ui.RPC_METHODS,"gateway.no-rpc-methods"]])),Ki=e("QualityTypes",[0,1,2]);var Yi;e("ServerSideRpcType",Yi),function(e){e.WithResponse="twoWay",e.WithoutResponse="oneWay"}(Yi||e("ServerSideRpcType",Yi={}));const Xi=e("HelpLinkByMappingTypeMap",new Map([[Ni.DATA,O+"/docs/iot-gateway/config/mqtt/#section-mapping"],[Ni.OPCUA,O+"/docs/iot-gateway/config/opc-ua/#section-mapping"],[Ni.REQUESTS,O+"/docs/iot-gateway/config/mqtt/#requests-mapping"]])),Zi=e("MappingHintTranslationsMap",new Map([[Ni.DATA,"gateway.data-mapping-hint"],[Ni.OPCUA,"gateway.opcua-data-mapping-hint"],[Ni.REQUESTS,"gateway.requests-mapping-hint"]]));var Qi,Ji,ea,ta,na,ia,aa,ra,oa;e("ServerSideRPCType",Qi),function(e){e.ONE_WAY="oneWay",e.TWO_WAY="twoWay"}(Qi||e("ServerSideRPCType",Qi={})),e("SecurityMode",Ji),function(e){e.basic="basic",e.certificates="certificates",e.extendedCertificates="extendedCertificates"}(Ji||e("SecurityMode",Ji={})),e("ModbusProtocolType",ea),function(e){e.TCP="tcp",e.UDP="udp",e.Serial="serial"}(ea||e("ModbusProtocolType",ea={})),e("ModbusMethodType",ta),function(e){e.SOCKET="socket",e.RTU="rtu"}(ta||e("ModbusMethodType",ta={})),e("ModbusSerialMethodType",na),function(e){e.RTU="rtu",e.ASCII="ascii"}(na||e("ModbusSerialMethodType",na={})),e("ModbusParity",ia),function(e){e.Even="E",e.Odd="O",e.None="N"}(ia||e("ModbusParity",ia={})),e("ModbusOrderType",aa),function(e){e.BIG="BIG",e.LITTLE="LITTLE"}(aa||e("ModbusOrderType",aa={})),e("ModbusRegisterType",ra),function(e){e.HoldingRegisters="holding_registers",e.CoilsInitializer="coils_initializer",e.InputRegisters="input_registers",e.DiscreteInputs="discrete_inputs"}(ra||e("ModbusRegisterType",ra={})),e("ModbusValueKey",oa),function(e){e.ATTRIBUTES="attributes",e.TIMESERIES="timeseries",e.ATTRIBUTES_UPDATES="attributeUpdates",e.RPC_REQUESTS="rpc"}(oa||e("ModbusValueKey",oa={}));const sa=e("ModbusBaudrates",[4800,9600,19200,38400,57600,115200,230400,460800,921600]),la=e("ModbusByteSizes",[5,6,7,8]),pa=e("ModbusDefaultRetries",3),ca=e("ModbusRegisterTranslationsMap",new Map([[ra.HoldingRegisters,"gateway.holding_registers"],[ra.CoilsInitializer,"gateway.coils_initializer"],[ra.InputRegisters,"gateway.input_registers"],[ra.DiscreteInputs,"gateway.discrete_inputs"]]));var da;e("ModbusBitTargetType",da),function(e){e.BooleanType="bool",e.IntegerType="int"}(da||e("ModbusBitTargetType",da={}));const ua=e("ModbusBitTargetTypeTranslationMap",new Map([[da.BooleanType,"gateway.boolean"],[da.IntegerType,"gateway.integer"]])),ma=e("ModbusMethodLabelsMap",new Map([[ta.SOCKET,"Socket"],[ta.RTU,"RTU"],[na.ASCII,"ASCII"]])),ha=e("ModbusProtocolLabelsMap",new Map([[ea.TCP,"TCP"],[ea.UDP,"UDP"],[ea.Serial,"Serial"]])),ga=e("ModbusParityLabelsMap",new Map([[ia.Even,"Even"],[ia.Odd,"Odd"],[ia.None,"None"]])),fa=e("ModbusKeysPanelTitleTranslationsMap",new Map([[oa.ATTRIBUTES,"gateway.attributes"],[oa.TIMESERIES,"gateway.timeseries"],[oa.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[oa.RPC_REQUESTS,"gateway.rpc-requests"]])),ya=e("ModbusKeysAddKeyTranslationsMap",new Map([[oa.ATTRIBUTES,"gateway.add-attribute"],[oa.TIMESERIES,"gateway.add-timeseries"],[oa.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[oa.RPC_REQUESTS,"gateway.add-rpc-request"]])),va=e("ModbusKeysDeleteKeyTranslationsMap",new Map([[oa.ATTRIBUTES,"gateway.delete-attribute"],[oa.TIMESERIES,"gateway.delete-timeseries"],[oa.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[oa.RPC_REQUESTS,"gateway.delete-rpc-request"]])),xa=e("ModbusKeysNoKeysTextTranslationsMap",new Map([[oa.ATTRIBUTES,"gateway.no-attributes"],[oa.TIMESERIES,"gateway.no-timeseries"],[oa.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[oa.RPC_REQUESTS,"gateway.no-rpc-requests"]]));var ba;e("ModifierType",ba),function(e){e.DIVIDER="divider",e.MULTIPLIER="multiplier"}(ba||e("ModifierType",ba={}));const wa=e("ModifierTypesMap",new Map([[ba.DIVIDER,{name:"gateway.divider",icon:"mdi:division"}],[ba.MULTIPLIER,{name:"gateway.multiplier",icon:"mdi:multiplication"}]]));var Sa,Ca;e("DeviceInfoType",Sa),function(e){e.FULL="full",e.PARTIAL="partial"}(Sa||e("DeviceInfoType",Sa={})),e("SegmentationType",Ca),function(e){e.BOTH="segmentedBoth",e.TRANSMIT="segmentedTransmit",e.RECEIVE="segmentedReceive",e.NO="noSegmentation"}(Ca||e("SegmentationType",Ca={}));const _a=e("SegmentationTypeTranslationsMap",new Map([[Ca.BOTH,"gateway.bacnet.segmentation.both"],[Ca.TRANSMIT,"gateway.bacnet.segmentation.transmit"],[Ca.RECEIVE,"gateway.bacnet.segmentation.receive"],[Ca.NO,"gateway.bacnet.segmentation.no"]]));var Ta;e("BacnetDeviceKeysType",Ta),function(e){e.ATTRIBUTES="attributes",e.TIMESERIES="timeseries",e.RPC_METHODS="serverSideRpc",e.ATTRIBUTES_UPDATES="attributeUpdates"}(Ta||e("BacnetDeviceKeysType",Ta={}));const Ia=e("BacnetDeviceKeysPanelTitleTranslationsMap",new Map([[Ta.ATTRIBUTES,"gateway.attributes"],[Ta.TIMESERIES,"gateway.timeseries"],[Ta.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[Ta.RPC_METHODS,"gateway.rpc-methods"]])),Ea=e("BacnetDeviceKeysAddKeyTranslationsMap",new Map([[Ta.ATTRIBUTES,"gateway.add-attribute"],[Ta.TIMESERIES,"gateway.add-timeseries"],[Ta.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[Ta.RPC_METHODS,"gateway.add-rpc-method"]])),Ma=e("BacnetDeviceKeysDeleteKeyTranslationsMap",new Map([[Ta.ATTRIBUTES,"gateway.delete-attribute"],[Ta.TIMESERIES,"gateway.delete-timeseries"],[Ta.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[Ta.RPC_METHODS,"gateway.delete-rpc-method"]])),ka=e("BacnetDeviceKeysNoKeysTextTranslationsMap",new Map([[Ta.ATTRIBUTES,"gateway.no-attributes"],[Ta.TIMESERIES,"gateway.no-timeseries"],[Ta.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[Ta.RPC_METHODS,"gateway.no-rpc-methods"]]));var Pa;e("BacnetKeyObjectType",Pa),function(e){e.analogInput="analogInput",e.analogOutput="analogOutput",e.analogValue="analogValue",e.binaryInput="binaryInput",e.binaryOutput="binaryOutput",e.binaryValue="binaryValue"}(Pa||e("BacnetKeyObjectType",Pa={}));const Da=e("BacnetKeyObjectTypeTranslationsMap",new Map([[Pa.analogInput,"gateway.bacnet.object-type.analog-input"],[Pa.analogOutput,"gateway.bacnet.object-type.analog-output"],[Pa.analogValue,"gateway.bacnet.object-type.analog-value"],[Pa.binaryInput,"gateway.bacnet.object-type.binary-input"],[Pa.binaryOutput,"gateway.bacnet.object-type.binary-output"],[Pa.binaryValue,"gateway.bacnet.object-type.binary-value"]]));var Oa;e("BacnetPropertyId",Oa),function(e){e.presentValue="presentValue",e.statusFlags="statusFlags",e.covIncrement="covIncrement",e.eventState="eventState",e.outOfService="outOfService",e.polarity="polarity",e.priorityArray="priorityArray",e.relinquishDefault="relinquishDefault",e.currentCommandPriority="currentCommandPriority",e.eventMessageTexts="eventMessageTexts",e.eventMessageTextsConfig="eventMessageTextsConfig",e.eventAlgorithmInhibitReference="eventAlgorithmInhibitReference",e.timeDelayNormal="timeDelayNormal"}(Oa||e("BacnetPropertyId",Oa={}));const Aa=e("BacnetPropertyIdByObjectType",new Map([[Pa.analogInput,[Oa.presentValue,Oa.statusFlags,Oa.covIncrement]],[Pa.analogOutput,[Oa.presentValue,Oa.statusFlags,Oa.covIncrement]],[Pa.analogValue,[Oa.presentValue,Oa.statusFlags,Oa.covIncrement]],[Pa.binaryInput,[Oa.presentValue,Oa.statusFlags,Oa.eventState,Oa.outOfService,Oa.polarity]],[Pa.binaryOutput,[Oa.presentValue,Oa.statusFlags,Oa.eventState,Oa.outOfService,Oa.polarity,Oa.priorityArray,Oa.relinquishDefault,Oa.currentCommandPriority,Oa.eventMessageTexts,Oa.eventMessageTextsConfig,Oa.eventAlgorithmInhibitReference,Oa.timeDelayNormal]],[Pa.binaryValue,[Oa.presentValue,Oa.statusFlags,Oa.eventState,Oa.outOfService]]])),Fa=e("BacnetPropertyIdTranslationsMap",new Map([[Oa.presentValue,"gateway.bacnet.property-id.present-value"],[Oa.statusFlags,"gateway.bacnet.property-id.status-flags"],[Oa.covIncrement,"gateway.bacnet.property-id.cov-increment"],[Oa.eventState,"gateway.bacnet.property-id.event-state"],[Oa.outOfService,"gateway.bacnet.property-id.out-of-service"],[Oa.polarity,"gateway.bacnet.property-id.polarity"],[Oa.priorityArray,"gateway.bacnet.property-id.priority-array"],[Oa.relinquishDefault,"gateway.bacnet.property-id.relinquish-default"],[Oa.currentCommandPriority,"gateway.bacnet.property-id.current-command-priority"],[Oa.eventMessageTexts,"gateway.bacnet.property-id.event-message-texts"],[Oa.eventMessageTextsConfig,"gateway.bacnet.property-id.event-message-texts-config"],[Oa.eventAlgorithmInhibitReference,"gateway.bacnet.property-id.event-algorithm-inhibit-reference"],[Oa.timeDelayNormal,"gateway.bacnet.property-id.time-delay-normal"]]));var Ra;e("BacnetRequestType",Ra),function(e){e.Write="writeProperty",e.Read="readProperty"}(Ra||e("BacnetRequestType",Ra={}));const Ba=e("BacnetRequestTypeTranslationsMap",new Map([[Ra.Write,"gateway.bacnet.request-type.write"],[Ra.Read,"gateway.bacnet.request-type.read"]]));class Na{static{this.mqttRequestTypeKeys=Object.values(ci)}static{this.mqttRequestMappingOldFields=["attributeNameJsonExpression","deviceNameJsonExpression","deviceNameTopicExpression","extension-config"]}static{this.mqttRequestMappingNewFields=["attributeNameExpressionSource","responseTopicQoS","extensionConfig"]}static mapMappingToUpgradedVersion(e){return e?.map((({converter:e,topicFilter:t,subscriptionQos:n=1})=>{const i=e.deviceInfo??this.extractConverterDeviceInfo(e),a={...e,deviceInfo:i,extensionConfig:e.extensionConfig||e["extension-config"]||null};return this.cleanUpOldFields(a),{converter:a,topicFilter:t,subscriptionQos:n}}))}static mapRequestsToUpgradedVersion(e){return this.mqttRequestTypeKeys.reduce(((t,n)=>e[n]?(t[n]=e[n].map((e=>{const t=this.mapRequestToUpgradedVersion(e,n);return this.cleanUpOldFields(t),t})),t):t),{})}static mapRequestsToDowngradedVersion(e){return this.mqttRequestTypeKeys.reduce(((t,n)=>e[n]?(t[n]=e[n].map((e=>{n===ci.SERVER_SIDE_RPC&&delete e.type;const{attributeNameExpression:t,deviceInfo:i,...a}=e,r={...a,attributeNameJsonExpression:t||null,deviceNameJsonExpression:i?.deviceNameExpressionSource!==oi.TOPIC?i?.deviceNameExpression:null,deviceNameTopicExpression:i?.deviceNameExpressionSource===oi.TOPIC?i?.deviceNameExpression:null};return this.cleanUpNewFields(r),r})),t):t),{})}static mapMappingToDowngradedVersion(e){return e?.map((e=>{const t=this.mapConverterToDowngradedVersion(e.converter);return this.cleanUpNewFields(t),{converter:t,topicFilter:e.topicFilter}}))}static mapConverterToDowngradedVersion(e){const{deviceInfo:t,...n}=e;return e.type!==ri.BYTES?{...n,deviceNameJsonExpression:t?.deviceNameExpressionSource===oi.MSG?t.deviceNameExpression:null,deviceTypeJsonExpression:t?.deviceProfileExpressionSource===oi.MSG?t.deviceProfileExpression:null,deviceNameTopicExpression:t?.deviceNameExpressionSource!==oi.MSG?t?.deviceNameExpression:null,deviceTypeTopicExpression:t?.deviceProfileExpressionSource!==oi.MSG?t?.deviceProfileExpression:null}:{...n,deviceNameExpression:t.deviceNameExpression,deviceTypeExpression:t.deviceProfileExpression,"extension-config":e.extensionConfig}}static cleanUpOldFields(e){this.mqttRequestMappingOldFields.forEach((t=>delete e[t])),Y(e)}static cleanUpNewFields(e){this.mqttRequestMappingNewFields.forEach((t=>delete e[t])),Y(e)}static getTypeSourceByValue(e){return e.includes("${")?oi.MSG:e.includes("/")?oi.TOPIC:oi.CONST}static extractConverterDeviceInfo(e){const t=e.deviceNameExpression||e.deviceNameJsonExpression||e.deviceNameTopicExpression||null,n=e.deviceNameExpressionSource?e.deviceNameExpressionSource:t?this.getTypeSourceByValue(t):null,i=e.deviceProfileExpression||e.deviceTypeTopicExpression||e.deviceTypeJsonExpression||"default",a=e.deviceProfileExpressionSource?e.deviceProfileExpressionSource:i?this.getTypeSourceByValue(i):null;return t||i?{deviceNameExpression:t,deviceNameExpressionSource:n,deviceProfileExpression:i,deviceProfileExpressionSource:a}:null}static mapRequestToUpgradedVersion(e,t){const n=e.deviceNameJsonExpression||e.deviceNameTopicExpression||null,i=e.deviceTypeTopicExpression||e.deviceTypeJsonExpression||"default",a=i?this.getTypeSourceByValue(i):null,r=e.attributeNameExpressionSource||e.attributeNameJsonExpression||null,o=t===ci.SERVER_SIDE_RPC?1:null,s=t===ci.SERVER_SIDE_RPC?e.responseTopicExpression?Yi.WithResponse:Yi.WithoutResponse:null;return{...e,attributeNameExpression:r,attributeNameExpressionSource:r?this.getTypeSourceByValue(r):null,deviceInfo:e.deviceInfo?e.deviceInfo:n?{deviceNameExpression:n,deviceNameExpressionSource:this.getTypeSourceByValue(n),deviceProfileExpression:i,deviceProfileExpressionSource:a}:null,responseTopicQoS:o,type:s}}}e("MqttVersionMappingUtil",Na);class La{constructor(e,t){this.gatewayVersionIn=e,this.connector=t,this.gatewayVersion=Ya.parseVersion(this.gatewayVersionIn),this.configVersion=Ya.parseVersion(this.connector.configVersion??this.connector.configurationJson.configVersion)}getProcessedByVersion(){return this.isVersionUpdateNeeded()?this.processVersionUpdate():this.connector}processVersionUpdate(){return this.isVersionUpgradeNeeded()?this.getUpgradedVersion():this.isVersionDowngradeNeeded()?this.getDowngradedVersion():this.connector}isVersionUpdateNeeded(){return!!this.gatewayVersion&&this.configVersion!==this.gatewayVersion}isVersionUpgradeNeeded(){const e=Ya.parseVersion(Ai.get(this.connector.type)),t=this.gatewayVersion>=e,n=!this.configVersion||this.configVersion=e&&e>this.gatewayVersion}}e("GatewayConnectorVersionProcessor",La);class Va extends La{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t,this.mqttRequestTypeKeys=Object.values(ci)}getUpgradedVersion(){const{connectRequests:e,disconnectRequests:t,attributeRequests:n,attributeUpdates:i,serverSideRpc:a}=this.connector.configurationJson;let r={...this.connector.configurationJson,requestsMapping:Na.mapRequestsToUpgradedVersion({connectRequests:e,disconnectRequests:t,attributeRequests:n,attributeUpdates:i,serverSideRpc:a}),mapping:Na.mapMappingToUpgradedVersion(this.connector.configurationJson.mapping)};return this.mqttRequestTypeKeys.forEach((e=>{const{[e]:t,...n}=r;r={...n}})),this.cleanUpConfigJson(r),{...this.connector,configurationJson:r,configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const{requestsMapping:e,mapping:t,...n}=this.connector.configurationJson,i=e?Na.mapRequestsToDowngradedVersion(e):{},a=Na.mapMappingToDowngradedVersion(t);return{...this.connector,configurationJson:{...n,...i,mapping:a},configVersion:this.gatewayVersionIn}}cleanUpConfigJson(e){W(e.requestsMapping,{})&&delete e.requestsMapping,W(e.mapping,[])&&delete e.mapping}}e("MqttVersionProcessor",Va);class qa extends La{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{master:e.master?.slaves?Xa.mapMasterToUpgradedVersion(e.master):{slaves:[]},slave:e.slave?Xa.mapSlaveToUpgradedVersion(e.slave):{}},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{...e,slave:e.slave?Xa.mapSlaveToDowngradedVersion(e.slave):{},master:e.master?.slaves?Xa.mapMasterToDowngradedVersion(e.master):{slaves:[]}},configVersion:this.gatewayVersionIn}}}e("ModbusVersionProcessor",qa);class Ga extends La{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson.server;let t;return t="mapping"in this.connector.configurationJson.server?Za.mapMappingToUpgradedVersion(this.connector.configurationJson.server.mapping):"mapping"in this.connector.configurationJson?Za.mapMappingToCurrentVersion(this.connector.configurationJson.mapping):[],{...this.connector,configurationJson:{server:e?Za.mapServerToUpgradedVersion(e):{},mapping:t},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){return{...this.connector,configurationJson:{server:Za.mapServerToDowngradedVersion(this.connector.configurationJson)},configVersion:this.gatewayVersionIn}}}e("OpcVersionProcessor",Ga);class za{constructor(){this.fb=i(ge),this.destroyRef=i(a),this.formGroup=this.initFormGroup(),this.observeValueChanges()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.formGroup.valid?null:{formGroup:{valid:!1}}}writeValue(e){this.onWriteValue(e)}onWriteValue(e){this.formGroup.patchValue(e,{emitEvent:!1})}mapOnChangeValue(e){return e}observeValueChanges(){this.formGroup.valueChanges.pipe(In(this.destroyRef)).subscribe((e=>this.onChange(this.mapOnChangeValue(e))))}static{this.ɵfac=function(e){return new(e||za)}}static{this.ɵdir=t.ɵɵdefineDirective({type:za})}}e("ControlValueAccessorBaseAbstract",za);class Ua extends za{constructor(){super(...arguments),this.withReportStrategy=!0,this.initialized=new u,this.isLegacy=!1,this.fb=i(ge)}get basicFormGroup(){return this.formGroup}ngAfterViewInit(){this.initialized.emit()}onWriteValue(e){this.formGroup.setValue(this.mapConfigToFormValue(e),{emitEvent:!1})}mapOnChangeValue(e){return this.getMappedValue(e)}initFormGroup(){return this.initBasicFormGroup()}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(Ua)))(n||Ua)}})()}static{this.ɵdir=t.ɵɵdefineDirective({type:Ua,inputs:{generalTabContent:"generalTabContent",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},outputs:{initialized:"initialized"},features:[t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature]})}}e("GatewayConnectorBasicConfigDirective",Ua);class ja extends La{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{socket:e?Qa.mapSocketToUpgradedVersion(e):{},devices:e?.devices?Qa.mapDevicesToUpgradedVersion(e.devices):[]},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){return{...this.connector,configurationJson:Qa.mapSocketToDowngradedVersion(this.connector.configurationJson),configVersion:this.gatewayVersionIn}}}e("SocketVersionProcessor",ja);class Ha extends La{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{application:e?.general?Ja.mapApplicationToUpgradedVersion(e.general):{},devices:e?.devices?Ja.mapDevicesToUpgradedVersion(e.devices):[]},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{general:e?.application?Ja.mapApplicationToDowngradedVersion(e.application):{},devices:e?.devices?Ja.mapDevicesToDowngradedVersion(e.devices):[]},configVersion:this.gatewayVersionIn}}}e("BacnetVersionProcessor",Ha);const Wa=["searchInput"];class $a{constructor(){this.withReportStrategy=!0,this.textSearchMode=!1,this.onChange=()=>{},this.translate=i(pe),this.dialog=i(ze),this.dialogService=i(X),this.fb=i(ge),this.cd=i(h),this.destroyRef=i(a),this.textSearch=this.fb.control("",{nonNullable:!0}),this.entityFormArray=this.fb.array([]),this.entityFormArray.valueChanges.pipe(In()).subscribe((e=>{this.updateTableData(e),this.onChange(e)})),this.dataSource=this.getDatasource()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(De(150),Pe(((e,t)=>(e??"")===t.trim())),In(this.destroyRef)).subscribe((e=>this.updateTableData(this.entityFormArray.value,e.trim())))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){this.entityFormArray.clear(),this.pushDataAsFormArrays(e)}enterFilterMode(){this.textSearchMode=!0,this.cd.detectChanges();const e=this.searchInputField.nativeElement;e.focus(),e.setSelectionRange(0,0)}exitFilterMode(){this.updateTableData(this.entityFormArray.value),this.textSearchMode=!1,this.textSearch.reset()}validate(){return this.entityFormArray.controls.length?null:{devicesFormGroup:{valid:!1}}}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.entityFormArray.push(this.fb.control(e))))}static{this.ɵfac=function(e){return new(e||$a)}}static{this.ɵdir=t.ɵɵdefineDirective({type:$a,viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(Wa,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},features:[t.ɵɵInputTransformsFeature]})}}e("AbstractDevicesConfigTableComponent",$a);class Ka extends La{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t,this.restRequestTypeKeys=Object.values(Ii)}getUpgradedVersion(){const{attributeRequests:e=[],attributeUpdates:t=[],serverSideRpc:n=[],mapping:i=[],...a}=this.connector.configurationJson;let r={server:Ya.cleanUpConfigBaseInfo(a),requestsMapping:{attributeRequests:e,attributeUpdates:t,serverSideRpc:n},mapping:er.mapMappingToUpgradedVersion(i)};return this.restRequestTypeKeys.forEach((e=>{const{[e]:t,...n}=r;r={...n}})),{...this.connector,configurationJson:r,configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const{requestsMapping:e={},mapping:t,server:n,...i}=this.connector.configurationJson,{attributeRequests:a=[],attributeUpdates:r=[],serverSideRpc:o=[]}=e;return{...this.connector,configurationJson:{...i,...n,attributeRequests:a,attributeUpdates:r,serverSideRpc:o,mapping:er.mapMappingToDowngradedVersion(t)},configVersion:this.gatewayVersionIn}}}e("RestVersionProcessor",Ka);class Ya{static getConfig(e,t){switch(e.type){case dt.MQTT:return new Va(t,e).getProcessedByVersion();case dt.OPCUA:return new Ga(t,e).getProcessedByVersion();case dt.MODBUS:return new qa(t,e).getProcessedByVersion();case dt.SOCKET:return new ja(t,e).getProcessedByVersion();case dt.BACNET:return new Ha(t,e).getProcessedByVersion();case dt.REST:return new Ka(t,e).getProcessedByVersion();default:return e}}static parseVersion(e){if(Z(e))return e;if(Q(e)){const[t,n="0",i="0"]=e.split(".");return parseFloat(`${t}.${n}${i.slice(0,1)}`)}return 0}static cleanUpConfigBaseInfo(e){const{name:t,id:n,enableRemoteLogging:i,logLevel:a,reportStrategy:r,configVersion:o,...s}=e;return s}}e("GatewayConnectorVersionMappingUtil",Ya);class Xa{static mapMasterToUpgradedVersion(e){return{slaves:e.slaves.map((e=>{const{sendDataOnlyOnChange:t,...n}=e;return{...n,deviceType:e.deviceType??"default",reportStrategy:t?{type:rn.OnChange}:{type:rn.OnReportPeriod,reportPeriod:e.pollPeriod}}}))}}static mapMasterToDowngradedVersion(e){return{slaves:e.slaves.map((e=>{const{reportStrategy:t,...n}=e;return{...n,sendDataOnlyOnChange:t?.type!==rn.OnReportPeriod}}))}}static mapSlaveToDowngradedVersion(e){if(!e?.values)return e;const t=Object.keys(e.values).reduce(((t,n)=>t={...t,[n]:[e.values[n]]}),{});return{...e,values:t}}static mapSlaveToUpgradedVersion(e){if(!e?.values)return e;const t=Object.keys(e.values).reduce(((t,n)=>t={...t,[n]:this.mapValuesToUpgradedVersion(e.values[n][0]??{})}),{});return{...e,values:t}}static mapValuesToUpgradedVersion(e){return Object.keys(e).reduce(((t,n)=>t={...t,[n]:e[n].map((e=>({...e,type:"int"===e.type?Zt.INT16:e.type})))}),{})}}e("ModbusVersionMappingUtil",Xa);class Za{static mapServerToUpgradedVersion(e){const{mapping:t,disableSubscriptions:n,pollPeriodInMillis:i,...a}=e;return{...a,pollPeriodInMillis:i??5e3,enableSubscriptions:!n}}static mapServerToDowngradedVersion(e){const{mapping:t,server:n}=e,{enableSubscriptions:i,...a}=n??{};return{...a,mapping:t?this.mapMappingToDowngradedVersion(t):[],disableSubscriptions:!i}}static mapMappingToUpgradedVersion(e){return e.map((e=>({deviceNodePattern:e.deviceNodePattern,deviceNodeSource:this.getDeviceNodeSourceByValue(e.deviceNodePattern),deviceInfo:{deviceNameExpression:e.deviceNamePattern,deviceNameExpressionSource:this.getTypeSourceByValue(e.deviceNamePattern),deviceProfileExpression:e.deviceTypePattern??"default",deviceProfileExpressionSource:this.getTypeSourceByValue(e.deviceTypePattern??"default")},attributes:e.attributes?.map((e=>({key:e.key,type:this.getTypeSourceByValue(e.path),value:e.path})))??[],attributes_updates:e.attributes_updates?.map((e=>({key:e.attributeOnThingsBoard,type:this.getTypeSourceByValue(e.attributeOnDevice),value:e.attributeOnDevice})))??[],timeseries:e.timeseries?.map((e=>({key:e.key,type:this.getTypeSourceByValue(e.path),value:e.path})))??[],rpc_methods:e.rpc_methods?.map((e=>({method:e.method,arguments:e.arguments.map((e=>({value:e,type:this.getArgumentType(e)})))})))??[]})))}static mapMappingToCurrentVersion(e){return e.map((e=>({deviceNodePattern:e.deviceNodePattern,deviceNodeSource:this.getDeviceNodeSourceByValue(e.deviceNodePattern),deviceInfo:{deviceNameExpression:e.deviceInfo.deviceNameExpression,deviceNameExpressionSource:this.getTypeSourceByValue(e?.deviceInfo?.deviceNameExpressionSource),deviceProfileExpression:e?.deviceInfo?.deviceProfileExpression??"default",deviceProfileExpressionSource:this.getTypeSourceByValue(e?.deviceInfo?.deviceProfileExpressionSource??"default")},attributes:e.attributes?.map((e=>({key:e.key,type:e.type,value:e?.value})))??[],attributes_updates:e.attributes_updates?.map((e=>({key:e.key,type:e.type,value:e.value})))??[],timeseries:e.timeseries?.map((e=>({key:e.key,type:e.type,value:e.value})))??[],rpc_methods:e.rpc_methods?.map((e=>({method:e.method,arguments:e.arguments.map((e=>({value:e,type:this.getArgumentType(e)})))})))??[]})))}static mapMappingToDowngradedVersion(e){return e.map((e=>({deviceNodePattern:e.deviceNodePattern,deviceNamePattern:e.deviceInfo.deviceNameExpression,deviceTypePattern:e.deviceInfo.deviceProfileExpression,attributes:e.attributes.map((e=>({key:e.key,path:e.value}))),attributes_updates:e.attributes_updates.map((e=>({attributeOnThingsBoard:e.key,attributeOnDevice:e.value}))),timeseries:e.timeseries.map((e=>({key:e.key,path:e.value}))),rpc_methods:e.rpc_methods.map((e=>({method:e.method,arguments:e.arguments.map((e=>e.value))})))})))}static getTypeSourceByValue(e){return e.includes("${")?Fi.IDENTIFIER:e.includes("/")||e.includes("\\")?Fi.PATH:Fi.CONST}static getDeviceNodeSourceByValue(e){return e.includes("${")?Fi.IDENTIFIER:Fi.PATH}static getArgumentType(e){switch(typeof e){case"boolean":return"boolean";case"number":return Number.isInteger(e)?"integer":"float";default:return"string"}}}e("OpcVersionMappingUtil",Za);class Qa{static mapSocketToUpgradedVersion(e){const{devices:t,...n}=e??{};return Ya.cleanUpConfigBaseInfo(n)}static mapSocketToDowngradedVersion(e){const{devices:t,socket:n}=e??{};return{...n,devices:this.mapDevicesToDowngradedVersion(t??[])}}static mapDevicesToUpgradedVersion(e){return e?.map((e=>({...e,attributeRequests:e.attributeRequests?.map((e=>({...e,requestExpressionSource:this.getExpressionSource(e.requestExpression),attributeNameExpressionSource:this.getExpressionSource(e.attributeNameExpression)})))??[]})))??[]}static mapDevicesToDowngradedVersion(e){return e.map((e=>({...e,attributeRequests:e.attributeRequests?.map((({requestExpressionSource:e,attributeNameExpressionSource:t,...n})=>n))??[]})))}static getExpressionSource(e){return e.includes("${")||e.includes("[")?yi.Expression:yi.Constant}}e("SocketVersionMappingUtil",Qa);class Ja{static mapApplicationToUpgradedVersion(e){const{address:t="",...n}=e,[i,a]=t.split(":"),[r,o]=i.split("/");return{host:r,port:a,mask:o,...n}}static mapApplicationToDowngradedVersion(e){const{host:t="",port:n="",mask:i="",...a}=e;return{address:i?`${t}/${i}:${n}`:`${t}:${n}`,...a}}static mapDevicesToUpgradedVersion(e){return e?.map((({address:e="",deviceName:t,deviceType:n,attributes:i,timeseries:a,attributeUpdates:r,serverSideRpc:o,...s})=>({...s,host:e.split(":")[0],port:e.split(":")[1],deviceInfo:{deviceNameExpression:t,deviceProfileExpression:n,deviceNameExpressionSource:this.getExpressionSource(t),deviceProfileExpressionSource:this.getExpressionSource(n)},attributes:this.getUpdateKeys(i),timeseries:this.getUpdateKeys(a),attributeUpdates:this.getUpdateKeys(r),serverSideRpc:this.getUpdateKeys(o)})))??[]}static mapDevicesToDowngradedVersion(e){return e?.map((({port:e,host:t,deviceInfo:n,attributes:i,timeseries:a,attributeUpdates:r,serverSideRpc:o,...s})=>({...s,address:`${t}:${e}`,deviceName:n?.deviceNameExpression,deviceType:n?.deviceProfileExpression,attributes:this.getDowngradedKeys(i),timeseries:this.getDowngradedKeys(a),attributeUpdates:this.getDowngradedKeys(r),serverSideRpc:this.getDowngradedKeys(o)})))??[]}static getExpressionSource(e){return e?.includes("${")||e?.includes("[")?yi.Expression:yi.Constant}static getUpdateKeys(e){return e?.map((({objectId:e="",...t})=>({objectType:e.split(":")[0],objectId:e.split(":")[1],...t})))??[]}static getDowngradedKeys(e){return e?.map((({objectId:e="",objectType:t="",...n})=>({objectId:`${t}:${e}`,...n})))??[]}}e("BacnetVersionMappingUtil",Ja);class er{static mapMappingToUpgradedVersion(e){return e?.map((({converter:e,...t})=>{const{deviceNameExpression:n,deviceTypeExpression:i,...a}=e;return{converter:{deviceInfo:{deviceNameExpressionSource:this.getTypeSourceByValue(n),deviceNameExpression:n,deviceProfileExpressionSource:this.getTypeSourceByValue(i),deviceProfileExpression:i},...a},...t}}))}static mapMappingToDowngradedVersion(e){return e?.map((({converter:e,...t})=>{const{deviceInfo:n,...i}=e;return{converter:{deviceNameExpression:n?.deviceNameExpression,deviceTypeExpression:n?.deviceProfileExpression,...i},...t}}))}static getTypeSourceByValue(e=""){return e.includes("${")?Si.REQUEST:Si.CONST}}e("RestVersionMappingUtil",er);class tr{transform(e,t){const n=Ya.parseVersion(e);return t===dt.MODBUS?n>=Ya.parseVersion(ct.v3_5_2):n>=Ya.parseVersion(ct.v3_6_0)}static{this.ɵfac=function(e){return new(e||tr)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"withReportStrategy",type:tr,pure:!0,standalone:!0})}}function nr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-required")," "))}function ir(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-pattern")," "))}function ar(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.name-already-exists")," "))}function rr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-required")," "))}function or(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-pattern")," "))}function sr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-min")," "))}function lr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.command-required")," "))}function pr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.command-pattern")," "))}e("ReportStrategyVersionPipe",tr);class cr extends A{constructor(e,t,n,i,a,r,o,s){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.dialogService=r,this.translate=o,this.destroyRef=s,this.commandHelpLink=O+"/docs/iot-gateway/configuration/#subsection-statistics",this.editCommandFormGroup=this.fb.group({attributeOnGateway:["",[me.required,me.pattern(pn),this.uniqNameRequired()]],command:["",[me.required,me.pattern(/^(?=\S).*\S$/)]],timeout:[10,[me.required,me.min(1),me.pattern(ln),me.pattern(/^[^.\s]+$/)]],installCmd:["",me.pattern(pn)]}),this.editCommandFormGroup.patchValue(this.data.command,{emitEvent:!1})}cancel(){this.confirmConnectorChange().pipe(Le(Boolean),In(this.destroyRef)).subscribe((()=>this.dialogRef.close(null)))}apply(){this.dialogRef.close({current:this.editCommandFormGroup.value,prev:this.data.command})}confirmConnectorChange(){return this.editCommandFormGroup.dirty?this.dialogService.confirm(this.translate.instant("gateway.statistics.change-command-title"),this.translate.instant("gateway.statistics.change-command-text"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0):_e(!0)}uniqNameRequired(){return e=>{const t=e.value?.trim().toLowerCase();return t&&this.data.existingCommands.some((e=>e.toLowerCase()===t))&&t!==this.data.command.attributeOnGateway.toLowerCase()?{duplicateName:{valid:!1}}:null}}static{this.ɵfac=function(e){return new(e||cr)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(K.DialogService),t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:cr,selectors:[["tb-edit-custom-command-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:64,vars:27,consts:[[1,"edit-command-container",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel","stroked","no-padding-bottom","no-gap","command-container"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["translate",""],["matInput","","formControlName","attributeOnGateway"],["matIconSuffix","",1,"cursor-pointer",3,"matTooltip"],["matInput","","formControlName","timeout","type","number","min","1"],["appearance","outline",1,"mat-block"],["matInput","","formControlName","command"],[1,"tb-settings","pb-4"],["translate","",1,"tb-form-panel-title"],["matInput","","formControlName","installCmd"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"mat-toolbar",1)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",2)(6,"div",3),t.ɵɵelementStart(7,"button",4),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",5),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",6)(11,"div",7)(12,"section",8)(13,"section",9)(14,"mat-form-field",10)(15,"mat-label",11),t.ɵɵtext(16,"gateway.statistics.name"),t.ɵɵelementEnd(),t.ɵɵelement(17,"input",12),t.ɵɵtemplate(18,nr,3,3,"mat-error")(19,ir,3,3,"mat-error")(20,ar,3,3,"mat-error"),t.ɵɵelementStart(21,"mat-icon",13),t.ɵɵpipe(22,"translate"),t.ɵɵtext(23,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"mat-form-field",10)(25,"mat-label",11),t.ɵɵtext(26,"gateway.statistics.timeout"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",14),t.ɵɵtemplate(28,rr,3,3,"mat-error")(29,or,3,3,"mat-error")(30,sr,3,3,"mat-error"),t.ɵɵelementStart(31,"mat-icon",13),t.ɵɵpipe(32,"translate"),t.ɵɵtext(33,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(34,"section")(35,"mat-form-field",15)(36,"mat-label",11),t.ɵɵtext(37,"gateway.statistics.command"),t.ɵɵelementEnd(),t.ɵɵelement(38,"input",16),t.ɵɵtemplate(39,lr,3,3,"mat-error")(40,pr,3,3,"mat-error"),t.ɵɵelementStart(41,"mat-icon",13),t.ɵɵpipe(42,"translate"),t.ɵɵtext(43,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(44,"section")(45,"mat-expansion-panel",17)(46,"mat-expansion-panel-header")(47,"mat-panel-title")(48,"div",18),t.ɵɵtext(49,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(50,"mat-form-field",10)(51,"mat-label",11),t.ɵɵtext(52,"gateway.statistics.install-cmd"),t.ɵɵelementEnd(),t.ɵɵelement(53,"input",19),t.ɵɵelementStart(54,"mat-icon",13),t.ɵɵpipe(55,"translate"),t.ɵɵtext(56,"info_outlined "),t.ɵɵelementEnd()()()()()()(),t.ɵɵelementStart(57,"div",20)(58,"button",21),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(59),t.ɵɵpipe(60,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(61,"button",22),t.ɵɵlistener("click",(function(){return n.apply()})),t.ɵɵtext(62),t.ɵɵpipe(63,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.editCommandFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,13,n.data.titleText)),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.commandHelpLink),t.ɵɵadvance(12),t.ɵɵconditional(n.editCommandFormGroup.get("attributeOnGateway").hasError("required")?18:n.editCommandFormGroup.get("attributeOnGateway").hasError("pattern")?19:n.editCommandFormGroup.get("attributeOnGateway").hasError("duplicateName")?20:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(22,15,"gateway.hints.attribute")),t.ɵɵadvance(7),t.ɵɵconditional(n.editCommandFormGroup.get("timeout").hasError("required")?28:n.editCommandFormGroup.get("timeout").hasError("pattern")?29:n.editCommandFormGroup.get("timeout").hasError("min")?30:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(32,17,"gateway.hints.timeout")),t.ɵɵadvance(8),t.ɵɵconditional(n.editCommandFormGroup.get("command").hasError("required")?39:n.editCommandFormGroup.get("command").hasError("pattern")?40:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(42,19,"gateway.hints.command")),t.ɵɵadvance(13),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(55,21,"gateway.hints.install-cmd")),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(60,23,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.editCommandFormGroup.invalid||!n.editCommandFormGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(63,25,n.data.buttonText)," "))},dependencies:t.ɵɵgetComponentDepsFactory(cr,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .edit-command-container[_ngcontent-%COMP%]{min-width:40vw;width:50vw}[_nghost-%COMP%] .pointer-event{pointer-events:all}[_nghost-%COMP%] .toggle-group span{padding:0 25px}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{color:#e0e0e0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix:hover{color:#9e9e9e}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex;align-items:center}']})}}var dr=function(){this.firefox=!1,this.ie=!1,this.edge=!1,this.newEdge=!1,this.weChat=!1},ur=new function(){this.browser=new dr,this.node=!1,this.wxa=!1,this.worker=!1,this.svgSupported=!1,this.touchEventsSupported=!1,this.pointerEventsSupported=!1,this.domSupported=!1,this.transformSupported=!1,this.transform3dSupported=!1,this.hasGlobalWindow="undefined"!=typeof window};"object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?(ur.wxa=!0,ur.touchEventsSupported=!0):"undefined"==typeof document&&"undefined"!=typeof self?ur.worker=!0:"undefined"==typeof navigator||0===navigator.userAgent.indexOf("Node.js")?(ur.node=!0,ur.svgSupported=!0):function(e,t){var n=t.browser,i=e.match(/Firefox\/([\d.]+)/),a=e.match(/MSIE\s([\d.]+)/)||e.match(/Trident\/.+?rv:(([\d.]+))/),r=e.match(/Edge?\/([\d.]+)/),o=/micromessenger/i.test(e);i&&(n.firefox=!0,n.version=i[1]);a&&(n.ie=!0,n.version=a[1]);r&&(n.edge=!0,n.version=r[1],n.newEdge=+r[1].split(".")[0]>18);o&&(n.weChat=!0);t.svgSupported="undefined"!=typeof SVGRect,t.touchEventsSupported="ontouchstart"in window&&!n.ie&&!n.edge,t.pointerEventsSupported="onpointerdown"in window&&(n.edge||n.ie&&+n.version>=11),t.domSupported="undefined"!=typeof document;var s=document.documentElement.style;t.transform3dSupported=(n.ie&&"transition"in s||n.edge||"WebKitCSSMatrix"in window&&"m11"in new WebKitCSSMatrix||"MozPerspective"in s)&&!("OTransition"in s),t.transformSupported=t.transform3dSupported||n.ie&&+n.version>=9}(navigator.userAgent,ur);var mr="sans-serif",hr="12px "+mr;var gr,fr,yr=function(e){var t={};if("undefined"==typeof JSON)return t;for(var n=0;n=0)r=a*e.length;else for(var o=0;o>1)%2;o.style.cssText=["position: absolute","visibility: hidden","padding: 0","margin: 0","border-width: 0","user-select: none","width:0","height:0",i[s]+":0",a[l]+":0",i[1-s]+":auto",a[1-l]+":auto",""].join("!important;"),e.appendChild(o),n.push(o)}return n}(t,r),s=function(e,t,n){for(var i=n?"invTrans":"trans",a=t[i],r=t.srcCoords,o=[],s=[],l=!0,p=0;p<4;p++){var c=e[p].getBoundingClientRect(),d=2*p,u=c.left,m=c.top;o.push(u,m),l=l&&r&&u===r[d]&&m===r[d+1],s.push(e[p].offsetLeft,e[p].offsetTop)}return l&&a?a:(t.srcCoords=o,t[i]=n?Jo(s,o):Jo(o,s))}(o,r,a);if(s)return s(e,n,i),!0}return!1}function is(e){return"CANVAS"===e.nodeName.toUpperCase()}var as=/([&<>"'])/g,rs={"&":"&","<":"<",">":">",'"':""","'":"'"};function os(e){return null==e?"":(e+"").replace(as,(function(e,t){return rs[t]}))}var ss=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ls=[],ps=ur.browser.firefox&&+ur.browser.version.split(".")[0]<39;function cs(e,t,n,i){return n=n||{},i?ds(e,t,n):ps&&null!=t.layerX&&t.layerX!==t.offsetX?(n.zrX=t.layerX,n.zrY=t.layerY):null!=t.offsetX?(n.zrX=t.offsetX,n.zrY=t.offsetY):ds(e,t,n),n}function ds(e,t,n){if(ur.domSupported&&e.getBoundingClientRect){var i=t.clientX,a=t.clientY;if(is(e)){var r=e.getBoundingClientRect();return n.zrX=i-r.left,void(n.zrY=a-r.top)}if(ns(ls,e,i,a))return n.zrX=ls[0],void(n.zrY=ls[1])}n.zrX=n.zrY=0}function us(e){return e||window.event}function ms(e,t,n){if(null!=(t=us(t)).zrX)return t;var i=t.type;if(i&&i.indexOf("touch")>=0){var a="touchend"!==i?t.targetTouches[0]:t.changedTouches[0];a&&cs(e,a,t,n)}else{cs(e,t,t,n);var r=function(e){var t=e.wheelDelta;if(t)return t;var n=e.deltaX,i=e.deltaY;if(null==n||null==i)return t;return 3*(0!==i?Math.abs(i):Math.abs(n))*(i>0?-1:i<0?1:n>0?-1:1)}(t);t.zrDelta=r?r/120:-(t.detail||0)/3}var o=t.button;return null==t.which&&void 0!==o&&ss.test(t.type)&&(t.which=1&o?1:2&o?3:4&o?2:0),t}function hs(e,t,n,i){e.addEventListener(t,n,i)}var gs=function(e){e.preventDefault(),e.stopPropagation(),e.cancelBubble=!0};function fs(e){return 2===e.which||3===e.which}var ys=function(){function e(){this._track=[]}return e.prototype.recognize=function(e,t,n){return this._doTrack(e,t,n),this._recognize(e)},e.prototype.clear=function(){return this._track.length=0,this},e.prototype._doTrack=function(e,t,n){var i=e.touches;if(i){for(var a={points:[],touches:[],target:t,event:e},r=0,o=i.length;r1&&a&&a.length>1){var o=vs(a)/vs(r);!isFinite(o)&&(o=1),t.pinchScale=o;var s=[((i=a)[0][0]+i[1][0])/2,(i[0][1]+i[1][1])/2];return t.pinchX=s[0],t.pinchY=s[1],{type:"pinch",target:e[0].target,event:t}}}}};function bs(e){return e[0]=1,e[1]=0,e[2]=0,e[3]=1,e[4]=0,e[5]=0,e}function ws(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e}function Ss(e,t,n){var i=t[0]*n[0]+t[2]*n[1],a=t[1]*n[0]+t[3]*n[1],r=t[0]*n[2]+t[2]*n[3],o=t[1]*n[2]+t[3]*n[3],s=t[0]*n[4]+t[2]*n[5]+t[4],l=t[1]*n[4]+t[3]*n[5]+t[5];return e[0]=i,e[1]=a,e[2]=r,e[3]=o,e[4]=s,e[5]=l,e}function Cs(e,t,n){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4]+n[0],e[5]=t[5]+n[1],e}function _s(e,t,n,i){void 0===i&&(i=[0,0]);var a=t[0],r=t[2],o=t[4],s=t[1],l=t[3],p=t[5],c=Math.sin(n),d=Math.cos(n);return e[0]=a*d+s*c,e[1]=-a*c+s*d,e[2]=r*d+l*c,e[3]=-r*c+d*l,e[4]=d*(o-i[0])+c*(p-i[1])+i[0],e[5]=d*(p-i[1])-c*(o-i[0])+i[1],e}function Ts(e,t,n){var i=n[0],a=n[1];return e[0]=t[0]*i,e[1]=t[1]*a,e[2]=t[2]*i,e[3]=t[3]*a,e[4]=t[4]*i,e[5]=t[5]*a,e}function Is(e,t){var n=t[0],i=t[2],a=t[4],r=t[1],o=t[3],s=t[5],l=n*o-r*i;return l?(l=1/l,e[0]=o*l,e[1]=-r*l,e[2]=-i*l,e[3]=n*l,e[4]=(i*s-o*a)*l,e[5]=(r*a-n*s)*l,e):null}function Es(e){var t=[1,0,0,1,0,0];return ws(t,e),t}var Ms=function(){function e(e,t){this.x=e||0,this.y=t||0}return e.prototype.copy=function(e){return this.x=e.x,this.y=e.y,this},e.prototype.clone=function(){return new e(this.x,this.y)},e.prototype.set=function(e,t){return this.x=e,this.y=t,this},e.prototype.equal=function(e){return e.x===this.x&&e.y===this.y},e.prototype.add=function(e){return this.x+=e.x,this.y+=e.y,this},e.prototype.scale=function(e){this.x*=e,this.y*=e},e.prototype.scaleAndAdd=function(e,t){this.x+=e.x*t,this.y+=e.y*t},e.prototype.sub=function(e){return this.x-=e.x,this.y-=e.y,this},e.prototype.dot=function(e){return this.x*e.x+this.y*e.y},e.prototype.len=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},e.prototype.lenSquare=function(){return this.x*this.x+this.y*this.y},e.prototype.normalize=function(){var e=this.len();return this.x/=e,this.y/=e,this},e.prototype.distance=function(e){var t=this.x-e.x,n=this.y-e.y;return Math.sqrt(t*t+n*n)},e.prototype.distanceSquare=function(e){var t=this.x-e.x,n=this.y-e.y;return t*t+n*n},e.prototype.negate=function(){return this.x=-this.x,this.y=-this.y,this},e.prototype.transform=function(e){if(e){var t=this.x,n=this.y;return this.x=e[0]*t+e[2]*n+e[4],this.y=e[1]*t+e[3]*n+e[5],this}},e.prototype.toArray=function(e){return e[0]=this.x,e[1]=this.y,e},e.prototype.fromArray=function(e){this.x=e[0],this.y=e[1]},e.set=function(e,t,n){e.x=t,e.y=n},e.copy=function(e,t){e.x=t.x,e.y=t.y},e.len=function(e){return Math.sqrt(e.x*e.x+e.y*e.y)},e.lenSquare=function(e){return e.x*e.x+e.y*e.y},e.dot=function(e,t){return e.x*t.x+e.y*t.y},e.add=function(e,t,n){e.x=t.x+n.x,e.y=t.y+n.y},e.sub=function(e,t,n){e.x=t.x-n.x,e.y=t.y-n.y},e.scale=function(e,t,n){e.x=t.x*n,e.y=t.y*n},e.scaleAndAdd=function(e,t,n,i){e.x=t.x+n.x*i,e.y=t.y+n.y*i},e.lerp=function(e,t,n,i){var a=1-i;e.x=a*t.x+i*n.x,e.y=a*t.y+i*n.y},e}(),ks=Math.min,Ps=Math.max,Ds=new Ms,Os=new Ms,As=new Ms,Fs=new Ms,Rs=new Ms,Bs=new Ms,Ns=function(){function e(e,t,n,i){n<0&&(e+=n,n=-n),i<0&&(t+=i,i=-i),this.x=e,this.y=t,this.width=n,this.height=i}return e.prototype.union=function(e){var t=ks(e.x,this.x),n=ks(e.y,this.y);isFinite(this.x)&&isFinite(this.width)?this.width=Ps(e.x+e.width,this.x+this.width)-t:this.width=e.width,isFinite(this.y)&&isFinite(this.height)?this.height=Ps(e.y+e.height,this.y+this.height)-n:this.height=e.height,this.x=t,this.y=n},e.prototype.applyTransform=function(t){e.applyTransform(this,this,t)},e.prototype.calculateTransform=function(e){var t=this,n=e.width/t.width,i=e.height/t.height,a=[1,0,0,1,0,0];return Cs(a,a,[-t.x,-t.y]),Ts(a,a,[n,i]),Cs(a,a,[e.x,e.y]),a},e.prototype.intersect=function(t,n){if(!t)return!1;t instanceof e||(t=e.create(t));var i=this,a=i.x,r=i.x+i.width,o=i.y,s=i.y+i.height,l=t.x,p=t.x+t.width,c=t.y,d=t.y+t.height,u=!(rh&&(h=x,gh&&(h=b,y=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height},e.prototype.clone=function(){return new e(this.x,this.y,this.width,this.height)},e.prototype.copy=function(t){e.copy(this,t)},e.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},e.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},e.prototype.isZero=function(){return 0===this.width||0===this.height},e.create=function(t){return new e(t.x,t.y,t.width,t.height)},e.copy=function(e,t){e.x=t.x,e.y=t.y,e.width=t.width,e.height=t.height},e.applyTransform=function(t,n,i){if(i){if(i[1]<1e-5&&i[1]>-1e-5&&i[2]<1e-5&&i[2]>-1e-5){var a=i[0],r=i[3],o=i[4],s=i[5];return t.x=n.x*a+o,t.y=n.y*r+s,t.width=n.width*a,t.height=n.height*r,t.width<0&&(t.x+=t.width,t.width=-t.width),void(t.height<0&&(t.y+=t.height,t.height=-t.height))}Ds.x=As.x=n.x,Ds.y=Fs.y=n.y,Os.x=Fs.x=n.x+n.width,Os.y=As.y=n.y+n.height,Ds.transform(i),Fs.transform(i),Os.transform(i),As.transform(i),t.x=ks(Ds.x,Os.x,As.x,Fs.x),t.y=ks(Ds.y,Os.y,As.y,Fs.y);var l=Ps(Ds.x,Os.x,As.x,Fs.x),p=Ps(Ds.y,Os.y,As.y,Fs.y);t.width=l-t.x,t.height=p-t.y}else t!==n&&e.copy(t,n)},e}(),Ls="silent";function Vs(){gs(this.event)}var qs=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.handler=null,t}return We(t,e),t.prototype.dispose=function(){},t.prototype.setCursor=function(){},t}(Xo),Gs=function(e,t){this.x=e,this.y=t},zs=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],Us=new Ns(0,0,0,0),js=function(e){function t(t,n,i,a,r){var o=e.call(this)||this;return o._hovered=new Gs(0,0),o.storage=t,o.painter=n,o.painterRoot=a,o._pointerSize=r,i=i||new qs,o.proxy=null,o.setHandlerProxy(i),o._draggingMgr=new Yo(o),o}return We(t,e),t.prototype.setHandlerProxy=function(e){this.proxy&&this.proxy.dispose(),e&&(zr(zs,(function(t){e.on&&e.on(t,this[t],this)}),this),e.handler=this),this.proxy=e},t.prototype.mousemove=function(e){var t=e.zrX,n=e.zrY,i=$s(this,t,n),a=this._hovered,r=a.target;r&&!r.__zr&&(r=(a=this.findHover(a.x,a.y)).target);var o=this._hovered=i?new Gs(t,n):this.findHover(t,n),s=o.target,l=this.proxy;l.setCursor&&l.setCursor(s?s.cursor:"default"),r&&s!==r&&this.dispatchToElement(a,"mouseout",e),this.dispatchToElement(o,"mousemove",e),s&&s!==r&&this.dispatchToElement(o,"mouseover",e)},t.prototype.mouseout=function(e){var t=e.zrEventControl;"only_globalout"!==t&&this.dispatchToElement(this._hovered,"mouseout",e),"no_globalout"!==t&&this.trigger("globalout",{type:"globalout",event:e})},t.prototype.resize=function(){this._hovered=new Gs(0,0)},t.prototype.dispatch=function(e,t){var n=this[e];n&&n.call(this,t)},t.prototype.dispose=function(){this.proxy.dispose(),this.storage=null,this.proxy=null,this.painter=null},t.prototype.setCursorStyle=function(e){var t=this.proxy;t.setCursor&&t.setCursor(e)},t.prototype.dispatchToElement=function(e,t,n){var i=(e=e||{}).target;if(!i||!i.silent){for(var a="on"+t,r=function(e,t,n){return{type:e,event:n,target:t.target,topTarget:t.topTarget,cancelBubble:!1,offsetX:n.zrX,offsetY:n.zrY,gestureEvent:n.gestureEvent,pinchX:n.pinchX,pinchY:n.pinchY,pinchScale:n.pinchScale,wheelDelta:n.zrDelta,zrByTouch:n.zrByTouch,which:n.which,stop:Vs}}(t,e,n);i&&(i[a]&&(r.cancelBubble=!!i[a].call(i,r)),i.trigger(t,r),i=i.__hostTarget?i.__hostTarget:i.parent,!r.cancelBubble););r.cancelBubble||(this.trigger(t,r),this.painter&&this.painter.eachOtherLayer&&this.painter.eachOtherLayer((function(e){"function"==typeof e[a]&&e[a].call(e,r),e.trigger&&e.trigger(t,r)})))}},t.prototype.findHover=function(e,t,n){var i=this.storage.getDisplayList(),a=new Gs(e,t);if(Ws(i,a,e,t,n),this._pointerSize&&!a.target){for(var r=[],o=this._pointerSize,s=o/2,l=new Ns(e-s,t-s,o,o),p=i.length-1;p>=0;p--){var c=i[p];c===n||c.ignore||c.ignoreCoarsePointer||c.parent&&c.parent.ignoreCoarsePointer||(Us.copy(c.getBoundingRect()),c.transform&&Us.applyTransform(c.transform),Us.intersect(l)&&r.push(c))}if(r.length)for(var d=Math.PI/12,u=2*Math.PI,m=0;m=0;r--){var o=e[r],s=void 0;if(o!==a&&!o.ignore&&(s=Hs(o,n,i))&&(!t.topTarget&&(t.topTarget=o),s!==Ls)){t.target=o;break}}}function $s(e,t,n){var i=e.painter;return t<0||t>i.getWidth()||n<0||n>i.getHeight()}zr(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],(function(e){js.prototype[e]=function(t){var n,i,a=t.zrX,r=t.zrY,o=$s(this,a,r);if("mouseup"===e&&o||(i=(n=this.findHover(a,r)).target),"mousedown"===e)this._downEl=i,this._downPoint=[t.zrX,t.zrY],this._upEl=i;else if("mouseup"===e)this._upEl=i;else if("click"===e){if(this._downEl!==this._upEl||!this._downPoint||Go(this._downPoint,[t.zrX,t.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,e,t)}}));function Ks(e,t,n,i){var a=t+1;if(a===n)return 1;if(i(e[a++],e[t])<0){for(;a=0;)a++;return a-t}function Ys(e,t,n,i,a){for(i===t&&i++;i>>1])<0?l=r:s=r+1;var p=i-s;switch(p){case 3:e[s+3]=e[s+2];case 2:e[s+2]=e[s+1];case 1:e[s+1]=e[s];break;default:for(;p>0;)e[s+p]=e[s+p-1],p--}e[s]=o}}function Xs(e,t,n,i,a,r){var o=0,s=0,l=1;if(r(e,t[n+a])>0){for(s=i-a;l0;)o=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),o+=a,l+=a}else{for(s=a+1;ls&&(l=s);var p=o;o=a-l,l=a-p}for(o++;o>>1);r(e,t[n+c])>0?o=c+1:l=c}return l}function Zs(e,t,n,i,a,r){var o=0,s=0,l=1;if(r(e,t[n+a])<0){for(s=a+1;ls&&(l=s);var p=o;o=a-l,l=a-p}else{for(s=i-a;l=0;)o=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),o+=a,l+=a}for(o++;o>>1);r(e,t[n+c])<0?l=c:o=c+1}return l}function Qs(e,t){var n,i,a=7,r=0,o=[];function s(s){var l=n[s],p=i[s],c=n[s+1],d=i[s+1];i[s]=p+d,s===r-3&&(n[s+1]=n[s+2],i[s+1]=i[s+2]),r--;var u=Zs(e[c],e,l,p,0,t);l+=u,0!==(p-=u)&&0!==(d=Xs(e[l+p-1],e,c,d,d-1,t))&&(p<=d?function(n,i,r,s){var l=0;for(l=0;l=7||m>=7);if(h)break;g<0&&(g=0),g+=2}if((a=g)<1&&(a=1),1===i){for(l=0;l=0;l--)e[m+l]=e[u+l];return void(e[d]=o[c])}var h=a;for(;;){var g=0,f=0,y=!1;do{if(t(o[c],e[p])<0){if(e[d--]=e[p--],g++,f=0,0==--i){y=!0;break}}else if(e[d--]=o[c--],f++,g=0,1==--s){y=!0;break}}while((g|f)=0;l--)e[m+l]=e[u+l];if(0===i){y=!0;break}}if(e[d--]=o[c--],1==--s){y=!0;break}if(0!==(f=s-Xs(e[p],o,0,s,s-1,t))){for(s-=f,m=(d-=f)+1,u=(c-=f)+1,l=0;l=7||f>=7);if(y)break;h<0&&(h=0),h+=2}(a=h)<1&&(a=1);if(1===s){for(m=(d-=i)+1,u=(p-=i)+1,l=i-1;l>=0;l--)e[m+l]=e[u+l];e[d]=o[c]}else{if(0===s)throw new Error;for(u=d-(s-1),l=0;l1;){var e=r-2;if(e>=1&&i[e-1]<=i[e]+i[e+1]||e>=2&&i[e-2]<=i[e]+i[e-1])i[e-1]i[e+1])break;s(e)}},forceMergeRuns:function(){for(;r>1;){var e=r-2;e>0&&i[e-1]=32;)t|=1&e,e>>=1;return e+t}(a);do{if((r=Ks(e,n,i,t))s&&(l=s),Ys(e,n,n+l,n+r,t),r=l}o.pushRun(n,r),o.mergeRuns(),a-=r,n+=r}while(0!==a);o.forceMergeRuns()}}}var el=!1;function tl(){el||(el=!0,console.warn("z / z2 / zlevel of displayable is invalid, which may cause unexpected errors"))}function nl(e,t){return e.zlevel===t.zlevel?e.z===t.z?e.z2-t.z2:e.z-t.z:e.zlevel-t.zlevel}var il=function(){function e(){this._roots=[],this._displayList=[],this._displayListLen=0,this.displayableSortFunc=nl}return e.prototype.traverse=function(e,t){for(var n=0;n0&&(p.__clipPaths=[]),isNaN(p.z)&&(tl(),p.z=0),isNaN(p.z2)&&(tl(),p.z2=0),isNaN(p.zlevel)&&(tl(),p.zlevel=0),this._displayList[this._displayListLen++]=p}var c=e.getDecalElement&&e.getDecalElement();c&&this._updateAndAddDisplayable(c,t,n);var d=e.getTextGuideLine();d&&this._updateAndAddDisplayable(d,t,n);var u=e.getTextContent();u&&this._updateAndAddDisplayable(u,t,n)}},e.prototype.addRoot=function(e){e.__zr&&e.__zr.storage===this||this._roots.push(e)},e.prototype.delRoot=function(e){if(e instanceof Array)for(var t=0,n=e.length;t=0&&this._roots.splice(i,1)}},e.prototype.delAllRoots=function(){this._roots=[],this._displayList=[],this._displayListLen=0},e.prototype.getRoots=function(){return this._roots},e.prototype.dispose=function(){this._displayList=null,this._roots=null},e}(),al=ur.hasGlobalWindow&&(window.requestAnimationFrame&&window.requestAnimationFrame.bind(window)||window.msRequestAnimationFrame&&window.msRequestAnimationFrame.bind(window)||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame)||function(e){return setTimeout(e,16)},rl={linear:function(e){return e},quadraticIn:function(e){return e*e},quadraticOut:function(e){return e*(2-e)},quadraticInOut:function(e){return(e*=2)<1?.5*e*e:-.5*(--e*(e-2)-1)},cubicIn:function(e){return e*e*e},cubicOut:function(e){return--e*e*e+1},cubicInOut:function(e){return(e*=2)<1?.5*e*e*e:.5*((e-=2)*e*e+2)},quarticIn:function(e){return e*e*e*e},quarticOut:function(e){return 1- --e*e*e*e},quarticInOut:function(e){return(e*=2)<1?.5*e*e*e*e:-.5*((e-=2)*e*e*e-2)},quinticIn:function(e){return e*e*e*e*e},quinticOut:function(e){return--e*e*e*e*e+1},quinticInOut:function(e){return(e*=2)<1?.5*e*e*e*e*e:.5*((e-=2)*e*e*e*e+2)},sinusoidalIn:function(e){return 1-Math.cos(e*Math.PI/2)},sinusoidalOut:function(e){return Math.sin(e*Math.PI/2)},sinusoidalInOut:function(e){return.5*(1-Math.cos(Math.PI*e))},exponentialIn:function(e){return 0===e?0:Math.pow(1024,e-1)},exponentialOut:function(e){return 1===e?1:1-Math.pow(2,-10*e)},exponentialInOut:function(e){return 0===e?0:1===e?1:(e*=2)<1?.5*Math.pow(1024,e-1):.5*(2-Math.pow(2,-10*(e-1)))},circularIn:function(e){return 1-Math.sqrt(1-e*e)},circularOut:function(e){return Math.sqrt(1- --e*e)},circularInOut:function(e){return(e*=2)<1?-.5*(Math.sqrt(1-e*e)-1):.5*(Math.sqrt(1-(e-=2)*e)+1)},elasticIn:function(e){var t,n=.1;return 0===e?0:1===e?1:(!n||n<1?(n=1,t=.1):t=.4*Math.asin(1/n)/(2*Math.PI),-n*Math.pow(2,10*(e-=1))*Math.sin((e-t)*(2*Math.PI)/.4))},elasticOut:function(e){var t,n=.1;return 0===e?0:1===e?1:(!n||n<1?(n=1,t=.1):t=.4*Math.asin(1/n)/(2*Math.PI),n*Math.pow(2,-10*e)*Math.sin((e-t)*(2*Math.PI)/.4)+1)},elasticInOut:function(e){var t,n=.1,i=.4;return 0===e?0:1===e?1:(!n||n<1?(n=1,t=.1):t=i*Math.asin(1/n)/(2*Math.PI),(e*=2)<1?n*Math.pow(2,10*(e-=1))*Math.sin((e-t)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(e-=1))*Math.sin((e-t)*(2*Math.PI)/i)*.5+1)},backIn:function(e){var t=1.70158;return e*e*((t+1)*e-t)},backOut:function(e){var t=1.70158;return--e*e*((t+1)*e+t)+1},backInOut:function(e){var t=2.5949095;return(e*=2)<1?e*e*((t+1)*e-t)*.5:.5*((e-=2)*e*((t+1)*e+t)+2)},bounceIn:function(e){return 1-rl.bounceOut(1-e)},bounceOut:function(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375},bounceInOut:function(e){return e<.5?.5*rl.bounceIn(2*e):.5*rl.bounceOut(2*e-1)+.5}},ol=Math.pow,sl=Math.sqrt,ll=1e-8,pl=1e-4,cl=sl(3),dl=1/3,ul=Po(),ml=Po(),hl=Po();function gl(e){return e>-1e-8&&ell||e<-1e-8}function yl(e,t,n,i,a){var r=1-a;return r*r*(r*e+3*a*t)+a*a*(a*i+3*r*n)}function vl(e,t,n,i,a){var r=1-a;return 3*(((t-e)*r+2*(n-t)*a)*r+(i-n)*a*a)}function xl(e,t,n,i,a,r){var o=i+3*(t-n)-e,s=3*(n-2*t+e),l=3*(t-e),p=e-a,c=s*s-3*o*l,d=s*l-9*o*p,u=l*l-3*s*p,m=0;if(gl(c)&&gl(d)){if(gl(s))r[0]=0;else(_=-l/s)>=0&&_<=1&&(r[m++]=_)}else{var h=d*d-4*c*u;if(gl(h)){var g=d/c,f=-g/2;(_=-s/o+g)>=0&&_<=1&&(r[m++]=_),f>=0&&f<=1&&(r[m++]=f)}else if(h>0){var y=sl(h),v=c*s+1.5*o*(-d+y),x=c*s+1.5*o*(-d-y);(_=(-s-((v=v<0?-ol(-v,dl):ol(v,dl))+(x=x<0?-ol(-x,dl):ol(x,dl))))/(3*o))>=0&&_<=1&&(r[m++]=_)}else{var b=(2*c*s-3*o*d)/(2*sl(c*c*c)),w=Math.acos(b)/3,S=sl(c),C=Math.cos(w),_=(-s-2*S*C)/(3*o),T=(f=(-s+S*(C+cl*Math.sin(w)))/(3*o),(-s+S*(C-cl*Math.sin(w)))/(3*o));_>=0&&_<=1&&(r[m++]=_),f>=0&&f<=1&&(r[m++]=f),T>=0&&T<=1&&(r[m++]=T)}}return m}function bl(e,t,n,i,a){var r=6*n-12*t+6*e,o=9*t+3*i-3*e-9*n,s=3*t-3*e,l=0;if(gl(o)){if(fl(r))(c=-s/r)>=0&&c<=1&&(a[l++]=c)}else{var p=r*r-4*o*s;if(gl(p))a[0]=-r/(2*o);else if(p>0){var c,d=sl(p),u=(-r-d)/(2*o);(c=(-r+d)/(2*o))>=0&&c<=1&&(a[l++]=c),u>=0&&u<=1&&(a[l++]=u)}}return l}function wl(e,t,n,i,a,r){var o=(t-e)*a+e,s=(n-t)*a+t,l=(i-n)*a+n,p=(s-o)*a+o,c=(l-s)*a+s,d=(c-p)*a+p;r[0]=e,r[1]=o,r[2]=p,r[3]=d,r[4]=d,r[5]=c,r[6]=l,r[7]=i}function Sl(e,t,n,i,a,r,o,s,l,p,c){var d,u,m,h,g,f=.005,y=1/0;ul[0]=l,ul[1]=p;for(var v=0;v<1;v+=.05)ml[0]=yl(e,n,a,o,v),ml[1]=yl(t,i,r,s,v),(h=Uo(ul,ml))=0&&h=0&&f=1?1:xl(0,i,r,1,e,s)&&yl(0,a,o,1,s[0])}}}var Ol=function(){function e(e){this._inited=!1,this._startTime=0,this._pausedTime=0,this._paused=!1,this._life=e.life||1e3,this._delay=e.delay||0,this.loop=e.loop||!1,this.onframe=e.onframe||Mo,this.ondestroy=e.ondestroy||Mo,this.onrestart=e.onrestart||Mo,e.easing&&this.setEasing(e.easing)}return e.prototype.step=function(e,t){if(this._inited||(this._startTime=e+this._delay,this._inited=!0),!this._paused){var n=this._life,i=e-this._startTime-this._pausedTime,a=i/n;a<0&&(a=0),a=Math.min(a,1);var r=this.easingFunc,o=r?r(a):a;if(this.onframe(o),1===a){if(!this.loop)return!0;var s=i%n;this._startTime=e-s,this._pausedTime=0,this.onrestart()}return!1}this._pausedTime+=t},e.prototype.pause=function(){this._paused=!0},e.prototype.resume=function(){this._paused=!1},e.prototype.setEasing=function(e){this.easing=e,this.easingFunc=Zr(e)?e:rl[e]||Dl(e)},e}(),Al=function(e){this.value=e},Fl=function(){function e(){this._len=0}return e.prototype.insert=function(e){var t=new Al(e);return this.insertEntry(t),t},e.prototype.insertEntry=function(e){this.head?(this.tail.next=e,e.prev=this.tail,e.next=null,this.tail=e):this.head=this.tail=e,this._len++},e.prototype.remove=function(e){var t=e.prev,n=e.next;t?t.next=n:this.head=n,n?n.prev=t:this.tail=t,e.next=e.prev=null,this._len--},e.prototype.len=function(){return this._len},e.prototype.clear=function(){this.head=this.tail=null,this._len=0},e}(),Rl=function(){function e(e){this._list=new Fl,this._maxSize=10,this._map={},this._maxSize=e}return e.prototype.put=function(e,t){var n=this._list,i=this._map,a=null;if(null==i[e]){var r=n.len(),o=this._lastRemovedEntry;if(r>=this._maxSize&&r>0){var s=n.head;n.remove(s),delete i[s.key],a=s.value,this._lastRemovedEntry=s}o?o.value=t:o=new Al(t),o.key=e,n.insertEntry(o),i[e]=o}return a},e.prototype.get=function(e){var t=this._map[e],n=this._list;if(null!=t)return t!==n.tail&&(n.remove(t),n.insertEntry(t)),t.value},e.prototype.clear=function(){this._list.clear(),this._map={}},e.prototype.len=function(){return this._list.len()},e}(),Bl={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function Nl(e){return(e=Math.round(e))<0?0:e>255?255:e}function Ll(e){return e<0?0:e>1?1:e}function Vl(e){var t=e;return t.length&&"%"===t.charAt(t.length-1)?Nl(parseFloat(t)/100*255):Nl(parseInt(t,10))}function ql(e){var t=e;return t.length&&"%"===t.charAt(t.length-1)?Ll(parseFloat(t)/100):Ll(parseFloat(t))}function Gl(e,t,n){return n<0?n+=1:n>1&&(n-=1),6*n<1?e+(t-e)*n*6:2*n<1?t:3*n<2?e+(t-e)*(2/3-n)*6:e}function zl(e,t,n){return e+(t-e)*n}function Ul(e,t,n,i,a){return e[0]=t,e[1]=n,e[2]=i,e[3]=a,e}function jl(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}var Hl=new Rl(20),Wl=null;function $l(e,t){Wl&&jl(Wl,t),Wl=Hl.put(e,Wl||t.slice())}function Kl(e,t){if(e){t=t||[];var n=Hl.get(e);if(n)return jl(t,n);var i=(e+="").replace(/ /g,"").toLowerCase();if(i in Bl)return jl(t,Bl[i]),$l(e,t),t;var a,r=i.length;if("#"===i.charAt(0))return 4===r||5===r?(a=parseInt(i.slice(1,4),16))>=0&&a<=4095?(Ul(t,(3840&a)>>4|(3840&a)>>8,240&a|(240&a)>>4,15&a|(15&a)<<4,5===r?parseInt(i.slice(4),16)/15:1),$l(e,t),t):void Ul(t,0,0,0,1):7===r||9===r?(a=parseInt(i.slice(1,7),16))>=0&&a<=16777215?(Ul(t,(16711680&a)>>16,(65280&a)>>8,255&a,9===r?parseInt(i.slice(7),16)/255:1),$l(e,t),t):void Ul(t,0,0,0,1):void 0;var o=i.indexOf("("),s=i.indexOf(")");if(-1!==o&&s+1===r){var l=i.substr(0,o),p=i.substr(o+1,s-(o+1)).split(","),c=1;switch(l){case"rgba":if(4!==p.length)return 3===p.length?Ul(t,+p[0],+p[1],+p[2],1):Ul(t,0,0,0,1);c=ql(p.pop());case"rgb":return p.length>=3?(Ul(t,Vl(p[0]),Vl(p[1]),Vl(p[2]),3===p.length?c:ql(p[3])),$l(e,t),t):void Ul(t,0,0,0,1);case"hsla":return 4!==p.length?void Ul(t,0,0,0,1):(p[3]=ql(p[3]),Yl(p,t),$l(e,t),t);case"hsl":return 3!==p.length?void Ul(t,0,0,0,1):(Yl(p,t),$l(e,t),t);default:return}}Ul(t,0,0,0,1)}}function Yl(e,t){var n=(parseFloat(e[0])%360+360)%360/360,i=ql(e[1]),a=ql(e[2]),r=a<=.5?a*(i+1):a+i-a*i,o=2*a-r;return Ul(t=t||[],Nl(255*Gl(o,r,n+1/3)),Nl(255*Gl(o,r,n)),Nl(255*Gl(o,r,n-1/3)),1),4===e.length&&(t[3]=e[3]),t}function Xl(e,t){var n=Kl(e);if(n){for(var i=0;i<3;i++)n[i]=t<0?n[i]*(1-t)|0:(255-n[i])*t+n[i]|0,n[i]>255?n[i]=255:n[i]<0&&(n[i]=0);return tp(n,4===n.length?"rgba":"rgb")}}function Zl(e,t,n){if(t&&t.length&&e>=0&&e<=1){n=n||[];var i=e*(t.length-1),a=Math.floor(i),r=Math.ceil(i),o=t[a],s=t[r],l=i-a;return n[0]=Nl(zl(o[0],s[0],l)),n[1]=Nl(zl(o[1],s[1],l)),n[2]=Nl(zl(o[2],s[2],l)),n[3]=Ll(zl(o[3],s[3],l)),n}}function Ql(e,t,n){if(t&&t.length&&e>=0&&e<=1){var i=e*(t.length-1),a=Math.floor(i),r=Math.ceil(i),o=Kl(t[a]),s=Kl(t[r]),l=i-a,p=tp([Nl(zl(o[0],s[0],l)),Nl(zl(o[1],s[1],l)),Nl(zl(o[2],s[2],l)),Ll(zl(o[3],s[3],l))],"rgba");return n?{color:p,leftIndex:a,rightIndex:r,value:i}:p}}function Jl(e,t,n,i){var a=Kl(e);if(e)return a=function(e){if(e){var t,n,i=e[0]/255,a=e[1]/255,r=e[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o,p=(s+o)/2;if(0===l)t=0,n=0;else{n=p<.5?l/(s+o):l/(2-s-o);var c=((s-i)/6+l/2)/l,d=((s-a)/6+l/2)/l,u=((s-r)/6+l/2)/l;i===s?t=u-d:a===s?t=1/3+c-u:r===s&&(t=2/3+d-c),t<0&&(t+=1),t>1&&(t-=1)}var m=[360*t,n,p];return null!=e[3]&&m.push(e[3]),m}}(a),null!=t&&(a[0]=function(e){return(e=Math.round(e))<0?0:e>360?360:e}(t)),null!=n&&(a[1]=ql(n)),null!=i&&(a[2]=ql(i)),tp(Yl(a),"rgba")}function ep(e,t){var n=Kl(e);if(n&&null!=t)return n[3]=Ll(t),tp(n,"rgba")}function tp(e,t){if(e&&e.length){var n=e[0]+","+e[1]+","+e[2];return"rgba"!==t&&"hsva"!==t&&"hsla"!==t||(n+=","+e[3]),t+"("+n+")"}}function np(e,t){var n=Kl(e);return n?(.299*n[0]+.587*n[1]+.114*n[2])*n[3]/255+(1-n[3])*t:0}var ip=new Rl(100);function ap(e){if(Qr(e)){var t=ip.get(e);return t||(t=Xl(e,-.1),ip.put(e,t)),t}if(ro(e)){var n=Br({},e);return n.colorStops=Ur(e.colorStops,(function(e){return{offset:e.offset,color:Xl(e.color,-.1)}})),n}return e}var rp=Math.round;function op(e){var t;if(e&&"transparent"!==e){if("string"==typeof e&&e.indexOf("rgba")>-1){var n=Kl(e);n&&(e="rgb("+n[0]+","+n[1]+","+n[2]+")",t=n[3])}}else e="none";return{color:e,opacity:null==t?1:t}}var sp=1e-4;function lp(e){return e-1e-4}function pp(e){return rp(1e3*e)/1e3}function cp(e){return rp(1e4*e)/1e4}var dp={left:"start",right:"end",center:"middle",middle:"middle"};function up(e){return e&&!!e.image}function mp(e){return up(e)||function(e){return e&&!!e.svgElement}(e)}function hp(e){return"linear"===e.type}function gp(e){return"radial"===e.type}function fp(e){return e&&("linear"===e.type||"radial"===e.type)}function yp(e){return"url(#"+e+")"}function vp(e){var t=e.getGlobalScale(),n=Math.max(t[0],t[1]);return Math.max(Math.ceil(Math.log(n)/Math.log(10)),1)}function xp(e){var t=e.x||0,n=e.y||0,i=(e.rotation||0)*ko,a=co(e.scaleX,1),r=co(e.scaleY,1),o=e.skewX||0,s=e.skewY||0,l=[];return(t||n)&&l.push("translate("+t+"px,"+n+"px)"),i&&l.push("rotate("+i+")"),1===a&&1===r||l.push("scale("+a+","+r+")"),(o||s)&&l.push("skew("+rp(o*ko)+"deg, "+rp(s*ko)+"deg)"),l.join(" ")}var bp=ur.hasGlobalWindow&&Zr(window.btoa)?function(e){return window.btoa(unescape(encodeURIComponent(e)))}:"undefined"!=typeof Buffer?function(e){return Buffer.from(e).toString("base64")}:function(e){return null},wp=Array.prototype.slice;function Sp(e,t,n){return(t-e)*n+e}function Cp(e,t,n,i){for(var a=t.length,r=0;ri?t:e,r=Math.min(n,i),o=a[r-1]||{color:[0,0,0,0],offset:0},s=r;so)i.length=o;else for(var s=r;s=1},e.prototype.getAdditiveTrack=function(){return this._additiveTrack},e.prototype.addKeyframe=function(e,t,n){this._needsSort=!0;var i=this.keyframes,a=i.length,r=!1,o=6,s=t;if(Gr(t)){var l=function(e){return Gr(e&&e[0])?2:1}(t);o=l,(1===l&&!eo(t[0])||2===l&&!eo(t[0][0]))&&(r=!0)}else if(eo(t)&&!lo(t))o=0;else if(Qr(t))if(isNaN(+t)){var p=Kl(t);p&&(s=p,o=3)}else o=0;else if(ro(t)){var c=Br({},s);c.colorStops=Ur(t.colorStops,(function(e){return{offset:e.offset,color:Kl(e.color)}})),hp(t)?o=4:gp(t)&&(o=5),s=c}0===a?this.valType=o:o===this.valType&&6!==o||(r=!0),this.discrete=this.discrete||r;var d={time:e,value:s,rawValue:t,percent:0};return n&&(d.easing=n,d.easingFunc=Zr(n)?n:rl[n]||Dl(n)),i.push(d),d},e.prototype.prepare=function(e,t){var n=this.keyframes;this._needsSort&&n.sort((function(e,t){return e.time-t.time}));for(var i=this.valType,a=n.length,r=n[a-1],o=this.discrete,s=Dp(i),l=Pp(i),p=0;p=0&&!(l[n].percent<=t);n--);n=m(n,p-2)}else{for(n=u;nt);n++);n=m(n-1,p-2)}a=l[n+1],i=l[n]}if(i&&a){this._lastFr=n,this._lastFrP=t;var h=a.percent-i.percent,g=0===h?1:m((t-i.percent)/h,1);a.easingFunc&&(g=a.easingFunc(g));var f=r?this._additiveValue:d?Op:e[c];if(!Dp(s)&&!d||f||(f=this._additiveValue=[]),this.discrete)e[c]=g<1?i.rawValue:a.rawValue;else if(Dp(s))1===s?Cp(f,i[o],a[o],g):function(e,t,n,i){for(var a=t.length,r=a&&t[0].length,o=0;o0&&s.addKeyframe(0,Mp(l),i),this._trackKeys.push(o)}s.addKeyframe(e,Mp(t[o]),i)}return this._maxTime=Math.max(this._maxTime,e),this},e.prototype.pause=function(){this._clip.pause(),this._paused=!0},e.prototype.resume=function(){this._clip.resume(),this._paused=!1},e.prototype.isPaused=function(){return!!this._paused},e.prototype.duration=function(e){return this._maxTime=e,this._force=!0,this},e.prototype._doneCallback=function(){this._setTracksFinished(),this._clip=null;var e=this._doneCbs;if(e)for(var t=e.length,n=0;n0)){this._started=1;for(var t=this,n=[],i=this._maxTime||0,a=0;a1){var o=r.pop();a.addKeyframe(o.time,e[i]),a.prepare(this._maxTime,a.getAdditiveTrack())}}}},e}();function Rp(){return(new Date).getTime()}var Bp,Np,Lp=function(e){function t(t){var n=e.call(this)||this;return n._running=!1,n._time=0,n._pausedTime=0,n._pauseStart=0,n._paused=!1,t=t||{},n.stage=t.stage||{},n}return We(t,e),t.prototype.addClip=function(e){e.animation&&this.removeClip(e),this._head?(this._tail.next=e,e.prev=this._tail,e.next=null,this._tail=e):this._head=this._tail=e,e.animation=this},t.prototype.addAnimator=function(e){e.animation=this;var t=e.getClip();t&&this.addClip(t)},t.prototype.removeClip=function(e){if(e.animation){var t=e.prev,n=e.next;t?t.next=n:this._head=n,n?n.prev=t:this._tail=t,e.next=e.prev=e.animation=null}},t.prototype.removeAnimator=function(e){var t=e.getClip();t&&this.removeClip(t),e.animation=null},t.prototype.update=function(e){for(var t=Rp()-this._pausedTime,n=t-this._time,i=this._head;i;){var a=i.next;i.step(t,n)?(i.ondestroy(),this.removeClip(i),i=a):i=a}this._time=t,e||(this.trigger("frame",n),this.stage.update&&this.stage.update())},t.prototype._startLoop=function(){var e=this;this._running=!0,al((function t(){e._running&&(al(t),!e._paused&&e.update())}))},t.prototype.start=function(){this._running||(this._time=Rp(),this._pausedTime=0,this._startLoop())},t.prototype.stop=function(){this._running=!1},t.prototype.pause=function(){this._paused||(this._pauseStart=Rp(),this._paused=!0)},t.prototype.resume=function(){this._paused&&(this._pausedTime+=Rp()-this._pauseStart,this._paused=!1)},t.prototype.clear=function(){for(var e=this._head;e;){var t=e.next;e.prev=e.next=e.animation=null,e=t}this._head=this._tail=null},t.prototype.isFinished=function(){return null==this._head},t.prototype.animate=function(e,t){t=t||{},this.start();var n=new Fp(e,t.loop);return this.addAnimator(n),n},t}(Xo),Vp=ur.domSupported,qp=(Np={pointerdown:1,pointerup:1,pointermove:1,pointerout:1},{mouse:Bp=["click","dblclick","mousewheel","wheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],touch:["touchstart","touchend","touchmove"],pointer:Ur(Bp,(function(e){var t=e.replace("mouse","pointer");return Np.hasOwnProperty(t)?t:e}))}),Gp=["mousemove","mouseup"],zp=["pointermove","pointerup"],Up=!1;function jp(e){var t=e.pointerType;return"pen"===t||"touch"===t}function Hp(e){e&&(e.zrByTouch=!0)}function Wp(e,t){for(var n=t,i=!1;n&&9!==n.nodeType&&!(i=n.domBelongToZr||n!==t&&n===e.painterRoot);)n=n.parentNode;return i}var $p=function(e,t){this.stopPropagation=Mo,this.stopImmediatePropagation=Mo,this.preventDefault=Mo,this.type=t.type,this.target=this.currentTarget=e.dom,this.pointerType=t.pointerType,this.clientX=t.clientX,this.clientY=t.clientY},Kp={mousedown:function(e){e=ms(this.dom,e),this.__mayPointerCapture=[e.zrX,e.zrY],this.trigger("mousedown",e)},mousemove:function(e){e=ms(this.dom,e);var t=this.__mayPointerCapture;!t||e.zrX===t[0]&&e.zrY===t[1]||this.__togglePointerCapture(!0),this.trigger("mousemove",e)},mouseup:function(e){e=ms(this.dom,e),this.__togglePointerCapture(!1),this.trigger("mouseup",e)},mouseout:function(e){Wp(this,(e=ms(this.dom,e)).toElement||e.relatedTarget)||(this.__pointerCapturing&&(e.zrEventControl="no_globalout"),this.trigger("mouseout",e))},wheel:function(e){Up=!0,e=ms(this.dom,e),this.trigger("mousewheel",e)},mousewheel:function(e){Up||(e=ms(this.dom,e),this.trigger("mousewheel",e))},touchstart:function(e){Hp(e=ms(this.dom,e)),this.__lastTouchMoment=new Date,this.handler.processGesture(e,"start"),Kp.mousemove.call(this,e),Kp.mousedown.call(this,e)},touchmove:function(e){Hp(e=ms(this.dom,e)),this.handler.processGesture(e,"change"),Kp.mousemove.call(this,e)},touchend:function(e){Hp(e=ms(this.dom,e)),this.handler.processGesture(e,"end"),Kp.mouseup.call(this,e),+new Date-+this.__lastTouchMoment<300&&Kp.click.call(this,e)},pointerdown:function(e){Kp.mousedown.call(this,e)},pointermove:function(e){jp(e)||Kp.mousemove.call(this,e)},pointerup:function(e){Kp.mouseup.call(this,e)},pointerout:function(e){jp(e)||Kp.mouseout.call(this,e)}};zr(["click","dblclick","contextmenu"],(function(e){Kp[e]=function(t){t=ms(this.dom,t),this.trigger(e,t)}}));var Yp={pointermove:function(e){jp(e)||Yp.mousemove.call(this,e)},pointerup:function(e){Yp.mouseup.call(this,e)},mousemove:function(e){this.trigger("mousemove",e)},mouseup:function(e){var t=this.__pointerCapturing;this.__togglePointerCapture(!1),this.trigger("mouseup",e),t&&(e.zrEventControl="only_globalout",this.trigger("mouseout",e))}};function Xp(e,t){var n=t.domHandlers;ur.pointerEventsSupported?zr(qp.pointer,(function(i){Qp(t,i,(function(t){n[i].call(e,t)}))})):(ur.touchEventsSupported&&zr(qp.touch,(function(i){Qp(t,i,(function(a){n[i].call(e,a),function(e){e.touching=!0,null!=e.touchTimer&&(clearTimeout(e.touchTimer),e.touchTimer=null),e.touchTimer=setTimeout((function(){e.touching=!1,e.touchTimer=null}),700)}(t)}))})),zr(qp.mouse,(function(i){Qp(t,i,(function(a){a=us(a),t.touching||n[i].call(e,a)}))})))}function Zp(e,t){function n(n){Qp(t,n,(function(i){i=us(i),Wp(e,i.target)||(i=function(e,t){return ms(e.dom,new $p(e,t),!0)}(e,i),t.domHandlers[n].call(e,i))}),{capture:!0})}ur.pointerEventsSupported?zr(zp,n):ur.touchEventsSupported||zr(Gp,n)}function Qp(e,t,n,i){e.mounted[t]=n,e.listenerOpts[t]=i,hs(e.domTarget,t,n,i)}function Jp(e){var t,n,i,a,r=e.mounted;for(var o in r)r.hasOwnProperty(o)&&(t=e.domTarget,n=o,i=r[o],a=e.listenerOpts[o],t.removeEventListener(n,i,a));e.mounted={}}var ec=function(e,t){this.mounted={},this.listenerOpts={},this.touching=!1,this.domTarget=e,this.domHandlers=t},tc=function(e){function t(t,n){var i=e.call(this)||this;return i.__pointerCapturing=!1,i.dom=t,i.painterRoot=n,i._localHandlerScope=new ec(t,Kp),Vp&&(i._globalHandlerScope=new ec(document,Yp)),Xp(i,i._localHandlerScope),i}return We(t,e),t.prototype.dispose=function(){Jp(this._localHandlerScope),Vp&&Jp(this._globalHandlerScope)},t.prototype.setCursor=function(e){this.dom.style&&(this.dom.style.cursor=e||"default")},t.prototype.__togglePointerCapture=function(e){if(this.__mayPointerCapture=null,Vp&&+this.__pointerCapturing^+e){this.__pointerCapturing=e;var t=this._globalHandlerScope;e?Zp(this,t):Jp(t)}},t}(Xo),nc=1;ur.hasGlobalWindow&&(nc=Math.max(window.devicePixelRatio||window.screen&&window.screen.deviceXDPI/window.screen.logicalXDPI||1,1));var ic=nc,ac="#333",rc="#ccc",oc=bs,sc=5e-5;function lc(e){return e>sc||e<-5e-5}var pc=[],cc=[],dc=[1,0,0,1,0,0],uc=Math.abs,mc=function(){function e(){}return e.prototype.getLocalTransform=function(t){return e.getLocalTransform(this,t)},e.prototype.setPosition=function(e){this.x=e[0],this.y=e[1]},e.prototype.setScale=function(e){this.scaleX=e[0],this.scaleY=e[1]},e.prototype.setSkew=function(e){this.skewX=e[0],this.skewY=e[1]},e.prototype.setOrigin=function(e){this.originX=e[0],this.originY=e[1]},e.prototype.needLocalTransform=function(){return lc(this.rotation)||lc(this.x)||lc(this.y)||lc(this.scaleX-1)||lc(this.scaleY-1)||lc(this.skewX)||lc(this.skewY)},e.prototype.updateTransform=function(){var e=this.parent&&this.parent.transform,t=this.needLocalTransform(),n=this.transform;t||e?(n=n||[1,0,0,1,0,0],t?this.getLocalTransform(n):oc(n),e&&(t?Ss(n,e,n):ws(n,e)),this.transform=n,this._resolveGlobalScaleRatio(n)):n&&(oc(n),this.invTransform=null)},e.prototype._resolveGlobalScaleRatio=function(e){var t=this.globalScaleRatio;if(null!=t&&1!==t){this.getGlobalScale(pc);var n=pc[0]<0?-1:1,i=pc[1]<0?-1:1,a=((pc[0]-n)*t+n)/pc[0]||0,r=((pc[1]-i)*t+i)/pc[1]||0;e[0]*=a,e[1]*=a,e[2]*=r,e[3]*=r}this.invTransform=this.invTransform||[1,0,0,1,0,0],Is(this.invTransform,e)},e.prototype.getComputedTransform=function(){for(var e=this,t=[];e;)t.push(e),e=e.parent;for(;e=t.pop();)e.updateTransform();return this.transform},e.prototype.setLocalTransform=function(e){if(e){var t=e[0]*e[0]+e[1]*e[1],n=e[2]*e[2]+e[3]*e[3],i=Math.atan2(e[1],e[0]),a=Math.PI/2+i-Math.atan2(e[3],e[2]);n=Math.sqrt(n)*Math.cos(a),t=Math.sqrt(t),this.skewX=a,this.skewY=0,this.rotation=-i,this.x=+e[4],this.y=+e[5],this.scaleX=t,this.scaleY=n,this.originX=0,this.originY=0}},e.prototype.decomposeTransform=function(){if(this.transform){var e=this.parent,t=this.transform;e&&e.transform&&(e.invTransform=e.invTransform||[1,0,0,1,0,0],Ss(cc,e.invTransform,t),t=cc);var n=this.originX,i=this.originY;(n||i)&&(dc[4]=n,dc[5]=i,Ss(cc,t,dc),cc[4]-=n,cc[5]-=i,t=cc),this.setLocalTransform(t)}},e.prototype.getGlobalScale=function(e){var t=this.transform;return e=e||[],t?(e[0]=Math.sqrt(t[0]*t[0]+t[1]*t[1]),e[1]=Math.sqrt(t[2]*t[2]+t[3]*t[3]),t[0]<0&&(e[0]=-e[0]),t[3]<0&&(e[1]=-e[1]),e):(e[0]=1,e[1]=1,e)},e.prototype.transformCoordToLocal=function(e,t){var n=[e,t],i=this.invTransform;return i&&Ho(n,n,i),n},e.prototype.transformCoordToGlobal=function(e,t){var n=[e,t],i=this.transform;return i&&Ho(n,n,i),n},e.prototype.getLineScale=function(){var e=this.transform;return e&&uc(e[0]-1)>1e-10&&uc(e[3]-1)>1e-10?Math.sqrt(uc(e[0]*e[3]-e[2]*e[1])):1},e.prototype.copyTransform=function(e){gc(this,e)},e.getLocalTransform=function(e,t){t=t||[];var n=e.originX||0,i=e.originY||0,a=e.scaleX,r=e.scaleY,o=e.anchorX,s=e.anchorY,l=e.rotation||0,p=e.x,c=e.y,d=e.skewX?Math.tan(e.skewX):0,u=e.skewY?Math.tan(-e.skewY):0;if(n||i||o||s){var m=n+o,h=i+s;t[4]=-m*a-d*h*r,t[5]=-h*r-u*m*a}else t[4]=t[5]=0;return t[0]=a,t[3]=r,t[1]=u*a,t[2]=d*r,l&&_s(t,t,l),t[4]+=n+p,t[5]+=i+c,t},e.initDefaultProps=function(){var t=e.prototype;t.scaleX=t.scaleY=t.globalScaleRatio=1,t.x=t.y=t.originX=t.originY=t.skewX=t.skewY=t.rotation=t.anchorX=t.anchorY=0}(),e}(),hc=["x","y","originX","originY","anchorX","anchorY","rotation","scaleX","scaleY","skewX","skewY"];function gc(e,t){for(var n=0;n=0?parseFloat(e)/100*t:parseFloat(e):e}function _c(e,t,n){var i=t.position||"inside",a=null!=t.distance?t.distance:5,r=n.height,o=n.width,s=r/2,l=n.x,p=n.y,c="left",d="top";if(i instanceof Array)l+=Cc(i[0],n.width),p+=Cc(i[1],n.height),c=null,d=null;else switch(i){case"left":l-=a,p+=s,c="right",d="middle";break;case"right":l+=a+o,p+=s,d="middle";break;case"top":l+=o/2,p-=a,c="center",d="bottom";break;case"bottom":l+=o/2,p+=r+a,c="center";break;case"inside":l+=o/2,p+=s,c="center",d="middle";break;case"insideLeft":l+=a,p+=s,d="middle";break;case"insideRight":l+=o-a,p+=s,c="right",d="middle";break;case"insideTop":l+=o/2,p+=a,c="center";break;case"insideBottom":l+=o/2,p+=r-a,c="center",d="bottom";break;case"insideTopLeft":l+=a,p+=a;break;case"insideTopRight":l+=o-a,p+=a,c="right";break;case"insideBottomLeft":l+=a,p+=r-a,d="bottom";break;case"insideBottomRight":l+=o-a,p+=r-a,c="right",d="bottom"}return(e=e||{}).x=l,e.y=p,e.align=c,e.verticalAlign=d,e}var Tc="__zr_normal__",Ic=hc.concat(["ignore"]),Ec=jr(hc,(function(e,t){return e[t]=!0,e}),{ignore:!1}),Mc={},kc=new Ns(0,0,0,0),Pc=function(){function e(e){this.id=Dr(),this.animators=[],this.currentStates=[],this.states={},this._init(e)}return e.prototype._init=function(e){this.attr(e)},e.prototype.drift=function(e,t,n){switch(this.draggable){case"horizontal":t=0;break;case"vertical":e=0}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=e,i[5]+=t,this.decomposeTransform(),this.markRedraw()},e.prototype.beforeUpdate=function(){},e.prototype.afterUpdate=function(){},e.prototype.update=function(){this.updateTransform(),this.__dirty&&this.updateInnerText()},e.prototype.updateInnerText=function(e){var t=this._textContent;if(t&&(!t.ignore||e)){this.textConfig||(this.textConfig={});var n=this.textConfig,i=n.local,a=t.innerTransformable,r=void 0,o=void 0,s=!1;a.parent=i?this:null;var l=!1;if(a.copyTransform(t),null!=n.position){var p=kc;n.layoutRect?p.copy(n.layoutRect):p.copy(this.getBoundingRect()),i||p.applyTransform(this.transform),this.calculateTextPosition?this.calculateTextPosition(Mc,n,p):_c(Mc,n,p),a.x=Mc.x,a.y=Mc.y,r=Mc.align,o=Mc.verticalAlign;var c=n.origin;if(c&&null!=n.rotation){var d=void 0,u=void 0;"center"===c?(d=.5*p.width,u=.5*p.height):(d=Cc(c[0],p.width),u=Cc(c[1],p.height)),l=!0,a.originX=-a.x+d+(i?0:p.x),a.originY=-a.y+u+(i?0:p.y)}}null!=n.rotation&&(a.rotation=n.rotation);var m=n.offset;m&&(a.x+=m[0],a.y+=m[1],l||(a.originX=-m[0],a.originY=-m[1]));var h=null==n.inside?"string"==typeof n.position&&n.position.indexOf("inside")>=0:n.inside,g=this._innerTextDefaultStyle||(this._innerTextDefaultStyle={}),f=void 0,y=void 0,v=void 0;h&&this.canBeInsideText()?(f=n.insideFill,y=n.insideStroke,null!=f&&"auto"!==f||(f=this.getInsideTextFill()),null!=y&&"auto"!==y||(y=this.getInsideTextStroke(f),v=!0)):(f=n.outsideFill,y=n.outsideStroke,null!=f&&"auto"!==f||(f=this.getOutsideFill()),null!=y&&"auto"!==y||(y=this.getOutsideStroke(f),v=!0)),(f=f||"#000")===g.fill&&y===g.stroke&&v===g.autoStroke&&r===g.align&&o===g.verticalAlign||(s=!0,g.fill=f,g.stroke=y,g.autoStroke=v,g.align=r,g.verticalAlign=o,t.setDefaultTextStyle(g)),t.__dirty|=1,s&&t.dirtyStyle(!0)}},e.prototype.canBeInsideText=function(){return!0},e.prototype.getInsideTextFill=function(){return"#fff"},e.prototype.getInsideTextStroke=function(e){return"#000"},e.prototype.getOutsideFill=function(){return this.__zr&&this.__zr.isDarkMode()?rc:ac},e.prototype.getOutsideStroke=function(e){var t=this.__zr&&this.__zr.getBackgroundColor(),n="string"==typeof t&&Kl(t);n||(n=[255,255,255,1]);for(var i=n[3],a=this.__zr.isDarkMode(),r=0;r<3;r++)n[r]=n[r]*i+(a?0:255)*(1-i);return n[3]=1,tp(n,"rgba")},e.prototype.traverse=function(e,t){},e.prototype.attrKV=function(e,t){"textConfig"===e?this.setTextConfig(t):"textContent"===e?this.setTextContent(t):"clipPath"===e?this.setClipPath(t):"extra"===e?(this.extra=this.extra||{},Br(this.extra,t)):this[e]=t},e.prototype.hide=function(){this.ignore=!0,this.markRedraw()},e.prototype.show=function(){this.ignore=!1,this.markRedraw()},e.prototype.attr=function(e,t){if("string"==typeof e)this.attrKV(e,t);else if(to(e))for(var n=$r(e),i=0;i0},e.prototype.getState=function(e){return this.states[e]},e.prototype.ensureState=function(e){var t=this.states;return t[e]||(t[e]={}),t[e]},e.prototype.clearStates=function(e){this.useState(Tc,!1,e)},e.prototype.useState=function(e,t,n,i){var a=e===Tc;if(this.hasState()||!a){var r=this.currentStates,o=this.stateTransition;if(!(Lr(r,e)>=0)||!t&&1!==r.length){var s;if(this.stateProxy&&!a&&(s=this.stateProxy(e)),s||(s=this.states&&this.states[e]),s||a){a||this.saveCurrentToNormalState(s);var l=!!(s&&s.hoverLayer||i);l&&this._toggleHoverLayerFlag(!0),this._applyStateObj(e,s,this._normalState,t,!n&&!this.__inHover&&o&&o.duration>0,o);var p=this._textContent,c=this._textGuide;return p&&p.useState(e,t,n,l),c&&c.useState(e,t,n,l),a?(this.currentStates=[],this._normalState={}):t?this.currentStates.push(e):this.currentStates=[e],this._updateAnimationTargets(),this.markRedraw(),!l&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2),s}Or("State "+e+" not exists.")}}},e.prototype.useStates=function(e,t,n){if(e.length){var i=[],a=this.currentStates,r=e.length,o=r===a.length;if(o)for(var s=0;s0,m);var h=this._textContent,g=this._textGuide;h&&h.useStates(e,t,d),g&&g.useStates(e,t,d),this._updateAnimationTargets(),this.currentStates=e.slice(),this.markRedraw(),!d&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2)}else this.clearStates()},e.prototype.isSilent=function(){for(var e=this.silent,t=this.parent;!e&&t;){if(t.silent){e=!0;break}t=t.parent}return e},e.prototype._updateAnimationTargets=function(){for(var e=0;e=0){var n=this.currentStates.slice();n.splice(t,1),this.useStates(n)}},e.prototype.replaceState=function(e,t,n){var i=this.currentStates.slice(),a=Lr(i,e),r=Lr(i,t)>=0;a>=0?r?i.splice(a,1):i[a]=t:n&&!r&&i.push(t),this.useStates(i)},e.prototype.toggleState=function(e,t){t?this.useState(e,!0):this.removeState(e)},e.prototype._mergeStates=function(e){for(var t,n={},i=0;i=0&&t.splice(n,1)})),this.animators.push(e),n&&n.animation.addAnimator(e),n&&n.wakeUp()},e.prototype.updateDuringAnimation=function(e){this.markRedraw()},e.prototype.stopAnimation=function(e,t){for(var n=this.animators,i=n.length,a=[],r=0;r0&&n.during&&r[0].during((function(e,t){n.during(t)}));for(var u=0;u0||a.force&&!o.length){var S,C=void 0,_=void 0,T=void 0;if(s){_={},u&&(C={});for(b=0;b=0&&(n.splice(i,0,e),this._doAdd(e))}return this},t.prototype.replace=function(e,t){var n=Lr(this._children,e);return n>=0&&this.replaceAt(t,n),this},t.prototype.replaceAt=function(e,t){var n=this._children,i=n[t];if(e&&e!==this&&e.parent!==this&&e!==i){n[t]=e,i.parent=null;var a=this.__zr;a&&i.removeSelfFromZr(a),this._doAdd(e)}return this},t.prototype._doAdd=function(e){e.parent&&e.parent.remove(e),e.parent=this;var t=this.__zr;t&&t!==e.__zr&&e.addSelfToZr(t),t&&t.refresh()},t.prototype.remove=function(e){var t=this.__zr,n=this._children,i=Lr(n,e);return i<0||(n.splice(i,1),e.parent=null,t&&e.removeSelfFromZr(t),t&&t.refresh()),this},t.prototype.removeAll=function(){for(var e=this._children,t=this.__zr,n=0;n0&&(this._stillFrameAccum++,this._stillFrameAccum>this._sleepAfterStill&&this.animation.stop())},e.prototype.setSleepAfterStill=function(e){this._sleepAfterStill=e},e.prototype.wakeUp=function(){this._disposed||(this.animation.start(),this._stillFrameAccum=0)},e.prototype.refreshHover=function(){this._needsRefreshHover=!0},e.prototype.refreshHoverImmediately=function(){this._disposed||(this._needsRefreshHover=!1,this.painter.refreshHover&&"canvas"===this.painter.getType()&&this.painter.refreshHover())},e.prototype.resize=function(e){this._disposed||(e=e||{},this.painter.resize(e.width,e.height),this.handler.resize())},e.prototype.clearAnimation=function(){this._disposed||this.animation.clear()},e.prototype.getWidth=function(){if(!this._disposed)return this.painter.getWidth()},e.prototype.getHeight=function(){if(!this._disposed)return this.painter.getHeight()},e.prototype.setCursorStyle=function(e){this._disposed||this.handler.setCursorStyle(e)},e.prototype.findHover=function(e,t){if(!this._disposed)return this.handler.findHover(e,t)},e.prototype.on=function(e,t,n){return this._disposed||this.handler.on(e,t,n),this},e.prototype.off=function(e,t){this._disposed||this.handler.off(e,t)},e.prototype.trigger=function(e,t){this._disposed||this.handler.trigger(e,t)},e.prototype.clear=function(){if(!this._disposed){for(var e=this.storage.getRoots(),t=0;t0){if(e<=a)return o;if(e>=r)return s}else{if(e>=a)return o;if(e<=r)return s}else{if(e===a)return o;if(e===r)return s}return(e-a)/l*p+o}function Wc(e,t){switch(e){case"center":case"middle":e="50%";break;case"left":case"top":e="0%";break;case"right":case"bottom":e="100%"}return Qr(e)?(n=e,n.replace(/^\s+|\s+$/g,"")).match(/%$/)?parseFloat(e)/100*t:parseFloat(e):null==e?NaN:+e;var n}function $c(e,t,n){return null==t&&(t=10),t=Math.min(Math.max(0,t),20),e=(+e).toFixed(t),n?e:+e}function Kc(e){return e.sort((function(e,t){return e-t})),e}function Yc(e){if(e=+e,isNaN(e))return 0;if(e>1e-14)for(var t=1,n=0;n<15;n++,t*=10)if(Math.round(e*t)/t===e)return n;return Xc(e)}function Xc(e){var t=e.toString().toLowerCase(),n=t.indexOf("e"),i=n>0?+t.slice(n+1):0,a=n>0?n:t.length,r=t.indexOf("."),o=r<0?0:a-1-r;return Math.max(0,o-i)}function Zc(e,t){var n=Math.log,i=Math.LN10,a=Math.floor(n(e[1]-e[0])/i),r=Math.round(n(Math.abs(t[1]-t[0]))/i),o=Math.min(Math.max(-a+r,0),20);return isFinite(o)?o:20}function Qc(e,t){var n=jr(e,(function(e,t){return e+(isNaN(t)?0:t)}),0);if(0===n)return[];for(var i=Math.pow(10,t),a=Ur(e,(function(e){return(isNaN(e)?0:e)/n*i*100})),r=100*i,o=Ur(a,(function(e){return Math.floor(e)})),s=jr(o,(function(e,t){return e+t}),0),l=Ur(a,(function(e,t){return e-o[t]}));sp&&(p=l[d],c=d);++o[c],l[c]=0,++s}return Ur(o,(function(e){return e/i}))}function Jc(e,t){var n=Math.max(Yc(e),Yc(t)),i=e+t;return n>20?i:$c(i,n)}function ed(e){var t=2*Math.PI;return(e%t+t)%t}function td(e){return e>-1e-4&&e=10&&t++,t}function od(e,t){var n=rd(e),i=Math.pow(10,n),a=e/i;return e=(t?a<1.5?1:a<2.5?2:a<4?3:a<7?5:10:a<1?1:a<2?2:a<3?3:a<5?5:10)*i,n>=-20?+e.toFixed(n<0?-n:0):e}function sd(e){e.sort((function(e,t){return s(e,t,0)?-1:1}));for(var t=-1/0,n=1,i=0;i=0||a&&Lr(a,s)<0)){var l=n.getShallow(s,t);null!=l&&(r[e[o][0]]=l)}}return r}}var Xd=Yd([["fill","color"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["opacity"],["shadowColor"]]),Zd=function(){function e(){}return e.prototype.getAreaStyle=function(e,t){return Xd(this,e,t)},e}(),Qd=new Rl(50);function Jd(e){if("string"==typeof e){var t=Qd.get(e);return t&&t.image}return e}function eu(e,t,n,i,a){if(e){if("string"==typeof e){if(t&&t.__zrImageSrc===e||!n)return t;var r=Qd.get(e),o={hostEl:n,cb:i,cbPayload:a};return r?!nu(t=r.image)&&r.pending.push(o):((t=vr.loadImage(e,tu,tu)).__zrImageSrc=e,Qd.put(e,t.__cachedImgObj={image:t,pending:[o]})),t}return e}return t}function tu(){var e=this.__cachedImgObj;this.onload=this.onerror=this.__cachedImgObj=null;for(var t=0;t=o;l++)s-=o;var p=yc(n,t);return p>s&&(n="",p=0),s=e-p,a.ellipsis=n,a.ellipsisWidth=p,a.contentWidth=s,a.containerWidth=e,a}function ou(e,t){var n=t.containerWidth,i=t.font,a=t.contentWidth;if(!n)return"";var r=yc(e,i);if(r<=n)return e;for(var o=0;;o++){if(r<=a||o>=t.maxIterations){e+=t.ellipsis;break}var s=0===o?su(e,a,t.ascCharWidth,t.cnCharWidth):r>0?Math.floor(e.length*a/r):0;r=yc(e=e.substr(0,s),i)}return""===e&&(e=t.placeholder),e}function su(e,t,n,i){for(var a=0,r=0,o=e.length;r0&&h+i.accumWidth>i.width&&(r=t.split("\n"),d=!0),i.accumWidth=h}else{var g=hu(t,c,i.width,i.breakAll,i.accumWidth);i.accumWidth=g.accumWidth+m,o=g.linesWidths,r=g.lines}}else r=t.split("\n");for(var f=0;f=32&&t<=591||t>=880&&t<=4351||t>=4608&&t<=5119||t>=7680&&t<=8303}(e)||!!uu[e]}function hu(e,t,n,i,a){for(var r=[],o=[],s="",l="",p=0,c=0,d=0;dn:a+c+m>n)?c?(s||l)&&(h?(s||(s=l,l="",c=p=0),r.push(s),o.push(c-p),l+=u,s="",c=p+=m):(l&&(s+=l,l="",p=0),r.push(s),o.push(c),s=u,c=m)):h?(r.push(l),o.push(p),l=u,p=m):(r.push(u),o.push(m)):(c+=m,h?(l+=u,p+=m):(l&&(s+=l,l="",p=0),s+=u))}else l&&(s+=l,c+=p),r.push(s),o.push(c),s="",l="",p=0,c=0}return r.length||s||(s=e,l="",p=0),l&&(s+=l),s&&(r.push(s),o.push(c)),1===r.length&&(c+=a),{accumWidth:c,lines:r,linesWidths:o}}var gu="__zr_style_"+Math.round(10*Math.random()),fu={shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,shadowColor:"#000",opacity:1,blend:"source-over"},yu={style:{shadowBlur:!0,shadowOffsetX:!0,shadowOffsetY:!0,shadowColor:!0,opacity:!0}};fu[gu]=!0;var vu=["z","z2","invisible"],xu=["invisible"],bu=function(e){function t(t){return e.call(this,t)||this}var n;return We(t,e),t.prototype._init=function(t){for(var n=$r(t),i=0;i1e-4)return s[0]=e-n,s[1]=t-i,l[0]=e+n,void(l[1]=t+i);if(Mu[0]=Iu(a)*n+e,Mu[1]=Tu(a)*i+t,ku[0]=Iu(r)*n+e,ku[1]=Tu(r)*i+t,p(s,Mu,ku),c(l,Mu,ku),(a%=Eu)<0&&(a+=Eu),(r%=Eu)<0&&(r+=Eu),a>r&&!o?r+=Eu:aa&&(Pu[0]=Iu(m)*n+e,Pu[1]=Tu(m)*i+t,p(s,Pu,s),c(l,Pu,l))}var Lu={M:1,L:2,C:3,Q:4,A:5,Z:6,R:7},Vu=[],qu=[],Gu=[],zu=[],Uu=[],ju=[],Hu=Math.min,Wu=Math.max,$u=Math.cos,Ku=Math.sin,Yu=Math.abs,Xu=Math.PI,Zu=2*Xu,Qu="undefined"!=typeof Float32Array,Ju=[];function em(e){return Math.round(e/Xu*1e8)/1e8%2*Xu}function tm(e,t){var n=em(e[0]);n<0&&(n+=Zu);var i=n-e[0],a=e[1];a+=i,!t&&a-n>=Zu?a=n+Zu:t&&n-a>=Zu?a=n-Zu:!t&&n>a?a=n+(Zu-em(n-a)):t&&n0&&(this._ux=Yu(n/ic/e)||0,this._uy=Yu(n/ic/t)||0)},e.prototype.setDPR=function(e){this.dpr=e},e.prototype.setContext=function(e){this._ctx=e},e.prototype.getContext=function(){return this._ctx},e.prototype.beginPath=function(){return this._ctx&&this._ctx.beginPath(),this.reset(),this},e.prototype.reset=function(){this._saveData&&(this._len=0),this._pathSegLen&&(this._pathSegLen=null,this._pathLen=0),this._version++},e.prototype.moveTo=function(e,t){return this._drawPendingPt(),this.addData(Lu.M,e,t),this._ctx&&this._ctx.moveTo(e,t),this._x0=e,this._y0=t,this._xi=e,this._yi=t,this},e.prototype.lineTo=function(e,t){var n=Yu(e-this._xi),i=Yu(t-this._yi),a=n>this._ux||i>this._uy;if(this.addData(Lu.L,e,t),this._ctx&&a&&this._ctx.lineTo(e,t),a)this._xi=e,this._yi=t,this._pendingPtDist=0;else{var r=n*n+i*i;r>this._pendingPtDist&&(this._pendingPtX=e,this._pendingPtY=t,this._pendingPtDist=r)}return this},e.prototype.bezierCurveTo=function(e,t,n,i,a,r){return this._drawPendingPt(),this.addData(Lu.C,e,t,n,i,a,r),this._ctx&&this._ctx.bezierCurveTo(e,t,n,i,a,r),this._xi=a,this._yi=r,this},e.prototype.quadraticCurveTo=function(e,t,n,i){return this._drawPendingPt(),this.addData(Lu.Q,e,t,n,i),this._ctx&&this._ctx.quadraticCurveTo(e,t,n,i),this._xi=n,this._yi=i,this},e.prototype.arc=function(e,t,n,i,a,r){this._drawPendingPt(),Ju[0]=i,Ju[1]=a,tm(Ju,r),i=Ju[0];var o=(a=Ju[1])-i;return this.addData(Lu.A,e,t,n,n,i,o,0,r?0:1),this._ctx&&this._ctx.arc(e,t,n,i,a,r),this._xi=$u(a)*n+e,this._yi=Ku(a)*n+t,this},e.prototype.arcTo=function(e,t,n,i,a){return this._drawPendingPt(),this._ctx&&this._ctx.arcTo(e,t,n,i,a),this},e.prototype.rect=function(e,t,n,i){return this._drawPendingPt(),this._ctx&&this._ctx.rect(e,t,n,i),this.addData(Lu.R,e,t,n,i),this},e.prototype.closePath=function(){this._drawPendingPt(),this.addData(Lu.Z);var e=this._ctx,t=this._x0,n=this._y0;return e&&e.closePath(),this._xi=t,this._yi=n,this},e.prototype.fill=function(e){e&&e.fill(),this.toStatic()},e.prototype.stroke=function(e){e&&e.stroke(),this.toStatic()},e.prototype.len=function(){return this._len},e.prototype.setData=function(e){var t=e.length;this.data&&this.data.length===t||!Qu||(this.data=new Float32Array(t));for(var n=0;np.length&&(this._expandData(),p=this.data);for(var c=0;c0&&(this._ctx&&this._ctx.lineTo(this._pendingPtX,this._pendingPtY),this._pendingPtDist=0)},e.prototype._expandData=function(){if(!(this.data instanceof Array)){for(var e=[],t=0;t11&&(this.data=new Float32Array(e)))}},e.prototype.getBoundingRect=function(){Gu[0]=Gu[1]=Uu[0]=Uu[1]=Number.MAX_VALUE,zu[0]=zu[1]=ju[0]=ju[1]=-Number.MAX_VALUE;var e,t=this.data,n=0,i=0,a=0,r=0;for(e=0;en||Yu(f)>i||d===t-1)&&(h=Math.sqrt(k*k+f*f),a=g,r=x);break;case Lu.C:var y=e[d++],v=e[d++],x=(g=e[d++],e[d++]),b=e[d++],w=e[d++];h=Cl(a,r,y,v,g,x,b,w,10),a=b,r=w;break;case Lu.Q:h=kl(a,r,y=e[d++],v=e[d++],g=e[d++],x=e[d++],10),a=g,r=x;break;case Lu.A:var S=e[d++],C=e[d++],_=e[d++],T=e[d++],I=e[d++],E=e[d++],M=E+I;d+=1,m&&(o=$u(I)*_+S,s=Ku(I)*T+C),h=Wu(_,T)*Hu(Zu,Math.abs(E)),a=$u(M)*_+S,r=Ku(M)*T+C;break;case Lu.R:o=a=e[d++],s=r=e[d++],h=2*e[d++]+2*e[d++];break;case Lu.Z:var k=o-a;f=s-r;h=Math.sqrt(k*k+f*f),a=o,r=s}h>=0&&(l[c++]=h,p+=h)}return this._pathLen=p,p},e.prototype.rebuildPath=function(e,t){var n,i,a,r,o,s,l,p,c,d,u=this.data,m=this._ux,h=this._uy,g=this._len,f=t<1,y=0,v=0,x=0;if(!f||(this._pathSegLen||this._calculateLength(),l=this._pathSegLen,p=t*this._pathLen))e:for(var b=0;b0&&(e.lineTo(c,d),x=0),w){case Lu.M:n=a=u[b++],i=r=u[b++],e.moveTo(a,r);break;case Lu.L:o=u[b++],s=u[b++];var C=Yu(o-a),_=Yu(s-r);if(C>m||_>h){if(f){if(y+(K=l[v++])>p){var T=(p-y)/K;e.lineTo(a*(1-T)+o*T,r*(1-T)+s*T);break e}y+=K}e.lineTo(o,s),a=o,r=s,x=0}else{var I=C*C+_*_;I>x&&(c=o,d=s,x=I)}break;case Lu.C:var E=u[b++],M=u[b++],k=u[b++],P=u[b++],D=u[b++],O=u[b++];if(f){if(y+(K=l[v++])>p){wl(a,E,k,D,T=(p-y)/K,Vu),wl(r,M,P,O,T,qu),e.bezierCurveTo(Vu[1],qu[1],Vu[2],qu[2],Vu[3],qu[3]);break e}y+=K}e.bezierCurveTo(E,M,k,P,D,O),a=D,r=O;break;case Lu.Q:E=u[b++],M=u[b++],k=u[b++],P=u[b++];if(f){if(y+(K=l[v++])>p){El(a,E,k,T=(p-y)/K,Vu),El(r,M,P,T,qu),e.quadraticCurveTo(Vu[1],qu[1],Vu[2],qu[2]);break e}y+=K}e.quadraticCurveTo(E,M,k,P),a=k,r=P;break;case Lu.A:var A=u[b++],F=u[b++],R=u[b++],B=u[b++],N=u[b++],L=u[b++],V=u[b++],q=!u[b++],G=R>B?R:B,z=Yu(R-B)>.001,U=N+L,j=!1;if(f)y+(K=l[v++])>p&&(U=N+L*(p-y)/K,j=!0),y+=K;if(z&&e.ellipse?e.ellipse(A,F,R,B,V,N,U,q):e.arc(A,F,G,N,U,q),j)break e;S&&(n=$u(N)*R+A,i=Ku(N)*B+F),a=$u(U)*R+A,r=Ku(U)*B+F;break;case Lu.R:n=a=u[b],i=r=u[b+1],o=u[b++],s=u[b++];var H=u[b++],W=u[b++];if(f){if(y+(K=l[v++])>p){var $=p-y;e.moveTo(o,s),e.lineTo(o+Hu($,H),s),($-=H)>0&&e.lineTo(o+H,s+Hu($,W)),($-=W)>0&&e.lineTo(o+Wu(H-$,0),s+W),($-=H)>0&&e.lineTo(o,s+Wu(W-$,0));break e}y+=K}e.rect(o,s,H,W);break;case Lu.Z:if(f){var K;if(y+(K=l[v++])>p){T=(p-y)/K;e.lineTo(a*(1-T)+n*T,r*(1-T)+i*T);break e}y+=K}e.closePath(),a=n,r=i}}},e.prototype.clone=function(){var t=new e,n=this.data;return t.data=n.slice?n.slice():Array.prototype.slice.call(n),t._len=this._len,t},e.CMD=Lu,e.initDefaultProps=function(){var t=e.prototype;t._saveData=!0,t._ux=0,t._uy=0,t._pendingPtDist=0,t._version=0}(),e}();function im(e,t,n,i,a,r,o){if(0===a)return!1;var s=a,l=0;if(o>t+s&&o>i+s||oe+s&&r>n+s||rt+d&&c>i+d&&c>r+d&&c>s+d||ce+d&&p>n+d&&p>a+d&&p>o+d||pt+p&&l>i+p&&l>r+p||le+p&&s>n+p&&s>a+p||sn||c+pa&&(a+=lm);var u=Math.atan2(l,s);return u<0&&(u+=lm),u>=i&&u<=a||u+lm>=i&&u+lm<=a}function cm(e,t,n,i,a,r){if(r>t&&r>i||ra?s:0}var dm=nm.CMD,um=2*Math.PI;var mm=[-1,-1,-1],hm=[-1,-1];function gm(e,t,n,i,a,r,o,s,l,p){if(p>t&&p>i&&p>r&&p>s||p1&&(c=void 0,c=hm[0],hm[0]=hm[1],hm[1]=c),h=yl(t,i,r,s,hm[0]),m>1&&(g=yl(t,i,r,s,hm[1]))),2===m?yt&&s>i&&s>r||s=0&&c<=1&&(a[l++]=c);else{var p=o*o-4*r*s;if(gl(p))(c=-o/(2*r))>=0&&c<=1&&(a[l++]=c);else if(p>0){var c,d=sl(p),u=(-o-d)/(2*r);(c=(-o+d)/(2*r))>=0&&c<=1&&(a[l++]=c),u>=0&&u<=1&&(a[l++]=u)}}return l}(t,i,r,s,mm);if(0===l)return 0;var p=Il(t,i,r);if(p>=0&&p<=1){for(var c=0,d=_l(t,i,r,p),u=0;un||s<-n)return 0;var l=Math.sqrt(n*n-s*s);mm[0]=-l,mm[1]=l;var p=Math.abs(i-a);if(p<1e-4)return 0;if(p>=um-1e-4){i=0,a=um;var c=r?1:-1;return o>=mm[0]+e&&o<=mm[1]+e?c:0}if(i>a){var d=i;i=a,a=d}i<0&&(i+=um,a+=um);for(var u=0,m=0;m<2;m++){var h=mm[m];if(h+e>o){var g=Math.atan2(s,h);c=r?1:-1;g<0&&(g=um+g),(g>=i&&g<=a||g+um>=i&&g+um<=a)&&(g>Math.PI/2&&g<1.5*Math.PI&&(c=-c),u+=c)}}return u}function vm(e,t,n,i,a){for(var r,o,s,l,p=e.data,c=e.len(),d=0,u=0,m=0,h=0,g=0,f=0;f1&&(n||(d+=cm(u,m,h,g,i,a))),v&&(h=u=p[f],g=m=p[f+1]),y){case dm.M:u=h=p[f++],m=g=p[f++];break;case dm.L:if(n){if(im(u,m,p[f],p[f+1],t,i,a))return!0}else d+=cm(u,m,p[f],p[f+1],i,a)||0;u=p[f++],m=p[f++];break;case dm.C:if(n){if(am(u,m,p[f++],p[f++],p[f++],p[f++],p[f],p[f+1],t,i,a))return!0}else d+=gm(u,m,p[f++],p[f++],p[f++],p[f++],p[f],p[f+1],i,a)||0;u=p[f++],m=p[f++];break;case dm.Q:if(n){if(rm(u,m,p[f++],p[f++],p[f],p[f+1],t,i,a))return!0}else d+=fm(u,m,p[f++],p[f++],p[f],p[f+1],i,a)||0;u=p[f++],m=p[f++];break;case dm.A:var x=p[f++],b=p[f++],w=p[f++],S=p[f++],C=p[f++],_=p[f++];f+=1;var T=!!(1-p[f++]);r=Math.cos(C)*w+x,o=Math.sin(C)*S+b,v?(h=r,g=o):d+=cm(u,m,r,o,i,a);var I=(i-x)*S/w+x;if(n){if(pm(x,b,S,C,C+_,T,t,I,a))return!0}else d+=ym(x,b,S,C,C+_,T,I,a);u=Math.cos(C+_)*w+x,m=Math.sin(C+_)*S+b;break;case dm.R:if(h=u=p[f++],g=m=p[f++],r=h+p[f++],o=g+p[f++],n){if(im(h,g,r,g,t,i,a)||im(r,g,r,o,t,i,a)||im(r,o,h,o,t,i,a)||im(h,o,h,g,t,i,a))return!0}else d+=cm(r,g,r,o,i,a),d+=cm(h,o,h,g,i,a);break;case dm.Z:if(n){if(im(u,m,h,g,t,i,a))return!0}else d+=cm(u,m,h,g,i,a);u=h,m=g}}return n||(s=m,l=g,Math.abs(s-l)<1e-4)||(d+=cm(u,m,h,g,i,a)||0),0!==d}var xm=Nr({fill:"#000",stroke:null,strokePercent:1,fillOpacity:1,strokeOpacity:1,lineDashOffset:0,lineWidth:1,lineCap:"butt",miterLimit:10,strokeNoScale:!1,strokeFirst:!1},fu),bm={style:Nr({fill:!0,stroke:!0,strokePercent:!0,fillOpacity:!0,strokeOpacity:!0,lineDashOffset:!0,lineWidth:!0,miterLimit:!0},yu.style)},wm=hc.concat(["invisible","culling","z","z2","zlevel","parent"]),Sm=function(e){function t(t){return e.call(this,t)||this}var n;return We(t,e),t.prototype.update=function(){var n=this;e.prototype.update.call(this);var i=this.style;if(i.decal){var a=this._decalEl=this._decalEl||new t;a.buildPath===t.prototype.buildPath&&(a.buildPath=function(e){n.buildPath(e,n.shape)}),a.silent=!0;var r=a.style;for(var o in i)r[o]!==i[o]&&(r[o]=i[o]);r.fill=i.fill?i.decal:null,r.decal=null,r.shadowColor=null,i.strokeFirst&&(r.stroke=null);for(var s=0;s.5?ac:t>.2?"#eee":rc}if(e)return rc}return ac},t.prototype.getInsideTextStroke=function(e){var t=this.style.fill;if(Qr(t)){var n=this.__zr;if(!(!n||!n.isDarkMode())===np(e,0)<.4)return t}},t.prototype.buildPath=function(e,t,n){},t.prototype.pathUpdated=function(){this.__dirty&=-5},t.prototype.getUpdatedPathProxy=function(e){return!this.path&&this.createPathProxy(),this.path.beginPath(),this.buildPath(this.path,this.shape,e),this.path},t.prototype.createPathProxy=function(){this.path=new nm(!1)},t.prototype.hasStroke=function(){var e=this.style,t=e.stroke;return!(null==t||"none"===t||!(e.lineWidth>0))},t.prototype.hasFill=function(){var e=this.style.fill;return null!=e&&"none"!==e},t.prototype.getBoundingRect=function(){var e=this._rect,t=this.style,n=!e;if(n){var i=!1;this.path||(i=!0,this.createPathProxy());var a=this.path;(i||4&this.__dirty)&&(a.beginPath(),this.buildPath(a,this.shape,!1),this.pathUpdated()),e=a.getBoundingRect()}if(this._rect=e,this.hasStroke()&&this.path&&this.path.len()>0){var r=this._rectStroke||(this._rectStroke=e.clone());if(this.__dirty||n){r.copy(e);var o=t.strokeNoScale?this.getLineScale():1,s=t.lineWidth;if(!this.hasFill()){var l=this.strokeContainThreshold;s=Math.max(s,null==l?4:l)}o>1e-10&&(r.width+=s/o,r.height+=s/o,r.x-=s/o/2,r.y-=s/o/2)}return r}return e},t.prototype.contain=function(e,t){var n=this.transformCoordToLocal(e,t),i=this.getBoundingRect(),a=this.style;if(e=n[0],t=n[1],i.contain(e,t)){var r=this.path;if(this.hasStroke()){var o=a.lineWidth,s=a.strokeNoScale?this.getLineScale():1;if(s>1e-10&&(this.hasFill()||(o=Math.max(o,this.strokeContainThreshold)),function(e,t,n,i){return vm(e,t,!0,n,i)}(r,o/s,e,t)))return!0}if(this.hasFill())return function(e,t,n){return vm(e,0,!1,t,n)}(r,e,t)}return!1},t.prototype.dirtyShape=function(){this.__dirty|=4,this._rect&&(this._rect=null),this._decalEl&&this._decalEl.dirtyShape(),this.markRedraw()},t.prototype.dirty=function(){this.dirtyStyle(),this.dirtyShape()},t.prototype.animateShape=function(e){return this.animate("shape",e)},t.prototype.updateDuringAnimation=function(e){"style"===e?this.dirtyStyle():"shape"===e?this.dirtyShape():this.markRedraw()},t.prototype.attrKV=function(t,n){"shape"===t?this.setShape(n):e.prototype.attrKV.call(this,t,n)},t.prototype.setShape=function(e,t){var n=this.shape;return n||(n=this.shape={}),"string"==typeof e?n[e]=t:Br(n,e),this.dirtyShape(),this},t.prototype.shapeChanged=function(){return!!(4&this.__dirty)},t.prototype.createStyle=function(e){return To(xm,e)},t.prototype._innerSaveToNormal=function(t){e.prototype._innerSaveToNormal.call(this,t);var n=this._normalState;t.shape&&!n.shape&&(n.shape=Br({},this.shape))},t.prototype._applyStateObj=function(t,n,i,a,r,o){e.prototype._applyStateObj.call(this,t,n,i,a,r,o);var s,l=!(n&&a);if(n&&n.shape?r?a?s=n.shape:(s=Br({},i.shape),Br(s,n.shape)):(s=Br({},a?this.shape:i.shape),Br(s,n.shape)):l&&(s=i.shape),s)if(r){this.shape=Br({},this.shape);for(var p={},c=$r(s),d=0;d0},t.prototype.hasFill=function(){var e=this.style.fill;return null!=e&&"none"!==e},t.prototype.createStyle=function(e){return To(Cm,e)},t.prototype.setBoundingRect=function(e){this._rect=e},t.prototype.getBoundingRect=function(){var e=this.style;if(!this._rect){var t=e.text;null!=t?t+="":t="";var n=xc(t,e.font,e.textAlign,e.textBaseline);if(n.x+=e.x||0,n.y+=e.y||0,this.hasStroke()){var i=e.lineWidth;n.x-=i/2,n.y-=i/2,n.width+=i,n.height+=i}this._rect=n}return this._rect},t.initDefaultProps=void(t.prototype.dirtyRectTolerance=10),t}(bu);_m.prototype.type="tspan";var Tm=Nr({x:0,y:0},fu),Im={style:Nr({x:!0,y:!0,width:!0,height:!0,sx:!0,sy:!0,sWidth:!0,sHeight:!0},yu.style)};var Em=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.createStyle=function(e){return To(Tm,e)},t.prototype._getSize=function(e){var t=this.style,n=t[e];if(null!=n)return n;var i,a=(i=t.image)&&"string"!=typeof i&&i.width&&i.height?t.image:this.__image;if(!a)return 0;var r="width"===e?"height":"width",o=t[r];return null==o?a[e]:a[e]/a[r]*o},t.prototype.getWidth=function(){return this._getSize("width")},t.prototype.getHeight=function(){return this._getSize("height")},t.prototype.getAnimationStyleProps=function(){return Im},t.prototype.getBoundingRect=function(){var e=this.style;return this._rect||(this._rect=new Ns(e.x||0,e.y||0,this.getWidth(),this.getHeight())),this._rect},t}(bu);Em.prototype.type="image";var Mm=Math.round;function km(e,t,n){if(t){var i=t.x1,a=t.x2,r=t.y1,o=t.y2;e.x1=i,e.x2=a,e.y1=r,e.y2=o;var s=n&&n.lineWidth;return s?(Mm(2*i)===Mm(2*a)&&(e.x1=e.x2=Dm(i,s,!0)),Mm(2*r)===Mm(2*o)&&(e.y1=e.y2=Dm(r,s,!0)),e):e}}function Pm(e,t,n){if(t){var i=t.x,a=t.y,r=t.width,o=t.height;e.x=i,e.y=a,e.width=r,e.height=o;var s=n&&n.lineWidth;return s?(e.x=Dm(i,s,!0),e.y=Dm(a,s,!0),e.width=Math.max(Dm(i+r,s,!1)-e.x,0===r?0:1),e.height=Math.max(Dm(a+o,s,!1)-e.y,0===o?0:1),e):e}}function Dm(e,t,n){if(!t)return e;var i=Mm(2*e);return(i+Mm(t))%2==0?i/2:(i+(n?1:-1))/2}var Om=function(){this.x=0,this.y=0,this.width=0,this.height=0},Am={},Fm=function(e){function t(t){return e.call(this,t)||this}return We(t,e),t.prototype.getDefaultShape=function(){return new Om},t.prototype.buildPath=function(e,t){var n,i,a,r;if(this.subPixelOptimize){var o=Pm(Am,t,this.style);n=o.x,i=o.y,a=o.width,r=o.height,o.r=t.r,t=o}else n=t.x,i=t.y,a=t.width,r=t.height;t.r?function(e,t){var n,i,a,r,o,s=t.x,l=t.y,p=t.width,c=t.height,d=t.r;p<0&&(s+=p,p=-p),c<0&&(l+=c,c=-c),"number"==typeof d?n=i=a=r=d:d instanceof Array?1===d.length?n=i=a=r=d[0]:2===d.length?(n=a=d[0],i=r=d[1]):3===d.length?(n=d[0],i=r=d[1],a=d[2]):(n=d[0],i=d[1],a=d[2],r=d[3]):n=i=a=r=0,n+i>p&&(n*=p/(o=n+i),i*=p/o),a+r>p&&(a*=p/(o=a+r),r*=p/o),i+a>c&&(i*=c/(o=i+a),a*=c/o),n+r>c&&(n*=c/(o=n+r),r*=c/o),e.moveTo(s+n,l),e.lineTo(s+p-i,l),0!==i&&e.arc(s+p-i,l+i,i,-Math.PI/2,0),e.lineTo(s+p,l+c-a),0!==a&&e.arc(s+p-a,l+c-a,a,0,Math.PI/2),e.lineTo(s+r,l+c),0!==r&&e.arc(s+r,l+c-r,r,Math.PI/2,Math.PI),e.lineTo(s,l+n),0!==n&&e.arc(s+n,l+n,n,Math.PI,1.5*Math.PI)}(e,t):e.rect(n,i,a,r)},t.prototype.isZeroArea=function(){return!this.shape.width||!this.shape.height},t}(Sm);Fm.prototype.type="rect";var Rm={fill:"#000"},Bm={style:Nr({fill:!0,stroke:!0,fillOpacity:!0,strokeOpacity:!0,lineWidth:!0,fontSize:!0,lineHeight:!0,width:!0,height:!0,textShadowColor:!0,textShadowBlur:!0,textShadowOffsetX:!0,textShadowOffsetY:!0,backgroundColor:!0,padding:!0,borderColor:!0,borderWidth:!0,borderRadius:!0},yu.style)},Nm=function(e){function t(t){var n=e.call(this)||this;return n.type="text",n._children=[],n._defaultStyle=Rm,n.attr(t),n}return We(t,e),t.prototype.childrenRef=function(){return this._children},t.prototype.update=function(){e.prototype.update.call(this),this.styleChanged()&&this._updateSubTexts();for(var t=0;tm&&c){var h=Math.floor(m/l);n=n.slice(0,h)}if(e&&o&&null!=d)for(var g=ru(d,r,t.ellipsis,{minChar:t.truncateMinChar,placeholder:t.placeholder}),f=0;f0,T=null!=e.width&&("truncate"===e.overflow||"break"===e.overflow||"breakAll"===e.overflow),I=i.calculatedLineHeight,E=0;El&&du(n,e.substring(l,p),t,s),du(n,i[2],t,s,i[1]),l=iu.lastIndex}lr){w>0?(v.tokens=v.tokens.slice(0,w),f(v,b,x),n.lines=n.lines.slice(0,y+1)):n.lines=n.lines.slice(0,y);break e}var E=S.width,M=null==E||"auto"===E;if("string"==typeof E&&"%"===E.charAt(E.length-1))O.percentWidth=E,c.push(O),O.contentWidth=yc(O.text,T);else{if(M){var k=S.backgroundColor,P=k&&k.image;P&&nu(P=Jd(P))&&(O.width=Math.max(O.width,P.width*I/P.height))}var D=h&&null!=a?a-b:null;null!=D&&D=0&&"right"===(E=x[I]).align;)this._placeToken(E,e,w,h,T,"right",f),S-=E.width,T-=E.width,I--;for(_+=(n-(_-m)-(g-T)-S)/2;C<=I;)E=x[C],this._placeToken(E,e,w,h,_+E.width/2,"center",f),_+=E.width,C++;h+=w}},t.prototype._placeToken=function(e,t,n,i,a,r,o){var s=t.rich[e.styleName]||{};s.text=e.text;var l=e.verticalAlign,p=i+n/2;"top"===l?p=i+e.height/2:"bottom"===l&&(p=i+n-e.height/2),!e.isLineHolder&&Ym(s)&&this._renderBackground(s,t,"right"===r?a-e.width:"center"===r?a-e.width/2:a,p-e.height/2,e.width,e.height);var c=!!s.backgroundColor,d=e.textPadding;d&&(a=$m(a,r,d),p-=e.height/2-d[0]-e.innerHeight/2);var u=this._getOrCreateChild(_m),m=u.createStyle();u.useStyle(m);var h=this._defaultStyle,g=!1,f=0,y=Wm("fill"in s?s.fill:"fill"in t?t.fill:(g=!0,h.fill)),v=Hm("stroke"in s?s.stroke:"stroke"in t?t.stroke:c||o||h.autoStroke&&!g?null:(f=2,h.stroke)),x=s.textShadowBlur>0||t.textShadowBlur>0;m.text=e.text,m.x=a,m.y=p,x&&(m.shadowBlur=s.textShadowBlur||t.textShadowBlur||0,m.shadowColor=s.textShadowColor||t.textShadowColor||"transparent",m.shadowOffsetX=s.textShadowOffsetX||t.textShadowOffsetX||0,m.shadowOffsetY=s.textShadowOffsetY||t.textShadowOffsetY||0),m.textAlign=r,m.textBaseline="middle",m.font=e.font||hr,m.opacity=uo(s.opacity,t.opacity,1),zm(m,s),v&&(m.lineWidth=uo(s.lineWidth,t.lineWidth,f),m.lineDash=co(s.lineDash,t.lineDash),m.lineDashOffset=t.lineDashOffset||0,m.stroke=v),y&&(m.fill=y);var b=e.contentWidth,w=e.contentHeight;u.setBoundingRect(new Ns(bc(m.x,b,m.textAlign),wc(m.y,w,m.textBaseline),b,w))},t.prototype._renderBackground=function(e,t,n,i,a,r){var o,s,l,p=e.backgroundColor,c=e.borderWidth,d=e.borderColor,u=p&&p.image,m=p&&!u,h=e.borderRadius,g=this;if(m||e.lineHeight||c&&d){(o=this._getOrCreateChild(Fm)).useStyle(o.createStyle()),o.style.fill=null;var f=o.shape;f.x=n,f.y=i,f.width=a,f.height=r,f.r=h,o.dirtyShape()}if(m)(l=o.style).fill=p||null,l.fillOpacity=co(e.fillOpacity,1);else if(u){(s=this._getOrCreateChild(Em)).onload=function(){g.dirtyStyle()};var y=s.style;y.image=p.image,y.x=n,y.y=i,y.width=a,y.height=r}c&&d&&((l=o.style).lineWidth=c,l.stroke=d,l.strokeOpacity=co(e.strokeOpacity,1),l.lineDash=e.borderDash,l.lineDashOffset=e.borderDashOffset||0,o.strokeContainThreshold=0,o.hasFill()&&o.hasStroke()&&(l.strokeFirst=!0,l.lineWidth*=2));var v=(o||s).style;v.shadowBlur=e.shadowBlur||0,v.shadowColor=e.shadowColor||"transparent",v.shadowOffsetX=e.shadowOffsetX||0,v.shadowOffsetY=e.shadowOffsetY||0,v.opacity=uo(e.opacity,t.opacity,1)},t.makeFont=function(e){var t="";return Um(e)&&(t=[e.fontStyle,e.fontWeight,Gm(e.fontSize),e.fontFamily||"sans-serif"].join(" ")),t&&fo(t)||e.textFont||e.font},t}(bu),Lm={left:!0,right:1,center:1},Vm={top:1,bottom:1,middle:1},qm=["fontStyle","fontWeight","fontSize","fontFamily"];function Gm(e){return"string"!=typeof e||-1===e.indexOf("px")&&-1===e.indexOf("rem")&&-1===e.indexOf("em")?isNaN(+e)?"12px":e+"px":e}function zm(e,t){for(var n=0;n=0,r=!1;if(e instanceof Sm){var o=eh(e),s=a&&o.selectFill||o.normalFill,l=a&&o.selectStroke||o.normalStroke;if(ch(s)||ch(l)){var p=(i=i||{}).style||{};"inherit"===p.fill?(r=!0,i=Br({},i),(p=Br({},p)).fill=s):!ch(p.fill)&&ch(s)?(r=!0,i=Br({},i),(p=Br({},p)).fill=ap(s)):!ch(p.stroke)&&ch(l)&&(r||(i=Br({},i),p=Br({},p)),p.stroke=ap(l)),i.style=p}}if(i&&null==i.z2){r||(i=Br({},i));var c=e.z2EmphasisLift;i.z2=e.z2+(null!=c?c:ah)}return i}(this,0,t,n);if("blur"===e)return function(e,t,n){var i=Lr(e.currentStates,t)>=0,a=e.style.opacity,r=i?null:function(e,t,n,i){for(var a=e.style,r={},o=0;o0){var r={dataIndex:a,seriesIndex:e.seriesIndex};null!=i&&(r.dataType=i),t.push(r)}}))})),t}function Lh(e,t,n){jh(e,!0),xh(e,Sh),qh(e,t,n)}function Vh(e,t,n,i){i?function(e){jh(e,!1)}(e):Lh(e,t,n)}function qh(e,t,n){var i=Xm(e);null!=t?(i.focus=t,i.blurScope=n):i.focus&&(i.focus=null)}var Gh=["emphasis","blur","select"],zh={itemStyle:"getItemStyle",lineStyle:"getLineStyle",areaStyle:"getAreaStyle"};function Uh(e,t,n,i){n=n||"itemStyle";for(var a=0;a1&&(o*=Jh(h),s*=Jh(h));var g=(a===r?-1:1)*Jh((o*o*(s*s)-o*o*(m*m)-s*s*(u*u))/(o*o*(m*m)+s*s*(u*u)))||0,f=g*o*m/s,y=g*-s*u/o,v=(e+n)/2+tg(d)*f-eg(d)*y,x=(t+i)/2+eg(d)*f+tg(d)*y,b=rg([1,0],[(u-f)/o,(m-y)/s]),w=[(u-f)/o,(m-y)/s],S=[(-1*u-f)/o,(-1*m-y)/s],C=rg(w,S);if(ag(w,S)<=-1&&(C=ng),ag(w,S)>=1&&(C=0),C<0){var _=Math.round(C/ng*1e6)/1e6;C=2*ng+_%2*ng}c.addData(p,v,x,o,s,b,C,d,r)}var sg=/([mlvhzcqtsa])([^mlvhzcqtsa]*)/gi,lg=/-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;var pg=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.applyTransform=function(e){},t}(Sm);function cg(e){return null!=e.setData}function dg(e,t){var n=function(e){var t=new nm;if(!e)return t;var n,i=0,a=0,r=i,o=a,s=nm.CMD,l=e.match(sg);if(!l)return t;for(var p=0;pP*P+D*D&&(_=I,T=E),{cx:_,cy:T,x0:-c,y0:-d,x1:_*(a/w-1),y1:T*(a/w-1)}}function kg(e,t){var n,i=Tg(t.r,0),a=Tg(t.r0||0,0),r=i>0;if(r||a>0){if(r||(i=a,a=0),a>i){var o=i;i=a,a=o}var s=t.startAngle,l=t.endAngle;if(!isNaN(s)&&!isNaN(l)){var p=t.cx,c=t.cy,d=!!t.clockwise,u=Cg(l-s),m=u>vg&&u%vg;if(m>Eg&&(u=m),i>Eg)if(u>vg-Eg)e.moveTo(p+i*bg(s),c+i*xg(s)),e.arc(p,c,i,s,l,!d),a>Eg&&(e.moveTo(p+a*bg(l),c+a*xg(l)),e.arc(p,c,a,l,s,d));else{var h=void 0,g=void 0,f=void 0,y=void 0,v=void 0,x=void 0,b=void 0,w=void 0,S=void 0,C=void 0,_=void 0,T=void 0,I=void 0,E=void 0,M=void 0,k=void 0,P=i*bg(s),D=i*xg(s),O=a*bg(l),A=a*xg(l),F=u>Eg;if(F){var R=t.cornerRadius;R&&(n=function(e){var t;if(Xr(e)){var n=e.length;if(!n)return e;t=1===n?[e[0],e[0],0,0]:2===n?[e[0],e[0],e[1],e[1]]:3===n?e.concat(e[2]):e}else t=[e,e,e,e];return t}(R),h=n[0],g=n[1],f=n[2],y=n[3]);var B=Cg(i-a)/2;if(v=Ig(B,f),x=Ig(B,y),b=Ig(B,h),w=Ig(B,g),_=S=Tg(v,x),T=C=Tg(b,w),(S>Eg||C>Eg)&&(I=i*bg(l),E=i*xg(l),M=a*bg(s),k=a*xg(s),uEg){var j=Ig(f,_),H=Ig(y,_),W=Mg(M,k,P,D,i,j,d),$=Mg(I,E,O,A,i,H,d);e.moveTo(p+W.cx+W.x0,c+W.cy+W.y0),_0&&e.arc(p+W.cx,c+W.cy,j,Sg(W.y0,W.x0),Sg(W.y1,W.x1),!d),e.arc(p,c,i,Sg(W.cy+W.y1,W.cx+W.x1),Sg($.cy+$.y1,$.cx+$.x1),!d),H>0&&e.arc(p+$.cx,c+$.cy,H,Sg($.y1,$.x1),Sg($.y0,$.x0),!d))}else e.moveTo(p+P,c+D),e.arc(p,c,i,s,l,!d);else e.moveTo(p+P,c+D);if(a>Eg&&F)if(T>Eg){j=Ig(h,T),W=Mg(O,A,I,E,a,-(H=Ig(g,T)),d),$=Mg(P,D,M,k,a,-j,d);e.lineTo(p+W.cx+W.x0,c+W.cy+W.y0),T0&&e.arc(p+W.cx,c+W.cy,H,Sg(W.y0,W.x0),Sg(W.y1,W.x1),!d),e.arc(p,c,a,Sg(W.cy+W.y1,W.cx+W.x1),Sg($.cy+$.y1,$.cx+$.x1),d),j>0&&e.arc(p+$.cx,c+$.cy,j,Sg($.y1,$.x1),Sg($.y0,$.x0),!d))}else e.lineTo(p+O,c+A),e.arc(p,c,a,l,s,d);else e.lineTo(p+O,c+A)}else e.moveTo(p,c);e.closePath()}}}var Pg=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0,this.cornerRadius=0},Dg=function(e){function t(t){return e.call(this,t)||this}return We(t,e),t.prototype.getDefaultShape=function(){return new Pg},t.prototype.buildPath=function(e,t){kg(e,t)},t.prototype.isZeroArea=function(){return this.shape.startAngle===this.shape.endAngle||this.shape.r===this.shape.r0},t}(Sm);Dg.prototype.type="sector";var Og=function(){this.cx=0,this.cy=0,this.r=0,this.r0=0},Ag=function(e){function t(t){return e.call(this,t)||this}return We(t,e),t.prototype.getDefaultShape=function(){return new Og},t.prototype.buildPath=function(e,t){var n=t.cx,i=t.cy,a=2*Math.PI;e.moveTo(n+t.r,i),e.arc(n,i,t.r,0,a,!1),e.moveTo(n+t.r0,i),e.arc(n,i,t.r0,0,a,!0)},t}(Sm);function Fg(e,t,n){var i=t.smooth,a=t.points;if(a&&a.length>=2){if(i){var r=function(e,t,n,i){var a,r,o,s,l=[],p=[],c=[],d=[];if(i){o=[1/0,1/0],s=[-1/0,-1/0];for(var u=0,m=e.length;uJg[1]){if(o=!1,a)return o;var p=Math.abs(Jg[0]-Qg[1]),c=Math.abs(Qg[0]-Jg[1]);Math.min(p,c)>i.len()&&(p0){var d={duration:c.duration,delay:c.delay||0,easing:c.easing,done:r,force:!!r||!!o,setToFinal:!p,scope:e,during:o};l?t.animateFrom(n,d):t.animateTo(n,d)}else t.stopAnimation(),!l&&t.attr(n),o&&o(1),r&&r()}function pf(e,t,n,i,a,r){lf("update",e,t,n,i,a,r)}function cf(e,t,n,i,a,r){lf("enter",e,t,n,i,a,r)}function df(e){if(!e.__zr)return!0;for(var t=0;tMath.abs(r[1])?r[0]>0?"right":"left":r[1]>0?"bottom":"top"}function Ff(e){return!e.isGroup}function Rf(e,t,n){if(e&&t){var i,a=(i={},e.traverse((function(e){Ff(e)&&e.anid&&(i[e.anid]=e)})),i);t.traverse((function(e){if(Ff(e)&&e.anid){var t=a[e.anid];if(t){var i=r(e);e.attr(r(t)),pf(e,i,n,Xm(e).dataIndex)}}}))}function r(e){var t={x:e.x,y:e.y,rotation:e.rotation};return function(e){return null!=e.shape}(e)&&(t.shape=Br({},e.shape)),t}}function Bf(e,t){return Ur(e,(function(e){var n=e[0];n=ff(n,t.x),n=yf(n,t.x+t.width);var i=e[1];return i=ff(i,t.y),[n,i=yf(i,t.y+t.height)]}))}function Nf(e,t){var n=ff(e.x,t.x),i=yf(e.x+e.width,t.x+t.width),a=ff(e.y,t.y),r=yf(e.y+e.height,t.y+t.height);if(i>=n&&r>=a)return{x:n,y:a,width:i-n,height:r-a}}function Lf(e,t,n){var i=Br({rectHover:!0},t),a=i.style={strokeNoScale:!0};if(n=n||{x:-1,y:-1,width:2,height:2},e)return 0===e.indexOf("image://")?(a.image=e.slice(8),Nr(a,n),new Em(i)):_f(e.replace("path://",""),i,n,"center")}function Vf(e,t,n,i,a){for(var r=0,o=a[a.length-1];r=-1e-6)return!1;var h=e-a,g=t-r,f=Gf(h,g,p,c)/m;if(f<0||f>1)return!1;var y=Gf(h,g,d,u)/m;return!(y<0||y>1)}function Gf(e,t,n,i){return e*i-n*t}function zf(e){var t=e.itemTooltipOption,n=e.componentModel,i=e.itemName,a=Qr(t)?{formatter:t}:t,r=n.mainType,o=n.componentIndex,s={componentType:r,name:i,$vars:["name"]};s[r+"Index"]=o;var l=e.formatterParamsExtra;l&&zr($r(l),(function(e){Eo(s,e)||(s[e]=l[e],s.$vars.push(e))}));var p=Xm(e.el);p.componentMainType=r,p.componentIndex=o,p.tooltipConfig={name:i,option:Nr({content:i,formatterParams:s},a)}}function Uf(e,t){var n;e.isGroup&&(n=t(e)),n||e.traverse(t)}function jf(e,t){if(e)if(Xr(e))for(var n=0;n-1?Cy:Ty;function ky(e,t){e=e.toUpperCase(),Ey[e]=new xy(t),Iy[e]=t}function Py(e){return Ey[e]}ky(_y,{time:{month:["January","February","March","April","May","June","July","August","September","October","November","December"],monthAbbr:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayOfWeekAbbr:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},legend:{selector:{all:"All",inverse:"Inv"}},toolbox:{brush:{title:{rect:"Box Select",polygon:"Lasso Select",lineX:"Horizontally Select",lineY:"Vertically Select",keep:"Keep Selections",clear:"Clear Selections"}},dataView:{title:"Data View",lang:["Data View","Close","Refresh"]},dataZoom:{title:{zoom:"Zoom",back:"Zoom Reset"}},magicType:{title:{line:"Switch to Line Chart",bar:"Switch to Bar Chart",stack:"Stack",tiled:"Tile"}},restore:{title:"Restore"},saveAsImage:{title:"Save as Image",lang:["Right Click to Save Image"]}},series:{typeNames:{pie:"Pie chart",bar:"Bar chart",line:"Line chart",scatter:"Scatter plot",effectScatter:"Ripple scatter plot",radar:"Radar chart",tree:"Tree",treemap:"Treemap",boxplot:"Boxplot",candlestick:"Candlestick",k:"K line chart",heatmap:"Heat map",map:"Map",parallel:"Parallel coordinate map",lines:"Line graph",graph:"Relationship graph",sankey:"Sankey diagram",funnel:"Funnel chart",gauge:"Gauge",pictorialBar:"Pictorial bar",themeRiver:"Theme River Map",sunburst:"Sunburst",custom:"Custom chart",chart:"Chart"}},aria:{general:{withTitle:'This is a chart about "{title}"',withoutTitle:"This is a chart"},series:{single:{prefix:"",withName:" with type {seriesType} named {seriesName}.",withoutName:" with type {seriesType}."},multiple:{prefix:". It consists of {seriesCount} series count.",withName:" The {seriesId} series is a {seriesType} representing {seriesName}.",withoutName:" The {seriesId} series is a {seriesType}.",separator:{middle:"",end:""}}},data:{allData:"The data is as follows: ",partialData:"The first {displayCnt} items are: ",withName:"the data for {name} is {value}",withoutName:"{value}",separator:{middle:", ",end:". "}}}}),ky(Cy,{time:{month:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthAbbr:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayOfWeekAbbr:["日","一","二","三","四","五","六"]},legend:{selector:{all:"全选",inverse:"反选"}},toolbox:{brush:{title:{rect:"矩形选择",polygon:"圈选",lineX:"横向选择",lineY:"纵向选择",keep:"保持选择",clear:"清除选择"}},dataView:{title:"数据视图",lang:["数据视图","关闭","刷新"]},dataZoom:{title:{zoom:"区域缩放",back:"区域缩放还原"}},magicType:{title:{line:"切换为折线图",bar:"切换为柱状图",stack:"切换为堆叠",tiled:"切换为平铺"}},restore:{title:"还原"},saveAsImage:{title:"保存为图片",lang:["右键另存为图片"]}},series:{typeNames:{pie:"饼图",bar:"柱状图",line:"折线图",scatter:"散点图",effectScatter:"涟漪散点图",radar:"雷达图",tree:"树图",treemap:"矩形树图",boxplot:"箱型图",candlestick:"K线图",k:"K线图",heatmap:"热力图",map:"地图",parallel:"平行坐标图",lines:"线图",graph:"关系图",sankey:"桑基图",funnel:"漏斗图",gauge:"仪表盘图",pictorialBar:"象形柱图",themeRiver:"主题河流图",sunburst:"旭日图",custom:"自定义图表",chart:"图表"}},aria:{general:{withTitle:"这是一个关于“{title}”的图表。",withoutTitle:"这是一个图表,"},series:{single:{prefix:"",withName:"图表类型是{seriesType},表示{seriesName}。",withoutName:"图表类型是{seriesType}。"},multiple:{prefix:"它由{seriesCount}个图表系列组成。",withName:"第{seriesId}个系列是一个表示{seriesName}的{seriesType},",withoutName:"第{seriesId}个系列是一个{seriesType},",separator:{middle:";",end:"。"}}},data:{allData:"其数据是——",partialData:"其中,前{displayCnt}项是——",withName:"{name}的数据是{value}",withoutName:"{value}",separator:{middle:",",end:""}}}});var Dy=1e3,Oy=6e4,Ay=36e5,Fy=864e5,Ry=31536e6,By={year:"{yyyy}",month:"{MMM}",day:"{d}",hour:"{HH}:{mm}",minute:"{HH}:{mm}",second:"{HH}:{mm}:{ss}",millisecond:"{HH}:{mm}:{ss} {SSS}",none:"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}"},Ny="{yyyy}-{MM}-{dd}",Ly={year:"{yyyy}",month:"{yyyy}-{MM}",day:Ny,hour:Ny+" "+By.hour,minute:Ny+" "+By.minute,second:Ny+" "+By.second,millisecond:By.none},Vy=["year","month","day","hour","minute","second","millisecond"],qy=["year","half-year","quarter","month","week","half-week","day","half-day","quarter-day","hour","minute","second","millisecond"];function Gy(e,t){return"0000".substr(0,t-(e+="").length)+e}function zy(e){switch(e){case"half-year":case"quarter":return"month";case"week":case"half-week":return"day";case"half-day":case"quarter-day":return"hour";default:return e}}function Uy(e){return e===zy(e)}function jy(e,t,n,i){var a=id(e),r=a[$y(n)](),o=a[Ky(n)]()+1,s=Math.floor((o-1)/3)+1,l=a[Yy(n)](),p=a["get"+(n?"UTC":"")+"Day"](),c=a[Xy(n)](),d=(c-1)%12+1,u=a[Zy(n)](),m=a[Qy(n)](),h=a[Jy(n)](),g=(i instanceof xy?i:Py(i||My)||Ey[Ty]).getModel("time"),f=g.get("month"),y=g.get("monthAbbr"),v=g.get("dayOfWeek"),x=g.get("dayOfWeekAbbr");return(t||"").replace(/{yyyy}/g,r+"").replace(/{yy}/g,Gy(r%100+"",2)).replace(/{Q}/g,s+"").replace(/{MMMM}/g,f[o-1]).replace(/{MMM}/g,y[o-1]).replace(/{MM}/g,Gy(o,2)).replace(/{M}/g,o+"").replace(/{dd}/g,Gy(l,2)).replace(/{d}/g,l+"").replace(/{eeee}/g,v[p]).replace(/{ee}/g,x[p]).replace(/{e}/g,p+"").replace(/{HH}/g,Gy(c,2)).replace(/{H}/g,c+"").replace(/{hh}/g,Gy(d+"",2)).replace(/{h}/g,d+"").replace(/{mm}/g,Gy(u,2)).replace(/{m}/g,u+"").replace(/{ss}/g,Gy(m,2)).replace(/{s}/g,m+"").replace(/{SSS}/g,Gy(h,3)).replace(/{S}/g,h+"")}function Hy(e,t){var n=id(e),i=n[Ky(t)]()+1,a=n[Yy(t)](),r=n[Xy(t)](),o=n[Zy(t)](),s=n[Qy(t)](),l=0===n[Jy(t)](),p=l&&0===s,c=p&&0===o,d=c&&0===r,u=d&&1===a;return u&&1===i?"year":u?"month":d?"day":c?"hour":p?"minute":l?"second":"millisecond"}function Wy(e,t,n){var i=eo(e)?id(e):e;switch(t=t||Hy(e,n)){case"year":return i[$y(n)]();case"half-year":return i[Ky(n)]()>=6?1:0;case"quarter":return Math.floor((i[Ky(n)]()+1)/4);case"month":return i[Ky(n)]();case"day":return i[Yy(n)]();case"half-day":return i[Xy(n)]()/24;case"hour":return i[Xy(n)]();case"minute":return i[Zy(n)]();case"second":return i[Qy(n)]();case"millisecond":return i[Jy(n)]()}}function $y(e){return e?"getUTCFullYear":"getFullYear"}function Ky(e){return e?"getUTCMonth":"getMonth"}function Yy(e){return e?"getUTCDate":"getDate"}function Xy(e){return e?"getUTCHours":"getHours"}function Zy(e){return e?"getUTCMinutes":"getMinutes"}function Qy(e){return e?"getUTCSeconds":"getSeconds"}function Jy(e){return e?"getUTCMilliseconds":"getMilliseconds"}function ev(e){return e?"setUTCFullYear":"setFullYear"}function tv(e){return e?"setUTCMonth":"setMonth"}function nv(e){return e?"setUTCDate":"setDate"}function iv(e){return e?"setUTCHours":"setHours"}function av(e){return e?"setUTCMinutes":"setMinutes"}function rv(e){return e?"setUTCSeconds":"setSeconds"}function ov(e){return e?"setUTCMilliseconds":"setMilliseconds"}function sv(e){if(!pd(e))return Qr(e)?e:"-";var t=(e+"").split(".");return t[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(t.length>1?"."+t[1]:"")}function lv(e,t){return e=(e||"").toLowerCase().replace(/-(.)/g,(function(e,t){return t.toUpperCase()})),t&&e&&(e=e.charAt(0).toUpperCase()+e.slice(1)),e}var pv=ho;function cv(e,t,n){function i(e){return e&&fo(e)?e:"-"}function a(e){return!(null==e||isNaN(e)||!isFinite(e))}var r="time"===t,o=e instanceof Date;if(r||o){var s=r?id(e):e;if(!isNaN(+s))return jy(s,"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}",n);if(o)return"-"}if("ordinal"===t)return Jr(e)?i(e):eo(e)&&a(e)?e+"":"-";var l=ld(e);return a(l)?sv(l):Jr(e)?i(e):"boolean"==typeof e?e+"":"-"}var dv=["a","b","c","d","e","f","g"],uv=function(e,t){return"{"+e+(null==t?"":t)+"}"};function mv(e,t,n){Xr(t)||(t=[t]);var i=t.length;if(!i)return"";for(var a=t[0].$vars||[],r=0;r':'':{renderMode:r,content:"{"+(n.markerId||"markerX")+"|} ",style:"subItem"===a?{width:4,height:4,borderRadius:2,backgroundColor:i}:{width:10,height:10,borderRadius:5,backgroundColor:i}}:""}function gv(e,t){return t=t||"transparent",Qr(e)?e:to(e)&&e.colorStops&&(e.colorStops[0]||{}).color||t}function fv(e,t){if("_blank"===t||"blank"===t){var n=window.open();n.opener=null,n.location.href=e}else window.open(e,t)}var yv=zr,vv=["left","right","top","bottom","width","height"],xv=[["width","left","right"],["height","top","bottom"]];function bv(e,t,n,i,a){var r=0,o=0;null==i&&(i=1/0),null==a&&(a=1/0);var s=0;t.eachChild((function(l,p){var c,d,u=l.getBoundingRect(),m=t.childAt(p+1),h=m&&m.getBoundingRect();if("horizontal"===e){var g=u.width+(h?-h.x+u.x:0);(c=r+g)>i||l.newline?(r=0,c=g,o+=s+n,s=u.height):s=Math.max(s,u.height)}else{var f=u.height+(h?-h.y+u.y:0);(d=o+f)>a||l.newline?(r+=s+n,o=0,d=f,s=u.width):s=Math.max(s,u.width)}l.newline||(l.x=r,l.y=o,l.markRedraw(),"horizontal"===e?r=c+n:o=d+n)}))}var wv=bv;Yr(bv,"vertical"),Yr(bv,"horizontal");function Sv(e,t,n){n=pv(n||0);var i=t.width,a=t.height,r=Wc(e.left,i),o=Wc(e.top,a),s=Wc(e.right,i),l=Wc(e.bottom,a),p=Wc(e.width,i),c=Wc(e.height,a),d=n[2]+n[0],u=n[1]+n[3],m=e.aspect;switch(isNaN(p)&&(p=i-s-u-r),isNaN(c)&&(c=a-l-d-o),null!=m&&(isNaN(p)&&isNaN(c)&&(m>i/a?p=.8*i:c=.8*a),isNaN(p)&&(p=m*c),isNaN(c)&&(c=p/m)),isNaN(r)&&(r=i-s-p-u),isNaN(o)&&(o=a-l-c-d),e.left||e.right){case"center":r=i/2-p/2-n[3];break;case"right":r=i-p-u}switch(e.top||e.bottom){case"middle":case"center":o=a/2-c/2-n[0];break;case"bottom":o=a-c-d}r=r||0,o=o||0,isNaN(p)&&(p=i-u-r-(s||0)),isNaN(c)&&(c=a-d-o-(l||0));var h=new Ns(r+n[3],o+n[0],p,c);return h.margin=n,h}function Cv(e,t,n,i,a,r){var o,s=!a||!a.hv||a.hv[0],l=!a||!a.hv||a.hv[1],p=a&&a.boundingMode||"all";if((r=r||e).x=e.x,r.y=e.y,!s&&!l)return!1;if("raw"===p)o="group"===e.type?new Ns(0,0,+t.width||0,+t.height||0):e.getBoundingRect();else if(o=e.getBoundingRect(),e.needLocalTransform()){var c=e.getLocalTransform();(o=o.clone()).applyTransform(c)}var d=Sv(Nr({width:o.width,height:o.height},t),n,i),u=s?d.x-o.x:0,m=l?d.y-o.y:0;return"raw"===p?(r.x=u,r.y=m):(r.x+=u,r.y+=m),r===e&&e.markRedraw(),!0}function _v(e){var t=e.layoutMode||e.constructor.layoutMode;return to(t)?t:t?{type:t}:null}function Tv(e,t,n){var i=n&&n.ignoreSize;!Xr(i)&&(i=[i,i]);var a=o(xv[0],0),r=o(xv[1],1);function o(n,a){var r={},o=0,p={},c=0;if(yv(n,(function(t){p[t]=e[t]})),yv(n,(function(e){s(t,e)&&(r[e]=p[e]=t[e]),l(r,e)&&o++,l(p,e)&&c++})),i[a])return l(t,n[1])?p[n[2]]=null:l(t,n[2])&&(p[n[1]]=null),p;if(2!==c&&o){if(o>=2)return r;for(var d=0;d=0;o--)r=Fr(r,n[o],!0);t.defaultOption=r}return t.defaultOption},t.prototype.getReferringComponents=function(e,t){var n=e+"Index",i=e+"Id";return Bd(this.ecModel,e,{index:this.get(n,!0),id:this.get(i,!0)},t)},t.prototype.getBoxLayoutParams=function(){var e=this;return{left:e.get("left"),top:e.get("top"),right:e.get("right"),bottom:e.get("bottom"),width:e.get("width"),height:e.get("height")}},t.prototype.getZLevelKey=function(){return""},t.prototype.setZLevel=function(e){this.option.zlevel=e},t.protoInitialize=function(){var e=t.prototype;e.type="component",e.id="",e.name="",e.mainType="",e.subType="",e.componentIndex=0}(),t}(xy);jd(kv,xy),Kd(kv),function(e){var t={};e.registerSubTypeDefaulter=function(e,n){var i=zd(e);t[i.main]=n},e.determineSubType=function(n,i){var a=i.type;if(!a){var r=zd(n).main;e.hasSubTypes(n)&&t[r]&&(a=t[r](i))}return a}}(kv),function(e,t){function n(e,t){return e[t]||(e[t]={predecessor:[],successor:[]}),e[t]}e.topologicalTravel=function(e,i,a,r){if(e.length){var o=function(e){var i={},a=[];return zr(e,(function(r){var o=n(i,r),s=function(e,t){var n=[];return zr(e,(function(e){Lr(t,e)>=0&&n.push(e)})),n}(o.originalDeps=t(r),e);o.entryCount=s.length,0===o.entryCount&&a.push(r),zr(s,(function(e){Lr(o.predecessor,e)<0&&o.predecessor.push(e);var t=n(i,e);Lr(t.successor,e)<0&&t.successor.push(r)}))})),{graph:i,noEntryList:a}}(i),s=o.graph,l=o.noEntryList,p={};for(zr(e,(function(e){p[e]=!0}));l.length;){var c=l.pop(),d=s[c],u=!!p[c];u&&(a.call(r,c,d.originalDeps.slice()),delete p[c]),zr(d.successor,u?h:m)}zr(p,(function(){var e="";throw new Error(e)}))}function m(e){s[e].entryCount--,0===s[e].entryCount&&l.push(e)}function h(e){p[e]=!0,m(e)}}}(kv,(function(e){var t=[];zr(kv.getClassesByMainType(e),(function(e){t=t.concat(e.dependencies||e.prototype.dependencies||[])})),t=Ur(t,(function(e){return zd(e).main})),"dataset"!==e&&Lr(t,"dataset")<=0&&t.unshift("dataset");return t}));var Pv="";"undefined"!=typeof navigator&&(Pv=navigator.platform||"");var Dv="rgba(0, 0, 0, 0.2)",Ov={darkMode:"auto",colorBy:"series",color:["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],gradientColor:["#f6efa6","#d88273","#bf444c"],aria:{decal:{decals:[{color:Dv,dashArrayX:[1,0],dashArrayY:[2,5],symbolSize:1,rotation:Math.PI/6},{color:Dv,symbol:"circle",dashArrayX:[[8,8],[0,8,8,0]],dashArrayY:[6,0],symbolSize:.8},{color:Dv,dashArrayX:[1,0],dashArrayY:[4,3],rotation:-Math.PI/4},{color:Dv,dashArrayX:[[6,6],[0,6,6,0]],dashArrayY:[6,0]},{color:Dv,dashArrayX:[[1,0],[1,6]],dashArrayY:[1,0,6,0],rotation:Math.PI/4},{color:Dv,symbol:"triangle",dashArrayX:[[9,9],[0,9,9,0]],dashArrayY:[7,2],symbolSize:.75}]}},textStyle:{fontFamily:Pv.match(/^Win/)?"Microsoft YaHei":"sans-serif",fontSize:12,fontStyle:"normal",fontWeight:"normal"},blendMode:null,stateAnimation:{duration:300,easing:"cubicOut"},animation:"auto",animationDuration:1e3,animationDurationUpdate:500,animationEasing:"cubicInOut",animationEasingUpdate:"cubicInOut",animationThreshold:2e3,progressiveThreshold:3e3,progressive:400,hoverLayerThreshold:3e3,useUTC:!1},Av=Co(["tooltip","label","itemName","itemId","itemGroupId","itemChildGroupId","seriesName"]),Fv="original",Rv="arrayRows",Bv="objectRows",Nv="keyedColumns",Lv="typedArray",Vv="unknown",qv="column",Gv="row",zv=1,Uv=2,jv=3,Hv=Pd();function Wv(e,t,n){var i={},a=Kv(t);if(!a||!e)return i;var r,o,s=[],l=[],p=t.ecModel,c=Hv(p).datasetMap,d=a.uid+"_"+n.seriesLayoutBy;zr(e=e.slice(),(function(t,n){var a=to(t)?t:e[n]={name:t};"ordinal"===a.type&&null==r&&(r=n,o=h(a)),i[a.name]=[]}));var u=c.get(d)||c.set(d,{categoryWayDim:o,valueWayDim:0});function m(e,t,n){for(var i=0;it)return e[i];return e[n-1]}(i,o):n;if((c=c||n)&&c.length){var d=c[l];return a&&(p[a]=d),s.paletteIdx=(l+1)%c.length,d}}var ox="\0_ec_inner";var sx=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.init=function(e,t,n,i,a,r){i=i||{},this.option=null,this._theme=new xy(i),this._locale=new xy(a),this._optionManager=r},t.prototype.setOption=function(e,t,n){var i=cx(t);this._optionManager.setOption(e,n,i),this._resetOption(null,i)},t.prototype.resetOption=function(e,t){return this._resetOption(e,cx(t))},t.prototype._resetOption=function(e,t){var n=!1,i=this._optionManager;if(!e||"recreate"===e){var a=i.mountOption("recreate"===e);0,this.option&&"recreate"!==e?(this.restoreData(),this._mergeOption(a,t)):ex(this,a),n=!0}if("timeline"!==e&&"media"!==e||this.restoreData(),!e||"recreate"===e||"timeline"===e){var r=i.getTimelineOption(this);r&&(n=!0,this._mergeOption(r,t))}if(!e||"recreate"===e||"media"===e){var o=i.getMediaOption(this);o.length&&zr(o,(function(e){n=!0,this._mergeOption(e,t)}),this)}return n},t.prototype.mergeOption=function(e){this._mergeOption(e,null)},t.prototype._mergeOption=function(e,t){var n=this.option,i=this._componentsMap,a=this._componentsCount,r=[],o=Co(),s=t&&t.replaceMergeMainTypeMap;Hv(this).datasetMap=Co(),zr(e,(function(e,t){null!=e&&(kv.hasClass(t)?t&&(r.push(t),o.set(t,!0)):n[t]=null==n[t]?Ar(e):Fr(n[t],e,!0))})),s&&s.each((function(e,t){kv.hasClass(t)&&!o.get(t)&&(r.push(t),o.set(t,!0))})),kv.topologicalTravel(r,kv.getAllClassMainTypes(),(function(t){var r=function(e,t,n){var i=Zv.get(t);if(!i)return n;var a=i(e);return a?n.concat(a):n}(this,t,vd(e[t])),o=i.get(t),l=o?s&&s.get(t)?"replaceMerge":"normalMerge":"replaceAll",p=Cd(o,r,l);(function(e,t,n){zr(e,(function(e){var i=e.newOption;to(i)&&(e.keyInfo.mainType=t,e.keyInfo.subType=function(e,t,n,i){return t.type?t.type:n?n.subType:i.determineSubType(e,t)}(t,i,e.existing,n))}))})(p,t,kv),n[t]=null,i.set(t,null),a.set(t,0);var c,d=[],u=[],m=0;zr(p,(function(e,n){var i=e.existing,a=e.newOption;if(a){var r="series"===t,o=kv.getClass(t,e.keyInfo.subType,!r);if(!o)return;if("tooltip"===t){if(c)return void 0;c=!0}if(i&&i.constructor===o)i.name=e.keyInfo.name,i.mergeOption(a,this),i.optionUpdated(a,!1);else{var s=Br({componentIndex:n},e.keyInfo);Br(i=new o(a,this,this,s),s),e.brandNew&&(i.__requireNewView=!0),i.init(a,this,this),i.optionUpdated(null,!0)}}else i&&(i.mergeOption({},this),i.optionUpdated({},!1));i?(d.push(i.option),u.push(i),m++):(d.push(void 0),u.push(void 0))}),this),n[t]=d,i.set(t,u),a.set(t,m),"series"===t&&Qv(this)}),this),this._seriesIndices||Qv(this)},t.prototype.getOption=function(){var e=Ar(this.option);return zr(e,(function(t,n){if(kv.hasClass(n)){for(var i=vd(t),a=i.length,r=!1,o=a-1;o>=0;o--)i[o]&&!Md(i[o])?r=!0:(i[o]=null,!r&&a--);i.length=a,e[n]=i}})),delete e[ox],e},t.prototype.getTheme=function(){return this._theme},t.prototype.getLocaleModel=function(){return this._locale},t.prototype.setUpdatePayload=function(e){this._payload=e},t.prototype.getUpdatePayload=function(){return this._payload},t.prototype.getComponent=function(e,t){var n=this._componentsMap.get(e);if(n){var i=n[t||0];if(i)return i;if(null==t)for(var a=0;a=t:"max"===n?e<=t:e===t})(i[o],e,r)||(a=!1)}})),a}var vx=zr,xx=to,bx=["areaStyle","lineStyle","nodeStyle","linkStyle","chordStyle","label","labelLine"];function Sx(e){var t=e&&e.itemStyle;if(t)for(var n=0,i=bx.length;n=0;g--){var f=e[g];if(s||(u=f.data.rawIndexOf(f.stackedByDimension,d)),u>=0){var y=f.data.getByRawIndex(f.stackResultDimension,u);if("all"===l||"positive"===l&&y>0||"negative"===l&&y<0||"samesign"===l&&m>=0&&y>0||"samesign"===l&&m<=0&&y<0){m=Jc(m,y),h=y;break}}}return i[0]=m,i[1]=h,i}))}))}var qx,Gx,zx,Ux,jx,Hx=function(e){this.data=e.data||(e.sourceFormat===Nv?{}:[]),this.sourceFormat=e.sourceFormat||Vv,this.seriesLayoutBy=e.seriesLayoutBy||qv,this.startIndex=e.startIndex||0,this.dimensionsDetectedCount=e.dimensionsDetectedCount,this.metaRawOption=e.metaRawOption;var t=this.dimensionsDefine=e.dimensionsDefine;if(t)for(var n=0;np&&(p=m)}s[0]=l,s[1]=p}},i=function(){return this._data?this._data.length/this._dimSize:0};function a(e){for(var t=0;t=0&&(s=r.interpolatedValue[l])}return null!=s?s+"":""})):void 0},e.prototype.getRawValue=function(e,t){return cb(this.getData(t),e)},e.prototype.formatTooltip=function(e,t,n){},e}();function mb(e){var t,n;return to(e)?e.type&&(n=e):t=e,{text:t,frag:n}}function hb(e){return new gb(e)}var gb=function(){function e(e){e=e||{},this._reset=e.reset,this._plan=e.plan,this._count=e.count,this._onDirty=e.onDirty,this._dirty=!0}return e.prototype.perform=function(e){var t,n=this._upstream,i=e&&e.skip;if(this._dirty&&n){var a=this.context;a.data=a.outputData=n.context.outputData}this.__pipeline&&(this.__pipeline.currentTask=this),this._plan&&!i&&(t=this._plan(this.context));var r,o=c(this._modBy),s=this._modDataCount||0,l=c(e&&e.modBy),p=e&&e.modDataCount||0;function c(e){return!(e>=1)&&(e=1),e}o===l&&s===p||(t="reset"),(this._dirty||"reset"===t)&&(this._dirty=!1,r=this._doReset(i)),this._modBy=l,this._modDataCount=p;var d=e&&e.step;if(this._dueEnd=n?n._outputDueEnd:this._count?this._count(this.context):1/0,this._progress){var u=this._dueIndex,m=Math.min(null!=d?this._dueIndex+d:1/0,this._dueEnd);if(!i&&(r||u1&&i>0?s:o}};return r;function o(){return t=e?null:rt},gte:function(e,t){return e>=t}},wb=function(){function e(e,t){if(!eo(t)){var n="";0,hd(n)}this._opFn=bb[e],this._rvalFloat=ld(t)}return e.prototype.evaluate=function(e){return eo(e)?this._opFn(e,this._rvalFloat):this._opFn(ld(e),this._rvalFloat)},e}(),Sb=function(){function e(e,t){var n="desc"===e;this._resultLT=n?1:-1,null==t&&(t=n?"min":"max"),this._incomparable="min"===t?-1/0:1/0}return e.prototype.evaluate=function(e,t){var n=eo(e)?e:ld(e),i=eo(t)?t:ld(t),a=isNaN(n),r=isNaN(i);if(a&&(n=this._incomparable),r&&(i=this._incomparable),a&&r){var o=Qr(e),s=Qr(t);o&&(n=s?e:0),s&&(i=o?t:0)}return ni?-this._resultLT:0},e}(),Cb=function(){function e(e,t){this._rval=t,this._isEQ=e,this._rvalTypeof=typeof t,this._rvalFloat=ld(t)}return e.prototype.evaluate=function(e){var t=e===this._rval;if(!t){var n=typeof e;n===this._rvalTypeof||"number"!==n&&"number"!==this._rvalTypeof||(t=ld(e)===this._rvalFloat)}return this._isEQ?t:!t},e}();function _b(e,t){return"eq"===e||"ne"===e?new Cb("eq"===e,t):Eo(bb,e)?new wb(e,t):null}var Tb=function(){function e(){}return e.prototype.getRawData=function(){throw new Error("not supported")},e.prototype.getRawDataItem=function(e){throw new Error("not supported")},e.prototype.cloneRawData=function(){},e.prototype.getDimensionInfo=function(e){},e.prototype.cloneAllDimensionInfo=function(){},e.prototype.count=function(){},e.prototype.retrieveValue=function(e,t){},e.prototype.retrieveValueFromItem=function(e,t){},e.prototype.convertValue=function(e,t){return yb(e,t)},e}();function Ib(e){var t=e.sourceFormat;if(!Ob(t)){var n="";0,hd(n)}return e.data}function Eb(e){var t=e.sourceFormat,n=e.data;if(!Ob(t)){var i="";0,hd(i)}if(t===Rv){for(var a=[],r=0,o=n.length;r65535?Rb:Bb}function Gb(e,t,n,i,a){var r=Vb[n||"float"];if(a){var o=e[t],s=o&&o.length;if(s!==i){for(var l=new r(i),p=0;pg[1]&&(g[1]=h)}return this._rawCount=this._count=s,{start:o,end:s}},e.prototype._initDataFromProvider=function(e,t,n){for(var i=this._provider,a=this._chunks,r=this._dimensions,o=r.length,s=this._rawExtent,l=Ur(r,(function(e){return e.property})),p=0;pf[1]&&(f[1]=g)}}!i.persistent&&i.clean&&i.clean(),this._rawCount=this._count=t,this._extent=[]},e.prototype.count=function(){return this._count},e.prototype.get=function(e,t){if(!(t>=0&&t=0&&t=this._rawCount||e<0)return-1;if(!this._indices)return e;var t=this._indices,n=t[e];if(null!=n&&ne))return r;a=r-1}}return-1},e.prototype.indicesOfNearest=function(e,t,n){var i=this._chunks[e],a=[];if(!i)return a;null==n&&(n=1/0);for(var r=1/0,o=-1,s=0,l=0,p=this.count();l=0&&o<0)&&(r=d,o=c,s=0),c===o&&(a[s++]=l))}return a.length=s,a},e.prototype.getIndices=function(){var e,t=this._indices;if(t){var n=t.constructor,i=this._count;if(n===Array){e=new n(i);for(var a=0;a=p&&x<=c||isNaN(x))&&(o[s++]=m),m++}u=!0}else if(2===a){h=d[i[0]];var f=d[i[1]],y=e[i[1]][0],v=e[i[1]][1];for(g=0;g=p&&x<=c||isNaN(x))&&(b>=y&&b<=v||isNaN(b))&&(o[s++]=m),m++}u=!0}}if(!u)if(1===a)for(g=0;g=p&&x<=c||isNaN(x))&&(o[s++]=w)}else for(g=0;ge[_][1])&&(S=!1)}S&&(o[s++]=t.getRawIndex(g))}return sf[1]&&(f[1]=g)}}}},e.prototype.lttbDownSample=function(e,t){var n,i,a,r=this.clone([e],!0),o=r._chunks[e],s=this.count(),l=0,p=Math.floor(1/t),c=this.getRawIndex(0),d=new(qb(this._rawCount))(Math.min(2*(Math.ceil(s/p)+2),s));d[l++]=c;for(var u=1;un&&(n=i,a=T)}_>0&&_p-m&&(s=p-m,o.length=s);for(var h=0;hc[1]&&(c[1]=f),d[u++]=y}return a._count=u,a._indices=d,a._updateGetRawIdx(),a},e.prototype.each=function(e,t){if(this._count)for(var n=e.length,i=this._chunks,a=0,r=this.count();ao&&(o=l)}return i=[r,o],this._extent[e]=i,i},e.prototype.getRawDataItem=function(e){var t=this.getRawIndex(e);if(this._provider.persistent)return this._provider.getItem(t);for(var n=[],i=this._chunks,a=0;a=0?this._indices[e]:-1},e.prototype._updateGetRawIdx=function(){this.getRawIndex=this._indices?this._getRawIdx:this._getRawIdxIdentity},e.internalField=function(){function e(e,t,n,i){return yb(e[i],this._dimensions[i])}Ab={arrayRows:e,objectRows:function(e,t,n,i){return yb(e[t],this._dimensions[i])},keyedColumns:e,original:function(e,t,n,i){var a=e&&(null==e.value?e:e.value);return yb(a instanceof Array?a[i]:a,this._dimensions[i])},typedArray:function(e,t,n,i){return e[i]}}}(),e}(),Ub=function(){function e(e){this._sourceList=[],this._storeList=[],this._upstreamSignList=[],this._versionSignBase=0,this._dirty=!0,this._sourceHost=e}return e.prototype.dirty=function(){this._setLocalSource([],[]),this._storeList=[],this._dirty=!0},e.prototype._setLocalSource=function(e,t){this._sourceList=e,this._upstreamSignList=t,this._versionSignBase++,this._versionSignBase>9e10&&(this._versionSignBase=0)},e.prototype._getVersionSign=function(){return this._sourceHost.uid+"_"+this._versionSignBase},e.prototype.prepareSource=function(){this._isDirty()&&(this._createSource(),this._dirty=!1)},e.prototype._createSource=function(){this._setLocalSource([],[]);var e,t,n=this._sourceHost,i=this._getUpstreamSourceManagers(),a=!!i.length;if(Hb(n)){var r=n,o=void 0,s=void 0,l=void 0;if(a){var p=i[0];p.prepareSource(),o=(l=p.getSource()).data,s=l.sourceFormat,t=[p._getVersionSign()]}else s=io(o=r.get("data",!0))?Lv:Fv,t=[];var c=this._getSourceMetaRawOption()||{},d=l&&l.metaRawOption||{},u=co(c.seriesLayoutBy,d.seriesLayoutBy)||null,m=co(c.sourceHeader,d.sourceHeader),h=co(c.dimensions,d.dimensions);e=u!==d.seriesLayoutBy||!!m!=!!d.sourceHeader||h?[$x(o,{seriesLayoutBy:u,sourceHeader:m,dimensions:h},s)]:[]}else{var g=n;if(a){var f=this._applyTransform(i);e=f.sourceList,t=f.upstreamSignList}else{e=[$x(g.get("source",!0),this._getSourceMetaRawOption(),null)],t=[]}}this._setLocalSource(e,t)},e.prototype._applyTransform=function(e){var t,n=this._sourceHost,i=n.get("transform",!0),a=n.get("fromTransformResult",!0);if(null!=a){var r="";1!==e.length&&Wb(r)}var o,s=[],l=[];return zr(e,(function(e){e.prepareSource();var t=e.getSource(a||0),n="";null==a||t||Wb(n),s.push(t),l.push(e._getVersionSign())})),i?t=function(e,t){var n=vd(e),i=n.length,a="";i||hd(a);for(var r=0,o=i;r1||n>0&&!e.noHeader;return zr(e.blocks,(function(e){var n=ew(e);n>=t&&(t=n+ +(i&&(!n||Qb(e)&&!e.noHeader)))})),t}return 0}function tw(e,t,n,i){var a,r=t.noHeader,o=(a=ew(t),{html:Yb[a],richText:Xb[a]}),s=[],l=t.blocks||[];go(!l||Xr(l)),l=l||[];var p=e.orderMode;if(t.sortBlocks&&p){l=l.slice();var c={valueAsc:"asc",valueDesc:"desc"};if(Eo(c,p)){var d=new Sb(c[p],null);l.sort((function(e,t){return d.evaluate(e.sortParam,t.sortParam)}))}else"seriesDesc"===p&&l.reverse()}zr(l,(function(n,a){var r=t.valueFormatter,l=Jb(n)(r?Br(Br({},e),{valueFormatter:r}):e,n,a>0?o.html:0,i);null!=l&&s.push(l)}));var u="richText"===e.renderMode?s.join(o.richText):aw(s.join(""),r?n:o.html);if(r)return u;var m=cv(t.header,"ordinal",e.useUTC),h=Kb(i,e.renderMode).nameStyle;return"richText"===e.renderMode?rw(e,m,h)+o.richText+u:aw('
'+os(m)+"
"+u,n)}function nw(e,t,n,i){var a=e.renderMode,r=t.noName,o=t.noValue,s=!t.markerType,l=t.name,p=e.useUTC,c=t.valueFormatter||e.valueFormatter||function(e){return Ur(e=Xr(e)?e:[e],(function(e,t){return cv(e,Xr(m)?m[t]:m,p)}))};if(!r||!o){var d=s?"":e.markupStyleCreator.makeTooltipMarker(t.markerType,t.markerColor||"#333",a),u=r?"":cv(l,"ordinal",p),m=t.valueType,h=o?[]:c(t.value,t.dataIndex),g=!s||!r,f=!s&&r,y=Kb(i,a),v=y.nameStyle,x=y.valueStyle;return"richText"===a?(s?"":d)+(r?"":rw(e,u,v))+(o?"":function(e,t,n,i,a){var r=[a],o=i?10:20;return n&&r.push({padding:[0,0,0,o],align:"right"}),e.markupStyleCreator.wrapRichTextStyle(Xr(t)?t.join(" "):t,r)}(e,h,g,f,x)):aw((s?"":d)+(r?"":function(e,t,n){return''+os(e)+""}(u,!s,v))+(o?"":function(e,t,n,i){var a=n?"10px":"20px",r=t?"float:right;margin-left:"+a:"";return e=Xr(e)?e:[e],''+Ur(e,(function(e){return os(e)})).join("  ")+""}(h,g,f,x)),n)}}function iw(e,t,n,i,a,r){if(e)return Jb(e)({useUTC:a,renderMode:n,orderMode:i,markupStyleCreator:t,valueFormatter:e.valueFormatter},e,0,r)}function aw(e,t){return'
'+e+'
'}function rw(e,t,n){return e.markupStyleCreator.wrapRichTextStyle(t,n)}function ow(e,t){return gv(e.getData().getItemVisual(t,"style")[e.visualDrawType])}function sw(e,t){var n=e.get("padding");return null!=n?n:"richText"===t?[8,10]:10}var lw=function(){function e(){this.richTextStyles={},this._nextStyleNameId=cd()}return e.prototype._generateStyleName=function(){return"__EC_aUTo_"+this._nextStyleNameId++},e.prototype.makeTooltipMarker=function(e,t,n){var i="richText"===n?this._generateStyleName():null,a=hv({color:t,type:e,renderMode:n,markerId:i});return Qr(a)?a:(this.richTextStyles[i]=a.style,a.content)},e.prototype.wrapRichTextStyle=function(e,t){var n={};Xr(t)?zr(t,(function(e){return Br(n,e)})):Br(n,t);var i=this._generateStyleName();return this.richTextStyles[i]=n,"{"+i+"|"+e+"}"},e}();function pw(e){var t,n,i,a,r=e.series,o=e.dataIndex,s=e.multipleSeries,l=r.getData(),p=l.mapDimensionsAll("defaultedTooltip"),c=p.length,d=r.getRawValue(o),u=Xr(d),m=ow(r,o);if(c>1||u&&!c){var h=function(e,t,n,i,a){var r=t.getData(),o=jr(e,(function(e,t,n){var i=r.getDimensionInfo(n);return e||i&&!1!==i.tooltip&&null!=i.displayName}),!1),s=[],l=[],p=[];function c(e,t){var n=r.getDimensionInfo(t);n&&!1!==n.otherDims.tooltip&&(o?p.push(Zb("nameValue",{markerType:"subItem",markerColor:a,name:n.displayName,value:e,valueType:n.type})):(s.push(e),l.push(n.type)))}return i.length?zr(i,(function(e){c(cb(r,n,e),e)})):zr(e,c),{inlineValues:s,inlineValueTypes:l,blocks:p}}(d,r,o,p,m);t=h.inlineValues,n=h.inlineValueTypes,i=h.blocks,a=h.inlineValues[0]}else if(c){var g=l.getDimensionInfo(p[0]);a=t=cb(l,o,p[0]),n=g.type}else a=t=u?d[0]:d;var f=Ed(r),y=f&&r.name||"",v=l.getName(o),x=s?y:v;return Zb("section",{header:y,noHeader:s||!f,sortParam:a,blocks:[Zb("nameValue",{markerType:"item",markerColor:m,name:x,noName:!fo(x),value:t,valueType:n,dataIndex:o})].concat(i||[])})}var cw=Pd();function dw(e,t){return e.getName(t)||e.getId(t)}var uw="__universalTransitionEnabled",mw=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t._selectedDataIndicesMap={},t}return We(t,e),t.prototype.init=function(e,t,n){this.seriesIndex=this.componentIndex,this.dataTask=hb({count:gw,reset:fw}),this.dataTask.context={model:this},this.mergeDefaultAndTheme(e,n),(cw(this).sourceManager=new Ub(this)).prepareSource();var i=this.getInitialData(e,n);vw(i,this),this.dataTask.context.data=i,cw(this).dataBeforeProcessed=i,hw(this),this._initSelectedMapFromData(i)},t.prototype.mergeDefaultAndTheme=function(e,t){var n=_v(this),i=n?Iv(e):{},a=this.subType;kv.hasClass(a)&&(a+="Series"),Fr(e,t.getTheme().get(this.subType)),Fr(e,this.getDefaultOption()),xd(e,"label",["show"]),this.fillDataTextStyle(e.data),n&&Tv(e,i,n)},t.prototype.mergeOption=function(e,t){e=Fr(this.option,e,!0),this.fillDataTextStyle(e.data);var n=_v(this);n&&Tv(this.option,e,n);var i=cw(this).sourceManager;i.dirty(),i.prepareSource();var a=this.getInitialData(e,t);vw(a,this),this.dataTask.dirty(),this.dataTask.context.data=a,cw(this).dataBeforeProcessed=a,hw(this),this._initSelectedMapFromData(a)},t.prototype.fillDataTextStyle=function(e){if(e&&!io(e))for(var t=["show"],n=0;nthis.getShallow("animationThreshold")&&(t=!1),!!t},t.prototype.restoreData=function(){this.dataTask.dirty()},t.prototype.getColorFromPalette=function(e,t,n){var i=this.ecModel,a=ix.prototype.getColorFromPalette.call(this,e,t,n);return a||(a=i.getColorFromPalette(e,t,n)),a},t.prototype.coordDimToDataDim=function(e){return this.getRawData().mapDimensionsAll(e)},t.prototype.getProgressive=function(){return this.get("progressive")},t.prototype.getProgressiveThreshold=function(){return this.get("progressiveThreshold")},t.prototype.select=function(e,t){this._innerSelect(this.getData(t),e)},t.prototype.unselect=function(e,t){var n=this.option.selectedMap;if(n){var i=this.option.selectedMode,a=this.getData(t);if("series"===i||"all"===n)return this.option.selectedMap={},void(this._selectedDataIndicesMap={});for(var r=0;r=0&&n.push(a)}return n},t.prototype.isSelected=function(e,t){var n=this.option.selectedMap;if(!n)return!1;var i=this.getData(t);return("all"===n||n[dw(i,e)])&&!i.getItemModel(e).get(["select","disabled"])},t.prototype.isUniversalTransitionEnabled=function(){if(this[uw])return!0;var e=this.option.universalTransition;return!!e&&(!0===e||e&&e.enabled)},t.prototype._innerSelect=function(e,t){var n,i,a=this.option,r=a.selectedMode,o=t.length;if(r&&o)if("series"===r)a.selectedMap="all";else if("multiple"===r){to(a.selectedMap)||(a.selectedMap={});for(var s=a.selectedMap,l=0;l0&&this._innerSelect(e,t)}},t.registerClass=function(e){return kv.registerClass(e)},t.protoInitialize=function(){var e=t.prototype;e.type="series.__base__",e.seriesIndex=0,e.ignoreStyleOnData=!1,e.hasSymbolVisual=!1,e.defaultSymbol="circle",e.visualStyleAccessPath="itemStyle",e.visualDrawType="fill"}(),t}(kv);function hw(e){var t=e.name;Ed(e)||(e.name=function(e){var t=e.getRawData(),n=t.mapDimensionsAll("seriesName"),i=[];return zr(n,(function(e){var n=t.getDimensionInfo(e);n.displayName&&i.push(n.displayName)})),i.join(" ")}(e)||t)}function gw(e){return e.model.getRawData().count()}function fw(e){var t=e.model;return t.setData(t.getRawData().cloneShallow()),yw}function yw(e,t){t.outputData&&e.end>t.outputData.count()&&t.model.getRawData().cloneShallow(t.outputData)}function vw(e,t){zr(_o(e.CHANGABLE_METHODS,e.DOWNSAMPLE_METHODS),(function(n){e.wrapMethod(n,Yr(xw,t))}))}function xw(e,t){var n=bw(e);return n&&n.setOutputEnd((t||this).count()),t}function bw(e){var t=(e.ecModel||{}).scheduler,n=t&&t.getPipeline(e.uid);if(n){var i=n.currentTask;if(i){var a=i.agentStubMap;a&&(i=a.get(e.uid))}return i}}qr(mw,ub),qr(mw,ix),jd(mw,kv);var ww=function(){function e(){this.group=new Rc,this.uid=wy("viewComponent")}return e.prototype.init=function(e,t){},e.prototype.render=function(e,t,n,i){},e.prototype.dispose=function(e,t){},e.prototype.updateView=function(e,t,n,i){},e.prototype.updateLayout=function(e,t,n,i){},e.prototype.updateVisual=function(e,t,n,i){},e.prototype.toggleBlurSeries=function(e,t,n){},e.prototype.eachRendered=function(e){var t=this.group;t&&t.traverse(e)},e}();function Sw(){var e=Pd();return function(t){var n=e(t),i=t.pipelineContext,a=!!n.large,r=!!n.progressiveRender,o=n.large=!(!i||!i.large),s=n.progressiveRender=!(!i||!i.progressiveRender);return!(a===o&&r===s)&&"reset"}}Ud(ww),Kd(ww);var Cw=Pd(),_w=Sw(),Tw=function(){function e(){this.group=new Rc,this.uid=wy("viewChart"),this.renderTask=hb({plan:Mw,reset:kw}),this.renderTask.context={view:this}}return e.prototype.init=function(e,t){},e.prototype.render=function(e,t,n,i){0},e.prototype.highlight=function(e,t,n,i){var a=e.getData(i&&i.dataType);a&&Ew(a,i,"emphasis")},e.prototype.downplay=function(e,t,n,i){var a=e.getData(i&&i.dataType);a&&Ew(a,i,"normal")},e.prototype.remove=function(e,t){this.group.removeAll()},e.prototype.dispose=function(e,t){},e.prototype.updateView=function(e,t,n,i){this.render(e,t,n,i)},e.prototype.updateLayout=function(e,t,n,i){this.render(e,t,n,i)},e.prototype.updateVisual=function(e,t,n,i){this.render(e,t,n,i)},e.prototype.eachRendered=function(e){jf(this.group,e)},e.markUpdateMethod=function(e,t){Cw(e).updateMethod=t},e.protoInitialize=void(e.prototype.type="chart"),e}();function Iw(e,t,n){e&&Hh(e)&&("emphasis"===t?Th:Ih)(e,n)}function Ew(e,t,n){var i=kd(e,t),a=t&&null!=t.highlightKey?function(e){var t=Jm[e];return null==t&&Qm<=32&&(t=Jm[e]=Qm++),t}(t.highlightKey):null;null!=i?zr(vd(i),(function(t){Iw(e.getItemGraphicEl(t),n,a)})):e.eachItemGraphicEl((function(e){Iw(e,n,a)}))}function Mw(e){return _w(e.model)}function kw(e){var t=e.model,n=e.ecModel,i=e.api,a=e.payload,r=t.pipelineContext.progressiveRender,o=e.view,s=a&&Cw(a).updateMethod,l=r?"incrementalPrepareRender":s&&o[s]?s:"render";return"render"!==l&&o[l](t,n,i,a),Pw[l]}Ud(Tw),Kd(Tw);var Pw={incrementalPrepareRender:{progress:function(e,t){t.view.incrementalRender(e,t.model,t.ecModel,t.api,t.payload)}},render:{forceFirstProgress:!0,progress:function(e,t){t.view.render(t.model,t.ecModel,t.api,t.payload)}}},Dw="\0__throttleOriginMethod",Ow="\0__throttleRate",Aw="\0__throttleType";function Fw(e,t,n){var i,a,r,o,s,l=0,p=0,c=null;function d(){p=(new Date).getTime(),c=null,e.apply(r,o||[])}t=t||0;var u=function(){for(var e=[],u=0;u=0?d():c=setTimeout(d,-a),l=i};return u.clear=function(){c&&(clearTimeout(c),c=null)},u.debounceNextCall=function(e){s=e},u}function Rw(e,t,n,i){var a=e[t];if(a){var r=a[Dw]||a,o=a[Aw];if(a[Ow]!==n||o!==i){if(null==n||!i)return e[t]=r;(a=e[t]=Fw(r,n,"debounce"===i))[Dw]=r,a[Aw]=i,a[Ow]=n}return a}}function Bw(e,t){var n=e[t];n&&n[Dw]&&(n.clear&&n.clear(),e[t]=n[Dw])}var Nw=Pd(),Lw={itemStyle:Yd(fy,!0),lineStyle:Yd(my,!0)},Vw={lineStyle:"stroke",itemStyle:"fill"};function qw(e,t){var n=e.visualStyleMapper||Lw[t];return n||(console.warn("Unknown style type '"+t+"'."),Lw.itemStyle)}function Gw(e,t){var n=e.visualDrawType||Vw[t];return n||(console.warn("Unknown style type '"+t+"'."),"fill")}var zw={createOnAllSeries:!0,performRawSeries:!0,reset:function(e,t){var n=e.getData(),i=e.visualStyleAccessPath||"itemStyle",a=e.getModel(i),r=qw(e,i)(a),o=a.getShallow("decal");o&&(n.setVisual("decal",o),o.dirty=!0);var s=Gw(e,i),l=r[s],p=Zr(l)?l:null,c="auto"===r.fill||"auto"===r.stroke;if(!r[s]||p||c){var d=e.getColorFromPalette(e.name,null,t.getSeriesCount());r[s]||(r[s]=d,n.setVisual("colorFromPalette",!0)),r.fill="auto"===r.fill||Zr(r.fill)?d:r.fill,r.stroke="auto"===r.stroke||Zr(r.stroke)?d:r.stroke}if(n.setVisual("style",r),n.setVisual("drawType",s),!t.isSeriesFiltered(e)&&p)return n.setVisual("colorFromPalette",!1),{dataEach:function(t,n){var i=e.getDataParams(n),a=Br({},r);a[s]=p(i),t.setItemVisual(n,"style",a)}}}},Uw=new xy,jw={createOnAllSeries:!0,performRawSeries:!0,reset:function(e,t){if(!e.ignoreStyleOnData&&!t.isSeriesFiltered(e)){var n=e.getData(),i=e.visualStyleAccessPath||"itemStyle",a=qw(e,i),r=n.getVisual("drawType");return{dataEach:n.hasItemOption?function(e,t){var n=e.getRawDataItem(t);if(n&&n[i]){Uw.option=n[i];var o=a(Uw);Br(e.ensureUniqueItemVisual(t,"style"),o),Uw.option.decal&&(e.setItemVisual(t,"decal",Uw.option.decal),Uw.option.decal.dirty=!0),r in o&&e.setItemVisual(t,"colorFromPalette",!1)}}:null}}}},Hw={performRawSeries:!0,overallReset:function(e){var t=Co();e.eachSeries((function(e){var n=e.getColorBy();if(!e.isColorBySeries()){var i=e.type+"-"+n,a=t.get(i);a||(a={},t.set(i,a)),Nw(e).scope=a}})),e.eachSeries((function(t){if(!t.isColorBySeries()&&!e.isSeriesFiltered(t)){var n=t.getRawData(),i={},a=t.getData(),r=Nw(t).scope,o=t.visualStyleAccessPath||"itemStyle",s=Gw(t,o);a.each((function(e){var t=a.getRawIndex(e);i[t]=e})),n.each((function(e){var o=i[e];if(a.getItemVisual(o,"colorFromPalette")){var l=a.ensureUniqueItemVisual(o,"style"),p=n.getName(e)||e+"",c=n.count();l[s]=t.getColorFromPalette(p,r,c)}}))}}))}},Ww=Math.PI;var $w=function(){function e(e,t,n,i){this._stageTaskMap=Co(),this.ecInstance=e,this.api=t,n=this._dataProcessorHandlers=n.slice(),i=this._visualHandlers=i.slice(),this._allHandlers=n.concat(i)}return e.prototype.restoreData=function(e,t){e.restoreData(t),this._stageTaskMap.each((function(e){var t=e.overallTask;t&&t.dirty()}))},e.prototype.getPerformArgs=function(e,t){if(e.__pipeline){var n=this._pipelineMap.get(e.__pipeline.id),i=n.context,a=!t&&n.progressiveEnabled&&(!i||i.progressiveRender)&&e.__idxInPipeline>n.blockIndex?n.step:null,r=i&&i.modDataCount;return{step:a,modBy:null!=r?Math.ceil(r/a):null,modDataCount:r}}},e.prototype.getPipeline=function(e){return this._pipelineMap.get(e)},e.prototype.updateStreamModes=function(e,t){var n=this._pipelineMap.get(e.uid),i=e.getData().count(),a=n.progressiveEnabled&&t.incrementalPrepareRender&&i>=n.threshold,r=e.get("large")&&i>=e.get("largeThreshold"),o="mod"===e.get("progressiveChunkMode")?i:null;e.pipelineContext=n.context={progressiveRender:a,modDataCount:o,large:r}},e.prototype.restorePipelines=function(e){var t=this,n=t._pipelineMap=Co();e.eachSeries((function(e){var i=e.getProgressive(),a=e.uid;n.set(a,{id:a,head:null,tail:null,threshold:e.getProgressiveThreshold(),progressiveEnabled:i&&!(e.preventIncremental&&e.preventIncremental()),blockIndex:-1,step:Math.round(i||700),count:0}),t._pipe(e,e.dataTask)}))},e.prototype.prepareStageTasks=function(){var e=this._stageTaskMap,t=this.api.getModel(),n=this.api;zr(this._allHandlers,(function(i){var a=e.get(i.uid)||e.set(i.uid,{}),r="";go(!(i.reset&&i.overallReset),r),i.reset&&this._createSeriesStageTask(i,a,t,n),i.overallReset&&this._createOverallStageTask(i,a,t,n)}),this)},e.prototype.prepareView=function(e,t,n,i){var a=e.renderTask,r=a.context;r.model=t,r.ecModel=n,r.api=i,a.__block=!e.incrementalPrepareRender,this._pipe(t,a)},e.prototype.performDataProcessorTasks=function(e,t){this._performStageTasks(this._dataProcessorHandlers,e,t,{block:!0})},e.prototype.performVisualTasks=function(e,t,n){this._performStageTasks(this._visualHandlers,e,t,n)},e.prototype._performStageTasks=function(e,t,n,i){i=i||{};var a=!1,r=this;function o(e,t){return e.setDirty&&(!e.dirtyMap||e.dirtyMap.get(t.__pipeline.id))}zr(e,(function(e,s){if(!i.visualType||i.visualType===e.visualType){var l=r._stageTaskMap.get(e.uid),p=l.seriesTaskMap,c=l.overallTask;if(c){var d,u=c.agentStubMap;u.each((function(e){o(i,e)&&(e.dirty(),d=!0)})),d&&c.dirty(),r.updatePayload(c,n);var m=r.getPerformArgs(c,i.block);u.each((function(e){e.perform(m)})),c.perform(m)&&(a=!0)}else p&&p.each((function(s,l){o(i,s)&&s.dirty();var p=r.getPerformArgs(s,i.block);p.skip=!e.performRawSeries&&t.isSeriesFiltered(s.context.model),r.updatePayload(s,n),s.perform(p)&&(a=!0)}))}})),this.unfinished=a||this.unfinished},e.prototype.performSeriesTasks=function(e){var t;e.eachSeries((function(e){t=e.dataTask.perform()||t})),this.unfinished=t||this.unfinished},e.prototype.plan=function(){this._pipelineMap.each((function(e){var t=e.tail;do{if(t.__block){e.blockIndex=t.__idxInPipeline;break}t=t.getUpstream()}while(t)}))},e.prototype.updatePayload=function(e,t){"remain"!==t&&(e.context.payload=t)},e.prototype._createSeriesStageTask=function(e,t,n,i){var a=this,r=t.seriesTaskMap,o=t.seriesTaskMap=Co(),s=e.seriesType,l=e.getTargetSeries;function p(t){var s=t.uid,l=o.set(s,r&&r.get(s)||hb({plan:Qw,reset:Jw,count:nS}));l.context={model:t,ecModel:n,api:i,useClearVisual:e.isVisual&&!e.isLayout,plan:e.plan,reset:e.reset,scheduler:a},a._pipe(t,l)}e.createOnAllSeries?n.eachRawSeries(p):s?n.eachRawSeriesByType(s,p):l&&l(n,i).each(p)},e.prototype._createOverallStageTask=function(e,t,n,i){var a=this,r=t.overallTask=t.overallTask||hb({reset:Kw});r.context={ecModel:n,api:i,overallReset:e.overallReset,scheduler:a};var o=r.agentStubMap,s=r.agentStubMap=Co(),l=e.seriesType,p=e.getTargetSeries,c=!0,d=!1,u="";function m(e){var t=e.uid,n=s.set(t,o&&o.get(t)||(d=!0,hb({reset:Yw,onDirty:Zw})));n.context={model:e,overallProgress:c},n.agent=r,n.__block=c,a._pipe(e,n)}go(!e.createOnAllSeries,u),l?n.eachRawSeriesByType(l,m):p?p(n,i).each(m):(c=!1,zr(n.getSeries(),m)),d&&r.dirty()},e.prototype._pipe=function(e,t){var n=e.uid,i=this._pipelineMap.get(n);!i.head&&(i.head=t),i.tail&&i.tail.pipe(t),i.tail=t,t.__idxInPipeline=i.count++,t.__pipeline=i},e.wrapStageHandler=function(e,t){return Zr(e)&&(e={overallReset:e,seriesType:iS(e)}),e.uid=wy("stageHandler"),t&&(e.visualType=t),e},e}();function Kw(e){e.overallReset(e.ecModel,e.api,e.payload)}function Yw(e){return e.overallProgress&&Xw}function Xw(){this.agent.dirty(),this.getDownstream().dirty()}function Zw(){this.agent&&this.agent.dirty()}function Qw(e){return e.plan?e.plan(e.model,e.ecModel,e.api,e.payload):null}function Jw(e){e.useClearVisual&&e.data.clearAllVisual();var t=e.resetDefines=vd(e.reset(e.model,e.ecModel,e.api,e.payload));return t.length>1?Ur(t,(function(e,t){return tS(t)})):eS}var eS=tS(0);function tS(e){return function(t,n){var i=n.data,a=n.resetDefines[e];if(a&&a.dataEach)for(var r=t.start;r0&&c===a.length-p.length){var d=a.slice(0,c);"data"!==d&&(t.mainType=d,t[p.toLowerCase()]=e,s=!0)}}o.hasOwnProperty(a)&&(n[a]=e,s=!0),s||(i[a]=e)}))}return{cptQuery:t,dataQuery:n,otherQuery:i}},e.prototype.filter=function(e,t){var n=this.eventInfo;if(!n)return!0;var i=n.targetEl,a=n.packedEvent,r=n.model,o=n.view;if(!r||!o)return!0;var s=t.cptQuery,l=t.dataQuery;return p(s,r,"mainType")&&p(s,r,"subType")&&p(s,r,"index","componentIndex")&&p(s,r,"name")&&p(s,r,"id")&&p(l,a,"name")&&p(l,a,"dataIndex")&&p(l,a,"dataType")&&(!o.filterForExposedEvent||o.filterForExposedEvent(e,t.otherQuery,i,a));function p(e,t,n,i){return null==e[n]||t[i||n]===e[n]}},e.prototype.afterTrigger=function(){this.eventInfo=null},e}(),fS=["symbol","symbolSize","symbolRotate","symbolOffset"],yS=fS.concat(["symbolKeepAspect"]),vS={createOnAllSeries:!0,performRawSeries:!0,reset:function(e,t){var n=e.getData();if(e.legendIcon&&n.setVisual("legendIcon",e.legendIcon),e.hasSymbolVisual){for(var i={},a={},r=!1,o=0;o=0&&qS(l)?l:.5,e.createRadialGradient(o,s,0,o,s,l)}(e,t,n):function(e,t,n){var i=null==t.x?0:t.x,a=null==t.x2?1:t.x2,r=null==t.y?0:t.y,o=null==t.y2?0:t.y2;return t.global||(i=i*n.width+n.x,a=a*n.width+n.x,r=r*n.height+n.y,o=o*n.height+n.y),i=qS(i)?i:0,a=qS(a)?a:1,r=qS(r)?r:0,o=qS(o)?o:0,e.createLinearGradient(i,r,a,o)}(e,t,n),a=t.colorStops,r=0;r0&&(t=i.lineDash,n=i.lineWidth,t&&"solid"!==t&&n>0?"dashed"===t?[4*n,2*n]:"dotted"===t?[n]:eo(t)?[t]:Xr(t)?t:null:null),r=i.lineDashOffset;if(a){var o=i.strokeNoScale&&e.getLineScale?e.getLineScale():1;o&&1!==o&&(a=Ur(a,(function(e){return e/o})),r/=o)}return[a,r]}var HS=new nm(!0);function WS(e){var t=e.stroke;return!(null==t||"none"===t||!(e.lineWidth>0))}function $S(e){return"string"==typeof e&&"none"!==e}function KS(e){var t=e.fill;return null!=t&&"none"!==t}function YS(e,t){if(null!=t.fillOpacity&&1!==t.fillOpacity){var n=e.globalAlpha;e.globalAlpha=t.fillOpacity*t.opacity,e.fill(),e.globalAlpha=n}else e.fill()}function XS(e,t){if(null!=t.strokeOpacity&&1!==t.strokeOpacity){var n=e.globalAlpha;e.globalAlpha=t.strokeOpacity*t.opacity,e.stroke(),e.globalAlpha=n}else e.stroke()}function ZS(e,t,n){var i=eu(t.image,t.__image,n);if(nu(i)){var a=e.createPattern(i,t.repeat||"repeat");if("function"==typeof DOMMatrix&&a&&a.setTransform){var r=new DOMMatrix;r.translateSelf(t.x||0,t.y||0),r.rotateSelf(0,0,(t.rotation||0)*ko),r.scaleSelf(t.scaleX||1,t.scaleY||1),a.setTransform(r)}return a}}var QS=["shadowBlur","shadowOffsetX","shadowOffsetY"],JS=[["lineCap","butt"],["lineJoin","miter"],["miterLimit",10]];function eC(e,t,n,i,a){var r=!1;if(!i&&t===(n=n||{}))return!1;if(i||t.opacity!==n.opacity){iC(e,a),r=!0;var o=Math.max(Math.min(t.opacity,1),0);e.globalAlpha=isNaN(o)?fu.opacity:o}(i||t.blend!==n.blend)&&(r||(iC(e,a),r=!0),e.globalCompositeOperation=t.blend||fu.blend);for(var s=0;s0&&e.unfinished);e.unfinished||this._zr.flush()}}},t.prototype.getDom=function(){return this._dom},t.prototype.getId=function(){return this.id},t.prototype.getZr=function(){return this._zr},t.prototype.isSSR=function(){return this._ssr},t.prototype.setOption=function(e,t,n){if(!this[bC])if(this._disposed)ZC(this.id);else{var i,a,r;if(to(t)&&(n=t.lazyUpdate,i=t.silent,a=t.replaceMerge,r=t.transition,t=t.notMerge),this[bC]=!0,!this._model||t){var o=new fx(this._api),s=this._theme,l=this._model=new sx;l.scheduler=this._scheduler,l.ssr=this._ssr,l.init(null,null,null,s,this._locale,o)}this._model.setOption(e,{replaceMerge:a},t_);var p={seriesTransition:r,optionChanged:!0};if(n)this[wC]={silent:i,updateParams:p},this[bC]=!1,this.getZr().wakeUp();else{try{MC(this),DC.update.call(this,null,p)}catch(e){throw this[wC]=null,this[bC]=!1,e}this._ssr||this._zr.flush(),this[wC]=null,this[bC]=!1,RC.call(this,i),BC.call(this,i)}}},t.prototype.setTheme=function(){md()},t.prototype.getModel=function(){return this._model},t.prototype.getOption=function(){return this._model&&this._model.getOption()},t.prototype.getWidth=function(){return this._zr.getWidth()},t.prototype.getHeight=function(){return this._zr.getHeight()},t.prototype.getDevicePixelRatio=function(){return this._zr.painter.dpr||ur.hasGlobalWindow&&window.devicePixelRatio||1},t.prototype.getRenderedCanvas=function(e){return this.renderToCanvas(e)},t.prototype.renderToCanvas=function(e){e=e||{};var t=this._zr.painter;return t.getRenderedCanvas({backgroundColor:e.backgroundColor||this._model.get("backgroundColor"),pixelRatio:e.pixelRatio||this.getDevicePixelRatio()})},t.prototype.renderToSVGString=function(e){e=e||{};var t=this._zr.painter;return t.renderToString({useViewBox:e.useViewBox})},t.prototype.getSvgDataURL=function(){if(ur.svgSupported){var e=this._zr;return zr(e.storage.getDisplayList(),(function(e){e.stopAnimation(null,!0)})),e.painter.toDataURL()}},t.prototype.getDataURL=function(e){if(!this._disposed){var t=(e=e||{}).excludeComponents,n=this._model,i=[],a=this;zr(t,(function(e){n.eachComponent({mainType:e},(function(e){var t=a._componentsMap[e.__viewId];t.group.ignore||(i.push(t),t.group.ignore=!0)}))}));var r="svg"===this._zr.painter.getType()?this.getSvgDataURL():this.renderToCanvas(e).toDataURL("image/"+(e&&e.type||"png"));return zr(i,(function(e){e.group.ignore=!1})),r}ZC(this.id)},t.prototype.getConnectedDataURL=function(e){if(!this._disposed){var t="svg"===e.type,n=this.group,i=Math.min,a=Math.max,r=1/0;if(o_[n]){var o=r,s=r,l=-1/0,p=-1/0,c=[],d=e&&e.pixelRatio||this.getDevicePixelRatio();zr(r_,(function(r,d){if(r.group===n){var u=t?r.getZr().painter.getSvgDom().innerHTML:r.renderToCanvas(Ar(e)),m=r.getDom().getBoundingClientRect();o=i(m.left,o),s=i(m.top,s),l=a(m.right,l),p=a(m.bottom,p),c.push({dom:u,left:m.left,top:m.top})}}));var u=(l*=d)-(o*=d),m=(p*=d)-(s*=d),h=vr.createCanvas(),g=qc(h,{renderer:t?"svg":"canvas"});if(g.resize({width:u,height:m}),t){var f="";return zr(c,(function(e){var t=e.left-o,n=e.top-s;f+=''+e.dom+""})),g.painter.getSvgRoot().innerHTML=f,e.connectedBackgroundColor&&g.painter.setBackgroundColor(e.connectedBackgroundColor),g.refreshImmediately(),g.painter.toDataURL()}return e.connectedBackgroundColor&&g.add(new Fm({shape:{x:0,y:0,width:u,height:m},style:{fill:e.connectedBackgroundColor}})),zr(c,(function(e){var t=new Em({style:{x:e.left*d-o,y:e.top*d-s,image:e.dom}});g.add(t)})),g.refreshImmediately(),h.toDataURL("image/"+(e&&e.type||"png"))}return this.getDataURL(e)}ZC(this.id)},t.prototype.convertToPixel=function(e,t){return OC(this,"convertToPixel",e,t)},t.prototype.convertFromPixel=function(e,t){return OC(this,"convertFromPixel",e,t)},t.prototype.containPixel=function(e,t){var n;if(!this._disposed)return zr(Od(this._model,e),(function(e,i){i.indexOf("Models")>=0&&zr(e,(function(e){var a=e.coordinateSystem;if(a&&a.containPoint)n=n||!!a.containPoint(t);else if("seriesModels"===i){var r=this._chartsMap[e.__viewId];r&&r.containPoint&&(n=n||r.containPoint(t,e))}else 0}),this)}),this),!!n;ZC(this.id)},t.prototype.getVisual=function(e,t){var n=Od(this._model,e,{defaultMainType:"series"}),i=n.seriesModel;var a=i.getData(),r=n.hasOwnProperty("dataIndexInside")?n.dataIndexInside:n.hasOwnProperty("dataIndex")?a.indexOfRawIndex(n.dataIndex):null;return null!=r?bS(a,r,t):wS(a,t)},t.prototype.getViewOfComponentModel=function(e){return this._componentsMap[e.__viewId]},t.prototype.getViewOfSeriesModel=function(e){return this._chartsMap[e.__viewId]},t.prototype._initEvents=function(){var e,t,n,i=this;zr(XC,(function(e){var t=function(t){var n,a=i.getModel(),r=t.target,o="globalout"===e;if(o?n={}:r&&TS(r,(function(e){var t=Xm(e);if(t&&null!=t.dataIndex){var i=t.dataModel||a.getSeriesByIndex(t.seriesIndex);return n=i&&i.getDataParams(t.dataIndex,t.dataType,r)||{},!0}if(t.eventData)return n=Br({},t.eventData),!0}),!0),n){var s=n.componentType,l=n.componentIndex;"markLine"!==s&&"markPoint"!==s&&"markArea"!==s||(s="series",l=n.seriesIndex);var p=s&&null!=l&&a.getComponent(s,l),c=p&&i["series"===p.mainType?"_chartsMap":"_componentsMap"][p.__viewId];0,n.event=t,n.type=e,i._$eventProcessor.eventInfo={targetEl:r,packedEvent:n,model:p,view:c},i.trigger(e,n)}};t.zrEventfulCallAtLast=!0,i._zr.on(e,t,i)})),zr(JC,(function(e,t){i._messageCenter.on(t,(function(e){this.trigger(t,e)}),i)})),zr(["selectchanged"],(function(e){i._messageCenter.on(e,(function(t){this.trigger(e,t)}),i)})),e=this._messageCenter,t=this,n=this._api,e.on("selectchanged",(function(e){var i=n.getModel();e.isFromClick?(_S("map","selectchanged",t,i,e),_S("pie","selectchanged",t,i,e)):"select"===e.fromAction?(_S("map","selected",t,i,e),_S("pie","selected",t,i,e)):"unselect"===e.fromAction&&(_S("map","unselected",t,i,e),_S("pie","unselected",t,i,e))}))},t.prototype.isDisposed=function(){return this._disposed},t.prototype.clear=function(){this._disposed?ZC(this.id):this.setOption({series:[]},!0)},t.prototype.dispose=function(){if(this._disposed)ZC(this.id);else{this._disposed=!0,this.getDom()&&Nd(this.getDom(),l_,"");var e=this,t=e._api,n=e._model;zr(e._componentsViews,(function(e){e.dispose(n,t)})),zr(e._chartsViews,(function(e){e.dispose(n,t)})),e._zr.dispose(),e._dom=e._model=e._chartsMap=e._componentsMap=e._chartsViews=e._componentsViews=e._scheduler=e._api=e._zr=e._throttledZrFlush=e._theme=e._coordSysMgr=e._messageCenter=null,delete r_[e.id]}},t.prototype.resize=function(e){if(!this[bC])if(this._disposed)ZC(this.id);else{this._zr.resize(e);var t=this._model;if(this._loadingFX&&this._loadingFX.resize(),t){var n=t.resetOption("media"),i=e&&e.silent;this[wC]&&(null==i&&(i=this[wC].silent),n=!0,this[wC]=null),this[bC]=!0;try{n&&MC(this),DC.update.call(this,{type:"resize",animation:Br({duration:0},e&&e.animation)})}catch(e){throw this[bC]=!1,e}this[bC]=!1,RC.call(this,i),BC.call(this,i)}}},t.prototype.showLoading=function(e,t){if(this._disposed)ZC(this.id);else if(to(e)&&(t=e,e=""),e=e||"default",this.hideLoading(),a_[e]){var n=a_[e](this._api,t),i=this._zr;this._loadingFX=n,i.add(n)}},t.prototype.hideLoading=function(){this._disposed?ZC(this.id):(this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null)},t.prototype.makeActionFromEvent=function(e){var t=Br({},e);return t.type=JC[e.type],t},t.prototype.dispatchAction=function(e,t){if(this._disposed)ZC(this.id);else if(to(t)||(t={silent:!!t}),QC[e.type]&&this._model)if(this[bC])this._pendingActions.push(e);else{var n=t.silent;FC.call(this,e,n);var i=t.flush;i?this._zr.flush():!1!==i&&ur.browser.weChat&&this._throttledZrFlush(),RC.call(this,n),BC.call(this,n)}},t.prototype.updateLabelLayout=function(){hC.trigger("series:layoutlabels",this._model,this._api,{updatedSeries:[]})},t.prototype.appendData=function(e){if(this._disposed)ZC(this.id);else{var t=e.seriesIndex,n=this.getModel().getSeriesByIndex(t);0,n.appendData(e),this._scheduler.unfinished=!0,this.getZr().wakeUp()}},t.internalField=function(){function e(e){e.clearColorPalette(),e.eachSeries((function(e){e.clearColorPalette()}))}function t(e){for(var t=[],n=e.currentStates,i=0;i0?{duration:r,delay:i.get("delay"),easing:i.get("easing")}:null;n.eachRendered((function(e){if(e.states&&e.states.emphasis){if(df(e))return;if(e instanceof Sm&&function(e){var t=eh(e);t.normalFill=e.style.fill,t.normalStroke=e.style.stroke;var n=e.states.select||{};t.selectFill=n.style&&n.style.fill||null,t.selectStroke=n.style&&n.style.stroke||null}(e),e.__dirty){var n=e.prevStates;n&&e.useStates(n)}if(a){e.stateTransition=o;var i=e.getTextContent(),r=e.getTextGuideLine();i&&(i.stateTransition=o),r&&(r.stateTransition=o)}e.__dirty&&t(e)}}))}MC=function(e){var t=e._scheduler;t.restorePipelines(e._model),t.prepareStageTasks(),kC(e,!0),kC(e,!1),t.plan()},kC=function(e,t){for(var n=e._model,i=e._scheduler,a=t?e._componentsViews:e._chartsViews,r=t?e._componentsMap:e._chartsMap,o=e._zr,s=e._api,l=0;lt.get("hoverLayerThreshold")&&!ur.node&&!ur.worker&&t.eachSeries((function(t){if(!t.preventUsingHoverLayer){var n=e._chartsMap[t.__viewId];n.__alive&&n.eachRendered((function(e){e.states.emphasis&&(e.states.emphasis.hoverLayer=!0)}))}}))}(e,t),hC.trigger("series:afterupdate",t,i,s)},jC=function(e){e[SC]=!0,e.getZr().wakeUp()},HC=function(e){e[SC]&&(e.getZr().storage.traverse((function(e){df(e)||t(e)})),e[SC]=!1)},zC=function(e){return new(function(t){function n(){return null!==t&&t.apply(this,arguments)||this}return We(n,t),n.prototype.getCoordinateSystems=function(){return e._coordSysMgr.getCoordinateSystems()},n.prototype.getComponentByElement=function(t){for(;t;){var n=t.__ecComponentInfo;if(null!=n)return e._model.getComponent(n.mainType,n.index);t=t.parent}},n.prototype.enterEmphasis=function(t,n){Th(t,n),jC(e)},n.prototype.leaveEmphasis=function(t,n){Ih(t,n),jC(e)},n.prototype.enterBlur=function(t){Eh(t),jC(e)},n.prototype.leaveBlur=function(t){Mh(t),jC(e)},n.prototype.enterSelect=function(t){kh(t),jC(e)},n.prototype.leaveSelect=function(t){Ph(t),jC(e)},n.prototype.getModel=function(){return e.getModel()},n.prototype.getViewOfComponentModel=function(t){return e.getViewOfComponentModel(t)},n.prototype.getViewOfSeriesModel=function(t){return e.getViewOfSeriesModel(t)},n}(ux))(e)},UC=function(e){function t(e,t){for(var n=0;n=0)){f_.push(n);var r=$w.wrapStageHandler(n,a);r.__prio=t,r.__raw=n,e.push(r)}}function v_(e,t){a_[e]=t}var x_=function(e){var t=(e=Ar(e)).type,n="";t||hd(n);var i=t.split(":");2!==i.length&&hd(n);var a=!1;"echarts"===i[0]&&(t=i[1],a=!0),e.__isBuiltIn=a,Pb.set(t,e)};function b_(e){return null==e?0:e.length||1}function w_(e){return e}g_(yC,zw),g_(vC,jw),g_(vC,Hw),g_(yC,vS),g_(vC,xS),g_(7e3,(function(e,t){e.eachRawSeries((function(n){if(!e.isSeriesFiltered(n)){var i=n.getData();i.hasItemVisual()&&i.each((function(e){var n=i.getItemVisual(e,"decal");n&&(i.ensureUniqueItemVisual(e,"style").decal=cC(n,t))}));var a=i.getVisual("decal");if(a)i.getVisual("style").decal=cC(a,t)}}))})),d_(Lx),u_(900,(function(e){var t=Co();e.eachSeries((function(e){var n=e.get("stack");if(n){var i=t.get(n)||t.set(n,[]),a=e.getData(),r={stackResultDimension:a.getCalculationInfo("stackResultDimension"),stackedOverDimension:a.getCalculationInfo("stackedOverDimension"),stackedDimension:a.getCalculationInfo("stackedDimension"),stackedByDimension:a.getCalculationInfo("stackedByDimension"),isStackedByIndex:a.getCalculationInfo("isStackedByIndex"),data:a,seriesModel:e};if(!r.stackedDimension||!r.isStackedByIndex&&!r.stackedByDimension)return;i.length&&a.setCalculationInfo("stackedOnSeries",i[i.length-1].seriesModel),i.push(r)}})),t.each(Vx)})),v_("default",(function(e,t){Nr(t=t||{},{text:"loading",textColor:"#000",fontSize:12,fontWeight:"normal",fontStyle:"normal",fontFamily:"sans-serif",maskColor:"rgba(255, 255, 255, 0.8)",showSpinner:!0,color:"#5470c6",spinnerRadius:10,lineWidth:5,zlevel:0});var n=new Rc,i=new Fm({style:{fill:t.maskColor},zlevel:t.zlevel,z:1e4});n.add(i);var a,r=new Nm({style:{text:t.text,fill:t.textColor,fontSize:t.fontSize,fontWeight:t.fontWeight,fontStyle:t.fontStyle,fontFamily:t.fontFamily},zlevel:t.zlevel,z:10001}),o=new Fm({style:{fill:"none"},textContent:r,textConfig:{position:"right",distance:10},zlevel:t.zlevel,z:10001});return n.add(o),t.showSpinner&&((a=new $g({shape:{startAngle:-Ww/2,endAngle:-Ww/2+.1,r:t.spinnerRadius},style:{stroke:t.color,lineCap:"round",lineWidth:t.lineWidth},zlevel:t.zlevel,z:10001})).animateShape(!0).when(1e3,{endAngle:3*Ww/2}).start("circularInOut"),a.animateShape(!0).when(1e3,{startAngle:3*Ww/2}).delay(300).start("circularInOut"),n.add(a)),n.resize=function(){var n=r.getBoundingRect().width,s=t.showSpinner?t.spinnerRadius:0,l=(e.getWidth()-2*s-(t.showSpinner&&n?10:0)-n)/2-(t.showSpinner&&n?0:5+n/2)+(t.showSpinner?0:n/2)+(n?0:s),p=e.getHeight()/2;t.showSpinner&&a.setShape({cx:l,cy:p}),o.setShape({x:l-s,y:p-s,width:2*s,height:2*s}),i.setShape({x:0,y:0,width:e.getWidth(),height:e.getHeight()})},n.resize(),n})),h_({type:rh,event:rh,update:rh},Mo),h_({type:oh,event:oh,update:oh},Mo),h_({type:sh,event:sh,update:sh},Mo),h_({type:lh,event:lh,update:lh},Mo),h_({type:ph,event:ph,update:ph},Mo),c_("light",pS),c_("dark",hS);var S_=function(){function e(e,t,n,i,a,r){this._old=e,this._new=t,this._oldKeyGetter=n||w_,this._newKeyGetter=i||w_,this.context=a,this._diffModeMultiple="multiple"===r}return e.prototype.add=function(e){return this._add=e,this},e.prototype.update=function(e){return this._update=e,this},e.prototype.updateManyToOne=function(e){return this._updateManyToOne=e,this},e.prototype.updateOneToMany=function(e){return this._updateOneToMany=e,this},e.prototype.updateManyToMany=function(e){return this._updateManyToMany=e,this},e.prototype.remove=function(e){return this._remove=e,this},e.prototype.execute=function(){this[this._diffModeMultiple?"_executeMultiple":"_executeOneToOne"]()},e.prototype._executeOneToOne=function(){var e=this._old,t=this._new,n={},i=new Array(e.length),a=new Array(t.length);this._initIndexMap(e,null,i,"_oldKeyGetter"),this._initIndexMap(t,n,a,"_newKeyGetter");for(var r=0;r1){var p=s.shift();1===s.length&&(n[o]=s[0]),this._update&&this._update(p,r)}else 1===l?(n[o]=null,this._update&&this._update(s,r)):this._remove&&this._remove(r)}this._performRestAdd(a,n)},e.prototype._executeMultiple=function(){var e=this._old,t=this._new,n={},i={},a=[],r=[];this._initIndexMap(e,n,a,"_oldKeyGetter"),this._initIndexMap(t,i,r,"_newKeyGetter");for(var o=0;o1&&1===d)this._updateManyToOne&&this._updateManyToOne(p,l),i[s]=null;else if(1===c&&d>1)this._updateOneToMany&&this._updateOneToMany(p,l),i[s]=null;else if(1===c&&1===d)this._update&&this._update(p,l),i[s]=null;else if(c>1&&d>1)this._updateManyToMany&&this._updateManyToMany(p,l),i[s]=null;else if(c>1)for(var u=0;u1)for(var o=0;o30}var F_,R_,B_,N_,L_,V_,q_,G_=to,z_=Ur,U_="undefined"==typeof Int32Array?Array:Int32Array,j_=["hasItemOption","_nameList","_idList","_invertedIndicesMap","_dimSummary","userOutput","_rawData","_dimValueGetter","_nameDimIdx","_idDimIdx","_nameRepeatCount"],H_=["_approximateExtent"],W_=function(){function e(e,t){var n;this.type="list",this._dimOmitted=!1,this._nameList=[],this._idList=[],this._visual={},this._layout={},this._itemVisuals=[],this._itemLayouts=[],this._graphicEls=[],this._approximateExtent={},this._calculationInfo={},this.hasItemOption=!1,this.TRANSFERABLE_METHODS=["cloneShallow","downSample","lttbDownSample","map"],this.CHANGABLE_METHODS=["filterSelf","selectRange"],this.DOWNSAMPLE_METHODS=["downSample","lttbDownSample"];var i=!1;P_(e)?(n=e.dimensions,this._dimOmitted=e.isDimensionOmitted(),this._schema=e):(i=!0,n=e),n=n||["x","y"];for(var a={},r=[],o={},s=!1,l={},p=0;p=t)){var n=this._store.getProvider();this._updateOrdinalMeta();var i=this._nameList,a=this._idList;if(n.getSource().sourceFormat===Fv&&!n.pure)for(var r=[],o=e;o0},e.prototype.ensureUniqueItemVisual=function(e,t){var n=this._itemVisuals,i=n[e];i||(i=n[e]={});var a=i[t];return null==a&&(Xr(a=this.getVisual(t))?a=a.slice():G_(a)&&(a=Br({},a)),i[t]=a),a},e.prototype.setItemVisual=function(e,t,n){var i=this._itemVisuals[e]||{};this._itemVisuals[e]=i,G_(t)?Br(i,t):i[t]=n},e.prototype.clearAllVisual=function(){this._visual={},this._itemVisuals=[]},e.prototype.setLayout=function(e,t){G_(e)?Br(this._layout,e):this._layout[e]=t},e.prototype.getLayout=function(e){return this._layout[e]},e.prototype.getItemLayout=function(e){return this._itemLayouts[e]},e.prototype.setItemLayout=function(e,t,n){this._itemLayouts[e]=n?Br(this._itemLayouts[e]||{},t):t},e.prototype.clearItemLayouts=function(){this._itemLayouts.length=0},e.prototype.setItemGraphicEl=function(e,t){var n=this.hostModel&&this.hostModel.seriesIndex;Zm(n,this.dataType,e,t),this._graphicEls[e]=t},e.prototype.getItemGraphicEl=function(e){return this._graphicEls[e]},e.prototype.eachItemGraphicEl=function(e,t){zr(this._graphicEls,(function(n,i){n&&e&&e.call(t,n,i)}))},e.prototype.cloneShallow=function(t){return t||(t=new e(this._schema?this._schema:z_(this.dimensions,this._getDimInfo,this),this.hostModel)),L_(t,this),t._store=this._store,t},e.prototype.wrapMethod=function(e,t){var n=this[e];Zr(n)&&(this.__wrappedMethods=this.__wrappedMethods||[],this.__wrappedMethods.push(e),this[e]=function(){var e=n.apply(this,arguments);return t.apply(this,[e].concat(mo(arguments)))})},e.internalField=(F_=function(e){var t=e._invertedIndicesMap;zr(t,(function(n,i){var a=e._dimInfos[i],r=a.ordinalMeta,o=a.stack,s=e._store;if(r||o){if(n=t[i]=o?new Array(s.count()):new U_(r.categories.length),r)for(var l=0;l1&&(s+="__ec__"+p),i[t]=s}})),e}();function $_(e,t){Wx(e)||(e=Kx(e));var n=(t=t||{}).coordDimensions||[],i=t.dimensionsDefine||e.dimensionsDefine||[],a=Co(),r=[],o=function(e,t,n,i){var a=Math.max(e.dimensionsDetectedCount||1,t.length,n.length,i||0);return zr(t,(function(e){var t;to(e)&&(t=e.dimsDef)&&(a=Math.max(a,t.length))})),a}(e,n,i,t.dimensionsCount),s=t.canOmitUnusedDimensions&&A_(o),l=i===e.dimensionsDefine,p=l?O_(e):D_(i),c=t.encodeDefine;!c&&t.encodeDefaulter&&(c=t.encodeDefaulter(e,o));for(var d=Co(c),u=new Nb(o),m=0;m0&&(i.name=a+(r-1)),r++,t.set(a,r)}}(r),new k_({source:e,dimensions:r,fullDimensionCount:o,dimensionOmitted:s})}function K_(e,t,n){if(n||t.hasKey(e)){for(var i=0;t.hasKey(e+i);)i++;e+=i}return t.set(e,!0),e}var Y_=function(e){this.coordSysDims=[],this.axisMap=Co(),this.categoryAxisMap=Co(),this.coordSysName=e};var X_={cartesian2d:function(e,t,n,i){var a=e.getReferringComponents("xAxis",Fd).models[0],r=e.getReferringComponents("yAxis",Fd).models[0];t.coordSysDims=["x","y"],n.set("x",a),n.set("y",r),Z_(a)&&(i.set("x",a),t.firstCategoryDimIndex=0),Z_(r)&&(i.set("y",r),null==t.firstCategoryDimIndex&&(t.firstCategoryDimIndex=1))},singleAxis:function(e,t,n,i){var a=e.getReferringComponents("singleAxis",Fd).models[0];t.coordSysDims=["single"],n.set("single",a),Z_(a)&&(i.set("single",a),t.firstCategoryDimIndex=0)},polar:function(e,t,n,i){var a=e.getReferringComponents("polar",Fd).models[0],r=a.findAxisModel("radiusAxis"),o=a.findAxisModel("angleAxis");t.coordSysDims=["radius","angle"],n.set("radius",r),n.set("angle",o),Z_(r)&&(i.set("radius",r),t.firstCategoryDimIndex=0),Z_(o)&&(i.set("angle",o),null==t.firstCategoryDimIndex&&(t.firstCategoryDimIndex=1))},geo:function(e,t,n,i){t.coordSysDims=["lng","lat"]},parallel:function(e,t,n,i){var a=e.ecModel,r=a.getComponent("parallel",e.get("parallelIndex")),o=t.coordSysDims=r.dimensions.slice();zr(r.parallelAxisIndex,(function(e,r){var s=a.getComponent("parallelAxis",e),l=o[r];n.set(l,s),Z_(s)&&(i.set(l,s),null==t.firstCategoryDimIndex&&(t.firstCategoryDimIndex=r))}))}};function Z_(e){return"category"===e.get("type")}function Q_(e,t,n){var i,a,r,o=(n=n||{}).byIndex,s=n.stackedCoordDimension;!function(e){return!P_(e.schema)}(t)?(a=t.schema,i=a.dimensions,r=t.store):i=t;var l,p,c,d,u=!(!e||!e.get("stack"));if(zr(i,(function(e,t){Qr(e)&&(i[t]=e={name:e}),u&&!e.isExtraCoord&&(o||l||!e.ordinalMeta&&!e.stack||(l=e),p||"ordinal"===e.type||"time"===e.type||s&&s!==e.coordDim||(p=e))})),!p||o||l||(o=!0),p){c="__\0ecstackresult_"+e.id,d="__\0ecstackedover_"+e.id,l&&(l.createInvertedIndices=!0);var m=p.coordDim,h=p.type,g=0;zr(i,(function(e){e.coordDim===m&&g++}));var f={name:c,coordDim:m,coordDimIndex:g,type:h,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length},y={name:d,coordDim:d,coordDimIndex:g+1,type:h,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length+1};a?(r&&(f.storeDimIndex=r.ensureCalculationDimension(d,h),y.storeDimIndex=r.ensureCalculationDimension(c,h)),a.appendCalculationDimension(f),a.appendCalculationDimension(y)):(i.push(f),i.push(y))}return{stackedDimension:p&&p.name,stackedByDimension:l&&l.name,isStackedByIndex:o,stackedOverDimension:d,stackResultDimension:c}}function J_(e,t){return!!t&&t===e.getCalculationInfo("stackedDimension")}function eT(e,t){return J_(e,t)?e.getCalculationInfo("stackResultDimension"):t}function tT(e,t,n){n=n||{};var i,a=t.getSourceManager(),r=!1;e?(r=!0,i=Kx(e)):r=(i=a.getSource()).sourceFormat===Fv;var o=function(e){var t=e.get("coordinateSystem"),n=new Y_(t),i=X_[t];if(i)return i(e,n,n.axisMap,n.categoryAxisMap),n}(t),s=function(e,t){var n,i=e.get("coordinateSystem"),a=hx.get(i);return t&&t.coordSysDims&&(n=Ur(t.coordSysDims,(function(e){var n={name:e},i=t.axisMap.get(e);if(i){var a=i.get("type");n.type=T_(a)}return n}))),n||(n=a&&(a.getDimensionsInfo?a.getDimensionsInfo():a.dimensions.slice())||["x","y"]),n}(t,o),l=n.useEncodeDefaulter,p=Zr(l)?l:l?Yr(Wv,s,t):null,c=$_(i,{coordDimensions:s,generateCoord:n.generateCoord,encodeDefine:t.getEncode(),encodeDefaulter:p,canOmitUnusedDimensions:!r}),d=function(e,t,n){var i,a;return n&&zr(e,(function(e,r){var o=e.coordDim,s=n.categoryAxisMap.get(o);s&&(null==i&&(i=r),e.ordinalMeta=s.getOrdinalMeta(),t&&(e.createInvertedIndices=!0)),null!=e.otherDims.itemName&&(a=!0)})),a||null==i||(e[i].otherDims.itemName=0),i}(c.dimensions,n.createInvertedIndices,o),u=r?null:a.getSharedDataStore(c),m=Q_(t,{schema:c,store:u}),h=new W_(c,t);h.setCalculationInfo(m);var g=null!=d&&function(e){if(e.sourceFormat===Fv){var t=function(e){var t=0;for(;tt[1]&&(t[1]=e[1])},e.prototype.unionExtentFromData=function(e,t){this.unionExtent(e.getApproximateExtent(t))},e.prototype.getExtent=function(){return this._extent.slice()},e.prototype.setExtent=function(e,t){var n=this._extent;isNaN(e)||(n[0]=e),isNaN(t)||(n[1]=t)},e.prototype.isInExtentRange=function(e){return this._extent[0]<=e&&this._extent[1]>=e},e.prototype.isBlank=function(){return this._isBlank},e.prototype.setBlank=function(e){this._isBlank=e},e}();Kd(nT);var iT=0,aT=function(){function e(e){this.categories=e.categories||[],this._needCollect=e.needCollect,this._deduplication=e.deduplication,this.uid=++iT}return e.createByAxisModel=function(t){var n=t.option,i=n.data,a=i&&Ur(i,rT);return new e({categories:a,needCollect:!a,deduplication:!1!==n.dedplication})},e.prototype.getOrdinal=function(e){return this._getOrCreateMap().get(e)},e.prototype.parseAndCollect=function(e){var t,n=this._needCollect;if(!Qr(e)&&!n)return e;if(n&&!this._deduplication)return t=this.categories.length,this.categories[t]=e,t;var i=this._getOrCreateMap();return null==(t=i.get(e))&&(n?(t=this.categories.length,this.categories[t]=e,i.set(e,t)):t=NaN),t},e.prototype._getOrCreateMap=function(){return this._map||(this._map=Co(this.categories))},e}();function rT(e){return to(e)&&null!=e.value?e.value:e+""}function oT(e){return"interval"===e.type||"log"===e.type}function sT(e,t,n,i){var a={},r=e[1]-e[0],o=a.interval=od(r/t,!0);null!=n&&oi&&(o=a.interval=i);var s=a.intervalPrecision=pT(o);return function(e,t){!isFinite(e[0])&&(e[0]=t[0]),!isFinite(e[1])&&(e[1]=t[1]),cT(e,0,t),cT(e,1,t),e[0]>e[1]&&(e[0]=e[1])}(a.niceTickExtent=[$c(Math.ceil(e[0]/o)*o,s),$c(Math.floor(e[1]/o)*o,s)],e),a}function lT(e){var t=Math.pow(10,rd(e)),n=e/t;return n?2===n?n=3:3===n?n=5:n*=2:n=1,$c(n*t)}function pT(e){return Yc(e)+2}function cT(e,t,n){e[t]=Math.max(Math.min(e[t],n[1]),n[0])}function dT(e,t){return e>=t[0]&&e<=t[1]}function uT(e,t){return t[1]===t[0]?.5:(e-t[0])/(t[1]-t[0])}function mT(e,t){return e*(t[1]-t[0])+t[0]}var hT=function(e){function t(t){var n=e.call(this,t)||this;n.type="ordinal";var i=n.getSetting("ordinalMeta");return i||(i=new aT({})),Xr(i)&&(i=new aT({categories:Ur(i,(function(e){return to(e)?e.value:e}))})),n._ordinalMeta=i,n._extent=n.getSetting("extent")||[0,i.categories.length-1],n}return We(t,e),t.prototype.parse=function(e){return null==e?NaN:Qr(e)?this._ordinalMeta.getOrdinal(e):Math.round(e)},t.prototype.contain=function(e){return dT(e=this.parse(e),this._extent)&&null!=this._ordinalMeta.categories[e]},t.prototype.normalize=function(e){return uT(e=this._getTickNumber(this.parse(e)),this._extent)},t.prototype.scale=function(e){return e=Math.round(mT(e,this._extent)),this.getRawOrdinalNumber(e)},t.prototype.getTicks=function(){for(var e=[],t=this._extent,n=t[0];n<=t[1];)e.push({value:n}),n++;return e},t.prototype.getMinorTicks=function(e){},t.prototype.setSortInfo=function(e){if(null!=e){for(var t=e.ordinalNumbers,n=this._ordinalNumbersByTick=[],i=this._ticksByOrdinalNumber=[],a=0,r=this._ordinalMeta.categories.length,o=Math.min(r,t.length);a=0&&e=0&&e=e},t.prototype.getOrdinalMeta=function(){return this._ordinalMeta},t.prototype.calcNiceTicks=function(){},t.prototype.calcNiceExtent=function(){},t.type="ordinal",t}(nT);nT.registerClass(hT);var gT=$c,fT=function(e){function t(t){var n=e.call(this,t)||this;n.type="interval",n._interval=0,n._intervalPrecision=2;var i=n.getSetting("ticksGenerator");return Zr(i)&&(n._ticksGenerator=i),n}return We(t,e),t.prototype.parse=function(e){return e},t.prototype.contain=function(e){return dT(e,this._extent)},t.prototype.normalize=function(e){return uT(e,this._extent)},t.prototype.scale=function(e){return mT(e,this._extent)},t.prototype.setExtent=function(e,t){var n=this._extent;isNaN(e)||(n[0]=parseFloat(e)),isNaN(t)||(n[1]=parseFloat(t))},t.prototype.unionExtent=function(e){var t=this._extent;e[0]t[1]&&(t[1]=e[1]),this.setExtent(t[0],t[1])},t.prototype.getInterval=function(){return this._interval},t.prototype.setInterval=function(e){this._interval=e,this._niceExtent=this._extent.slice(),this._intervalPrecision=pT(e)},t.prototype.getTicks=function(e){var t,n=this._interval,i=this._extent,a=this._niceExtent,r=this._intervalPrecision,o=this._ticksGenerator;if(o)try{if(t=o(i,n,a,r))return t}catch(e){}if(t=[],!n)return t;i[0]1e4)return[];var l=t.length?t[t.length-1].value:a[1];return i[1]>l&&(e?t.push({value:gT(l+n,r)}):t.push({value:i[1]})),t},t.prototype.getMinorTicks=function(e){for(var t=this.getTicks(!0),n=[],i=this.getExtent(),a=1;ai[0]&&c0&&(r=null===r?s:Math.min(r,s))}n[i]=r}}return n}(e),n=[];return zr(e,(function(e){var i,a=e.coordinateSystem.getBaseAxis(),r=a.getExtent();if("category"===a.type)i=a.getBandWidth();else if("value"===a.type||"time"===a.type){var o=a.dim+"_"+a.index,s=t[o],l=Math.abs(r[1]-r[0]),p=a.scale.getExtent(),c=Math.abs(p[1]-p[0]);i=s?l/c*s:l}else{var d=e.getData();i=Math.abs(r[1]-r[0])/d.count()}var u=Wc(e.get("barWidth"),i),m=Wc(e.get("barMaxWidth"),i),h=Wc(e.get("barMinWidth")||(kT(e)?.5:1),i),g=e.get("barGap"),f=e.get("barCategoryGap");n.push({bandWidth:i,barWidth:u,barMaxWidth:m,barMinWidth:h,barGap:g,barCategoryGap:f,axisKey:ST(a),stackId:wT(e)})})),TT(n)}function TT(e){var t={};zr(e,(function(e,n){var i=e.axisKey,a=e.bandWidth,r=t[i]||{bandWidth:a,remainedWidth:a,autoWidthCount:0,categoryGap:null,gap:"20%",stacks:{}},o=r.stacks;t[i]=r;var s=e.stackId;o[s]||r.autoWidthCount++,o[s]=o[s]||{width:0,maxWidth:0};var l=e.barWidth;l&&!o[s].width&&(o[s].width=l,l=Math.min(r.remainedWidth,l),r.remainedWidth-=l);var p=e.barMaxWidth;p&&(o[s].maxWidth=p);var c=e.barMinWidth;c&&(o[s].minWidth=c);var d=e.barGap;null!=d&&(r.gap=d);var u=e.barCategoryGap;null!=u&&(r.categoryGap=u)}));var n={};return zr(t,(function(e,t){n[t]={};var i=e.stacks,a=e.bandWidth,r=e.categoryGap;if(null==r){var o=$r(i).length;r=Math.max(35-4*o,15)+"%"}var s=Wc(r,a),l=Wc(e.gap,1),p=e.remainedWidth,c=e.autoWidthCount,d=(p-s)/(c+(c-1)*l);d=Math.max(d,0),zr(i,(function(e){var t=e.maxWidth,n=e.minWidth;if(e.width){i=e.width;t&&(i=Math.min(i,t)),n&&(i=Math.max(i,n)),e.width=i,p-=i+l*i,c--}else{var i=d;t&&ti&&(i=n),i!==d&&(e.width=i,p-=i+l*i,c--)}})),d=(p-s)/(c+(c-1)*l),d=Math.max(d,0);var u,m=0;zr(i,(function(e,t){e.width||(e.width=d),u=e,m+=e.width*(1+l)})),u&&(m-=u.width*l);var h=-m/2;zr(i,(function(e,i){n[t][i]=n[t][i]||{bandWidth:a,offset:h,width:e.width},h+=e.width*(1+l)}))})),n}function IT(e,t){var n=CT(e,t),i=_T(n);zr(n,(function(e){var t=e.getData(),n=e.coordinateSystem.getBaseAxis(),a=wT(e),r=i[ST(n)][a],o=r.offset,s=r.width;t.setLayout({bandWidth:r.bandWidth,offset:o,size:s})}))}function ET(e){return{seriesType:e,plan:Sw(),reset:function(e){if(MT(e)){var t=e.getData(),n=e.coordinateSystem,i=n.getBaseAxis(),a=n.getOtherAxis(i),r=t.getDimensionIndex(t.mapDimension(a.dim)),o=t.getDimensionIndex(t.mapDimension(i.dim)),s=e.get("showBackground",!0),l=t.mapDimension(a.dim),p=t.getCalculationInfo("stackResultDimension"),c=J_(t,l)&&!!t.getCalculationInfo("stackedOnSeries"),d=a.isHorizontal(),u=function(e,t){return t.toGlobalCoord(t.dataToCoord("log"===t.type?1:0))}(0,a),m=kT(e),h=e.get("barMinHeight")||0,g=p&&t.getDimensionIndex(p),f=t.getLayout("size"),y=t.getLayout("offset");return{progress:function(e,t){for(var i,a=e.count,l=m&&xT(3*a),p=m&&s&&xT(3*a),v=m&&xT(a),x=n.master.getRect(),b=d?x.width:x.height,w=t.getStore(),S=0;null!=(i=e.next());){var C=w.get(c?g:r,i),_=w.get(o,i),T=u,I=void 0;c&&(I=+C-w.get(r,i));var E=void 0,M=void 0,k=void 0,P=void 0;if(d){var D=n.dataToPoint([C,_]);if(c)T=n.dataToPoint([I,_])[0];E=T,M=D[1]+y,k=D[0]-T,P=f,Math.abs(k)0)for(var s=0;s=0;--s)if(l[p]){r=l[p];break}r=r||o.none}if(Xr(r)){var c=null==e.level?0:e.level>=0?e.level:r.length+e.level;r=r[c=Math.min(c,r.length-1)]}}return jy(new Date(e.value),r,a,i)}(e,t,n,this.getSetting("locale"),i)},t.prototype.getTicks=function(){var e=this._interval,t=this._extent,n=[];if(!e)return n;n.push({value:t[0],level:0});var i=this.getSetting("useUTC"),a=function(e,t,n,i){var a=1e4,r=qy,o=0;function s(e,t,n,a,r,o,s){for(var l=new Date(t),p=t,c=l[a]();p1&&0===p&&r.unshift({value:r[0].value-u})}}for(p=0;p=i[0]&&y<=i[1]&&d++)}var v=(i[1]-i[0])/t;if(d>1.5*v&&u>v/1.5)break;if(p.push(g),d>v||e===r[m])break}c=[]}}0;var x=Hr(Ur(p,(function(e){return Hr(e,(function(e){return e.value>=i[0]&&e.value<=i[1]&&!e.notAdd}))})),(function(e){return e.length>0})),b=[],w=x.length-1;for(m=0;mn&&(this._approxInterval=n);var r=DT.length,o=Math.min(function(e,t,n,i){for(;n>>1;e[a][1]16?16:e>7.5?7:e>3.5?4:e>1.5?2:1}function AT(e){return(e/=2592e6)>6?6:e>3?3:e>2?2:1}function FT(e){return(e/=Ay)>12?12:e>6?6:e>3.5?4:e>2?2:1}function RT(e,t){return(e/=t?Oy:Dy)>30?30:e>20?20:e>15?15:e>10?10:e>5?5:e>2?2:1}function BT(e){return od(e,!0)}function NT(e,t,n){var i=new Date(e);switch(zy(t)){case"year":case"month":i[tv(n)](0);case"day":i[nv(n)](1);case"hour":i[iv(n)](0);case"minute":i[av(n)](0);case"second":i[rv(n)](0),i[ov(n)](0)}return i.getTime()}nT.registerClass(PT);var LT=nT.prototype,VT=fT.prototype,qT=$c,GT=Math.floor,zT=Math.ceil,UT=Math.pow,jT=Math.log,HT=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type="log",t.base=10,t._originalScale=new fT,t._interval=0,t}return We(t,e),t.prototype.getTicks=function(e){var t=this._originalScale,n=this._extent,i=t.getExtent();return Ur(VT.getTicks.call(this,e),(function(e){var t=e.value,a=$c(UT(this.base,t));return a=t===n[0]&&this._fixMin?$T(a,i[0]):a,{value:a=t===n[1]&&this._fixMax?$T(a,i[1]):a}}),this)},t.prototype.setExtent=function(e,t){var n=jT(this.base);e=jT(Math.max(0,e))/n,t=jT(Math.max(0,t))/n,VT.setExtent.call(this,e,t)},t.prototype.getExtent=function(){var e=this.base,t=LT.getExtent.call(this);t[0]=UT(e,t[0]),t[1]=UT(e,t[1]);var n=this._originalScale.getExtent();return this._fixMin&&(t[0]=$T(t[0],n[0])),this._fixMax&&(t[1]=$T(t[1],n[1])),t},t.prototype.unionExtent=function(e){this._originalScale.unionExtent(e);var t=this.base;e[0]=jT(e[0])/jT(t),e[1]=jT(e[1])/jT(t),LT.unionExtent.call(this,e)},t.prototype.unionExtentFromData=function(e,t){this.unionExtent(e.getApproximateExtent(t))},t.prototype.calcNiceTicks=function(e){e=e||10;var t=this._extent,n=t[1]-t[0];if(!(n===1/0||n<=0)){var i=ad(n);for(e/n*i<=.5&&(i*=10);!isNaN(i)&&Math.abs(i)<1&&Math.abs(i)>0;)i*=10;var a=[$c(zT(t[0]/i)*i),$c(GT(t[1]/i)*i)];this._interval=i,this._niceExtent=a}},t.prototype.calcNiceExtent=function(e){VT.calcNiceExtent.call(this,e),this._fixMin=e.fixMin,this._fixMax=e.fixMax},t.prototype.parse=function(e){return e},t.prototype.contain=function(e){return dT(e=jT(e)/jT(this.base),this._extent)},t.prototype.normalize=function(e){return uT(e=jT(e)/jT(this.base),this._extent)},t.prototype.scale=function(e){return e=mT(e,this._extent),UT(this.base,e)},t.type="log",t}(nT),WT=HT.prototype;function $T(e,t){return qT(e,Yc(t))}WT.getMinorTicks=VT.getMinorTicks,WT.getLabel=VT.getLabel,nT.registerClass(HT);var KT=function(){function e(e,t,n){this._prepareParams(e,t,n)}return e.prototype._prepareParams=function(e,t,n){n[1]0&&s>0&&!l&&(o=0),o<0&&s<0&&!p&&(s=0));var d=this._determinedMin,u=this._determinedMax;return null!=d&&(o=d,l=!0),null!=u&&(s=u,p=!0),{min:o,max:s,minFixed:l,maxFixed:p,isBlank:c}},e.prototype.modifyDataMinMax=function(e,t){this[XT[e]]=t},e.prototype.setDeterminedMinMax=function(e,t){var n=YT[e];this[n]=t},e.prototype.freeze=function(){this.frozen=!0},e}(),YT={min:"_determinedMin",max:"_determinedMax"},XT={min:"_dataMin",max:"_dataMax"};function ZT(e,t,n){var i=e.rawExtentInfo;return i||(i=new KT(e,t,n),e.rawExtentInfo=i,i)}function QT(e,t){return null==t?null:lo(t)?NaN:e.parse(t)}function JT(e,t){var n=e.type,i=ZT(e,t,e.getExtent()).calculate();e.setBlank(i.isBlank);var a=i.min,r=i.max,o=t.ecModel;if(o&&"time"===n){var s=CT("bar",o),l=!1;if(zr(s,(function(e){l=l||e.getBaseAxis()===t.axis})),l){var p=_T(s),c=function(e,t,n,i){var a=n.axis.getExtent(),r=a[1]-a[0],o=function(e,t,n){if(e&&t){var i=e[ST(t)];return null!=i&&null!=n?i[wT(n)]:i}}(i,n.axis);if(void 0===o)return{min:e,max:t};var s=1/0;zr(o,(function(e){s=Math.min(e.offset,s)}));var l=-1/0;zr(o,(function(e){l=Math.max(e.offset+e.width,l)})),s=Math.abs(s),l=Math.abs(l);var p=s+l,c=t-e,d=c/(1-(s+l)/r)-c;return t+=d*(l/p),e-=d*(s/p),{min:e,max:t}}(a,r,t,p);a=c.min,r=c.max}}return{extent:[a,r],fixMin:i.minFixed,fixMax:i.maxFixed}}function eI(e,t){var n=t,i=JT(e,n),a=i.extent,r=n.get("splitNumber");e instanceof HT&&(e.base=n.get("logBase"));var o=e.type,s=n.get("interval"),l="interval"===o||"time"===o;e.setExtent(a[0],a[1]),e.calcNiceExtent({splitNumber:r,fixMin:i.fixMin,fixMax:i.fixMax,minInterval:l?n.get("minInterval"):null,maxInterval:l?n.get("maxInterval"):null}),null!=s&&e.setInterval&&e.setInterval(s)}function tI(e,t){if(t=t||e.get("type"))switch(t){case"category":return new hT({ordinalMeta:e.getOrdinalMeta?e.getOrdinalMeta():e.getCategories(),extent:[1/0,-1/0]});case"time":return new PT({locale:e.ecModel.getLocaleModel(),useUTC:e.ecModel.get("useUTC")});case"value":return new fT({ticksGenerator:e.getTicksGenerator()});default:return new(nT.getClass(t)||fT)}}function nI(e){var t,n,i=e.getLabelModel().get("formatter"),a="category"===e.type?e.scale.getExtent()[0]:null;return"time"===e.scale.type?(n=i,function(t,i){return e.scale.getFormattedLabel(t,i,n)}):Qr(i)?function(t){return function(n){var i=e.scale.getLabel(n);return t.replace("{value}",null!=i?i:"")}}(i):Zr(i)?(t=i,function(n,i){return null!=a&&(i=n.value-a),t(iI(e,n),i,null!=n.level?{level:n.level}:null)}):function(t){return e.scale.getLabel(t)}}function iI(e,t){return"category"===e.type?e.scale.getLabel(t):t.value}function aI(e,t){var n=t*Math.PI/180,i=e.width,a=e.height,r=i*Math.abs(Math.cos(n))+Math.abs(a*Math.sin(n)),o=i*Math.abs(Math.sin(n))+Math.abs(a*Math.cos(n));return new Ns(e.x,e.y,r,o)}function rI(e){var t=e.get("interval");return null==t?"auto":t}function oI(e){return"category"===e.type&&0===rI(e.getLabelModel())}function sI(e,t){var n={};return zr(e.mapDimensionsAll(t),(function(t){n[eT(e,t)]=!0})),$r(n)}var lI=function(){function e(){}return e.prototype.getNeedCrossZero=function(){return!this.option.scale},e.prototype.getCoordSysModel=function(){},e}();var pI=[],cI={registerPreprocessor:d_,registerProcessor:u_,registerPostInit:function(e){m_("afterinit",e)},registerPostUpdate:function(e){m_("afterupdate",e)},registerUpdateLifecycle:m_,registerAction:h_,registerCoordinateSystem:function(e,t){hx.register(e,t)},registerLayout:function(e,t){y_(n_,e,t,1e3,"layout")},registerVisual:g_,registerTransform:x_,registerLoading:v_,registerMap:function(e,t,n){var i=fC("registerMap");i&&i(e,t,n)},registerImpl:function(e,t){gC[e]=t},PRIORITY:xC,ComponentModel:kv,ComponentView:ww,SeriesModel:mw,ChartView:Tw,registerComponentModel:function(e){kv.registerClass(e)},registerComponentView:function(e){ww.registerClass(e)},registerSeriesModel:function(e){mw.registerClass(e)},registerChartView:function(e){Tw.registerClass(e)},registerSubTypeDefaulter:function(e,t){kv.registerSubTypeDefaulter(e,t)},registerPainter:function(e,t){Gc(e,t)}};function dI(e){Xr(e)?zr(e,(function(e){dI(e)})):Lr(pI,e)>=0||(pI.push(e),Zr(e)&&(e={install:e}),e.install(cI))}function uI(e,t){return Math.abs(e-t)<1e-8}function mI(e,t,n){var i=0,a=e[0];if(!a)return!1;for(var r=1;rn&&(e=a,n=o)}if(e)return function(e){for(var t=0,n=0,i=0,a=e.length,r=e[a-1][0],o=e[a-1][1],s=0;s>1^-(1&s),l=l>>1^-(1&l),a=s+=a,r=l+=r,i.push([s/n,l/n])}return i}function _I(e,t){return Ur(Hr((e=function(e){if(!e.UTF8Encoding)return e;var t=e,n=t.UTF8Scale;return null==n&&(n=1024),zr(t.features,(function(e){var t=e.geometry,i=t.encodeOffsets,a=t.coordinates;if(i)switch(t.type){case"LineString":t.coordinates=CI(a,i,n);break;case"Polygon":case"MultiLineString":SI(a,i,n);break;case"MultiPolygon":zr(a,(function(e,t){return SI(e,i[t],n)}))}})),t.UTF8Encoding=!1,t}(e)).features,(function(e){return e.geometry&&e.properties&&e.geometry.coordinates.length>0})),(function(e){var n=e.properties,i=e.geometry,a=[];switch(i.type){case"Polygon":var r=i.coordinates;a.push(new vI(r[0],r.slice(1)));break;case"MultiPolygon":zr(i.coordinates,(function(e){e[0]&&a.push(new vI(e[0],e.slice(1)))}));break;case"LineString":a.push(new xI([i.coordinates]));break;case"MultiLineString":a.push(new xI(i.coordinates))}var o=new bI(n[t||"name"],a,n.cp);return o.properties=n,o}))}var TI=Pd();function II(e){return"category"===e.type?function(e){var t=e.getLabelModel(),n=MI(e,t);return!t.get("show")||e.scale.isBlank()?{labels:[],labelCategoryInterval:n.labelCategoryInterval}:n}(e):function(e){var t=e.scale.getTicks(),n=nI(e);return{labels:Ur(t,(function(t,i){return{level:t.level,formattedLabel:n(t,i),rawLabel:e.scale.getLabel(t),tickValue:t.value}}))}}(e)}function EI(e,t){return"category"===e.type?function(e,t){var n,i,a=kI(e,"ticks"),r=rI(t),o=PI(a,r);if(o)return o;t.get("show")&&!e.scale.isBlank()||(n=[]);if(Zr(r))n=AI(e,r,!0);else if("auto"===r){var s=MI(e,e.getLabelModel());i=s.labelCategoryInterval,n=Ur(s.labels,(function(e){return e.tickValue}))}else n=OI(e,i=r,!0);return DI(a,r,{ticks:n,tickCategoryInterval:i})}(e,t):{ticks:Ur(e.scale.getTicks(),(function(e){return e.value}))}}function MI(e,t){var n,i,a=kI(e,"labels"),r=rI(t),o=PI(a,r);return o||(Zr(r)?n=AI(e,r):(i="auto"===r?function(e){var t=TI(e).autoInterval;return null!=t?t:TI(e).autoInterval=e.calculateCategoryInterval()}(e):r,n=OI(e,i)),DI(a,r,{labels:n,labelCategoryInterval:i}))}function kI(e,t){return TI(e)[t]||(TI(e)[t]=[])}function PI(e,t){for(var n=0;n1&&c/l>2&&(p=Math.round(Math.ceil(p/l)*l));var d=oI(e),u=o.get("showMinLabel")||d,m=o.get("showMaxLabel")||d;u&&p!==r[0]&&g(r[0]);for(var h=p;h<=r[1];h+=l)g(h);function g(e){var t={value:e};s.push(n?e:{formattedLabel:i(t),rawLabel:a.getLabel(t),tickValue:e})}return m&&h-l!==r[1]&&g(r[1]),s}function AI(e,t,n){var i=e.scale,a=nI(e),r=[];return zr(i.getTicks(),(function(e){var o=i.getLabel(e),s=e.value;t(e.value,o)&&r.push(n?s:{formattedLabel:a(e),rawLabel:o,tickValue:s})})),r}var FI=[0,1],RI=function(){function e(e,t,n){this.onBand=!1,this.inverse=!1,this.dim=e,this.scale=t,this._extent=n||[0,0]}return e.prototype.contain=function(e){var t=this._extent,n=Math.min(t[0],t[1]),i=Math.max(t[0],t[1]);return e>=n&&e<=i},e.prototype.containData=function(e){return this.scale.contain(e)},e.prototype.getExtent=function(){return this._extent.slice()},e.prototype.getPixelPrecision=function(e){return Zc(e||this.scale.getExtent(),this._extent)},e.prototype.setExtent=function(e,t){var n=this._extent;n[0]=e,n[1]=t},e.prototype.dataToCoord=function(e,t){var n=this._extent,i=this.scale;return e=i.normalize(e),this.onBand&&"ordinal"===i.type&&BI(n=n.slice(),i.count()),Hc(e,FI,n,t)},e.prototype.coordToData=function(e,t){var n=this._extent,i=this.scale;this.onBand&&"ordinal"===i.type&&BI(n=n.slice(),i.count());var a=Hc(e,n,FI,t);return this.scale.scale(a)},e.prototype.pointToData=function(e,t){},e.prototype.getTicksCoords=function(e){var t=(e=e||{}).tickModel||this.getTickModel(),n=Ur(EI(this,t).ticks,(function(e){return{coord:this.dataToCoord("ordinal"===this.scale.type?this.scale.getRawOrdinalNumber(e):e),tickValue:e}}),this);return function(e,t,n,i){var a=t.length;if(!e.onBand||n||!a)return;var r,o,s=e.getExtent();if(1===a)t[0].coord=s[0],r=t[1]={coord:s[1]};else{var l=t[a-1].tickValue-t[0].tickValue,p=(t[a-1].coord-t[0].coord)/l;zr(t,(function(e){e.coord-=p/2})),o=1+e.scale.getExtent()[1]-t[a-1].tickValue,r={coord:t[a-1].coord+p*o},t.push(r)}var c=s[0]>s[1];d(t[0].coord,s[0])&&(i?t[0].coord=s[0]:t.shift());i&&d(s[0],t[0].coord)&&t.unshift({coord:s[0]});d(s[1],r.coord)&&(i?r.coord=s[1]:t.pop());i&&d(r.coord,s[1])&&t.push({coord:s[1]});function d(e,t){return e=$c(e),t=$c(t),c?e>t:e0&&e<100||(e=5),Ur(this.scale.getMinorTicks(e),(function(e){return Ur(e,(function(e){return{coord:this.dataToCoord(e),tickValue:e}}),this)}),this)},e.prototype.getViewLabels=function(){return II(this).labels},e.prototype.getLabelModel=function(){return this.model.getModel("axisLabel")},e.prototype.getTickModel=function(){return this.model.getModel("axisTick")},e.prototype.getBandWidth=function(){if("time"===this.type){var e=this.model,t=e.get("bandWidthCalculator"),n=void 0;if(Zr(t))try{if(n=t(e))return n}catch(e){}}var i=this._extent,a=this.scale.getExtent(),r=a[1]-a[0]+(this.onBand?1:0);0===r&&(r=1);var o=Math.abs(i[1]-i[0]);return Math.abs(o)/r},e.prototype.calculateCategoryInterval=function(){return function(e){var t=function(e){var t=e.getLabelModel();return{axisRotate:e.getRotate?e.getRotate():e.isHorizontal&&!e.isHorizontal()?90:0,labelRotate:t.get("rotate")||0,font:t.getFont()}}(e),n=nI(e),i=(t.axisRotate-t.labelRotate)/180*Math.PI,a=e.scale,r=a.getExtent(),o=a.count();if(r[1]-r[0]<1)return 0;var s=1;o>40&&(s=Math.max(1,Math.floor(o/40)));for(var l=r[0],p=e.dataToCoord(l+1)-e.dataToCoord(l),c=Math.abs(p*Math.cos(i)),d=Math.abs(p*Math.sin(i)),u=0,m=0;l<=r[1];l+=s){var h,g,f=xc(n({value:l}),t.font,"center","top");h=1.3*f.width,g=1.3*f.height,u=Math.max(u,h,7),m=Math.max(m,g,7)}var y=u/c,v=m/d;isNaN(y)&&(y=1/0),isNaN(v)&&(v=1/0);var x=Math.max(0,Math.floor(Math.min(y,v))),b=TI(e.model),w=e.getExtent(),S=b.lastAutoInterval,C=b.lastTickCount;return null!=S&&null!=C&&Math.abs(S-x)<=1&&Math.abs(C-o)<=1&&S>x&&b.axisExtent0===w[0]&&b.axisExtent1===w[1]?x=S:(b.lastTickCount=o,b.lastAutoInterval=x,b.axisExtent0=w[0],b.axisExtent1=w[1]),x}(this)},e}();function BI(e,t){var n=(e[1]-e[0])/t/2;e[0]+=n,e[1]-=n}var NI=2*Math.PI,LI=nm.CMD,VI=["top","right","bottom","left"];function qI(e,t,n,i,a){var r=n.width,o=n.height;switch(e){case"top":i.set(n.x+r/2,n.y-t),a.set(0,-1);break;case"bottom":i.set(n.x+r/2,n.y+o+t),a.set(0,1);break;case"left":i.set(n.x-t,n.y+o/2),a.set(-1,0);break;case"right":i.set(n.x+r+t,n.y+o/2),a.set(1,0)}}function GI(e,t,n,i,a,r,o,s,l){o-=e,s-=t;var p=Math.sqrt(o*o+s*s),c=(o/=p)*n+e,d=(s/=p)*n+t;if(Math.abs(i-a)%NI<1e-4)return l[0]=c,l[1]=d,p-n;if(r){var u=i;i=sm(a),a=sm(u)}else i=sm(i),a=sm(a);i>a&&(a+=NI);var m=Math.atan2(s,o);if(m<0&&(m+=NI),m>=i&&m<=a||m+NI>=i&&m+NI<=a)return l[0]=c,l[1]=d,p-n;var h=n*Math.cos(i)+e,g=n*Math.sin(i)+t,f=n*Math.cos(a)+e,y=n*Math.sin(a)+t,v=(h-o)*(h-o)+(g-s)*(g-s),x=(f-o)*(f-o)+(y-s)*(y-s);return v0){t=t/180*Math.PI,$I.fromArray(e[0]),KI.fromArray(e[1]),YI.fromArray(e[2]),Ms.sub(XI,$I,KI),Ms.sub(ZI,YI,KI);var n=XI.len(),i=ZI.len();if(!(n<.001||i<.001)){XI.scale(1/n),ZI.scale(1/i);var a=XI.dot(ZI);if(Math.cos(t)1&&Ms.copy(eE,YI),eE.toArray(e[1])}}}}function nE(e,t,n){if(n<=180&&n>0){n=n/180*Math.PI,$I.fromArray(e[0]),KI.fromArray(e[1]),YI.fromArray(e[2]),Ms.sub(XI,KI,$I),Ms.sub(ZI,YI,KI);var i=XI.len(),a=ZI.len();if(!(i<.001||a<.001))if(XI.scale(1/i),ZI.scale(1/a),XI.dot(t)=o)Ms.copy(eE,YI);else{eE.scaleAndAdd(ZI,r/Math.tan(Math.PI/2-s));var l=YI.x!==KI.x?(eE.x-KI.x)/(YI.x-KI.x):(eE.y-KI.y)/(YI.y-KI.y);if(isNaN(l))return;l<0?Ms.copy(eE,KI):l>1&&Ms.copy(eE,YI)}eE.toArray(e[1])}}}function iE(e,t,n,i){var a="normal"===n,r=a?e:e.ensureState(n);r.ignore=t;var o=i.get("smooth");o&&!0===o&&(o=.3),r.shape=r.shape||{},o>0&&(r.shape.smooth=o);var s=i.getModel("lineStyle").getLineStyle();a?e.useStyle(s):r.style=s}function aE(e,t){var n=t.smooth,i=t.points;if(i)if(e.moveTo(i[0][0],i[0][1]),n>0&&i.length>=3){var a=Go(i[0],i[1]),r=Go(i[1],i[2]);if(!a||!r)return e.lineTo(i[1][0],i[1][1]),void e.lineTo(i[2][0],i[2][1]);var o=Math.min(a,r)*n,s=jo([],i[1],i[0],o/a),l=jo([],i[1],i[2],o/r),p=jo([],s,l,.5);e.bezierCurveTo(s[0],s[1],s[0],s[1],p[0],p[1]),e.bezierCurveTo(l[0],l[1],l[0],l[1],i[2][0],i[2][1])}else for(var c=1;c0&&r&&S(-d/o,0,o);var f,y,v=e[0],x=e[o-1];return b(),f<0&&C(-f,.8),y<0&&C(y,.8),b(),w(f,y,1),w(y,f,-1),b(),f<0&&_(-f),y<0&&_(y),p}function b(){f=v.rect[t]-i,y=a-x.rect[t]-x.rect[n]}function w(e,t,n){if(e<0){var i=Math.min(t,-e);if(i>0){S(i*n,0,o);var a=i+e;a<0&&C(-a*n,1)}else C(-e*n,1)}}function S(n,i,a){0!==n&&(p=!0);for(var r=i;r0)for(l=0;l0;l--){S(-(r[l-1]*d),l,o)}}}function _(e){var t=e<0?-1:1;e=Math.abs(e);for(var n=Math.ceil(e/(o-1)),i=0;i0?S(n,0,i+1):S(-n,o-i-1,o),(e-=n)<=0)return}}function pE(e,t,n,i){return lE(e,"y","height",t,n,i)}function cE(e){var t=[];e.sort((function(e,t){return t.priority-e.priority}));var n=new Ns(0,0,0,0);function i(e){if(!e.ignore){var t=e.ensureState("emphasis");null==t.ignore&&(t.ignore=!1)}e.ignore=!0}for(var a=0;a=0&&n.attr(m.oldLayoutSelect),Lr(p,"emphasis")>=0&&n.attr(m.oldLayoutEmphasis)),pf(n,s,t,o)}else if(n.attr(s),!ay(n).valueAnimation){var c=co(n.style.opacity,1);n.style.opacity=0,cf(n,{style:{opacity:c}},t,o)}if(m.oldLayout=s,n.states.select){var d=m.oldLayoutSelect={};yE(d,s,vE),yE(d,n.states.select,vE)}if(n.states.emphasis){var u=m.oldLayoutEmphasis={};yE(u,s,vE),yE(u,n.states.emphasis,vE)}oy(n,o,l,t,t)}if(i&&!i.ignore&&!i.invisible){a=(m=fE(i)).oldLayout;var m,h={points:i.shape.points};a?(i.attr({shape:a}),pf(i,{shape:h},t)):(i.setShape(h),i.style.strokePercent=0,cf(i,{style:{strokePercent:1}},t)),m.oldLayout=h}},e}(),bE=Pd();function wE(e){e.registerUpdateLifecycle("series:beforeupdate",(function(e,t,n){var i=bE(t).labelManager;i||(i=bE(t).labelManager=new xE),i.clearLabels()})),e.registerUpdateLifecycle("series:layoutlabels",(function(e,t,n){var i=bE(t).labelManager;n.updatedSeries.forEach((function(e){i.addLabelsOfSeries(t.getViewOfSeriesModel(e))})),i.updateLayoutConfig(t),i.layout(t),i.processLabelsOverall()}))}dI(wE);var SE=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n}return We(t,e),t.prototype.getInitialData=function(e){return tT(null,this,{useEncodeDefaulter:!0})},t.prototype.getLegendIcon=function(e){var t=new Rc,n=NS("line",0,e.itemHeight/2,e.itemWidth,0,e.lineStyle.stroke,!1);t.add(n),n.setStyle(e.lineStyle);var i=this.getData().getVisual("symbol"),a=this.getData().getVisual("symbolRotate"),r="none"===i?"circle":i,o=.8*e.itemHeight,s=NS(r,(e.itemWidth-o)/2,(e.itemHeight-o)/2,o,o,e.itemStyle.fill);t.add(s),s.setStyle(e.itemStyle);var l="inherit"===e.iconRotate?a:e.iconRotate||0;return s.rotation=l*Math.PI/180,s.setOrigin([e.itemWidth/2,e.itemHeight/2]),r.indexOf("empty")>-1&&(s.style.stroke=s.style.fill,s.style.fill="#fff",s.style.lineWidth=2),t},t.type="series.line",t.dependencies=["grid","polar"],t.defaultOption={z:3,coordinateSystem:"cartesian2d",legendHoverLink:!0,clip:!0,label:{position:"top"},endLabel:{show:!1,valueAnimation:!0,distance:8},lineStyle:{width:2,type:"solid"},emphasis:{scale:!0},step:!1,smooth:!1,smoothMonotone:null,symbol:"emptyCircle",symbolSize:4,symbolRotate:null,showSymbol:!0,showAllSymbol:"auto",connectNulls:!1,sampling:"none",animationEasing:"linear",progressive:0,hoverLayerThreshold:1/0,universalTransition:{divideShape:"clone"},triggerLineEvent:!1},t}(mw);function CE(e,t){var n=e.mapDimensionsAll("defaultedLabel"),i=n.length;if(1===i){var a=cb(e,t,n[0]);return null!=a?a+"":null}if(i){for(var r=[],o=0;o=0&&i.push(t[r])}return i.join(" ")}var TE=function(e){function t(t,n,i,a){var r=e.call(this)||this;return r.updateData(t,n,i,a),r}return We(t,e),t.prototype._createSymbol=function(e,t,n,i,a){this.removeAll();var r=NS(e,-1,-1,2,2,null,a);r.attr({z2:100,culling:!0,scaleX:i[0]/2,scaleY:i[1]/2}),r.drift=IE,this._symbolType=e,this.add(r)},t.prototype.stopSymbolAnimation=function(e){this.childAt(0).stopAnimation(null,e)},t.prototype.getSymbolType=function(){return this._symbolType},t.prototype.getSymbolPath=function(){return this.childAt(0)},t.prototype.highlight=function(){Th(this.childAt(0))},t.prototype.downplay=function(){Ih(this.childAt(0))},t.prototype.setZ=function(e,t){var n=this.childAt(0);n.zlevel=e,n.z=t},t.prototype.setDraggable=function(e,t){var n=this.childAt(0);n.draggable=e,n.cursor=!t&&e?"move":n.cursor},t.prototype.updateData=function(e,n,i,a){this.silent=!1;var r=e.getItemVisual(n,"symbol")||"circle",o=e.hostModel,s=t.getSymbolSize(e,n),l=r!==this._symbolType,p=a&&a.disableAnimation;if(l){var c=e.getItemVisual(n,"symbolKeepAspect");this._createSymbol(r,e,n,s,c)}else{(u=this.childAt(0)).silent=!1;var d={scaleX:s[0]/2,scaleY:s[1]/2};p?u.attr(d):pf(u,d,o,n),gf(u)}if(this._updateCommon(e,n,s,i,a),l){var u=this.childAt(0);if(!p){d={scaleX:this._sizeX,scaleY:this._sizeY,style:{opacity:u.style.opacity}};u.scaleX=u.scaleY=0,u.style.opacity=0,cf(u,d,o,n)}}p&&this.childAt(0).stopAnimation("leave")},t.prototype._updateCommon=function(e,t,n,i,a){var r,o,s,l,p,c,d,u,m,h=this.childAt(0),g=e.hostModel;if(i&&(r=i.emphasisItemStyle,o=i.blurItemStyle,s=i.selectItemStyle,l=i.focus,p=i.blurScope,d=i.labelStatesModels,u=i.hoverScale,m=i.cursorStyle,c=i.emphasisDisabled),!i||e.hasItemOption){var f=i&&i.itemModel?i.itemModel:e.getItemModel(t),y=f.getModel("emphasis");r=y.getModel("itemStyle").getItemStyle(),s=f.getModel(["select","itemStyle"]).getItemStyle(),o=f.getModel(["blur","itemStyle"]).getItemStyle(),l=y.get("focus"),p=y.get("blurScope"),c=y.get("disabled"),d=Xf(f),u=y.getShallow("scale"),m=f.getShallow("cursor")}var v=e.getItemVisual(t,"symbolRotate");h.attr("rotation",(v||0)*Math.PI/180||0);var x=VS(e.getItemVisual(t,"symbolOffset"),n);x&&(h.x=x[0],h.y=x[1]),m&&h.attr("cursor",m);var b=e.getItemVisual(t,"style"),w=b.fill;if(h instanceof Em){var S=h.style;h.useStyle(Br({image:S.image,x:S.x,y:S.y,width:S.width,height:S.height},b))}else h.__isEmptyBrush?h.useStyle(Br({},b)):h.useStyle(b),h.style.decal=null,h.setColor(w,a&&a.symbolInnerColor),h.style.strokeNoScale=!0;var C=e.getItemVisual(t,"liftZ"),_=this._z2;null!=C?null==_&&(this._z2=h.z2,h.z2+=C):null!=_&&(h.z2=_,this._z2=null);var T=a&&a.useNameLabel;Yf(h,d,{labelFetcher:g,labelDataIndex:t,defaultText:function(t){return T?e.getName(t):CE(e,t)},inheritColor:w,defaultOpacity:b.opacity}),this._sizeX=n[0]/2,this._sizeY=n[1]/2;var I=h.ensureState("emphasis");I.style=r,h.ensureState("select").style=s,h.ensureState("blur").style=o;var E=null==u||!0===u?Math.max(1.1,3/this._sizeY):isFinite(u)&&u>0?+u:1;I.scaleX=this._sizeX*E,I.scaleY=this._sizeY*E,this.setSymbolScale(1),Vh(this,l,p,c)},t.prototype.setSymbolScale=function(e){this.scaleX=this.scaleY=e},t.prototype.fadeOut=function(e,t,n){var i=this.childAt(0),a=Xm(this).dataIndex,r=n&&n.animation;if(this.silent=i.silent=!0,n&&n.fadeLabel){var o=i.getTextContent();o&&uf(o,{style:{opacity:0}},t,{dataIndex:a,removeOpt:r,cb:function(){i.removeTextContent()}})}else i.removeTextContent();uf(i,{style:{opacity:0},scaleX:0,scaleY:0},t,{dataIndex:a,cb:e,removeOpt:r})},t.getSymbolSize=function(e,t){return LS(e.getItemVisual(t,"symbolSize"))},t}(Rc);function IE(e,t){this.parent.drift(e,t)}function EE(e,t,n,i){return t&&!isNaN(t[0])&&!isNaN(t[1])&&!(i.isIgnore&&i.isIgnore(n))&&!(i.clipShape&&!i.clipShape.contain(t[0],t[1]))&&"none"!==e.getItemVisual(n,"symbol")}function ME(e){return null==e||to(e)||(e={isIgnore:e}),e||{}}function kE(e){var t=e.hostModel,n=t.getModel("emphasis");return{emphasisItemStyle:n.getModel("itemStyle").getItemStyle(),blurItemStyle:t.getModel(["blur","itemStyle"]).getItemStyle(),selectItemStyle:t.getModel(["select","itemStyle"]).getItemStyle(),focus:n.get("focus"),blurScope:n.get("blurScope"),emphasisDisabled:n.get("disabled"),hoverScale:n.get("scale"),labelStatesModels:Xf(t),cursorStyle:t.get("cursor")}}var PE=function(){function e(e){this.group=new Rc,this._SymbolCtor=e||TE}return e.prototype.updateData=function(e,t){this._progressiveEls=null,t=ME(t);var n=this.group,i=e.hostModel,a=this._data,r=this._SymbolCtor,o=t.disableAnimation,s=kE(e),l={disableAnimation:o},p=t.getSymbolPoint||function(t){return e.getItemLayout(t)};a||n.removeAll(),e.diff(a).add((function(i){var a=p(i);if(EE(e,a,i,t)){var o=new r(e,i,s,l);o.setPosition(a),e.setItemGraphicEl(i,o),n.add(o)}})).update((function(c,d){var u=a.getItemGraphicEl(d),m=p(c);if(EE(e,m,c,t)){var h=e.getItemVisual(c,"symbol")||"circle",g=u&&u.getSymbolType&&u.getSymbolType();if(!u||g&&g!==h)n.remove(u),(u=new r(e,c,s,l)).setPosition(m);else{u.updateData(e,c,s,l);var f={x:m[0],y:m[1]};o?u.attr(f):pf(u,f,i)}n.add(u),e.setItemGraphicEl(c,u)}else n.remove(u)})).remove((function(e){var t=a.getItemGraphicEl(e);t&&t.fadeOut((function(){n.remove(t)}),i)})).execute(),this._getSymbolPoint=p,this._data=e},e.prototype.updateLayout=function(){var e=this,t=this._data;t&&t.eachItemGraphicEl((function(t,n){var i=e._getSymbolPoint(n);t.setPosition(i),t.markRedraw()}))},e.prototype.incrementalPrepareUpdate=function(e){this._seriesScope=kE(e),this._data=null,this.group.removeAll()},e.prototype.incrementalUpdate=function(e,t,n){function i(e){e.isGroup||(e.incremental=!0,e.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[],n=ME(n);for(var a=e.start;a0?n=i[0]:i[1]<0&&(n=i[1]);return n}(a,n),o=i.dim,s=a.dim,l=t.mapDimension(s),p=t.mapDimension(o),c="x"===s||"radius"===s?1:0,d=Ur(e.dimensions,(function(e){return t.mapDimension(e)})),u=!1,m=t.getCalculationInfo("stackResultDimension");return J_(t,d[0])&&(u=!0,d[0]=m),J_(t,d[1])&&(u=!0,d[1]=m),{dataDimsForPoint:d,valueStart:r,valueAxisDim:s,baseAxisDim:o,stacked:!!u,valueDim:l,baseDim:p,baseDataOffset:c,stackedOverDimension:t.getCalculationInfo("stackedOverDimension")}}function OE(e,t,n,i){var a=NaN;e.stacked&&(a=n.get(n.getCalculationInfo("stackedOverDimension"),i)),isNaN(a)&&(a=e.valueStart);var r=e.baseDataOffset,o=[];return o[r]=n.get(e.baseDim,i),o[1-r]=a,t.dataToPoint(o)}var AE=Math.min,FE=Math.max;function RE(e,t){return isNaN(e)||isNaN(t)}function BE(e,t,n,i,a,r,o,s,l){for(var p,c,d,u,m,h,g=n,f=0;f=a||g<0)break;if(RE(y,v)){if(l){g+=r;continue}break}if(g===n)e[r>0?"moveTo":"lineTo"](y,v),d=y,u=v;else{var x=y-p,b=v-c;if(x*x+b*b<.5){g+=r;continue}if(o>0){for(var w=g+r,S=t[2*w],C=t[2*w+1];S===y&&C===v&&f=i||RE(S,C))m=y,h=v;else{I=S-p,E=C-c;var P=y-p,D=S-y,O=v-c,A=C-v,F=void 0,R=void 0;if("x"===s){var B=I>0?1:-1;m=y-B*(F=Math.abs(P))*o,h=v,M=y+B*(R=Math.abs(D))*o,k=v}else if("y"===s){var N=E>0?1:-1;m=y,h=v-N*(F=Math.abs(O))*o,M=y,k=v+N*(R=Math.abs(A))*o}else F=Math.sqrt(P*P+O*O),m=y-I*o*(1-(T=(R=Math.sqrt(D*D+A*A))/(R+F))),h=v-E*o*(1-T),k=v+E*o*T,M=AE(M=y+I*o*T,FE(S,y)),k=AE(k,FE(C,v)),M=FE(M,AE(S,y)),h=v-(E=(k=FE(k,AE(C,v)))-v)*F/R,m=AE(m=y-(I=M-y)*F/R,FE(p,y)),h=AE(h,FE(c,v)),M=y+(I=y-(m=FE(m,AE(p,y))))*R/F,k=v+(E=v-(h=FE(h,AE(c,v))))*R/F}e.bezierCurveTo(d,u,m,h,y,v),d=M,u=k}else e.lineTo(y,v)}p=y,c=v,g+=r}return f}var NE=function(){this.smooth=0,this.smoothConstraint=!0},LE=function(e){function t(t){var n=e.call(this,t)||this;return n.type="ec-polyline",n}return We(t,e),t.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},t.prototype.getDefaultShape=function(){return new NE},t.prototype.buildPath=function(e,t){var n=t.points,i=0,a=n.length/2;if(t.connectNulls){for(;a>0&&RE(n[2*a-2],n[2*a-1]);a--);for(;i=0){var f=o?(c-i)*g+i:(p-n)*g+n;return o?[e,f]:[f,e]}n=p,i=c;break;case r.C:p=a[l++],c=a[l++],d=a[l++],u=a[l++],m=a[l++],h=a[l++];var y=o?xl(n,p,d,m,e,s):xl(i,c,u,h,e,s);if(y>0)for(var v=0;v=0){f=o?yl(i,c,u,h,x):yl(n,p,d,m,x);return o?[e,f]:[f,e]}}n=m,i=h}}},t}(Sm),VE=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t}(NE),qE=function(e){function t(t){var n=e.call(this,t)||this;return n.type="ec-polygon",n}return We(t,e),t.prototype.getDefaultShape=function(){return new VE},t.prototype.buildPath=function(e,t){var n=t.points,i=t.stackedOnPoints,a=0,r=n.length/2,o=t.smoothMonotone;if(t.connectNulls){for(;r>0&&RE(n[2*r-2],n[2*r-1]);r--);for(;a=0;o--){var s=e.getDimensionInfo(i[o].dimension);if("x"===(a=s&&s.coordDim)||"y"===a){r=i[o];break}}if(r){var l=t.getAxis(a),p=Ur(r.stops,(function(e){return{coord:l.toGlobalCoord(l.dataToCoord(e.value)),color:e.color}})),c=p.length,d=r.outerColors.slice();c&&p[0].coord>p[c-1].coord&&(p.reverse(),d.reverse());var u=function(e,t){var n,i,a=[],r=e.length;function o(e,t,n){var i=e.coord;return{coord:n,color:Ql((n-i)/(t.coord-i),[e.color,t.color])}}for(var s=0;st){i?a.push(o(i,l,t)):n&&a.push(o(n,l,0),o(n,l,t));break}n&&(a.push(o(n,l,0)),n=null),a.push(l),i=l}}return a}(p,"x"===a?n.getWidth():n.getHeight()),m=u.length;if(!m&&c)return p[0].coord<0?d[1]?d[1]:p[c-1].color:d[0]?d[0]:p[0].color;var h=u[0].coord-10,g=u[m-1].coord+10,f=g-h;if(f<.001)return"transparent";zr(u,(function(e){e.offset=(e.coord-h)/f})),u.push({offset:m?u[m-1].offset:.5,color:d[1]||"transparent"}),u.unshift({offset:m?u[0].offset:.5,color:d[0]||"transparent"});var y=new Xg(0,0,0,0,u,!0);return y[a]=h,y[a+"2"]=g,y}}}function ZE(e,t,n){var i=e.get("showAllSymbol"),a="auto"===i;if(!i||a){var r=n.getAxesByScale("ordinal")[0];if(r&&(!a||!function(e,t){var n=e.getExtent(),i=Math.abs(n[1]-n[0])/e.scale.count();isNaN(i)&&(i=0);for(var a=t.count(),r=Math.max(1,Math.round(a/5)),o=0;oi)return!1;return!0}(r,t))){var o=t.mapDimension(r.dim),s={};return zr(r.getViewLabels(),(function(e){var t=r.scale.getRawOrdinalNumber(e.tickValue);s[t]=1})),function(e){return!s.hasOwnProperty(t.get(o,e))}}}}function QE(e,t){return[e[2*t],e[2*t+1]]}function JE(e){if(e.get(["endLabel","show"]))return!0;for(var t=0;t0&&"bolder"===e.get(["emphasis","lineStyle","width"]))&&(m.getState("emphasis").style.lineWidth=+m.style.lineWidth+1);Xm(m).seriesIndex=e.seriesIndex,Vh(m,P,D,O);var A=KE(e.get("smooth")),F=e.get("smoothMonotone");if(m.setShape({smooth:A,smoothMonotone:F,connectNulls:S}),h){var R=o.getCalculationInfo("stackedOnSeries"),B=0;h.useStyle(Nr(l.getAreaStyle(),{fill:E,opacity:.7,lineJoin:"bevel",decal:o.getVisual("style").decal})),R&&(B=KE(R.get("smooth"))),h.setShape({smooth:A,stackedOnSmooth:B,smoothMonotone:F,connectNulls:S}),Uh(h,e,"areaStyle"),Xm(h).seriesIndex=e.seriesIndex,Vh(h,P,D,O)}var N=function(e){i._changePolyState(e)};o.eachItemGraphicEl((function(e){e&&(e.onHoverStateChange=N)})),this._polyline.onHoverStateChange=N,this._data=o,this._coordSys=a,this._stackedOnPoints=b,this._points=p,this._step=I,this._valueOrigin=v,e.get("triggerLineEvent")&&(this.packEventData(e,m),h&&this.packEventData(e,h))},t.prototype.packEventData=function(e,t){Xm(t).eventData={componentType:"series",componentSubType:"line",componentIndex:e.componentIndex,seriesIndex:e.seriesIndex,seriesName:e.name,seriesType:"line"}},t.prototype.highlight=function(e,t,n,i){var a=e.getData(),r=kd(a,i);if(this._changePolyState("emphasis"),!(r instanceof Array)&&null!=r&&r>=0){var o=a.getLayout("points"),s=a.getItemGraphicEl(r);if(!s){var l=o[2*r],p=o[2*r+1];if(isNaN(l)||isNaN(p))return;if(this._clipShapeForSymbol&&!this._clipShapeForSymbol.contain(l,p))return;var c=e.get("zlevel")||0,d=e.get("z")||0;(s=new TE(a,r)).x=l,s.y=p,s.setZ(c,d);var u=s.getSymbolPath().getTextContent();u&&(u.zlevel=c,u.z=d,u.z2=this._polyline.z2+1),s.__temp=!0,a.setItemGraphicEl(r,s),s.stopSymbolAnimation(!0),this.group.add(s)}s.highlight()}else Tw.prototype.highlight.call(this,e,t,n,i)},t.prototype.downplay=function(e,t,n,i){var a=e.getData(),r=kd(a,i);if(this._changePolyState("normal"),null!=r&&r>=0){var o=a.getItemGraphicEl(r);o&&(o.__temp?(a.setItemGraphicEl(r,null),this.group.remove(o)):o.downplay())}else Tw.prototype.downplay.call(this,e,t,n,i)},t.prototype._changePolyState=function(e){var t=this._polygon;bh(this._polyline,e),t&&bh(t,e)},t.prototype._newPolyline=function(e){var t=this._polyline;return t&&this._lineGroup.remove(t),t=new LE({shape:{points:e},segmentIgnoreThreshold:2,z2:10}),this._lineGroup.add(t),this._polyline=t,t},t.prototype._newPolygon=function(e,t){var n=this._polygon;return n&&this._lineGroup.remove(n),n=new qE({shape:{points:e,stackedOnPoints:t},segmentIgnoreThreshold:2}),this._lineGroup.add(n),this._polygon=n,n},t.prototype._initSymbolLabelAnimation=function(e,t,n){var i,a,r=t.getBaseAxis(),o=r.inverse;"cartesian2d"===t.type?(i=r.isHorizontal(),a=!1):"polar"===t.type&&(i="angle"===r.dim,a=!0);var s=e.hostModel,l=s.get("animationDuration");Zr(l)&&(l=l(null));var p=s.get("animationDelay")||0,c=Zr(p)?p(null):p;e.eachItemGraphicEl((function(e,r){var s=e;if(s){var d=[e.x,e.y],u=void 0,m=void 0,h=void 0;if(n)if(a){var g=n,f=t.pointToCoord(d);i?(u=g.startAngle,m=g.endAngle,h=-f[1]/180*Math.PI):(u=g.r0,m=g.r,h=f[0])}else{var y=n;i?(u=y.x,m=y.x+y.width,h=e.x):(u=y.y+y.height,m=y.y,h=e.y)}var v=m===u?0:(h-u)/(m-u);o&&(v=1-v);var x=Zr(p)?p(r):l*v+c,b=s.getSymbolPath(),w=b.getTextContent();s.attr({scaleX:0,scaleY:0}),s.animateTo({scaleX:1,scaleY:1},{duration:200,setToFinal:!0,delay:x}),w&&w.animateFrom({style:{opacity:0}},{duration:300,delay:x}),b.disableLabelAnimation=!0}}))},t.prototype._initOrUpdateEndLabel=function(e,t,n){var i=e.getModel("endLabel");if(JE(e)){var a=e.getData(),r=this._polyline,o=a.getLayout("points");if(!o)return r.removeTextContent(),void(this._endLabel=null);var s=this._endLabel;s||((s=this._endLabel=new Nm({z2:200})).ignoreClip=!0,r.setTextContent(this._endLabel),r.disableLabelAnimation=!0);var l=function(e){for(var t,n,i=e.length/2;i>0&&(t=e[2*i-2],n=e[2*i-1],isNaN(t)||isNaN(n));i--);return i-1}(o);l>=0&&(Yf(r,Xf(e,"endLabel"),{inheritColor:n,labelFetcher:e,labelDataIndex:l,defaultText:function(e,t,n){return null!=n?_E(a,n):CE(a,e)},enableTextSetter:!0},function(e,t){var n=t.getBaseAxis(),i=n.isHorizontal(),a=n.inverse,r=i?a?"right":"left":"center",o=i?"middle":a?"top":"bottom";return{normal:{align:e.get("align")||r,verticalAlign:e.get("verticalAlign")||o}}}(i,t)),r.textConfig.position=null)}else this._endLabel&&(this._polyline.removeTextContent(),this._endLabel=null)},t.prototype._endLabelOnDuring=function(e,t,n,i,a,r,o){var s=this._endLabel,l=this._polyline;if(s){e<1&&null==i.originalX&&(i.originalX=s.x,i.originalY=s.y);var p=n.getLayout("points"),c=n.hostModel,d=c.get("connectNulls"),u=r.get("precision"),m=r.get("distance")||0,h=o.getBaseAxis(),g=h.isHorizontal(),f=h.inverse,y=t.shape,v=f?g?y.x:y.y+y.height:g?y.x+y.width:y.y,x=(g?m:0)*(f?-1:1),b=(g?0:-m)*(f?-1:1),w=g?"x":"y",S=function(e,t,n){for(var i,a,r=e.length/2,o="x"===n?0:1,s=0,l=-1,p=0;p=t||i>=t&&a<=t){l=p;break}s=p,i=a}else i=a;return{range:[s,l],t:(t-i)/(a-i)}}(p,v,w),C=S.range,_=C[1]-C[0],T=void 0;if(_>=1){if(_>1&&!d){var I=QE(p,C[0]);s.attr({x:I[0]+x,y:I[1]+b}),a&&(T=c.getRawValue(C[0]))}else{(I=l.getPointOn(v,w))&&s.attr({x:I[0]+x,y:I[1]+b});var E=c.getRawValue(C[0]),M=c.getRawValue(C[1]);a&&(T=Vd(n,u,E,M,S.t))}i.lastFrameIndex=C[0]}else{var k=1===e||i.lastFrameIndex>0?C[0]:0;I=QE(p,k);a&&(T=c.getRawValue(k)),s.attr({x:I[0]+x,y:I[1]+b})}if(a){var P=ay(s);"function"==typeof P.setLabelText&&P.setLabelText(T)}}},t.prototype._doUpdateAnimation=function(e,t,n,i,a,r,o){var s=this._polyline,l=this._polygon,p=e.hostModel,c=function(e,t,n,i,a,r,o){for(var s=function(e,t){var n=[];return t.diff(e).add((function(e){n.push({cmd:"+",idx:e})})).update((function(e,t){n.push({cmd:"=",idx:t,idx1:e})})).remove((function(e){n.push({cmd:"-",idx:e})})).execute(),n}(e,t),l=[],p=[],c=[],d=[],u=[],m=[],h=[],g=DE(a,t,o),f=e.getLayout("points")||[],y=t.getLayout("points")||[],v=0;v3e3||l&&$E(u,h)>3e3)return s.stopAnimation(),s.setShape({points:m}),void(l&&(l.stopAnimation(),l.setShape({points:m,stackedOnPoints:h})));s.shape.__points=c.current,s.shape.points=d;var g={shape:{points:m}};c.current!==d&&(g.shape.__points=c.next),s.stopAnimation(),pf(s,g,p),l&&(l.setShape({points:d,stackedOnPoints:u}),l.stopAnimation(),pf(l,{shape:{stackedOnPoints:h}},p),s.shape.points!==l.shape.points&&(l.shape.points=s.shape.points));for(var f=[],y=c.status,v=0;vt&&(t=e[n]);return isFinite(t)?t:NaN},min:function(e){for(var t=1/0,n=0;nt&&(t=r,n=a)}return isFinite(n)?n:NaN},nearest:function(e){return e[0]}},aM=function(e){return Math.round(e.length/2)};function rM(e){return{seriesType:e,reset:function(e,t,n){var i=e.getData(),a=e.get("sampling"),r=e.coordinateSystem,o=i.count();if(o>10&&"cartesian2d"===r.type&&a){var s=r.getBaseAxis(),l=r.getOtherAxis(s),p=s.getExtent(),c=n.getDevicePixelRatio(),d=Math.abs(p[1]-p[0])*(c||1),u=Math.round(o/d);if(isFinite(u)&&u>1){"lttb"===a&&e.setData(i.lttbDownSample(i.mapDimension(l.dim),1/u));var m=void 0;Qr(a)?m=iM[a]:Zr(a)&&(m=a),m&&e.setData(i.downSample(i.mapDimension(l.dim),1/u,m,aM))}}}}}function oM(e){e.registerChartView(tM),e.registerSeriesModel(SE),e.registerLayout(nM("line",!0)),e.registerVisual({seriesType:"line",reset:function(e){var t=e.getData(),n=e.getModel("lineStyle").getLineStyle();n&&!n.stroke&&(n.stroke=t.getVisual("style").fill),t.setVisual("legendLineStyle",n)}}),e.registerProcessor(e.PRIORITY.PROCESSOR.STATISTIC,rM("line"))}var sM=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.getInitialData=function(e,t){return tT(null,this,{useEncodeDefaulter:!0})},t.prototype.getMarkerPosition=function(e,t,n){var i=this.coordinateSystem;if(i&&i.clampData){var a=i.clampData(e),r=i.dataToPoint(a);if(n)zr(i.getAxes(),(function(e,n){if("category"===e.type&&null!=t){var i=e.getTicksCoords(),o=e.getTickModel().get("alignWithLabel"),s=a[n],l="x1"===t[n]||"y1"===t[n];if(l&&!o&&(s+=1),i.length<2)return;if(2===i.length)return void(r[n]=e.toGlobalCoord(e.getExtent()[l?1:0]));for(var p=void 0,c=void 0,d=1,u=0;us){c=(m+p)/2;break}1===u&&(d=h-i[0].tickValue)}null==c&&(p?p&&(c=i[i.length-1].coord):c=i[0].coord),r[n]=e.toGlobalCoord(c)}}));else{var o=this.getData(),s=o.getLayout("offset"),l=o.getLayout("size"),p=i.getBaseAxis().isHorizontal()?0:1;r[p]+=s+l/2}return r}return[NaN,NaN]},t.type="series.__base_bar__",t.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,barMinHeight:0,barMinAngle:0,large:!1,largeThreshold:400,progressive:3e3,progressiveChunkMode:"mod"},t}(mw);mw.registerClass(sM);var lM=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.getInitialData=function(){return tT(null,this,{useEncodeDefaulter:!0,createInvertedIndices:!!this.get("realtimeSort",!0)||null})},t.prototype.getProgressive=function(){return!!this.get("large")&&this.get("progressive")},t.prototype.getProgressiveThreshold=function(){var e=this.get("progressiveThreshold"),t=this.get("largeThreshold");return t>e&&(e=t),e},t.prototype.brushSelector=function(e,t,n){return n.rect(t.getItemLayout(e))},t.type="series.bar",t.dependencies=["grid","polar"],t.defaultOption=Sy(sM.defaultOption,{clip:!0,roundCap:!1,showBackground:!1,backgroundStyle:{color:"rgba(180, 180, 180, 0.2)",borderColor:null,borderWidth:0,borderType:"solid",borderRadius:0,shadowBlur:0,shadowColor:null,shadowOffsetX:0,shadowOffsetY:0,opacity:1},select:{itemStyle:{borderColor:"#212121"}},realtimeSort:!1}),t}(sM),pM=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0},cM=function(e){function t(t){var n=e.call(this,t)||this;return n.type="sausage",n}return We(t,e),t.prototype.getDefaultShape=function(){return new pM},t.prototype.buildPath=function(e,t){var n=t.cx,i=t.cy,a=Math.max(t.r0||0,0),r=Math.max(t.r,0),o=.5*(r-a),s=a+o,l=t.startAngle,p=t.endAngle,c=t.clockwise,d=2*Math.PI,u=c?p-lr)return!0;r=p}return!1},t.prototype._isOrderDifferentInView=function(e,t){for(var n=t.scale,i=n.getExtent(),a=Math.max(0,i[0]),r=Math.min(i[1],n.getOrdinalMeta().categories.length-1);a<=r;++a)if(e.ordinalNumbers[a]!==n.getRawOrdinalNumber(a))return!0},t.prototype._updateSortWithinSameData=function(e,t,n,i){if(this._isOrderChangedWithinSameData(e,t,n)){var a=this._dataSort(e,n,t);this._isOrderDifferentInView(a,n)&&(this._removeOnRenderedListener(i),i.dispatchAction({type:"changeAxisOrder",componentType:n.dim+"Axis",axisId:n.index,sortInfo:a}))}},t.prototype._dispatchInitSort=function(e,t,n){var i=t.baseAxis,a=this._dataSort(e,i,(function(n){return e.get(e.mapDimension(t.otherAxis.dim),n)}));n.dispatchAction({type:"changeAxisOrder",componentType:i.dim+"Axis",isInitSort:!0,axisId:i.index,sortInfo:a})},t.prototype.remove=function(e,t){this._clear(this._model),this._removeOnRenderedListener(t)},t.prototype.dispose=function(e,t){this._removeOnRenderedListener(t)},t.prototype._removeOnRenderedListener=function(e){this._onRendered&&(e.getZr().off("rendered",this._onRendered),this._onRendered=null)},t.prototype._clear=function(e){var t=this.group,n=this._data;e&&e.isAnimationEnabled()&&n&&!this._isLargeDraw?(this._removeBackground(),this._backgroundEls=[],n.eachItemGraphicEl((function(t){hf(t,e,Xm(t).dataIndex)}))):t.removeAll(),this._data=null,this._isFirstFrame=!0},t.prototype._removeBackground=function(){this.group.remove(this._backgroundGroup),this._backgroundGroup=null},t.type="bar",t}(Tw),yM={cartesian2d:function(e,t){var n=t.width<0?-1:1,i=t.height<0?-1:1;n<0&&(t.x+=t.width,t.width=-t.width),i<0&&(t.y+=t.height,t.height=-t.height);var a=e.x+e.width,r=e.y+e.height,o=hM(t.x,e.x),s=gM(t.x+t.width,a),l=hM(t.y,e.y),p=gM(t.y+t.height,r),c=sa?s:o,t.y=d&&l>r?p:l,t.width=c?0:s-o,t.height=d?0:p-l,n<0&&(t.x+=t.width,t.width=-t.width),i<0&&(t.y+=t.height,t.height=-t.height),c||d},polar:function(e,t){var n=t.r0<=t.r?1:-1;if(n<0){var i=t.r;t.r=t.r0,t.r0=i}var a=gM(t.r,e.r),r=hM(t.r0,e.r0);t.r=a,t.r0=r;var o=a-r<0;if(n<0){i=t.r;t.r=t.r0,t.r0=i}return o}},vM={cartesian2d:function(e,t,n,i,a,r,o,s,l){var p=new Fm({shape:Br({},i),z2:1});(p.__dataIndex=n,p.name="item",r)&&(p.shape[a?"height":"width"]=0);return p},polar:function(e,t,n,i,a,r,o,s,l){var p=!a&&l?cM:Dg,c=new p({shape:i,z2:1});c.name="item";var d,u,m=TM(a);if(c.calculateTextPosition=(d=m,u=({isRoundCap:p===cM}||{}).isRoundCap,function(e,t,n){var i=t.position;if(!i||i instanceof Array)return _c(e,t,n);var a=d(i),r=null!=t.distance?t.distance:5,o=this.shape,s=o.cx,l=o.cy,p=o.r,c=o.r0,m=(p+c)/2,h=o.startAngle,g=o.endAngle,f=(h+g)/2,y=u?Math.abs(p-c)/2:0,v=Math.cos,x=Math.sin,b=s+p*v(h),w=l+p*x(h),S="left",C="top";switch(a){case"startArc":b=s+(c-r)*v(f),w=l+(c-r)*x(f),S="center",C="top";break;case"insideStartArc":b=s+(c+r)*v(f),w=l+(c+r)*x(f),S="center",C="bottom";break;case"startAngle":b=s+m*v(h)+dM(h,r+y,!1),w=l+m*x(h)+uM(h,r+y,!1),S="right",C="middle";break;case"insideStartAngle":b=s+m*v(h)+dM(h,-r+y,!1),w=l+m*x(h)+uM(h,-r+y,!1),S="left",C="middle";break;case"middle":b=s+m*v(f),w=l+m*x(f),S="center",C="middle";break;case"endArc":b=s+(p+r)*v(f),w=l+(p+r)*x(f),S="center",C="bottom";break;case"insideEndArc":b=s+(p-r)*v(f),w=l+(p-r)*x(f),S="center",C="top";break;case"endAngle":b=s+m*v(g)+dM(g,r+y,!0),w=l+m*x(g)+uM(g,r+y,!0),S="left",C="middle";break;case"insideEndAngle":b=s+m*v(g)+dM(g,-r+y,!0),w=l+m*x(g)+uM(g,-r+y,!0),S="right",C="middle";break;default:return _c(e,t,n)}return(e=e||{}).x=b,e.y=w,e.align=S,e.verticalAlign=C,e}),r){var h=a?"r":"endAngle",g={};c.shape[h]=a?i.r0:i.startAngle,g[h]=i[h],(s?pf:cf)(c,{shape:g},r)}return c}};function xM(e,t,n,i,a,r,o,s){var l,p;r?(p={x:i.x,width:i.width},l={y:i.y,height:i.height}):(p={y:i.y,height:i.height},l={x:i.x,width:i.width}),s||(o?pf:cf)(n,{shape:l},t,a,null),(o?pf:cf)(n,{shape:p},t?e.baseAxis.model:null,a)}function bM(e,t){for(var n=0;n0?1:-1,o=i.height>0?1:-1;return{x:i.x+r*a/2,y:i.y+o*a/2,width:i.width-r*a,height:i.height-o*a}},polar:function(e,t,n){var i=e.getItemLayout(t);return{cx:i.cx,cy:i.cy,r0:i.r0,r:i.r,startAngle:i.startAngle,endAngle:i.endAngle,clockwise:i.clockwise}}};function TM(e){return function(e){var t=e?"Arc":"Angle";return function(e){switch(e){case"start":case"insideStart":case"end":case"insideEnd":return e+t;default:return e}}}(e)}function IM(e,t,n,i,a,r,o,s){var l=t.getItemVisual(n,"style");if(s){if(!r.get("roundCap")){var p=e.shape;Br(p,mM(i.getModel("itemStyle"),p,!0)),e.setShape(p)}}else{var c=i.get(["itemStyle","borderRadius"])||0;e.setShape("r",c)}e.useStyle(l);var d=i.getShallow("cursor");d&&e.attr("cursor",d);var u=s?o?a.r>=a.r0?"endArc":"startArc":a.endAngle>=a.startAngle?"endAngle":"startAngle":o?a.height>=0?"bottom":"top":a.width>=0?"right":"left",m=Xf(i);Yf(e,m,{labelFetcher:r,labelDataIndex:n,defaultText:CE(r.getData(),n),inheritColor:l.fill,defaultOpacity:l.opacity,defaultOutsidePosition:u});var h=e.getTextContent();if(s&&h){var g=i.get(["label","position"]);e.textConfig.inside="middle"===g||null,function(e,t,n,i){if(eo(i))e.setTextConfig({rotation:i});else if(Xr(t))e.setTextConfig({rotation:0});else{var a,r=e.shape,o=r.clockwise?r.startAngle:r.endAngle,s=r.clockwise?r.endAngle:r.startAngle,l=(o+s)/2,p=n(t);switch(p){case"startArc":case"insideStartArc":case"middle":case"insideEndArc":case"endArc":a=l;break;case"startAngle":case"insideStartAngle":a=o;break;case"endAngle":case"insideEndAngle":a=s;break;default:return void e.setTextConfig({rotation:0})}var c=1.5*Math.PI-a;"middle"===p&&c>Math.PI/2&&c<1.5*Math.PI&&(c-=Math.PI),e.setTextConfig({rotation:c})}}(e,"outside"===g?u:g,TM(o),i.get(["label","rotate"]))}ry(h,m,r.getRawValue(n),(function(e){return _E(t,e)}));var f=i.getModel(["emphasis"]);Vh(e,f.get("focus"),f.get("blurScope"),f.get("disabled")),Uh(e,i),function(e){return null!=e.startAngle&&null!=e.endAngle&&e.startAngle===e.endAngle}(a)&&(e.style.fill="none",e.style.stroke="none",zr(e.states,(function(e){e.style&&(e.style.fill=e.style.stroke="none")})))}var EM=function(){},MM=function(e){function t(t){var n=e.call(this,t)||this;return n.type="largeBar",n}return We(t,e),t.prototype.getDefaultShape=function(){return new EM},t.prototype.buildPath=function(e,t){for(var n=t.points,i=this.baseDimIdx,a=1-this.baseDimIdx,r=[],o=[],s=this.barWidth,l=0;l=s[0]&&t<=s[0]+l[0]&&n>=s[1]&&n<=s[1]+l[1])return o[c]}return-1}(this,e.offsetX,e.offsetY);Xm(this).dataIndex=t>=0?t:null}),30,!1);function DM(e,t,n){if(jE(n,"cartesian2d")){var i=t,a=n.getArea();return{x:e?i.x:a.x,y:e?a.y:i.y,width:e?i.width:a.width,height:e?a.height:i.height}}var r=t;return{cx:(a=n.getArea()).cx,cy:a.cy,r0:e?a.r0:r.r0,r:e?a.r:r.r,startAngle:e?r.startAngle:0,endAngle:e?r.endAngle:2*Math.PI}}function OM(e){e.registerChartView(fM),e.registerSeriesModel(lM),e.registerLayout(e.PRIORITY.VISUAL.LAYOUT,Yr(IT,"bar")),e.registerLayout(e.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT,ET("bar")),e.registerProcessor(e.PRIORITY.PROCESSOR.STATISTIC,rM("bar")),e.registerAction({type:"changeAxisOrder",event:"changeAxisOrder",update:"update"},(function(e,t){var n=e.componentType||"series";t.eachComponent({mainType:n,query:e},(function(t){e.sortInfo&&t.axis.setCategorySortInfo(e.sortInfo)}))}))}var AM=2*Math.PI,FM=Math.PI/180;function RM(e,t){return Sv(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()})}function BM(e,t){var n=RM(e,t),i=e.get("center"),a=e.get("radius");Xr(a)||(a=[0,a]);var r,o,s=Wc(n.width,t.getWidth()),l=Wc(n.height,t.getHeight()),p=Math.min(s,l),c=Wc(a[0],p/2),d=Wc(a[1],p/2),u=e.coordinateSystem;if(u){var m=u.dataToPoint(i);r=m[0]||0,o=m[1]||0}else Xr(i)||(i=[i,i]),r=Wc(i[0],s)+n.x,o=Wc(i[1],l)+n.y;return{cx:r,cy:o,r0:c,r:d}}function NM(e,t,n){t.eachSeriesByType(e,(function(e){var t=e.getData(),i=t.mapDimension("value"),a=RM(e,n),r=BM(e,n),o=r.cx,s=r.cy,l=r.r,p=r.r0,c=-e.get("startAngle")*FM,d=e.get("endAngle"),u=e.get("padAngle")*FM;d="auto"===d?c-AM:-d*FM;var m=e.get("minAngle")*FM+u,h=0;t.each(i,(function(e){!isNaN(e)&&h++}));var g=t.getSum(i),f=Math.PI/(g||h)*2,y=e.get("clockwise"),v=e.get("roseType"),x=e.get("stillShowZeroSum"),b=t.getDataExtent(i);b[0]=0;var w=y?1:-1,S=[c,d],C=w*u/2;tm(S,!y),c=S[0],d=S[1];var _=Math.abs(d-c),T=_,I=0,E=c;if(t.setLayout({viewRect:a,r:l}),t.each(i,(function(e,n){var i;if(isNaN(e))t.setItemLayout(n,{angle:NaN,startAngle:NaN,endAngle:NaN,clockwise:y,cx:o,cy:s,r0:p,r:v?NaN:l});else{(i="area"!==v?0===g&&x?f:e*f:_/h)i?c=r=E+w*i/2:(r=E+C,c=a-C),t.setItemLayout(n,{angle:i,startAngle:r,endAngle:c,clockwise:y,cx:o,cy:s,r0:p,r:v?Hc(e,b,[p,l]):l}),E=a}})),Tn?o:r,c=Math.abs(l.label.y-n);if(c>=p.maxY){var d=l.label.x-t-l.len2*a,u=i+l.len,h=Math.abs(d)e.unconstrainedWidth?null:m:null;i.setStyle("width",h)}var g=i.getBoundingRect();r.width=g.width;var f=(i.style.margin||0)+2.1;r.height=g.height+f,r.y-=(r.height-d)/2}}}function zM(e){return"center"===e.position}function UM(e){var t,n,i=e.getData(),a=[],r=!1,o=(e.get("minShowLabelAngle")||0)*VM,s=i.getLayout("viewRect"),l=i.getLayout("r"),p=s.width,c=s.x,d=s.y,u=s.height;function m(e){e.ignore=!0}i.each((function(e){var s=i.getItemGraphicEl(e),d=s.shape,u=s.getTextContent(),h=s.getTextGuideLine(),g=i.getItemModel(e),f=g.getModel("label"),y=f.get("position")||g.get(["emphasis","label","position"]),v=f.get("distanceToLabelLine"),x=f.get("alignTo"),b=Wc(f.get("edgeDistance"),p),w=f.get("bleedMargin"),S=g.getModel("labelLine"),C=S.get("length");C=Wc(C,p);var _=S.get("length2");if(_=Wc(_,p),Math.abs(d.endAngle-d.startAngle)0?"right":"left":P>0?"left":"right"}var L=Math.PI,V=0,q=f.get("rotate");if(eo(q))V=q*(L/180);else if("center"===y)V=0;else if("radial"===q||!0===q){V=P<0?-k+L:-k}else if("tangential"===q&&"outside"!==y&&"outer"!==y){var G=Math.atan2(P,D);G<0&&(G=2*L+G),D>0&&(G=L+G),V=G-L}if(r=!!V,u.x=T,u.y=I,u.rotation=V,u.setStyle({verticalAlign:"middle"}),O){u.setStyle({align:M});var z=u.states.select;z&&(z.x+=u.x,z.y+=u.y)}else{var U=u.getBoundingRect().clone();U.applyTransform(u.getComputedTransform());var j=(u.style.margin||0)+2.1;U.y-=j/2,U.height+=j,a.push({label:u,labelLine:h,position:y,len:C,len2:_,minTurnAngle:S.get("minTurnAngle"),maxSurfaceAngle:S.get("maxSurfaceAngle"),surfaceNormal:new Ms(P,D),linePoints:E,textAlign:M,labelDistance:v,labelAlignTo:x,edgeDistance:b,bleedMargin:w,rect:U,unconstrainedWidth:U.width,labelStyleWidth:u.style.width})}s.setTextConfig({inside:O})}})),!r&&e.get("avoidLabelOverlap")&&function(e,t,n,i,a,r,o,s){for(var l=[],p=[],c=Number.MAX_VALUE,d=-Number.MAX_VALUE,u=0;u0){for(var l=r.getItemLayout(0),p=1;isNaN(l&&l.startAngle)&&p=n.r0}},t.type="pie",t}(Tw);function WM(e,t,n){t=Xr(t)&&{coordDimensions:t}||Br({encodeDefine:e.getEncode()},t);var i=e.getSource(),a=$_(i,t).dimensions,r=new W_(a,e);return r.initData(i,n),r}var $M=function(){function e(e,t){this._getDataWithEncodedVisual=e,this._getRawData=t}return e.prototype.getAllNames=function(){var e=this._getRawData();return e.mapArray(e.getName)},e.prototype.containName=function(e){return this._getRawData().indexOfName(e)>=0},e.prototype.indexOfName=function(e){return this._getDataWithEncodedVisual().indexOfName(e)},e.prototype.getItemVisual=function(e,t){return this._getDataWithEncodedVisual().getItemVisual(e,t)},e}(),KM=Pd(),YM=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.init=function(t){e.prototype.init.apply(this,arguments),this.legendVisualProvider=new $M(Kr(this.getData,this),Kr(this.getRawData,this)),this._defaultLabelLine(t)},t.prototype.mergeOption=function(){e.prototype.mergeOption.apply(this,arguments)},t.prototype.getInitialData=function(){return WM(this,{coordDimensions:["value"],encodeDefaulter:Yr($v,this)})},t.prototype.getDataParams=function(t){var n=this.getData(),i=KM(n),a=i.seats;if(!a){var r=[];n.each(n.mapDimension("value"),(function(e){r.push(e)})),a=i.seats=Qc(r,n.hostModel.get("percentPrecision"))}var o=e.prototype.getDataParams.call(this,t);return o.percent=a[t]||0,o.$vars.push("percent"),o},t.prototype._defaultLabelLine=function(e){xd(e,"labelLine",["show"]);var t=e.labelLine,n=e.emphasis.labelLine;t.show=t.show&&e.label.show,n.show=n.show&&e.emphasis.label.show},t.type="series.pie",t.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,endAngle:"auto",padAngle:0,minAngle:0,minShowLabelAngle:0,selectedOffset:10,percentPrecision:2,stillShowZeroSum:!0,left:0,top:0,right:0,bottom:0,width:null,height:null,label:{rotate:0,show:!0,overflow:"truncate",position:"outer",alignTo:"none",edgeDistance:"25%",bleedMargin:10,distanceToLabelLine:5},labelLine:{show:!0,length:15,length2:15,smooth:!1,minTurnAngle:90,maxSurfaceAngle:90,lineStyle:{width:1,type:"solid"}},itemStyle:{borderWidth:1,borderJoin:"round"},showEmptyCircle:!0,emptyCircleStyle:{color:"lightgray",opacity:1},labelLayout:{hideOverlap:!0},emphasis:{scale:!0,scaleSize:5},avoidLabelOverlap:!0,animationType:"expansion",animationDuration:1e3,animationTypeUpdate:"transition",animationEasingUpdate:"cubicInOut",animationDurationUpdate:500,animationEasing:"cubicInOut"},t}(mw);function XM(e){e.registerChartView(HM),e.registerSeriesModel(YM),CS("pie",e.registerAction),e.registerLayout(Yr(NM,"pie")),e.registerProcessor(LM("pie")),e.registerProcessor(function(e){return{seriesType:e,reset:function(e,t){var n=e.getData();n.filterSelf((function(e){var t=n.mapDimension("value"),i=n.get(t,e);return!(eo(i)&&!isNaN(i)&&i<0)}))}}}("pie"))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n}We(t,e),t.prototype.getInitialData=function(e,t){return tT(null,this,{useEncodeDefaulter:!0})},t.prototype.getProgressive=function(){var e=this.option.progressive;return null==e?this.option.large?5e3:this.get("progressive"):e},t.prototype.getProgressiveThreshold=function(){var e=this.option.progressiveThreshold;return null==e?this.option.large?1e4:this.get("progressiveThreshold"):e},t.prototype.brushSelector=function(e,t,n){return n.point(t.getItemLayout(e))},t.prototype.getZLevelKey=function(){return this.getData().count()>this.getProgressiveThreshold()?this.id:""},t.type="series.scatter",t.dependencies=["grid","polar","geo","singleAxis","calendar"],t.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,symbolSize:10,large:!1,largeThreshold:2e3,itemStyle:{opacity:.8},emphasis:{scale:!0},clip:!0,select:{itemStyle:{borderColor:"#212121"}},universalTransition:{divideShape:"clone"}}}(mw);var ZM=function(){},QM=function(e){function t(t){var n=e.call(this,t)||this;return n._off=0,n.hoverDataIdx=-1,n}return We(t,e),t.prototype.getDefaultShape=function(){return new ZM},t.prototype.reset=function(){this.notClear=!1,this._off=0},t.prototype.buildPath=function(e,t){var n,i=t.points,a=t.size,r=this.symbolProxy,o=r.shape,s=e.getContext?e.getContext():e,l=s&&a[0]<4,p=this.softClipShape;if(l)this._ctx=s;else{for(this._ctx=null,n=this._off;n=0;s--){var l=2*s,p=i[l]-r/2,c=i[l+1]-o/2;if(e>=p&&t>=c&&e<=p+r&&t<=c+o)return s}return-1},t.prototype.contain=function(e,t){var n=this.transformCoordToLocal(e,t),i=this.getBoundingRect();return e=n[0],t=n[1],i.contain(e,t)?(this.hoverDataIdx=this.findDataIndex(e,t))>=0:(this.hoverDataIdx=-1,!1)},t.prototype.getBoundingRect=function(){var e=this._rect;if(!e){for(var t=this.shape,n=t.points,i=t.size,a=i[0],r=i[1],o=1/0,s=1/0,l=-1/0,p=-1/0,c=0;c=0&&(l.dataIndex=n+(e.startIndex||0))}))},e.prototype.remove=function(){this._clear()},e.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},e}(),ek=(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.render=function(e,t,n){var i=e.getData();this._updateSymbolDraw(i,e).updateData(i,{clipShape:this._getClipShape(e)}),this._finished=!0},t.prototype.incrementalPrepareRender=function(e,t,n){var i=e.getData();this._updateSymbolDraw(i,e).incrementalPrepareUpdate(i),this._finished=!1},t.prototype.incrementalRender=function(e,t,n){this._symbolDraw.incrementalUpdate(e,t.getData(),{clipShape:this._getClipShape(t)}),this._finished=e.end===t.getData().count()},t.prototype.updateTransform=function(e,t,n){var i=e.getData();if(this.group.dirty(),!this._finished||i.count()>1e4)return{update:!0};var a=nM("").reset(e,t,n);a.progress&&a.progress({start:0,end:i.count(),count:i.count()},i),this._symbolDraw.updateLayout(i)},t.prototype.eachRendered=function(e){this._symbolDraw&&this._symbolDraw.eachRendered(e)},t.prototype._getClipShape=function(e){if(e.get("clip",!0)){var t=e.coordinateSystem;return t&&t.getArea&&t.getArea(.1)}},t.prototype._updateSymbolDraw=function(e,t){var n=this._symbolDraw,i=t.pipelineContext.large;return n&&i===this._isLargeDraw||(n&&n.remove(),n=this._symbolDraw=i?new JM:new PE,this._isLargeDraw=i,this.group.removeAll()),this.group.add(n.group),n},t.prototype.remove=function(e,t){this._symbolDraw&&this._symbolDraw.remove(!0),this._symbolDraw=null},t.prototype.dispose=function(){},t.type="scatter"}(Tw),function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.type="grid",t.dependencies=["xAxis","yAxis"],t.layoutMode="box",t.defaultOption={show:!1,z:0,left:"10%",top:60,right:"10%",bottom:70,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"},t}(kv)),tk=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.getCoordSysModel=function(){return this.getReferringComponents("grid",Fd).models[0]},t.type="cartesian2dAxis",t}(kv);qr(tk,lI);var nk={show:!0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#6E7079",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#E0E6F1"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.2)","rgba(210,219,238,0.2)"]}}},ik=Fr({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},nk),ak=Fr({boundaryGap:[0,0],axisLine:{show:"auto"},axisTick:{show:"auto"},splitNumber:5,minorTick:{show:!1,splitNumber:5,length:3,lineStyle:{}},minorSplitLine:{show:!1,lineStyle:{color:"#F4F7FD",width:1}}},nk),rk={category:ik,value:ak,time:Fr({splitNumber:6,axisLabel:{showMinLabel:!1,showMaxLabel:!1,rich:{primary:{fontWeight:"bold"}}},splitLine:{show:!1}},ak),log:Nr({logBase:10},ak)},ok={value:1,category:1,time:1,log:1};function sk(e,t,n,i){zr(ok,(function(a,r){var o=Fr(Fr({},rk[r],!0),i,!0),s=function(e){function n(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t+"Axis."+r,n}return We(n,e),n.prototype.mergeDefaultAndTheme=function(e,t){var n=_v(this),i=n?Iv(e):{};Fr(e,t.getTheme().get(r+"Axis")),Fr(e,this.getDefaultOption()),e.type=lk(e),n&&Tv(e,i,n)},n.prototype.optionUpdated=function(){"category"===this.option.type&&(this.__ordinalMeta=aT.createByAxisModel(this))},n.prototype.getCategories=function(e){var t=this.option;if("category"===t.type)return e?t.data:this.__ordinalMeta.categories},n.prototype.getOrdinalMeta=function(){return this.__ordinalMeta},n.prototype.getTicksGenerator=function(){var e=this.option;if("value"===e.type)return e.ticksGenerator},n.type=t+"Axis."+r,n.defaultOption=o,n}(n);e.registerComponentModel(s)})),e.registerSubTypeDefaulter(t+"Axis",lk)}function lk(e){return e.type||(e.data?"category":"value")}var pk=function(){function e(e){this.type="cartesian",this._dimList=[],this._axes={},this.name=e||""}return e.prototype.getAxis=function(e){return this._axes[e]},e.prototype.getAxes=function(){return Ur(this._dimList,(function(e){return this._axes[e]}),this)},e.prototype.getAxesByScale=function(e){return e=e.toLowerCase(),Hr(this.getAxes(),(function(t){return t.scale.type===e}))},e.prototype.addAxis=function(e){var t=e.dim;this._axes[t]=e,this._dimList.push(t)},e}(),ck=["x","y"];function dk(e){return"interval"===e.type||"time"===e.type}var uk=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type="cartesian2d",t.dimensions=ck,t}return We(t,e),t.prototype.calcAffineTransform=function(){this._transform=this._invTransform=null;var e=this.getAxis("x").scale,t=this.getAxis("y").scale;if(dk(e)&&dk(t)){var n=e.getExtent(),i=t.getExtent(),a=this.dataToPoint([n[0],i[0]]),r=this.dataToPoint([n[1],i[1]]),o=n[1]-n[0],s=i[1]-i[0];if(o&&s){var l=(r[0]-a[0])/o,p=(r[1]-a[1])/s,c=a[0]-n[0]*l,d=a[1]-i[0]*p,u=this._transform=[l,0,0,p,c,d];this._invTransform=Is([],u)}}},t.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAxis("x")},t.prototype.containPoint=function(e){var t=this.getAxis("x"),n=this.getAxis("y");return t.contain(t.toLocalCoord(e[0]))&&n.contain(n.toLocalCoord(e[1]))},t.prototype.containData=function(e){return this.getAxis("x").containData(e[0])&&this.getAxis("y").containData(e[1])},t.prototype.containZone=function(e,t){var n=this.dataToPoint(e),i=this.dataToPoint(t),a=this.getArea(),r=new Ns(n[0],n[1],i[0]-n[0],i[1]-n[1]);return a.intersect(r)},t.prototype.dataToPoint=function(e,t,n){n=n||[];var i=e[0],a=e[1];if(this._transform&&null!=i&&isFinite(i)&&null!=a&&isFinite(a))return Ho(n,e,this._transform);var r=this.getAxis("x"),o=this.getAxis("y");return n[0]=r.toGlobalCoord(r.dataToCoord(i,t)),n[1]=o.toGlobalCoord(o.dataToCoord(a,t)),n},t.prototype.clampData=function(e,t){var n=this.getAxis("x").scale,i=this.getAxis("y").scale,a=n.getExtent(),r=i.getExtent(),o=n.parse(e[0]),s=i.parse(e[1]);return(t=t||[])[0]=Math.min(Math.max(Math.min(a[0],a[1]),o),Math.max(a[0],a[1])),t[1]=Math.min(Math.max(Math.min(r[0],r[1]),s),Math.max(r[0],r[1])),t},t.prototype.pointToData=function(e,t){var n=[];if(this._invTransform)return Ho(n,e,this._invTransform);var i=this.getAxis("x"),a=this.getAxis("y");return n[0]=i.coordToData(i.toLocalCoord(e[0]),t),n[1]=a.coordToData(a.toLocalCoord(e[1]),t),n},t.prototype.getOtherAxis=function(e){return this.getAxis("x"===e.dim?"y":"x")},t.prototype.getArea=function(e){e=e||0;var t=this.getAxis("x").getGlobalExtent(),n=this.getAxis("y").getGlobalExtent(),i=Math.min(t[0],t[1])-e,a=Math.min(n[0],n[1])-e,r=Math.max(t[0],t[1])-i+e,o=Math.max(n[0],n[1])-a+e;return new Ns(i,a,r,o)},t}(pk),mk=function(e){function t(t,n,i,a,r){var o=e.call(this,t,n,i)||this;return o.index=0,o.type=a||"value",o.position=r||"bottom",o}return We(t,e),t.prototype.isHorizontal=function(){var e=this.position;return"top"===e||"bottom"===e},t.prototype.getGlobalExtent=function(e){var t=this.getExtent();return t[0]=this.toGlobalCoord(t[0]),t[1]=this.toGlobalCoord(t[1]),e&&t[0]>t[1]&&t.reverse(),t},t.prototype.pointToData=function(e,t){return this.coordToData(this.toLocalCoord(e["x"===this.dim?0:1]),t)},t.prototype.setCategorySortInfo=function(e){if("category"!==this.type)return!1;this.model.option.categorySortInfo=e,this.scale.setSortInfo(e)},t}(RI);function hk(e,t,n){n=n||{};var i=e.coordinateSystem,a=t.axis,r={},o=a.getAxesOnZeroOf()[0],s=a.position,l=o?"onZero":s,p=a.dim,c=i.getRect(),d=[c.x,c.x+c.width,c.y,c.y+c.height],u={left:0,right:1,top:0,bottom:1,onZero:2},m=t.get("offset")||0,h="x"===p?[d[2]-m,d[3]+m]:[d[0]-m,d[1]+m];if(o){var g=o.toGlobalCoord(o.dataToCoord(0));h[u.onZero]=Math.max(Math.min(g,h[1]),h[0])}r.position=["y"===p?h[u[l]]:d[0],"x"===p?h[u[l]]:d[3]],r.rotation=Math.PI/2*("x"===p?0:1);r.labelDirection=r.tickDirection=r.nameDirection={top:-1,bottom:1,left:-1,right:1}[s],r.labelOffset=o?h[u[s]]-h[u.onZero]:0,t.get(["axisTick","inside"])&&(r.tickDirection=-r.tickDirection),po(n.labelInside,t.get(["axisLabel","inside"]))&&(r.labelDirection=-r.labelDirection);var f=t.get(["axisLabel","rotate"]);return r.labelRotate="top"===l?-f:f,r.z2=1,r}function gk(e){return"cartesian2d"===e.get("coordinateSystem")}function fk(e){var t={xAxisModel:null,yAxisModel:null};return zr(t,(function(n,i){var a=i.replace(/Model$/,""),r=e.getReferringComponents(a,Fd).models[0];t[i]=r})),t}var yk=Math.log;function vk(e,t,n){var i=fT.prototype,a=i.getTicks.call(n),r=i.getTicks.call(n,!0),o=a.length-1,s=i.getInterval.call(n),l=JT(e,t),p=l.extent,c=l.fixMin,d=l.fixMax;if("log"===e.type){var u=yk(e.base);p=[yk(p[0])/u,yk(p[1])/u]}e.setExtent(p[0],p[1]),e.calcNiceExtent({splitNumber:o,fixMin:c,fixMax:d});var m=i.getExtent.call(e);c&&(p[0]=m[0]),d&&(p[1]=m[1]);var h=i.getInterval.call(e),g=p[0],f=p[1];if(c&&d)h=(f-g)/o;else if(c)for(f=p[0]+h*o;fp[0]&&isFinite(g)&&isFinite(p[0]);)h=lT(h),g=p[1]-h*o;else{e.getTicks().length-1>o&&(h=lT(h));var y=h*o;(g=$c((f=Math.ceil(p[1]/h)*h)-y))<0&&p[0]>=0?(g=0,f=$c(y)):f>0&&p[1]<=0&&(f=0,g=-$c(y))}var v=(a[0].value-r[0].value)/s,x=(a[o].value-r[o].value)/s;i.setExtent.call(e,g+h*v,f+h*x),i.setInterval.call(e,h),(v||x)&&i.setNiceExtent.call(e,g+h,f-h)}var xk=function(){function e(e,t,n){this.type="grid",this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this.axisPointerEnabled=!0,this.dimensions=ck,this._initCartesian(e,t,n),this.model=e}return e.prototype.getRect=function(){return this._rect},e.prototype.update=function(e,t){var n=this._axesMap;function i(e){var t,n=$r(e),i=n.length;if(i){for(var a=[],r=i-1;r>=0;r--){var o=(l=e[+n[r]]).model,s=l.scale;oT(s)&&o.get("alignTicks")&&null==o.get("interval")&&null==o.getTicksGenerator()?a.push(l):(eI(s,o),oT(s)&&!s.isBlank()&&(t=l))}if(a.length){for(;!t&&a.length;){var l;eI((l=a.pop()).scale,l.model),l.scale.isBlank()||(t=l)}a.length&&t&&zr(a,(function(e){vk(e.scale,e.model,t.scale)}))}}}this._updateScale(e,this.model),i(n.x),i(n.y);var a={};zr(n.x,(function(e){wk(n,"y",e,a)})),zr(n.y,(function(e){wk(n,"x",e,a)})),this.resize(this.model,t)},e.prototype.resize=function(e,t,n){var i=e.getBoxLayoutParams(),a=!n&&e.get("containLabel"),r=Sv(i,{width:t.getWidth(),height:t.getHeight()});this._rect=r;var o=this._axesList;function s(){zr(o,(function(e){var t=e.isHorizontal(),n=t?[0,r.width]:[0,r.height],i=e.inverse?1:0;e.setExtent(n[i],n[1-i]),function(e,t){var n=e.getExtent(),i=n[0]+n[1];e.toGlobalCoord="x"===e.dim?function(e){return e+t}:function(e){return i-e+t},e.toLocalCoord="x"===e.dim?function(e){return e-t}:function(e){return i-e+t}}(e,t?r.x:r.y)}))}s(),a&&(zr(o,(function(e){if(!e.model.get(["axisLabel","inside"])){var t=function(e){var t=e.model,n=e.scale;if(t.get(["axisLabel","show"])&&!n.isBlank()){var i,a,r=n.getExtent();a=n instanceof hT?n.count():(i=n.getTicks()).length;var o,s=e.getLabelModel(),l=nI(e),p=1;a>40&&(p=Math.ceil(a/40));for(var c=0;c0&&i>0||n<0&&i<0)}(e)}var Ck=Math.PI,_k=function(){function e(e,t){this.group=new Rc,this.opt=t,this.axisModel=e,Nr(t,{labelOffset:0,nameDirection:1,tickDirection:1,labelDirection:1,silent:!0,handleAutoShown:function(){return!0}});var n=new Rc({x:t.position[0],y:t.position[1],rotation:t.rotation});n.updateTransform(),this._transformGroup=n}return e.prototype.hasBuilder=function(e){return!!Tk[e]},e.prototype.add=function(e){Tk[e](this.opt,this.axisModel,this.group,this._transformGroup)},e.prototype.getGroup=function(){return this.group},e.innerTextLayout=function(e,t,n){var i,a,r=ed(t-e);return td(r)?(a=n>0?"top":"bottom",i="center"):td(r-Ck)?(a=n>0?"bottom":"top",i="center"):(a="middle",i=r>0&&r0?"right":"left":n>0?"left":"right"),{rotation:r,textAlign:i,textVerticalAlign:a}},e.makeAxisEventDataBase=function(e){var t={componentType:e.mainType,componentIndex:e.componentIndex};return t[e.mainType+"Index"]=e.componentIndex,t},e.isLabelSilent=function(e){var t=e.get("tooltip");return e.get("silent")||!(e.get("triggerEvent")||t&&t.show)},e}(),Tk={axisLine:function(e,t,n,i){var a=t.get(["axisLine","show"]);if("auto"===a&&e.handleAutoShown&&(a=e.handleAutoShown("axisLine")),a){var r=t.axis.getExtent(),o=i.transform,s=[r[0],0],l=[r[1],0],p=s[0]>l[0];o&&(Ho(s,s,o),Ho(l,l,o));var c=Br({lineCap:"round"},t.getModel(["axisLine","lineStyle"]).getLineStyle()),d=new Gg({shape:{x1:s[0],y1:s[1],x2:l[0],y2:l[1]},style:c,strokeContainThreshold:e.strokeContainThreshold||5,silent:!0,z2:1});kf(d.shape,d.style.lineWidth),d.anid="line",n.add(d);var u=t.get(["axisLine","symbol"]);if(null!=u){var m=t.get(["axisLine","symbolSize"]);Qr(u)&&(u=[u,u]),(Qr(m)||eo(m))&&(m=[m,m]);var h=VS(t.get(["axisLine","symbolOffset"])||0,m),g=m[0],f=m[1];zr([{rotate:e.rotation+Math.PI/2,offset:h[0],r:0},{rotate:e.rotation-Math.PI/2,offset:h[1],r:Math.sqrt((s[0]-l[0])*(s[0]-l[0])+(s[1]-l[1])*(s[1]-l[1]))}],(function(t,i){if("none"!==u[i]&&null!=u[i]){var a=NS(u[i],-g/2,-f/2,g,f,c.stroke,!0),r=t.r+t.offset,o=p?l:s;a.attr({rotation:t.rotate,x:o[0]+r*Math.cos(e.rotation),y:o[1]-r*Math.sin(e.rotation),silent:!0,z2:11}),n.add(a)}}))}}},axisTickLabel:function(e,t,n,i){var a=function(e,t,n,i){var a=n.axis,r=n.getModel("axisTick"),o=r.get("show");"auto"===o&&i.handleAutoShown&&(o=i.handleAutoShown("axisTick"));if(!o||a.scale.isBlank())return;for(var s=r.getModel("lineStyle"),l=i.tickDirection*r.get("length"),p=kk(a.getTicksCoords(),t.transform,l,Nr(s.getLineStyle(),{stroke:n.get(["axisLine","lineStyle","color"])}),"ticks"),c=0;cd[1]?-1:1,m=["start"===s?d[0]-u*c:"end"===s?d[1]+u*c:(d[0]+d[1])/2,Mk(s)?e.labelOffset+l*c:0],h=t.get("nameRotate");null!=h&&(h=h*Ck/180),Mk(s)?r=_k.innerTextLayout(e.rotation,null!=h?h:e.rotation,l):(r=function(e,t,n,i){var a,r,o=ed(n-e),s=i[0]>i[1],l="start"===t&&!s||"start"!==t&&s;td(o-Ck/2)?(r=l?"bottom":"top",a="center"):td(o-1.5*Ck)?(r=l?"top":"bottom",a="center"):(r="middle",a=o<1.5*Ck&&o>Ck/2?l?"left":"right":l?"right":"left");return{rotation:o,textAlign:a,textVerticalAlign:r}}(e.rotation,s,h||0,d),null!=(o=e.axisNameAvailableWidth)&&(o=Math.abs(o/Math.sin(r.rotation)),!isFinite(o)&&(o=null)));var g=p.getFont(),f=t.get("nameTruncate",!0)||{},y=f.ellipsis,v=po(e.nameTruncateMaxWidth,f.maxWidth,o),x=new Nm({x:m[0],y:m[1],rotation:r.rotation,silent:_k.isLabelSilent(t),style:Zf(p,{text:a,font:g,overflow:"truncate",width:v,ellipsis:y,fill:p.getTextColor()||t.get(["axisLine","lineStyle","color"]),align:p.get("align")||r.textAlign,verticalAlign:p.get("verticalAlign")||r.textVerticalAlign}),z2:1});if(zf({el:x,componentModel:t,itemName:a}),x.__fullText=a,x.anid="name",t.get("triggerEvent")){var b=_k.makeAxisEventDataBase(t);b.targetType="axisName",b.name=a,Xm(x).eventData=b}i.add(x),x.updateTransform(),n.add(x),x.decomposeTransform()}}};function Ik(e){e&&(e.ignore=!0)}function Ek(e,t){var n=e&&e.getBoundingRect().clone(),i=t&&t.getBoundingRect().clone();if(n&&i){var a=bs([]);return _s(a,a,-e.rotation),n.applyTransform(Ss([],a,e.getLocalTransform())),i.applyTransform(Ss([],a,t.getLocalTransform())),n.intersect(i)}}function Mk(e){return"middle"===e||"center"===e}function kk(e,t,n,i,a){for(var r=[],o=[],s=[],l=0;l=0||e===t}function Ok(e){var t=(e.ecModel.getComponent("axisPointer")||{}).coordSysAxesInfo;return t&&t.axesInfo[Fk(e)]}function Ak(e){return!!e.get(["handle","show"])}function Fk(e){return e.type+"||"+e.id}var Rk={},Bk=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.render=function(t,n,i,a){this.axisPointerClass&&function(e){var t=Ok(e);if(t){var n=t.axisPointerModel,i=t.axis.scale,a=n.option,r=n.get("status"),o=n.get("value");null!=o&&(o=i.parse(o));var s=Ak(n);null==r&&(a.status=s?"show":"hide");var l=i.getExtent().slice();l[0]>l[1]&&l.reverse(),(null==o||o>l[1])&&(o=l[1]),o0&&!d.min?d.min=0:null!=d.min&&d.min<0&&!d.max&&(d.max=0);var u=o;null!=d.color&&(u=Nr({color:d.color},o));var m=Fr(Ar(d),{boundaryGap:e,splitNumber:t,scale:n,axisLine:i,axisTick:a,axisLabel:r,name:d.text,showName:s,nameLocation:"end",nameGap:p,nameTextStyle:u,triggerEvent:c},!1);if(Qr(l)){var h=m.name;m.name=l.replace("{value}",null!=h?h:"")}else Zr(l)&&(m.name=l(m.name,m));var g=new xy(m,null,this.ecModel);return qr(g,lI.prototype),g.mainType="radar",g.componentIndex=this.componentIndex,g}),this);this._indicatorModels=d},t.prototype.getIndicatorModels=function(){return this._indicatorModels},t.type="radar",t.defaultOption={z:0,center:["50%","50%"],radius:"75%",startAngle:90,axisName:{show:!0},boundaryGap:[0,0],splitNumber:5,axisNameGap:15,scale:!1,shape:"polygon",axisLine:Fr({lineStyle:{color:"#bbb"}},tP.axisLine),axisLabel:nP(tP.axisLabel,!1),axisTick:nP(tP.axisTick,!1),splitLine:nP(tP.splitLine,!0),splitArea:nP(tP.splitArea,!0),indicator:[]},t}(kv),aP=["axisLine","axisTickLabel","axisName"],rP=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.render=function(e,t,n){this.group.removeAll(),this._buildAxes(e),this._buildSplitLineAndArea(e)},t.prototype._buildAxes=function(e){var t=e.coordinateSystem;zr(Ur(t.getIndicatorAxes(),(function(e){var n=e.model.get("showName")?e.name:"";return new _k(e.model,{axisName:n,position:[t.cx,t.cy],rotation:e.angle,labelDirection:-1,tickDirection:-1,nameDirection:1})})),(function(e){zr(aP,e.add,e),this.group.add(e.getGroup())}),this)},t.prototype._buildSplitLineAndArea=function(e){var t=e.coordinateSystem,n=t.getIndicatorAxes();if(n.length){var i=e.get("shape"),a=e.getModel("splitLine"),r=e.getModel("splitArea"),o=a.getModel("lineStyle"),s=r.getModel("areaStyle"),l=a.get("show"),p=r.get("show"),c=o.get("color"),d=s.get("color"),u=Xr(c)?c:[c],m=Xr(d)?d:[d],h=[],g=[];if("circle"===i)for(var f=n[0].getTicksCoords(),y=t.cx,v=t.cy,x=0;x3?1.4:a>1?1.2:1.1;hP(this,"zoom","zoomOnMouseWheel",e,{scale:i>0?s:1/s,originX:r,originY:o,isAvailableBehavior:null})}if(n){var l=Math.abs(i);hP(this,"scrollMove","moveOnMouseWheel",e,{scrollDelta:(i>0?1:-1)*(l>3?.4:l>1?.15:.05),originX:r,originY:o,isAvailableBehavior:null})}}},t.prototype._pinchHandler=function(e){dP(this._zr,"globalPan")||hP(this,"zoom",null,e,{scale:e.pinchScale>1?1.1:1/1.1,originX:e.pinchX,originY:e.pinchY,isAvailableBehavior:null})},t}(Xo);function hP(e,t,n,i,a){e.pointerChecker&&e.pointerChecker(i,a.originX,a.originY)&&(gs(i.event),gP(e,t,n,i,a))}function gP(e,t,n,i,a){a.isAvailableBehavior=Kr(fP,null,n,i),e.trigger(t,a)}function fP(e,t,n){var i=n[e];return!e||i&&(!Qr(i)||t.event[i+"Key"])}function yP(e,t,n){var i=e.target;i.x+=t,i.y+=n,i.dirty()}function vP(e,t,n,i){var a=e.target,r=e.zoomLimit,o=e.zoom=e.zoom||1;if(o*=t,r){var s=r.min||0,l=r.max||1/0;o=Math.max(Math.min(l,o),s)}var p=o/e.zoom;e.zoom=o,a.x-=(n-a.x)*(p-1),a.y-=(i-a.y)*(p-1),a.scaleX*=p,a.scaleY*=p,a.dirty()}var xP,bP={axisPointer:1,tooltip:1,brush:1};function wP(e,t,n){var i=t.getComponentByElement(e.topTarget),a=i&&i.coordinateSystem;return i&&i!==n&&!bP.hasOwnProperty(i.mainType)&&a&&a.model!==n}function SP(e){Qr(e)&&(e=(new DOMParser).parseFromString(e,"text/xml"));var t=e;for(9===t.nodeType&&(t=t.firstChild);"svg"!==t.nodeName.toLowerCase()||1!==t.nodeType;)t=t.nextSibling;return t}var CP={fill:"fill",stroke:"stroke","stroke-width":"lineWidth",opacity:"opacity","fill-opacity":"fillOpacity","stroke-opacity":"strokeOpacity","stroke-dasharray":"lineDash","stroke-dashoffset":"lineDashOffset","stroke-linecap":"lineCap","stroke-linejoin":"lineJoin","stroke-miterlimit":"miterLimit","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","text-anchor":"textAlign",visibility:"visibility",display:"display"},_P=$r(CP),TP={"alignment-baseline":"textBaseline","stop-color":"stopColor"},IP=$r(TP),EP=function(){function e(){this._defs={},this._root=null}return e.prototype.parse=function(e,t){t=t||{};var n=SP(e);this._defsUsePending=[];var i=new Rc;this._root=i;var a=[],r=n.getAttribute("viewBox")||"",o=parseFloat(n.getAttribute("width")||t.width),s=parseFloat(n.getAttribute("height")||t.height);isNaN(o)&&(o=null),isNaN(s)&&(s=null),AP(n,i,null,!0,!1);for(var l,p,c=n.firstChild;c;)this._parseNode(c,i,a,null,!1,!1),c=c.nextSibling;if(function(e,t){for(var n=0;n=4&&(l={x:parseFloat(d[0]||0),y:parseFloat(d[1]||0),width:parseFloat(d[2]),height:parseFloat(d[3])})}if(l&&null!=o&&null!=s&&(p=zP(l,{x:0,y:0,width:o,height:s}),!t.ignoreViewBox)){var u=i;(i=new Rc).add(u),u.scaleX=u.scaleY=p.scale,u.x=p.x,u.y=p.y}return t.ignoreRootClip||null==o||null==s||i.setClipPath(new Fm({shape:{x:0,y:0,width:o,height:s}})),{root:i,width:o,height:s,viewBoxRect:l,viewBoxTransform:p,named:a}},e.prototype._parseNode=function(e,t,n,i,a,r){var o,s=e.nodeName.toLowerCase(),l=i;if("defs"===s&&(a=!0),"text"===s&&(r=!0),"defs"===s||"switch"===s)o=t;else{if(!a){var p=xP[s];if(p&&Eo(xP,s)){o=p.call(this,e,t);var c=e.getAttribute("name");if(c){var d={name:c,namedFrom:null,svgNodeTagLower:s,el:o};n.push(d),"g"===s&&(l=d)}else i&&n.push({name:i.name,namedFrom:i,svgNodeTagLower:s,el:o});t.add(o)}}var u=MP[s];if(u&&Eo(MP,s)){var m=u.call(this,e),h=e.getAttribute("id");h&&(this._defs[h]=m)}}if(o&&o.isGroup)for(var g=e.firstChild;g;)1===g.nodeType?this._parseNode(g,o,n,l,a,r):3===g.nodeType&&r&&this._parseText(g,o),g=g.nextSibling},e.prototype._parseText=function(e,t){var n=new _m({style:{text:e.textContent},silent:!0,x:this._textX||0,y:this._textY||0});DP(t,n),AP(e,n,this._defsUsePending,!1,!1),function(e,t){var n=t.__selfStyle;if(n){var i=n.textBaseline,a=i;i&&"auto"!==i?"baseline"===i?a="alphabetic":"before-edge"===i||"text-before-edge"===i?a="top":"after-edge"===i||"text-after-edge"===i?a="bottom":"central"!==i&&"mathematical"!==i||(a="middle"):a="alphabetic",e.style.textBaseline=a}var r=t.__inheritedStyle;if(r){var o=r.textAlign,s=o;o&&("middle"===o&&(s="center"),e.style.textAlign=s)}}(n,t);var i=n.style,a=i.fontSize;a&&a<9&&(i.fontSize=9,n.scaleX*=a/9,n.scaleY*=a/9);var r=(i.fontSize||i.fontFamily)&&[i.fontStyle,i.fontWeight,(i.fontSize||12)+"px",i.fontFamily||"sans-serif"].join(" ");i.font=r;var o=n.getBoundingRect();return this._textX+=o.width,t.add(n),n},e.internalField=void(xP={g:function(e,t){var n=new Rc;return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n},rect:function(e,t){var n=new Fm;return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n.setShape({x:parseFloat(e.getAttribute("x")||"0"),y:parseFloat(e.getAttribute("y")||"0"),width:parseFloat(e.getAttribute("width")||"0"),height:parseFloat(e.getAttribute("height")||"0")}),n.silent=!0,n},circle:function(e,t){var n=new hg;return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(e.getAttribute("cx")||"0"),cy:parseFloat(e.getAttribute("cy")||"0"),r:parseFloat(e.getAttribute("r")||"0")}),n.silent=!0,n},line:function(e,t){var n=new Gg;return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n.setShape({x1:parseFloat(e.getAttribute("x1")||"0"),y1:parseFloat(e.getAttribute("y1")||"0"),x2:parseFloat(e.getAttribute("x2")||"0"),y2:parseFloat(e.getAttribute("y2")||"0")}),n.silent=!0,n},ellipse:function(e,t){var n=new fg;return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(e.getAttribute("cx")||"0"),cy:parseFloat(e.getAttribute("cy")||"0"),rx:parseFloat(e.getAttribute("rx")||"0"),ry:parseFloat(e.getAttribute("ry")||"0")}),n.silent=!0,n},polygon:function(e,t){var n,i=e.getAttribute("points");i&&(n=OP(i));var a=new Bg({shape:{points:n||[]},silent:!0});return DP(t,a),AP(e,a,this._defsUsePending,!1,!1),a},polyline:function(e,t){var n,i=e.getAttribute("points");i&&(n=OP(i));var a=new Lg({shape:{points:n||[]},silent:!0});return DP(t,a),AP(e,a,this._defsUsePending,!1,!1),a},image:function(e,t){var n=new Em;return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n.setStyle({image:e.getAttribute("xlink:href")||e.getAttribute("href"),x:+e.getAttribute("x"),y:+e.getAttribute("y"),width:+e.getAttribute("width"),height:+e.getAttribute("height")}),n.silent=!0,n},text:function(e,t){var n=e.getAttribute("x")||"0",i=e.getAttribute("y")||"0",a=e.getAttribute("dx")||"0",r=e.getAttribute("dy")||"0";this._textX=parseFloat(n)+parseFloat(a),this._textY=parseFloat(i)+parseFloat(r);var o=new Rc;return DP(t,o),AP(e,o,this._defsUsePending,!1,!0),o},tspan:function(e,t){var n=e.getAttribute("x"),i=e.getAttribute("y");null!=n&&(this._textX=parseFloat(n)),null!=i&&(this._textY=parseFloat(i));var a=e.getAttribute("dx")||"0",r=e.getAttribute("dy")||"0",o=new Rc;return DP(t,o),AP(e,o,this._defsUsePending,!1,!0),this._textX+=parseFloat(a),this._textY+=parseFloat(r),o},path:function(e,t){var n=ug(e.getAttribute("d")||"");return DP(t,n),AP(e,n,this._defsUsePending,!1,!1),n.silent=!0,n}}),e}(),MP={lineargradient:function(e){var t=parseInt(e.getAttribute("x1")||"0",10),n=parseInt(e.getAttribute("y1")||"0",10),i=parseInt(e.getAttribute("x2")||"10",10),a=parseInt(e.getAttribute("y2")||"0",10),r=new Xg(t,n,i,a);return kP(e,r),PP(e,r),r},radialgradient:function(e){var t=parseInt(e.getAttribute("cx")||"0",10),n=parseInt(e.getAttribute("cy")||"0",10),i=parseInt(e.getAttribute("r")||"0",10),a=new Zg(t,n,i);return kP(e,a),PP(e,a),a}};function kP(e,t){"userSpaceOnUse"===e.getAttribute("gradientUnits")&&(t.global=!0)}function PP(e,t){for(var n=e.firstChild;n;){if(1===n.nodeType&&"stop"===n.nodeName.toLocaleLowerCase()){var i=n.getAttribute("offset"),a=void 0;a=i&&i.indexOf("%")>0?parseInt(i,10)/100:i?parseFloat(i):0;var r={};GP(n,r,r);var o=r.stopColor||n.getAttribute("stop-color")||"#000000";t.colorStops.push({offset:a,color:o})}n=n.nextSibling}}function DP(e,t){e&&e.__inheritedStyle&&(t.__inheritedStyle||(t.__inheritedStyle={}),Nr(t.__inheritedStyle,e.__inheritedStyle))}function OP(e){for(var t=NP(e),n=[],i=0;i0;r-=2){var o=i[r],s=i[r-1],l=NP(o);switch(a=a||[1,0,0,1,0,0],s){case"translate":Cs(a,a,[parseFloat(l[0]),parseFloat(l[1]||"0")]);break;case"scale":Ts(a,a,[parseFloat(l[0]),parseFloat(l[1]||l[0])]);break;case"rotate":_s(a,a,-parseFloat(l[0])*VP,[parseFloat(l[1]||"0"),parseFloat(l[2]||"0")]);break;case"skewX":Ss(a,[1,0,Math.tan(parseFloat(l[0])*VP),1,0,0],a);break;case"skewY":Ss(a,[1,Math.tan(parseFloat(l[0])*VP),0,1,0,0],a);break;case"matrix":a[0]=parseFloat(l[0]),a[1]=parseFloat(l[1]),a[2]=parseFloat(l[2]),a[3]=parseFloat(l[3]),a[4]=parseFloat(l[4]),a[5]=parseFloat(l[5])}}t.setLocalTransform(a)}}(e,t),GP(e,o,s),i||function(e,t,n){for(var i=0;i<_P.length;i++){var a=_P[i];null!=(r=e.getAttribute(a))&&(t[CP[a]]=r)}for(i=0;i0,h={api:n,geo:s,mapOrGeoModel:e,data:o,isVisualEncodedByVisualMap:m,isGeo:r,transformInfoRaw:d};"geoJSON"===s.resourceType?this._buildGeoJSON(h):"geoSVG"===s.resourceType&&this._buildSVG(h),this._updateController(e,t,n),this._updateMapSelectHandler(e,l,n,i)},e.prototype._buildGeoJSON=function(e){var t=this._regionsGroupByName=Co(),n=Co(),i=this._regionsGroup,a=e.transformInfoRaw,r=e.mapOrGeoModel,o=e.data,s=e.geo.projection,l=s&&s.stream;function p(e,t){return t&&(e=t(e)),e&&[e[0]*a.scaleX+a.x,e[1]*a.scaleY+a.y]}function c(e){for(var t=[],n=!l&&s&&s.project,i=0;i=0)&&(u=a);var m=o?{normal:{align:"center",verticalAlign:"middle"}}:null;Yf(t,Xf(i),{labelFetcher:u,labelDataIndex:d,defaultText:n},m);var h=t.getTextContent();if(h&&(rD(h).ignore=h.ignore,t.textConfig&&o)){var g=t.getBoundingRect().clone();t.textConfig.layoutRect=g,t.textConfig.position=[(o[0]-g.x)/g.width*100+"%",(o[1]-g.y)/g.height*100+"%"]}t.disableLabelAnimation=!0}else t.removeTextContent(),t.removeTextConfig(),t.disableLabelAnimation=null}function dD(e,t,n,i,a,r){e.data?e.data.setItemGraphicEl(r,t):Xm(t).eventData={componentType:"geo",componentIndex:a.componentIndex,geoIndex:a.componentIndex,name:n,region:i&&i.option||{}}}function uD(e,t,n,i,a){e.data||zf({el:t,componentModel:a,itemName:n,itemTooltipOption:i.get("tooltip")})}function mD(e,t,n,i,a){t.highDownSilentOnTouch=!!a.get("selectedMode");var r=i.getModel("emphasis"),o=r.get("focus");return Vh(t,o,r.get("blurScope"),r.get("disabled")),e.isGeo&&function(e,t,n){var i=Xm(e);i.componentMainType=t.mainType,i.componentIndex=t.componentIndex,i.componentHighDownName=n}(t,a,n),o}function hD(e,t,n){var i,a=[];function r(){i=[]}function o(){i.length&&(a.push(i),i=[])}var s=t({polygonStart:r,polygonEnd:o,lineStart:r,lineEnd:o,point:function(e,t){isFinite(e)&&isFinite(t)&&i.push([e,t])},sphere:function(){}});return!n&&s.polygonStart(),zr(e,(function(e){s.lineStart();for(var t=0;t-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2),n},t.type="series.map",t.dependencies=["geo"],t.layoutMode="box",t.defaultOption={z:2,coordinateSystem:"geo",map:"",left:"center",top:"center",aspectScale:null,showLegendSymbol:!0,boundingCoords:null,center:null,zoom:1,scaleLimit:null,selectedMode:!0,label:{show:!1,color:"#000"},itemStyle:{borderWidth:.5,borderColor:"#444",areaColor:"#eee"},emphasis:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{areaColor:"rgba(255,215,0,0.8)"}},select:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{color:"rgba(255,215,0,0.8)"}},nameProperty:"name"}}(mw);var gD=Ho,fD=function(e){function t(t){var n=e.call(this)||this;return n.type="view",n.dimensions=["x","y"],n._roamTransformable=new mc,n._rawTransformable=new mc,n.name=t,n}return We(t,e),t.prototype.setBoundingRect=function(e,t,n,i){return this._rect=new Ns(e,t,n,i),this._rect},t.prototype.getBoundingRect=function(){return this._rect},t.prototype.setViewRect=function(e,t,n,i){this._transformTo(e,t,n,i),this._viewRect=new Ns(e,t,n,i)},t.prototype._transformTo=function(e,t,n,i){var a=this.getBoundingRect(),r=this._rawTransformable;r.transform=a.calculateTransform(new Ns(e,t,n,i));var o=r.parent;r.parent=null,r.decomposeTransform(),r.parent=o,this._updateTransform()},t.prototype.setCenter=function(e,t){e&&(this._center=[Wc(e[0],t.getWidth()),Wc(e[1],t.getHeight())],this._updateCenterAndZoom())},t.prototype.setZoom=function(e){e=e||1;var t=this.zoomLimit;t&&(null!=t.max&&(e=Math.min(t.max,e)),null!=t.min&&(e=Math.max(t.min,e))),this._zoom=e,this._updateCenterAndZoom()},t.prototype.getDefaultCenter=function(){var e=this.getBoundingRect();return[e.x+e.width/2,e.y+e.height/2]},t.prototype.getCenter=function(){return this._center||this.getDefaultCenter()},t.prototype.getZoom=function(){return this._zoom||1},t.prototype.getRoamTransform=function(){return this._roamTransformable.getLocalTransform()},t.prototype._updateCenterAndZoom=function(){var e=this._rawTransformable.getLocalTransform(),t=this._roamTransformable,n=this.getDefaultCenter(),i=this.getCenter(),a=this.getZoom();i=Ho([],i,e),n=Ho([],n,e),t.originX=i[0],t.originY=i[1],t.x=n[0]-i[0],t.y=n[1]-i[1],t.scaleX=t.scaleY=a,this._updateTransform()},t.prototype._updateTransform=function(){var e=this._roamTransformable,t=this._rawTransformable;t.parent=e,e.updateTransform(),t.updateTransform(),ws(this.transform||(this.transform=[]),t.transform||[1,0,0,1,0,0]),this._rawTransform=t.getLocalTransform(),this.invTransform=this.invTransform||[],Is(this.invTransform,this.transform),this.decomposeTransform()},t.prototype.getTransformInfo=function(){var e=this._rawTransformable,t=this._roamTransformable,n=new mc;return n.transform=t.transform,n.decomposeTransform(),{roam:{x:n.x,y:n.y,scaleX:n.scaleX,scaleY:n.scaleY},raw:{x:e.x,y:e.y,scaleX:e.scaleX,scaleY:e.scaleY}}},t.prototype.getViewRect=function(){return this._viewRect},t.prototype.getViewRectAfterRoam=function(){var e=this.getBoundingRect().clone();return e.applyTransform(this.transform),e},t.prototype.dataToPoint=function(e,t,n){var i=t?this._rawTransform:this.transform;return n=n||[],i?gD(n,e,i):Do(n,e)},t.prototype.pointToData=function(e){var t=this.invTransform;return t?gD([],e,t):[e[0],e[1]]},t.prototype.convertToPixel=function(e,t,n){var i=yD(t);return i===this?i.dataToPoint(n):null},t.prototype.convertFromPixel=function(e,t,n){var i=yD(t);return i===this?i.pointToData(n):null},t.prototype.containPoint=function(e){return this.getViewRectAfterRoam().contain(e[0],e[1])},t.dimensions=["x","y"],t}(mc);function yD(e){var t=e.seriesModel;return t?t.coordinateSystem:null}var vD={geoJSON:{aspectScale:.75,invertLongitute:!0},geoSVG:{aspectScale:1,invertLongitute:!1}},xD=["lng","lat"],bD=function(e){function t(t,n,i){var a=e.call(this,t)||this;a.dimensions=xD,a.type="geo",a._nameCoordMap=Co(),a.map=n;var r,o=i.projection,s=eD(n,i.nameMap,i.nameProperty),l=JP(n),p=(a.resourceType=l?l.type:null,a.regions=s.regions),c=vD[l.type];if(a._regionsMap=s.regionsMap,a.regions=s.regions,a.projection=o,o)for(var d=0;d1?(m.width=u,m.height=u/x):(m.height=u,m.width=u*x),m.y=d[1]-m.height/2,m.x=d[0]-m.width/2;else{var w=e.getBoxLayoutParams();w.aspect=x,m=Sv(w,{width:y,height:v})}this.setViewRect(m.x,m.y,m.width,m.height),this.setCenter(e.get("center"),t),this.setZoom(e.get("zoom"))}qr(bD,fD);var CD=function(){function e(){this.dimensions=xD}return e.prototype.create=function(e,t){var n=[];function i(e){return{nameProperty:e.get("nameProperty"),aspectScale:e.get("aspectScale"),projection:e.get("projection")}}e.eachComponent("geo",(function(e,a){var r=e.get("map"),o=new bD(r+a,r,Br({nameMap:e.get("nameMap")},i(e)));o.zoomLimit=e.get("scaleLimit"),n.push(o),e.coordinateSystem=o,o.model=e,o.resize=SD,o.resize(e,t)})),e.eachSeries((function(e){if("geo"===e.get("coordinateSystem")){var t=e.get("geoIndex")||0;e.coordinateSystem=n[t]}}));var a={};return e.eachSeriesByType("map",(function(e){if(!e.getHostGeoModel()){var t=e.getMapType();a[t]=a[t]||[],a[t].push(e)}})),zr(a,(function(e,a){var r=Ur(e,(function(e){return e.get("nameMap")})),o=new bD(a,a,Br({nameMap:Rr(r)},i(e[0])));o.zoomLimit=po.apply(null,Ur(e,(function(e){return e.get("scaleLimit")}))),n.push(o),o.resize=SD,o.resize(e[0],t),zr(e,(function(e){e.coordinateSystem=o,function(e,t){zr(t.get("geoCoord"),(function(t,n){e.addGeoCoord(n,t)}))}(o,e)}))})),n},e.prototype.getFilledRegions=function(e,t,n,i){for(var a=(e||[]).slice(),r=Co(),o=0;ov.x)||(b-=Math.PI);var C=w?"left":"right",_=s.getModel("label"),T=_.get("rotate"),I=T*(Math.PI/180),E=f.getTextContent();E&&(f.setTextConfig({position:_.get("position")||C,rotation:null==T?-b:I,origin:"center"}),E.setStyle("verticalAlign","middle"))}var M=s.get(["emphasis","focus"]),k="relative"===M?_o(o.getAncestorsIndices(),o.getDescendantIndices()):"ancestor"===M?o.getAncestorsIndices():"descendant"===M?o.getDescendantIndices():null;k&&(Xm(n).focus=k),function(e,t,n,i,a,r,o,s){var l=t.getModel(),p=e.get("edgeShape"),c=e.get("layout"),d=e.getOrient(),u=e.get(["lineStyle","curveness"]),m=e.get("edgeForkPosition"),h=l.getModel("lineStyle").getLineStyle(),g=i.__edge;if("curve"===p)t.parentNode&&t.parentNode!==n&&(g||(g=i.__edge=new Hg({shape:AD(c,d,u,a,a)})),pf(g,{shape:AD(c,d,u,r,o)},e));else if("polyline"===p)if("orthogonal"===c){if(t!==n&&t.children&&0!==t.children.length&&!0===t.isExpand){for(var f=t.children,y=[],v=0;vt&&(t=i.height)}this.height=t+1},e.prototype.getNodeById=function(e){if(this.getId()===e)return this;for(var t=0,n=this.children,i=n.length;t=0&&this.hostTree.data.setItemLayout(this.dataIndex,e,t)},e.prototype.getLayout=function(){return this.hostTree.data.getItemLayout(this.dataIndex)},e.prototype.getModel=function(e){if(!(this.dataIndex<0))return this.hostTree.data.getItemModel(this.dataIndex).getModel(e)},e.prototype.getLevelModel=function(){return(this.hostTree.levelModels||[])[this.depth]},e.prototype.setVisual=function(e,t){this.dataIndex>=0&&this.hostTree.data.setItemVisual(this.dataIndex,e,t)},e.prototype.getVisual=function(e){return this.hostTree.data.getItemVisual(this.dataIndex,e)},e.prototype.getRawIndex=function(){return this.hostTree.data.getRawIndex(this.dataIndex)},e.prototype.getId=function(){return this.hostTree.data.getId(this.dataIndex)},e.prototype.getChildIndex=function(){if(this.parentNode){for(var e=this.parentNode.children,t=0;t=0){var i=n.getData().tree.root,a=e.targetNode;if(Qr(a)&&(a=i.getNodeById(a)),a&&i.contains(a))return{node:a};var r=e.targetNodeId;if(null!=r&&(a=i.getNodeById(r)))return{node:a}}}function WD(e){for(var t=[];e;)(e=e.parentNode)&&t.push(e);return t.reverse()}function $D(e,t){return Lr(WD(e),t)>=0}function KD(e,t){for(var n=[];e;){var i=e.dataIndex;n.push({name:e.name,dataIndex:i,value:t.getRawValue(i)}),e=e.parentNode}return n.reverse(),n}!function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.hasSymbolVisual=!0,t.ignoreStyleOnData=!0,t}We(t,e),t.prototype.getInitialData=function(e){var t={name:e.name,children:e.data},n=e.leaves||{},i=new xy(n,this,this.ecModel),a=jD.createTree(t,this,(function(e){e.wrapMethod("getItemModel",(function(e,t){var n=a.getNodeByDataIndex(t);return n&&n.children.length&&n.isExpand||(e.parentModel=i),e}))}));var r=0;a.eachNode("preorder",(function(e){e.depth>r&&(r=e.depth)}));var o=e.expandAndCollapse&&e.initialTreeDepth>=0?e.initialTreeDepth:r;return a.root.eachNode("preorder",(function(e){var t=e.hostTree.data.getRawDataItem(e.dataIndex);e.isExpand=t&&null!=t.collapsed?!t.collapsed:e.depth<=o})),a.data},t.prototype.getOrient=function(){var e=this.get("orient");return"horizontal"===e?e="LR":"vertical"===e&&(e="TB"),e},t.prototype.setZoom=function(e){this.option.zoom=e},t.prototype.setCenter=function(e){this.option.center=e},t.prototype.formatTooltip=function(e,t,n){for(var i=this.getData().tree,a=i.root.children[0],r=i.getNodeByDataIndex(e),o=r.getValue(),s=r.name;r&&r!==a;)s=r.parentNode.name+"."+s,r=r.parentNode;return Zb("nameValue",{name:s,value:o,noValue:isNaN(o)||null==o})},t.prototype.getDataParams=function(t){var n=e.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return n.treeAncestors=KD(i,this),n.collapsed=!i.isExpand,n},t.type="series.tree",t.layoutMode="box",t.defaultOption={z:2,coordinateSystem:"view",left:"12%",top:"12%",right:"12%",bottom:"12%",layout:"orthogonal",edgeShape:"curve",edgeForkPosition:"50%",roam:!1,nodeScaleRatio:.4,center:null,zoom:1,orient:"LR",symbol:"emptyCircle",symbolSize:7,expandAndCollapse:!0,initialTreeDepth:2,lineStyle:{color:"#ccc",width:1.5,curveness:.5},itemStyle:{color:"lightsteelblue",borderWidth:1.5},label:{show:!0},animationEasing:"linear",animationDuration:700,animationDurationUpdate:500}}(mw);function YD(e){var t=e.getData().tree,n={};t.eachNode((function(t){for(var i=t;i&&i.depth>1;)i=i.parentNode;var a=ax(e.ecModel,i.name||i.dataIndex+"",n);t.setVisual("decal",a)}))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.preventUsingHoverLayer=!0,n}We(t,e),t.prototype.getInitialData=function(e,t){var n={name:e.name,children:e.data};XD(n);var i=e.levels||[],a=this.designatedVisualItemStyle={},r=new xy({itemStyle:a},this,t);i=e.levels=function(e,t){var n,i,a=vd(t.get("color")),r=vd(t.get(["aria","decal","decals"]));if(!a)return;e=e||[],zr(e,(function(e){var t=new xy(e),a=t.get("color"),r=t.get("decal");(t.get(["itemStyle","color"])||a&&"none"!==a)&&(n=!0),(t.get(["itemStyle","decal"])||r&&"none"!==r)&&(i=!0)}));var o=e[0]||(e[0]={});n||(o.color=a.slice());!i&&r&&(o.decal=r.slice());return e}(i,t);var o=Ur(i||[],(function(e){return new xy(e,r,t)}),this),s=jD.createTree(n,this,(function(e){e.wrapMethod("getItemModel",(function(e,t){var n=s.getNodeByDataIndex(t),i=n?o[n.depth]:null;return e.parentModel=i||r,e}))}));return s.data},t.prototype.optionUpdated=function(){this.resetViewRoot()},t.prototype.formatTooltip=function(e,t,n){var i=this.getData(),a=this.getRawValue(e);return Zb("nameValue",{name:i.getName(e),value:a})},t.prototype.getDataParams=function(t){var n=e.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return n.treeAncestors=KD(i,this),n.treePathInfo=n.treeAncestors,n},t.prototype.setLayoutInfo=function(e){this.layoutInfo=this.layoutInfo||{},Br(this.layoutInfo,e)},t.prototype.mapIdToIndex=function(e){var t=this._idIndexMap;t||(t=this._idIndexMap=Co(),this._idIndexMapCount=0);var n=t.get(e);return null==n&&t.set(e,n=this._idIndexMapCount++),n},t.prototype.getViewRoot=function(){return this._viewRoot},t.prototype.resetViewRoot=function(e){e?this._viewRoot=e:e=this._viewRoot;var t=this.getRawData().tree.root;e&&(e===t||t.contains(e))||(this._viewRoot=t)},t.prototype.enableAriaDecal=function(){YD(this)},t.type="series.treemap",t.layoutMode="box",t.defaultOption={progressive:0,left:"center",top:"middle",width:"80%",height:"80%",sort:!0,clipWindow:"origin",squareRatio:.5*(1+Math.sqrt(5)),leafDepth:null,drillDownIcon:"▶",zoomToNodeRatio:.1024,roam:!0,nodeClick:"zoomToNode",animation:!0,animationDurationUpdate:900,animationEasing:"quinticInOut",breadcrumb:{show:!0,height:22,left:"center",top:"bottom",emptyItemWidth:25,itemStyle:{color:"rgba(0,0,0,0.7)",textStyle:{color:"#fff"}},emphasis:{itemStyle:{color:"rgba(0,0,0,0.9)"}}},label:{show:!0,distance:0,padding:5,position:"inside",color:"#fff",overflow:"truncate"},upperLabel:{show:!1,position:[0,"50%"],height:20,overflow:"truncate",verticalAlign:"middle"},itemStyle:{color:null,colorAlpha:null,colorSaturation:null,borderWidth:0,gapWidth:0,borderColor:"#fff",borderColorSaturation:null},emphasis:{upperLabel:{show:!0,position:[0,"50%"],overflow:"truncate",verticalAlign:"middle"}},visualDimension:0,visualMin:null,visualMax:null,color:[],colorAlpha:null,colorSaturation:null,colorMappingBy:"index",visibleMin:10,childrenVisibleMin:null,levels:[]}}(mw);function XD(e){var t=0;zr(e.children,(function(e){XD(e);var n=e.value;Xr(n)&&(n=n[0]),t+=n}));var n=e.value;Xr(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=t),n<0&&(n=0),Xr(e.value)?e.value[0]=n:e.value=n}var ZD=function(){function e(e){this.group=new Rc,e.add(this.group)}return e.prototype.render=function(e,t,n,i){var a=e.getModel("breadcrumb"),r=this.group;if(r.removeAll(),a.get("show")&&n){var o=a.getModel("itemStyle"),s=a.getModel("emphasis"),l=o.getModel("textStyle"),p=s.getModel(["itemStyle","textStyle"]),c={pos:{left:a.get("left"),right:a.get("right"),top:a.get("top"),bottom:a.get("bottom")},box:{width:t.getWidth(),height:t.getHeight()},emptyItemWidth:a.get("emptyItemWidth"),totalWidth:0,renderList:[]};this._prepare(n,c,l),this._renderContent(e,c,o,s,l,p,i),Cv(r,c.pos,c.box)}},e.prototype._prepare=function(e,t,n){for(var i=e;i;i=i.parentNode){var a=Id(i.getModel().get("name"),""),r=n.getTextRect(a),o=Math.max(r.width+16,t.emptyItemWidth);t.totalWidth+=o+8,t.renderList.push({node:i,text:a,width:o})}},e.prototype._renderContent=function(e,t,n,i,a,r,o){for(var s,l,p,c,d,u,m,h,g,f=0,y=t.emptyItemWidth,v=e.get(["breadcrumb","height"]),x=(s=t.pos,l=t.box,c=l.width,d=l.height,u=Wc(s.left,c),m=Wc(s.top,d),h=Wc(s.right,c),g=Wc(s.bottom,d),(isNaN(u)||isNaN(parseFloat(s.left)))&&(u=0),(isNaN(h)||isNaN(parseFloat(s.right)))&&(h=c),(isNaN(m)||isNaN(parseFloat(s.top)))&&(m=0),(isNaN(g)||isNaN(parseFloat(s.bottom)))&&(g=d),p=pv(p||0),{width:Math.max(h-u-p[1]-p[3],0),height:Math.max(g-m-p[0]-p[2],0)}),b=t.totalWidth,w=t.renderList,S=i.getModel("itemStyle").getItemStyle(),C=w.length-1;C>=0;C--){var _=w[C],T=_.node,I=_.width,E=_.text;b>x.width&&(b-=I-y,I=y,E=null);var M=new Bg({shape:{points:QD(f,0,I,v,C===w.length-1,0===C)},style:Nr(n.getItemStyle(),{lineJoin:"bevel"}),textContent:new Nm({style:Zf(a,{text:E})}),textConfig:{position:"inside"},z2:1e5,onclick:Yr(o,T)});M.disableLabelAnimation=!0,M.getTextContent().ensureState("emphasis").style=Zf(r,{text:E}),M.ensureState("emphasis").style=S,Vh(M,i.get("focus"),i.get("blurScope"),i.get("disabled")),this.group.add(M),JD(M,e,T),f+=I+8}},e.prototype.remove=function(){this.group.removeAll()},e}();function QD(e,t,n,i,a,r){var o=[[a?e:e-5,t],[e+n,t],[e+n,t+i],[a?e:e-5,t+i]];return!r&&o.splice(2,0,[e+n+5,t+i/2]),!a&&o.push([e,t+i/2]),o}function JD(e,t,n){Xm(e).eventData={componentType:"series",componentSubType:"treemap",componentIndex:t.componentIndex,seriesIndex:t.seriesIndex,seriesName:t.name,seriesType:"treemap",selfType:"breadcrumb",nodeData:{dataIndex:n&&n.dataIndex,name:n&&n.name},treePathInfo:n&&KD(n,t)}}var eO=function(){function e(){this._storage=[],this._elExistsMap={}}return e.prototype.add=function(e,t,n,i,a){return!this._elExistsMap[e.id]&&(this._elExistsMap[e.id]=!0,this._storage.push({el:e,target:t,duration:n,delay:i,easing:a}),!0)},e.prototype.finished=function(e){return this._finishedCallback=e,this},e.prototype.start=function(){for(var e=this,t=this._storage.length,n=function(){--t<=0&&(e._storage.length=0,e._elExistsMap={},e._finishedCallback&&e._finishedCallback())},i=0,a=this._storage.length;i3||Math.abs(e.dy)>3)){var t=this.seriesModel.getData().tree.root;if(!t)return;var n=t.getLayout();if(!n)return;this.api.dispatchAction({type:"treemapMove",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:n.x+e.dx,y:n.y+e.dy,width:n.width,height:n.height}})}},t.prototype._onZoom=function(e){var t=e.originX,n=e.originY;if("animating"!==this._state){var i=this.seriesModel.getData().tree.root;if(!i)return;var a=i.getLayout();if(!a)return;var r=new Ns(a.x,a.y,a.width,a.height),o=this.seriesModel.layoutInfo,s=[1,0,0,1,0,0];Cs(s,s,[-(t-=o.x),-(n-=o.y)]),Ts(s,s,[e.scale,e.scale]),Cs(s,s,[t,n]),r.applyTransform(s),this.api.dispatchAction({type:"treemapRender",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:r.x,y:r.y,width:r.width,height:r.height}})}},t.prototype._initEvents=function(e){var t=this;e.on("click",(function(e){if("ready"===t._state){var n=t.seriesModel.get("nodeClick",!0);if(n){var i=t.findTarget(e.offsetX,e.offsetY);if(i){var a=i.node;if(a.getLayout().isLeafRoot)t._rootToNode(i);else if("zoomToNode"===n)t._zoomToNode(i);else if("link"===n){var r=a.hostTree.data.getItemModel(a.dataIndex),o=r.get("link",!0),s=r.get("target",!0)||"blank";o&&fv(o,s)}}}}}),this)},t.prototype._renderBreadcrumb=function(e,t,n){var i=this;n||(n=null!=e.get("leafDepth",!0)?{node:e.getViewRoot()}:this.findTarget(t.getWidth()/2,t.getHeight()/2))||(n={node:e.getData().tree.root}),(this._breadcrumb||(this._breadcrumb=new ZD(this.group))).render(e,t,n.node,(function(t){"animating"!==i._state&&($D(e.getViewRoot(),t)?i._rootToNode({node:t}):i._zoomToNode({node:t}))}))},t.prototype.remove=function(){this._clearController(),this._containerGroup&&this._containerGroup.removeAll(),this._storage={nodeGroup:[],background:[],content:[]},this._state="ready",this._breadcrumb&&this._breadcrumb.remove()},t.prototype.dispose=function(){this._clearController()},t.prototype._zoomToNode=function(e){this.api.dispatchAction({type:"treemapZoomToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:e.node})},t.prototype._rootToNode=function(e){this.api.dispatchAction({type:"treemapRootToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:e.node})},t.prototype.findTarget=function(e,t){var n;return this.seriesModel.getViewRoot().eachNode({attr:"viewChildren",order:"preorder"},(function(i){var a=this._storage.background[i.getRawIndex()];if(a){var r=a.transformCoordToLocal(e,t),o=a.shape;if(!(o.x<=r[0]&&r[0]<=o.x+o.width&&o.y<=r[1]&&r[1]<=o.y+o.height))return!1;n={node:i,offsetX:r[0],offsetY:r[1]}}}),this),n},t.type="treemap"}(Tw);var lO=zr,pO=to,cO=-1,dO=function(){function e(t){var n=t.mappingMethod,i=t.type,a=this.option=Ar(t);this.type=i,this.mappingMethod=n,this._normalizeData=wO[n];var r=e.visualHandlers[i];this.applyVisual=r.applyVisual,this.getColorMapper=r.getColorMapper,this._normalizedToVisual=r._normalizedToVisual[n],"piecewise"===n?(uO(a),function(e){var t=e.pieceList;e.hasSpecialVisual=!1,zr(t,(function(t,n){t.originIndex=n,null!=t.visual&&(e.hasSpecialVisual=!0)}))}(a)):"category"===n?a.categories?function(e){var t=e.categories,n=e.categoryMap={},i=e.visual;if(lO(t,(function(e,t){n[e]=t})),!Xr(i)){var a=[];to(i)?lO(i,(function(e,t){var i=n[t];a[null!=i?i:cO]=e})):a[-1]=i,i=bO(e,a)}for(var r=t.length-1;r>=0;r--)null==i[r]&&(delete n[t[r]],t.pop())}(a):uO(a,!0):(go("linear"!==n||a.dataExtent),uO(a))}return e.prototype.mapValueToVisual=function(e){var t=this._normalizeData(e);return this._normalizedToVisual(t,e)},e.prototype.getNormalizer=function(){return Kr(this._normalizeData,this)},e.listVisualTypes=function(){return $r(e.visualHandlers)},e.isValidType=function(t){return e.visualHandlers.hasOwnProperty(t)},e.eachVisual=function(e,t,n){to(e)?zr(e,t,n):t.call(n,e)},e.mapVisual=function(t,n,i){var a,r=Xr(t)?[]:to(t)?{}:(a=!0,null);return e.eachVisual(t,(function(e,t){var o=n.call(i,e,t);a?r=o:r[t]=o})),r},e.retrieveVisuals=function(t){var n,i={};return t&&lO(e.visualHandlers,(function(e,a){t.hasOwnProperty(a)&&(i[a]=t[a],n=!0)})),n?i:null},e.prepareVisualTypes=function(e){if(Xr(e))e=e.slice();else{if(!pO(e))return[];var t=[];lO(e,(function(e,n){t.push(n)})),e=t}return e.sort((function(e,t){return"color"===t&&"color"!==e&&0===e.indexOf("color")?1:-1})),e},e.dependsOn=function(e,t){return"color"===t?!(!e||0!==e.indexOf(t)):e===t},e.findPieceIndex=function(e,t,n){for(var i,a=1/0,r=0,o=t.length;ri&&(i=t);var r=i%2?i+2:i+3;a=[];for(var o=0;o0&&(v[0]=-v[0],v[1]=-v[1]);var b=y[0]<0?-1:1;if("start"!==i.__position&&"end"!==i.__position){var w=-Math.atan2(y[1],y[0]);p[0].8?"left":c[0]<-.8?"right":"center",u=c[1]>.8?"top":c[1]<-.8?"bottom":"middle";break;case"start":i.x=-c[0]*h+l[0],i.y=-c[1]*g+l[1],d=c[0]>.8?"right":c[0]<-.8?"left":"center",u=c[1]>.8?"bottom":c[1]<-.8?"top":"middle";break;case"insideStartTop":case"insideStart":case"insideStartBottom":i.x=h*b+l[0],i.y=l[1]+S,d=y[0]<0?"right":"left",i.originX=-h*b,i.originY=-S;break;case"insideMiddleTop":case"insideMiddle":case"insideMiddleBottom":case"middle":i.x=x[0],i.y=x[1]+S,d="center",i.originY=-S;break;case"insideEndTop":case"insideEnd":case"insideEndBottom":i.x=-h*b+p[0],i.y=p[1]+S,d=y[0]>=0?"right":"left",i.originX=h*b,i.originY=-S}i.scaleX=i.scaleY=a,i.setStyle({verticalAlign:i.__verticalAlign||u,align:i.__align||d})}}}function C(e,t){var n=e.__specifiedRotation;if(null==n){var i=o.tangentAt(t);e.attr("rotation",(1===t?-1:1)*Math.PI/2-Math.atan2(i[1],i[0]))}else e.attr("rotation",n)}},t}(Rc),YO=function(){function e(e){this.group=new Rc,this._LineCtor=e||KO}return e.prototype.updateData=function(e){var t=this;this._progressiveEls=null;var n=this,i=n.group,a=n._lineData;n._lineData=e,a||i.removeAll();var r=XO(e);e.diff(a).add((function(n){t._doAdd(e,n,r)})).update((function(n,i){t._doUpdate(a,e,i,n,r)})).remove((function(e){i.remove(a.getItemGraphicEl(e))})).execute()},e.prototype.updateLayout=function(){var e=this._lineData;e&&e.eachItemGraphicEl((function(t,n){t.updateLayout(e,n)}),this)},e.prototype.incrementalPrepareUpdate=function(e){this._seriesScope=XO(e),this._lineData=null,this.group.removeAll()},e.prototype.incrementalUpdate=function(e,t){function n(e){e.isGroup||function(e){return e.animators&&e.animators.length>0}(e)||(e.incremental=!0,e.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[];for(var i=e.start;i=0?i+=p:i-=p:h>=0?i-=p:i+=p}return i}function oA(e,t){var n=[],i=El,a=[[],[],[]],r=[[],[]],o=[];t/=2,e.eachEdge((function(e,s){var l=e.getLayout(),p=e.getVisual("fromSymbol"),c=e.getVisual("toSymbol");l.__original||(l.__original=[Oo(l[0]),Oo(l[1])],l[2]&&l.__original.push(Oo(l[2])));var d=l.__original;if(null!=l[2]){if(Do(a[0],d[0]),Do(a[1],d[2]),Do(a[2],d[1]),p&&"none"!==p){var u=OO(e.node1),m=rA(a,d[0],u*t);i(a[0][0],a[1][0],a[2][0],m,n),a[0][0]=n[3],a[1][0]=n[4],i(a[0][1],a[1][1],a[2][1],m,n),a[0][1]=n[3],a[1][1]=n[4]}if(c&&"none"!==c){u=OO(e.node2),m=rA(a,d[1],u*t);i(a[0][0],a[1][0],a[2][0],m,n),a[1][0]=n[1],a[2][0]=n[2],i(a[0][1],a[1][1],a[2][1],m,n),a[1][1]=n[1],a[2][1]=n[2]}Do(l[0],a[0]),Do(l[1],a[2]),Do(l[2],a[1])}else{if(Do(r[0],d[0]),Do(r[1],d[1]),Ro(o,r[1],r[0]),Vo(o,o),p&&"none"!==p){u=OO(e.node1);Fo(r[0],r[0],o,u*t)}if(c&&"none"!==c){u=OO(e.node2);Fo(r[1],r[1],o,-u*t)}Do(l[0],r[0]),Do(l[1],r[1])}}))}function sA(e){return"view"===e.type}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.init=function(e,t){var n=new PE,i=new YO,a=this.group;this._controller=new mP(t.getZr()),this._controllerHost={target:a},a.add(n.group),a.add(i.group),this._symbolDraw=n,this._lineDraw=i,this._firstRender=!0},t.prototype.render=function(e,t,n){var i=this,a=e.coordinateSystem;this._model=e;var r=this._symbolDraw,o=this._lineDraw,s=this.group;if(sA(a)){var l={x:a.x,y:a.y,scaleX:a.scaleX,scaleY:a.scaleY};this._firstRender?s.attr(l):pf(s,l,e)}oA(e.getGraph(),DO(e));var p=e.getData();r.updateData(p);var c=e.getEdgeData();o.updateData(c),this._updateNodeAndLinkScale(),this._updateController(e,t,n),clearTimeout(this._layoutTimeout);var d=e.forceLayout,u=e.get(["force","layoutAnimation"]);d&&this._startForceLayoutIteration(d,u);var m=e.get("layout");p.graph.eachNode((function(t){var n=t.dataIndex,a=t.getGraphicEl(),r=t.getModel();if(a){a.off("drag").off("dragend");var o=r.get("draggable");o&&a.on("drag",(function(r){switch(m){case"force":d.warmUp(),!i._layouting&&i._startForceLayoutIteration(d,u),d.setFixed(n),p.setItemLayout(n,[a.x,a.y]);break;case"circular":p.setItemLayout(n,[a.x,a.y]),t.setLayout({fixed:!0},!0),RO(e,"symbolSize",t,[r.offsetX,r.offsetY]),i.updateLayout(e);break;default:p.setItemLayout(n,[a.x,a.y]),PO(e.getGraph(),e),i.updateLayout(e)}})).on("dragend",(function(){d&&d.setUnfixed(n)})),a.setDraggable(o,!!r.get("cursor")),"adjacency"===r.get(["emphasis","focus"])&&(Xm(a).focus=t.getAdjacentDataIndices())}})),p.graph.eachEdge((function(e){var t=e.getGraphicEl(),n=e.getModel().get(["emphasis","focus"]);t&&"adjacency"===n&&(Xm(t).focus={edge:[e.dataIndex],node:[e.node1.dataIndex,e.node2.dataIndex]})}));var h="circular"===e.get("layout")&&e.get(["circular","rotateLabel"]),g=p.getLayout("cx"),f=p.getLayout("cy");p.graph.eachNode((function(e){NO(e,h,g,f)})),this._firstRender=!1},t.prototype.dispose=function(){this.remove(),this._controller&&this._controller.dispose(),this._controllerHost=null},t.prototype._startForceLayoutIteration=function(e,t){var n=this;!function i(){e.step((function(e){n.updateLayout(n._model),(n._layouting=!e)&&(t?n._layoutTimeout=setTimeout(i,16):i())}))}()},t.prototype._updateController=function(e,t,n){var i=this,a=this._controller,r=this._controllerHost,o=this.group;a.setPointerChecker((function(t,i,a){var r=o.getBoundingRect();return r.applyTransform(o.transform),r.contain(i,a)&&!wP(t,n,e)})),sA(e.coordinateSystem)?(a.enable(e.get("roam")),r.zoomLimit=e.get("scaleLimit"),r.zoom=e.coordinateSystem.getZoom(),a.off("pan").off("zoom").on("pan",(function(t){yP(r,t.dx,t.dy),n.dispatchAction({seriesId:e.id,type:"graphRoam",dx:t.dx,dy:t.dy})})).on("zoom",(function(t){vP(r,t.scale,t.originX,t.originY),n.dispatchAction({seriesId:e.id,type:"graphRoam",zoom:t.scale,originX:t.originX,originY:t.originY}),i._updateNodeAndLinkScale(),oA(e.getGraph(),DO(e)),i._lineDraw.updateLayout(),n.updateLabelLayout()}))):a.disable()},t.prototype._updateNodeAndLinkScale=function(){var e=this._model,t=e.getData(),n=DO(e);t.eachItemGraphicEl((function(e,t){e&&e.setSymbolScale(n)}))},t.prototype.updateLayout=function(e){oA(e.getGraph(),DO(e)),this._symbolDraw.updateLayout(),this._lineDraw.updateLayout()},t.prototype.remove=function(){clearTimeout(this._layoutTimeout),this._layouting=!1,this._layoutTimeout=null,this._symbolDraw&&this._symbolDraw.remove(),this._lineDraw&&this._lineDraw.remove()},t.type="graph"}(Tw);function lA(e){return"_EC_"+e}var pA=function(){function e(e){this.type="graph",this.nodes=[],this.edges=[],this._nodesMap={},this._edgesMap={},this._directed=e||!1}return e.prototype.isDirected=function(){return this._directed},e.prototype.addNode=function(e,t){e=null==e?""+t:""+e;var n=this._nodesMap;if(!n[lA(e)]){var i=new cA(e,t);return i.hostGraph=this,this.nodes.push(i),n[lA(e)]=i,i}},e.prototype.getNodeByIndex=function(e){var t=this.data.getRawIndex(e);return this.nodes[t]},e.prototype.getNodeById=function(e){return this._nodesMap[lA(e)]},e.prototype.addEdge=function(e,t,n){var i=this._nodesMap,a=this._edgesMap;if(eo(e)&&(e=this.nodes[e]),eo(t)&&(t=this.nodes[t]),e instanceof cA||(e=i[lA(e)]),t instanceof cA||(t=i[lA(t)]),e&&t){var r=e.id+"-"+t.id,o=new dA(e,t,n);return o.hostGraph=this,this._directed&&(e.outEdges.push(o),t.inEdges.push(o)),e.edges.push(o),e!==t&&t.edges.push(o),this.edges.push(o),a[r]=o,o}},e.prototype.getEdgeByIndex=function(e){var t=this.edgeData.getRawIndex(e);return this.edges[t]},e.prototype.getEdge=function(e,t){e instanceof cA&&(e=e.id),t instanceof cA&&(t=t.id);var n=this._edgesMap;return this._directed?n[e+"-"+t]:n[e+"-"+t]||n[t+"-"+e]},e.prototype.eachNode=function(e,t){for(var n=this.nodes,i=n.length,a=0;a=0&&e.call(t,n[a],a)},e.prototype.eachEdge=function(e,t){for(var n=this.edges,i=n.length,a=0;a=0&&n[a].node1.dataIndex>=0&&n[a].node2.dataIndex>=0&&e.call(t,n[a],a)},e.prototype.breadthFirstTraverse=function(e,t,n,i){if(t instanceof cA||(t=this._nodesMap[lA(t)]),t){for(var a="out"===n?"outEdges":"in"===n?"inEdges":"edges",r=0;r=0&&n.node2.dataIndex>=0}));for(a=0,r=i.length;a=0&&this[e][t].setItemVisual(this.dataIndex,n,i)},getVisual:function(n){return this[e][t].getItemVisual(this.dataIndex,n)},setLayout:function(n,i){this.dataIndex>=0&&this[e][t].setItemLayout(this.dataIndex,n,i)},getLayout:function(){return this[e][t].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[e][t].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[e][t].getRawIndex(this.dataIndex)}}}function mA(e,t,n,i,a){for(var r=new pA(i),o=0;o "+u)),p++)}var m,h=n.get("coordinateSystem");if("cartesian2d"===h||"polar"===h)m=tT(e,n);else{var g=hx.get(h),f=g&&g.dimensions||[];Lr(f,"value")<0&&f.concat(["value"]);var y=$_(e,{coordDimensions:f,encodeDefine:n.getEncode()}).dimensions;(m=new W_(y,n)).initData(e)}var v=new W_(["value"],n);return v.initData(l,s),a&&a(m,v),RD({mainData:m,struct:r,structAttr:"graph",datas:{node:m,edge:v},datasAttr:{node:"data",edge:"edgeData"}}),r.update(),r}qr(cA,uA("hostGraph","data")),qr(dA,uA("hostGraph","edgeData"));!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n}We(t,e),t.prototype.init=function(t){e.prototype.init.apply(this,arguments);var n=this;function i(){return n._categoriesData}this.legendVisualProvider=new $M(i,i),this.fillDataTextStyle(t.edges||t.links),this._updateCategoriesData()},t.prototype.mergeOption=function(t){e.prototype.mergeOption.apply(this,arguments),this.fillDataTextStyle(t.edges||t.links),this._updateCategoriesData()},t.prototype.mergeDefaultAndTheme=function(t){e.prototype.mergeDefaultAndTheme.apply(this,arguments),xd(t,"edgeLabel",["show"])},t.prototype.getInitialData=function(e,t){var n,i=e.edges||e.links||[],a=e.data||e.nodes||[],r=this;if(a&&i){_O(n=this)&&(n.__curvenessList=[],n.__edgeMap={},TO(n));var o=mA(a,i,this,!0,(function(e,t){e.wrapMethod("getItemModel",(function(e){var t=r._categoriesModels[e.getShallow("category")];return t&&(t.parentModel=e.parentModel,e.parentModel=t),e}));var n=xy.prototype.getModel;function i(e,t){var i=n.call(this,e,t);return i.resolveParentPath=a,i}function a(e){if(e&&("label"===e[0]||"label"===e[1])){var t=e.slice();return"label"===e[0]?t[0]="edgeLabel":"label"===e[1]&&(t[1]="edgeLabel"),t}return e}t.wrapMethod("getItemModel",(function(e){return e.resolveParentPath=a,e.getModel=i,e}))}));return zr(o.edges,(function(e){!function(e,t,n,i){if(_O(n)){var a=IO(e,t,n),r=n.__edgeMap,o=r[EO(a)];r[a]&&!o?r[a].isForward=!0:o&&r[a]&&(o.isForward=!0,r[a].isForward=!1),r[a]=r[a]||[],r[a].push(i)}}(e.node1,e.node2,this,e.dataIndex)}),this),o.data}},t.prototype.getGraph=function(){return this.getData().graph},t.prototype.getEdgeData=function(){return this.getGraph().edgeData},t.prototype.getCategoriesData=function(){return this._categoriesData},t.prototype.formatTooltip=function(e,t,n){if("edge"===n){var i=this.getData(),a=this.getDataParams(e,n),r=i.graph.getEdgeByIndex(e),o=i.getName(r.node1.dataIndex),s=i.getName(r.node2.dataIndex),l=[];return null!=o&&l.push(o),null!=s&&l.push(s),Zb("nameValue",{name:l.join(" > "),value:a.value,noValue:null==a.value})}return pw({series:this,dataIndex:e,multipleSeries:t})},t.prototype._updateCategoriesData=function(){var e=Ur(this.option.categories||[],(function(e){return null!=e.value?e:Br({value:0},e)})),t=new W_(["value"],this);t.initData(e),this._categoriesData=t,this._categoriesModels=t.mapArray((function(e){return t.getItemModel(e)}))},t.prototype.setZoom=function(e){this.option.zoom=e},t.prototype.setCenter=function(e){this.option.center=e},t.prototype.isAnimationEnabled=function(){return e.prototype.isAnimationEnabled.call(this)&&!("force"===this.get("layout")&&this.get(["force","layoutAnimation"]))},t.type="series.graph",t.dependencies=["grid","polar","geo","singleAxis","calendar"],t.defaultOption={z:2,coordinateSystem:"view",legendHoverLink:!0,layout:null,circular:{rotateLabel:!1},force:{initLayout:null,repulsion:[0,50],gravity:.1,friction:.6,edgeLength:30,layoutAnimation:!0},left:"center",top:"center",symbol:"circle",symbolSize:10,edgeSymbol:["none","none"],edgeSymbolSize:10,edgeLabel:{position:"middle",distance:5},draggable:!1,roam:!1,center:null,zoom:1,nodeScaleRatio:.6,label:{show:!1,formatter:"{b}"},itemStyle:{},lineStyle:{color:"#aaa",width:1,opacity:.5},emphasis:{scale:!0,label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}}}(mw);var hA=function(){this.angle=0,this.width=10,this.r=10,this.x=0,this.y=0},gA=function(e){function t(t){var n=e.call(this,t)||this;return n.type="pointer",n}return We(t,e),t.prototype.getDefaultShape=function(){return new hA},t.prototype.buildPath=function(e,t){var n=Math.cos,i=Math.sin,a=t.r,r=t.width,o=t.angle,s=t.x-n(o)*r*(r>=a/3?1:2),l=t.y-i(o)*r*(r>=a/3?1:2);o=t.angle-Math.PI/2,e.moveTo(s,l),e.lineTo(t.x+n(o)*r,t.y+i(o)*r),e.lineTo(t.x+n(t.angle)*a,t.y+i(t.angle)*a),e.lineTo(t.x-n(o)*r,t.y-i(o)*r),e.lineTo(s,l)},t}(Sm);function fA(e,t){var n=null==e?"":e+"";return t&&(Qr(t)?n=t.replace("{value}",n):Zr(t)&&(n=t(e))),n}(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.render=function(e,t,n){this.group.removeAll();var i=e.get(["axisLine","lineStyle","color"]),a=function(e,t){var n=e.get("center"),i=t.getWidth(),a=t.getHeight(),r=Math.min(i,a);return{cx:Wc(n[0],t.getWidth()),cy:Wc(n[1],t.getHeight()),r:Wc(e.get("radius"),r/2)}}(e,n);this._renderMain(e,t,n,i,a),this._data=e.getData()},t.prototype.dispose=function(){},t.prototype._renderMain=function(e,t,n,i,a){var r=this.group,o=e.get("clockwise"),s=-e.get("startAngle")/180*Math.PI,l=-e.get("endAngle")/180*Math.PI,p=e.getModel("axisLine"),c=p.get("roundCap")?cM:Dg,d=p.get("show"),u=p.getModel("lineStyle"),m=u.get("width"),h=[s,l];tm(h,!o);for(var g=(l=h[1])-(s=h[0]),f=s,y=[],v=0;d&&v=e&&(0===t?0:i[t-1][0])Math.PI/2&&(L+=Math.PI):"tangential"===N?L=-_-Math.PI/2:eo(N)&&(L=N*Math.PI/180),0===L?d.add(new Nm({style:Zf(x,{text:A,x:R,y:B,verticalAlign:c<-.8?"top":c>.8?"bottom":"middle",align:p<-.4?"left":p>.4?"right":"center"},{inheritColor:F}),silent:!0})):d.add(new Nm({style:Zf(x,{text:A,x:R,y:B,verticalAlign:"middle",align:"center"},{inheritColor:F}),silent:!0,originX:R,originY:B,rotation:L}))}if(v.get("show")&&P!==b){O=(O=v.get("distance"))?O+l:l;for(var V=0;V<=w;V++){p=Math.cos(_),c=Math.sin(_);var q=new Gg({shape:{x1:p*(h-O)+u,y1:c*(h-O)+m,x2:p*(h-C-O)+u,y2:c*(h-C-O)+m},silent:!0,style:M});"auto"===M.stroke&&q.setStyle({stroke:i((P+V/w)/b)}),d.add(q),_+=I}_-=I}else _+=T}},t.prototype._renderPointer=function(e,t,n,i,a,r,o,s,l){var p=this.group,c=this._data,d=this._progressEls,u=[],m=e.get(["pointer","show"]),h=e.getModel("progress"),g=h.get("show"),f=e.getData(),y=f.mapDimension("value"),v=+e.get("min"),x=+e.get("max"),b=[v,x],w=[r,o];function S(t,n){var i,r=f.getItemModel(t).getModel("pointer"),o=Wc(r.get("width"),a.r),s=Wc(r.get("length"),a.r),l=e.get(["pointer","icon"]),p=r.get("offsetCenter"),c=Wc(p[0],a.r),d=Wc(p[1],a.r),u=r.get("keepAspect");return(i=l?NS(l,c-o/2,d-s,o,s,null,u):new gA({shape:{angle:-Math.PI/2,width:o,r:s,x:c,y:d}})).rotation=-(n+Math.PI/2),i.x=a.cx,i.y=a.cy,i}function C(e,t){var n=h.get("roundCap")?cM:Dg,i=h.get("overlap"),o=i?h.get("width"):l/f.count(),p=i?a.r-o:a.r-(e+1)*o,c=i?a.r:a.r-e*o,d=new n({shape:{startAngle:r,endAngle:t,cx:a.cx,cy:a.cy,clockwise:s,r0:p,r:c}});return i&&(d.z2=x-f.get(y,e)%x),d}(g||m)&&(f.diff(c).add((function(t){var n=f.get(y,t);if(m){var i=S(t,r);cf(i,{rotation:-((isNaN(+n)?w[0]:Hc(n,b,w,!0))+Math.PI/2)},e),p.add(i),f.setItemGraphicEl(t,i)}if(g){var a=C(t,r),o=h.get("clip");cf(a,{shape:{endAngle:Hc(n,b,w,o)}},e),p.add(a),Zm(e.seriesIndex,f.dataType,t,a),u[t]=a}})).update((function(t,n){var i=f.get(y,t);if(m){var a=c.getItemGraphicEl(n),o=a?a.rotation:r,s=S(t,o);s.rotation=o,pf(s,{rotation:-((isNaN(+i)?w[0]:Hc(i,b,w,!0))+Math.PI/2)},e),p.add(s),f.setItemGraphicEl(t,s)}if(g){var l=d[n],v=C(t,l?l.shape.endAngle:r),x=h.get("clip");pf(v,{shape:{endAngle:Hc(i,b,w,x)}},e),p.add(v),Zm(e.seriesIndex,f.dataType,t,v),u[t]=v}})).execute(),f.each((function(e){var t=f.getItemModel(e),n=t.getModel("emphasis"),a=n.get("focus"),r=n.get("blurScope"),o=n.get("disabled");if(m){var s=f.getItemGraphicEl(e),l=f.getItemVisual(e,"style"),p=l.fill;if(s instanceof Em){var c=s.style;s.useStyle(Br({image:c.image,x:c.x,y:c.y,width:c.width,height:c.height},l))}else s.useStyle(l),"pointer"!==s.type&&s.setColor(p);s.setStyle(t.getModel(["pointer","itemStyle"]).getItemStyle()),"auto"===s.style.fill&&s.setStyle("fill",i(Hc(f.get(y,e),b,[0,1],!0))),s.z2EmphasisLift=0,Uh(s,t),Vh(s,a,r,o)}if(g){var d=u[e];d.useStyle(f.getItemVisual(e,"style")),d.setStyle(t.getModel(["progress","itemStyle"]).getItemStyle()),d.z2EmphasisLift=0,Uh(d,t),Vh(d,a,r,o)}})),this._progressEls=u)},t.prototype._renderAnchor=function(e,t){var n=e.getModel("anchor");if(n.get("show")){var i=n.get("size"),a=n.get("icon"),r=n.get("offsetCenter"),o=n.get("keepAspect"),s=NS(a,t.cx-i/2+Wc(r[0],t.r),t.cy-i/2+Wc(r[1],t.r),i,i,null,o);s.z2=n.get("showAbove")?1:0,s.setStyle(n.getModel("itemStyle").getItemStyle()),this.group.add(s)}},t.prototype._renderTitleAndDetail=function(e,t,n,i,a){var r=this,o=e.getData(),s=o.mapDimension("value"),l=+e.get("min"),p=+e.get("max"),c=new Rc,d=[],u=[],m=e.isAnimationEnabled(),h=e.get(["pointer","showAbove"]);o.diff(this._data).add((function(e){d[e]=new Nm({silent:!0}),u[e]=new Nm({silent:!0})})).update((function(e,t){d[e]=r._titleEls[t],u[e]=r._detailEls[t]})).execute(),o.each((function(t){var n=o.getItemModel(t),r=o.get(s,t),g=new Rc,f=i(Hc(r,[l,p],[0,1],!0)),y=n.getModel("title");if(y.get("show")){var v=y.get("offsetCenter"),x=a.cx+Wc(v[0],a.r),b=a.cy+Wc(v[1],a.r);(M=d[t]).attr({z2:h?0:2,style:Zf(y,{x:x,y:b,text:o.getName(t),align:"center",verticalAlign:"middle"},{inheritColor:f})}),g.add(M)}var w=n.getModel("detail");if(w.get("show")){var S=w.get("offsetCenter"),C=a.cx+Wc(S[0],a.r),_=a.cy+Wc(S[1],a.r),T=Wc(w.get("width"),a.r),I=Wc(w.get("height"),a.r),E=e.get(["progress","show"])?o.getItemVisual(t,"style").fill:f,M=u[t],k=w.get("formatter");M.attr({z2:h?0:2,style:Zf(w,{x:C,y:_,text:fA(r,k),width:isNaN(T)?null:T,height:isNaN(I)?null:I,align:"center",verticalAlign:"middle"},{inheritColor:E})}),ry(M,{normal:w},r,(function(e){return fA(e,k)})),m&&oy(M,t,o,e,{getFormattedLabel:function(e,t,n,i,a,o){return fA(o?o.interpolatedValue:r,k)}}),g.add(M)}c.add(g)})),this.group.add(c),this._titleEls=d,this._detailEls=u},t.type="gauge"})(Tw),function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.visualStyleAccessPath="itemStyle",n}We(t,e),t.prototype.getInitialData=function(e,t){return WM(this,["value"])},t.type="series.gauge",t.defaultOption={z:2,colorBy:"data",center:["50%","50%"],legendHoverLink:!0,radius:"75%",startAngle:225,endAngle:-45,clockwise:!0,min:0,max:100,splitNumber:10,axisLine:{show:!0,roundCap:!1,lineStyle:{color:[[1,"#E6EBF8"]],width:10}},progress:{show:!1,overlap:!0,width:10,roundCap:!1,clip:!0},splitLine:{show:!0,length:10,distance:10,lineStyle:{color:"#63677A",width:3,type:"solid"}},axisTick:{show:!0,splitNumber:5,length:6,distance:10,lineStyle:{color:"#63677A",width:1,type:"solid"}},axisLabel:{show:!0,distance:15,color:"#464646",fontSize:12,rotate:0},pointer:{icon:null,offsetCenter:[0,0],show:!0,showAbove:!0,length:"60%",width:6,keepAspect:!1},anchor:{show:!1,showAbove:!1,size:6,icon:"circle",offsetCenter:[0,0],keepAspect:!1,itemStyle:{color:"#fff",borderWidth:0,borderColor:"#5470c6"}},title:{show:!0,offsetCenter:[0,"20%"],color:"#464646",fontSize:16,valueAnimation:!1},detail:{show:!0,backgroundColor:"rgba(0,0,0,0)",borderWidth:0,borderColor:"#ccc",width:100,height:null,padding:[5,10],offsetCenter:[0,"40%"],color:"#464646",fontSize:30,fontWeight:"bold",lineHeight:30,valueAnimation:!1}}}(mw);var yA=["itemStyle","opacity"],vA=function(e){function t(t,n){var i=e.call(this)||this,a=i,r=new Lg,o=new Nm;return a.setTextContent(o),i.setTextGuideLine(r),i.updateData(t,n,!0),i}return We(t,e),t.prototype.updateData=function(e,t,n){var i=this,a=e.hostModel,r=e.getItemModel(t),o=e.getItemLayout(t),s=r.getModel("emphasis"),l=r.get(yA);l=null==l?1:l,n||gf(i),i.useStyle(e.getItemVisual(t,"style")),i.style.lineJoin="round",n?(i.setShape({points:o.points}),i.style.opacity=0,cf(i,{style:{opacity:l}},a,t)):pf(i,{style:{opacity:l},shape:{points:o.points}},a,t),Uh(i,r),this._updateLabel(e,t),Vh(this,s.get("focus"),s.get("blurScope"),s.get("disabled"))},t.prototype._updateLabel=function(e,t){var n=this,i=this.getTextGuideLine(),a=n.getTextContent(),r=e.hostModel,o=e.getItemModel(t),s=e.getItemLayout(t).label,l=e.getItemVisual(t,"style"),p=l.fill;Yf(a,Xf(o),{labelFetcher:e.hostModel,labelDataIndex:t,defaultOpacity:l.opacity,defaultText:e.getName(t)},{normal:{align:s.textAlign,verticalAlign:s.verticalAlign}}),n.setTextConfig({local:!0,inside:!!s.inside,insideStroke:p,outsideFill:p});var c=s.linePoints;i.setShape({points:c}),n.textGuideLineConfig={anchor:c?new Ms(c[0][0],c[0][1]):null},pf(a,{style:{x:s.x,y:s.y}},r,t),a.attr({rotation:s.rotation,originX:s.x,originY:s.y,z2:10}),rE(n,oE(o),{stroke:p})},t}(Bg);(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.ignoreLabelLineUpdate=!0,n}We(t,e),t.prototype.render=function(e,t,n){var i=e.getData(),a=this._data,r=this.group;i.diff(a).add((function(e){var t=new vA(i,e);i.setItemGraphicEl(e,t),r.add(t)})).update((function(e,t){var n=a.getItemGraphicEl(t);n.updateData(i,e),r.add(n),i.setItemGraphicEl(e,n)})).remove((function(t){hf(a.getItemGraphicEl(t),e,t)})).execute(),this._data=i},t.prototype.remove=function(){this.group.removeAll(),this._data=null},t.prototype.dispose=function(){},t.type="funnel"})(Tw),function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.init=function(t){e.prototype.init.apply(this,arguments),this.legendVisualProvider=new $M(Kr(this.getData,this),Kr(this.getRawData,this)),this._defaultLabelLine(t)},t.prototype.getInitialData=function(e,t){return WM(this,{coordDimensions:["value"],encodeDefaulter:Yr($v,this)})},t.prototype._defaultLabelLine=function(e){xd(e,"labelLine",["show"]);var t=e.labelLine,n=e.emphasis.labelLine;t.show=t.show&&e.label.show,n.show=n.show&&e.emphasis.label.show},t.prototype.getDataParams=function(t){var n=this.getData(),i=e.prototype.getDataParams.call(this,t),a=n.mapDimension("value"),r=n.getSum(a);return i.percent=r?+(n.get(a,t)/r*100).toFixed(2):0,i.$vars.push("percent"),i},t.type="series.funnel",t.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",left:80,top:60,right:80,bottom:60,minSize:"0%",maxSize:"100%",sort:"descending",orient:"vertical",gap:0,funnelAlign:"center",label:{show:!0,position:"outer"},labelLine:{show:!0,length:20,lineStyle:{width:1}},itemStyle:{borderColor:"#fff",borderWidth:1},emphasis:{label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}}}(mw);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._dataGroup=new Rc,n._initialized=!1,n}We(t,e),t.prototype.init=function(){this.group.add(this._dataGroup)},t.prototype.render=function(e,t,n,i){this._progressiveEls=null;var a=this._dataGroup,r=e.getData(),o=this._data,s=e.coordinateSystem,l=s.dimensions,p=wA(e);if(r.diff(o).add((function(e){SA(bA(r,a,e,l,s),r,e,p)})).update((function(t,n){var i=o.getItemGraphicEl(n),a=xA(r,t,l,s);r.setItemGraphicEl(t,i),pf(i,{shape:{points:a}},e,t),gf(i),SA(i,r,t,p)})).remove((function(e){var t=o.getItemGraphicEl(e);a.remove(t)})).execute(),!this._initialized){this._initialized=!0;var c=function(e,t,n){var i=e.model,a=e.getRect(),r=new Fm({shape:{x:a.x,y:a.y,width:a.width,height:a.height}}),o="horizontal"===i.get("layout")?"width":"height";return r.setShape(o,0),cf(r,{shape:{width:a.width,height:a.height}},t,n),r}(s,e,(function(){setTimeout((function(){a.removeClipPath()}))}));a.setClipPath(c)}this._data=r},t.prototype.incrementalPrepareRender=function(e,t,n){this._initialized=!0,this._data=null,this._dataGroup.removeAll()},t.prototype.incrementalRender=function(e,t,n){for(var i=t.getData(),a=t.coordinateSystem,r=a.dimensions,o=wA(t),s=this._progressiveEls=[],l=e.start;l5)return;var i=this._model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX,e.offsetY]);"none"!==i.behavior&&this._dispatchExpand({axisExpandWindow:i.axisExpandWindow})}this._mouseDownPoint=null},mousemove:function(e){if(!this._mouseDownPoint&&TA(this,"mousemove")){var t=this._model,n=t.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX,e.offsetY]),i=n.behavior;"jump"===i&&this._throttledDispatchExpand.debounceNextCall(t.get("axisExpandDebounce")),this._throttledDispatchExpand("none"===i?null:{axisExpandWindow:n.axisExpandWindow,animation:"jump"===i?null:{duration:0}})}}};function TA(e,t){var n=e._model;return n.get("axisExpandable")&&n.get("axisExpandTriggerOn")===t}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.init=function(){e.prototype.init.apply(this,arguments),this.mergeOption({})},t.prototype.mergeOption=function(e){var t=this.option;e&&Fr(t,e,!0),this._initDimensions()},t.prototype.contains=function(e,t){var n=e.get("parallelIndex");return null!=n&&t.getComponent("parallel",n)===this},t.prototype.setAxisExpand=function(e){zr(["axisExpandable","axisExpandCenter","axisExpandCount","axisExpandWidth","axisExpandWindow"],(function(t){e.hasOwnProperty(t)&&(this.option[t]=e[t])}),this)},t.prototype._initDimensions=function(){var e=this.dimensions=[],t=this.parallelAxisIndex=[];zr(Hr(this.ecModel.queryComponents({mainType:"parallelAxis"}),(function(e){return(e.get("parallelIndex")||0)===this.componentIndex}),this),(function(n){e.push("dim"+n.get("dim")),t.push(n.componentIndex)}))},t.type="parallel",t.dependencies=["parallelAxis"],t.layoutMode="box",t.defaultOption={z:0,left:80,top:60,right:80,bottom:60,layout:"horizontal",axisExpandable:!1,axisExpandCenter:null,axisExpandCount:0,axisExpandWidth:50,axisExpandRate:17,axisExpandDebounce:50,axisExpandSlideTriggerArea:[-.15,.05,.4],axisExpandTriggerOn:"click",parallelAxisDefault:null}}(kv);var IA=function(e){function t(t,n,i,a,r){var o=e.call(this,t,n,i)||this;return o.type=a||"value",o.axisIndex=r,o}return We(t,e),t.prototype.isHorizontal=function(){return"horizontal"!==this.coordinateSystem.getModel().get("layout")},t}(RI);function EA(e,t,n,i,a,r){e=e||0;var o=n[1]-n[0];if(null!=a&&(a=kA(a,[0,o])),null!=r&&(r=Math.max(r,null!=a?a:0)),"all"===i){var s=Math.abs(t[1]-t[0]);s=kA(s,[0,o]),a=r=kA(s,[a,r]),i=0}t[0]=kA(t[0],n),t[1]=kA(t[1],n);var l=MA(t,i);t[i]+=e;var p,c=a||0,d=n.slice();return l.sign<0?d[0]+=c:d[1]-=c,t[i]=kA(t[i],d),p=MA(t,i),null!=a&&(p.sign!==l.sign||p.spanr&&(t[1-i]=t[i]+p.sign*r),t}function MA(e,t){var n=e[t]-e[1-t];return{span:Math.abs(n),sign:n>0?-1:n<0?1:t?-1:1}}function kA(e,t){return Math.min(null!=t[1]?t[1]:1/0,Math.max(null!=t[0]?t[0]:-1/0,e))}var PA=zr,DA=Math.min,OA=Math.max,AA=Math.floor,FA=Math.ceil,RA=$c,BA=Math.PI;!function(){function e(e,t,n){this.type="parallel",this._axesMap=Co(),this._axesLayout={},this.dimensions=e.dimensions,this._model=e,this._init(e,t,n)}e.prototype._init=function(e,t,n){var i=e.dimensions,a=e.parallelAxisIndex;PA(i,(function(e,n){var i=a[n],r=t.getComponent("parallelAxis",i),o=this._axesMap.set(e,new IA(e,tI(r),[0,0],r.get("type"),i)),s="category"===o.type;o.onBand=s&&r.get("boundaryGap"),o.inverse=r.get("inverse"),r.axis=o,o.model=r,o.coordinateSystem=r.coordinateSystem=this}),this)},e.prototype.update=function(e,t){this._updateAxesFromSeries(this._model,e)},e.prototype.containPoint=function(e){var t=this._makeLayoutInfo(),n=t.axisBase,i=t.layoutBase,a=t.pixelDimIndex,r=e[1-a],o=e[a];return r>=n&&r<=n+t.axisLength&&o>=i&&o<=i+t.layoutLength},e.prototype.getModel=function(){return this._model},e.prototype._updateAxesFromSeries=function(e,t){t.eachSeries((function(n){if(e.contains(n,t)){var i=n.getData();PA(this.dimensions,(function(e){var t=this._axesMap.get(e);t.scale.unionExtentFromData(i,i.mapDimension(e)),eI(t.scale,t.model)}),this)}}),this)},e.prototype.resize=function(e,t){this._rect=Sv(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()}),this._layoutAxes()},e.prototype.getRect=function(){return this._rect},e.prototype._makeLayoutInfo=function(){var e,t=this._model,n=this._rect,i=["x","y"],a=["width","height"],r=t.get("layout"),o="horizontal"===r?0:1,s=n[a[o]],l=[0,s],p=this.dimensions.length,c=NA(t.get("axisExpandWidth"),l),d=NA(t.get("axisExpandCount")||0,[0,p]),u=t.get("axisExpandable")&&p>3&&p>d&&d>1&&c>0&&s>0,m=t.get("axisExpandWindow");m?(e=NA(m[1]-m[0],l),m[1]=m[0]+e):(e=NA(c*(d-1),l),(m=[c*(t.get("axisExpandCenter")||AA(p/2))-e/2])[1]=m[0]+e);var h=(s-e)/(p-d);h<3&&(h=0);var g=[AA(RA(m[0]/c,1))+1,FA(RA(m[1]/c,1))-1],f=h/c*m[0];return{layout:r,pixelDimIndex:o,layoutBase:n[i[o]],layoutLength:s,axisBase:n[i[1-o]],axisLength:n[a[1-o]],axisExpandable:u,axisExpandWidth:c,axisCollapseWidth:h,axisExpandWindow:m,axisCount:p,winInnerIndices:g,axisExpandWindow0Pos:f}},e.prototype._layoutAxes=function(){var e=this._rect,t=this._axesMap,n=this.dimensions,i=this._makeLayoutInfo(),a=i.layout;t.each((function(e){var t=[0,i.axisLength],n=e.inverse?1:0;e.setExtent(t[n],t[1-n])})),PA(n,(function(t,n){var r=(i.axisExpandable?VA:LA)(n,i),o={horizontal:{x:r.position,y:i.axisLength},vertical:{x:0,y:r.position}},s={horizontal:BA/2,vertical:0},l=[o[a].x+e.x,o[a].y+e.y],p=s[a],c=[1,0,0,1,0,0];_s(c,c,p),Cs(c,c,l),this._axesLayout[t]={position:l,rotation:p,transform:c,axisNameAvailableWidth:r.axisNameAvailableWidth,axisLabelShow:r.axisLabelShow,nameTruncateMaxWidth:r.nameTruncateMaxWidth,tickDirection:1,labelDirection:1}}),this)},e.prototype.getAxis=function(e){return this._axesMap.get(e)},e.prototype.dataToPoint=function(e,t){return this.axisCoordToPoint(this._axesMap.get(t).dataToCoord(e),t)},e.prototype.eachActiveState=function(e,t,n,i){null==n&&(n=0),null==i&&(i=e.count());var a=this._axesMap,r=this.dimensions,o=[],s=[];zr(r,(function(t){o.push(e.mapDimension(t)),s.push(a.get(t).model)}));for(var l=this.hasAxisBrushed(),p=n;pa*(1-c[0])?(l="jump",o=s-a*(1-c[2])):(o=s-a*c[1])>=0&&(o=s-a*(1-c[1]))<=0&&(o=0),(o*=t.axisExpandWidth/p)?EA(o,i,r,"all"):l="none";else{var u=i[1]-i[0];(i=[OA(0,r[1]*s/u-u/2)])[1]=DA(r[1],i[0]+u),i[0]=i[1]-u}return{axisExpandWindow:i,behavior:l}}}();function NA(e,t){return DA(OA(e,t[0]),t[1])}function LA(e,t){var n=t.layoutLength/(t.axisCount-1);return{position:n*e,axisNameAvailableWidth:n,axisLabelShow:!0}}function VA(e,t){var n,i,a=t.layoutLength,r=t.axisExpandWidth,o=t.axisCount,s=t.axisCollapseWidth,l=t.winInnerIndices,p=s,c=!1;return e=0;n--)Kc(t[n])},t.prototype.getActiveState=function(e){var t=this.activeIntervals;if(!t.length)return"normal";if(null==e||isNaN(+e))return"inactive";if(1===t.length){var n=t[0];if(n[0]<=e&&e<=n[1])return"active"}else for(var i=0,a=t.length;i6}(e)||r){if(o&&!r){"single"===s.brushMode&&rF(e);var l=Ar(s);l.brushType=SF(l.brushType,o),l.panelId=o===GA?null:o.panelId,r=e._creatingCover=ZA(e,l),e._covers.push(r)}if(r){var p=TF[SF(e._brushType,o)];r.__brushOption.range=p.getCreatingRange(vF(e,r,e._track)),i&&(QA(e,r),p.updateCommon(e,r)),JA(e,r),a={isEnd:i}}}else i&&"single"===s.brushMode&&s.removeOnClick&&iF(e,t,n)&&rF(e)&&(a={isEnd:i,removeOnClick:!0});return a}function SF(e,t){return"auto"===e?t.defaultBrushType:e}var CF={mousedown:function(e){if(this._dragging)_F(this,e);else if(!e.target||!e.target.draggable){xF(e);var t=this.group.transformCoordToLocal(e.offsetX,e.offsetY);this._creatingCover=null,(this._creatingPanel=iF(this,e,t))&&(this._dragging=!0,this._track=[t.slice()])}},mousemove:function(e){var t=e.offsetX,n=e.offsetY,i=this.group.transformCoordToLocal(t,n);if(function(e,t,n){if(e._brushType&&!function(e,t,n){var i=e._zr;return t<0||t>i.getWidth()||n<0||n>i.getHeight()}(e,t.offsetX,t.offsetY)){var i=e._zr,a=e._covers,r=iF(e,t,n);if(!e._dragging)for(var o=0;o=0&&(r[a[o].depth]=new xy(a[o],this,t));if(i&&n){var s=mA(i,n,this,!0,(function(e,t){e.wrapMethod("getItemModel",(function(e,t){var n=e.parentModel,i=n.getData().getItemLayout(t);if(i){var a=i.depth,r=n.levelModels[a];r&&(e.parentModel=r)}return e})),t.wrapMethod("getItemModel",(function(e,t){var n=e.parentModel,i=n.getGraph().getEdgeByIndex(t).node1.getLayout();if(i){var a=i.depth,r=n.levelModels[a];r&&(e.parentModel=r)}return e}))}));return s.data}},t.prototype.setNodePosition=function(e,t){var n=(this.option.data||this.option.nodes)[e];n.localX=t[0],n.localY=t[1]},t.prototype.getGraph=function(){return this.getData().graph},t.prototype.getEdgeData=function(){return this.getGraph().edgeData},t.prototype.formatTooltip=function(e,t,n){function i(e){return isNaN(e)||null==e}if("edge"===n){var a=this.getDataParams(e,n),r=a.data,o=a.value;return Zb("nameValue",{name:r.source+" -- "+r.target,value:o,noValue:i(o)})}var s=this.getGraph().getNodeByIndex(e).getLayout().value,l=this.getDataParams(e,n).data.name;return Zb("nameValue",{name:null!=l?l+"":null,value:s,noValue:i(s)})},t.prototype.optionUpdated=function(){},t.prototype.getDataParams=function(t,n){var i=e.prototype.getDataParams.call(this,t,n);if(null==i.value&&"node"===n){var a=this.getGraph().getNodeByIndex(t).getLayout().value;i.value=a}return i},t.type="series.sankey",t.defaultOption={z:2,coordinateSystem:"view",left:"5%",top:"5%",right:"20%",bottom:"5%",orient:"horizontal",nodeWidth:20,nodeGap:8,draggable:!0,layoutIterations:32,label:{show:!0,position:"right",fontSize:12},edgeLabel:{show:!1,fontSize:12},levels:[],nodeAlign:"justify",lineStyle:{color:"#314656",opacity:.2,curveness:.5},emphasis:{label:{show:!0},lineStyle:{opacity:.5}},select:{itemStyle:{borderColor:"#212121"}},animationEasing:"linear",animationDuration:1e3}}(mw);var RF=function(){function e(){}return e.prototype.getInitialData=function(e,t){var n,i,a=t.getComponent("xAxis",this.get("xAxisIndex")),r=t.getComponent("yAxis",this.get("yAxisIndex")),o=a.get("type"),s=r.get("type");"category"===o?(e.layout="horizontal",n=a.getOrdinalMeta(),i=!0):"category"===s?(e.layout="vertical",n=r.getOrdinalMeta(),i=!0):e.layout=e.layout||"horizontal";var l=["x","y"],p="horizontal"===e.layout?0:1,c=this._baseAxisDim=l[p],d=l[1-p],u=[a,r],m=u[p].get("type"),h=u[1-p].get("type"),g=e.data;if(g&&i){var f=[];zr(g,(function(e,t){var n;Xr(e)?(n=e.slice(),e.unshift(t)):Xr(e.value)?((n=Br({},e)).value=n.value.slice(),e.value.unshift(t)):n=e,f.push(n)})),e.data=f}var y=this.defaultValueDimensions,v=[{name:c,type:T_(m),ordinalMeta:n,otherDims:{tooltip:!1,itemName:0},dimsDef:["base"]},{name:d,type:T_(h),dimsDef:y.slice()}];return WM(this,{coordDimensions:v,dimensionsCount:y.length+1,encodeDefaulter:Yr(Wv,v,this)})},e.prototype.getBaseAxis=function(){var e=this._baseAxisDim;return this.ecModel.getComponent(e+"Axis",this.get(e+"AxisIndex")).axis},e}(),BF=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.defaultValueDimensions=[{name:"min",defaultTooltip:!0},{name:"Q1",defaultTooltip:!0},{name:"median",defaultTooltip:!0},{name:"Q3",defaultTooltip:!0},{name:"max",defaultTooltip:!0}],n.visualDrawType="stroke",n}return We(t,e),t.type="series.boxplot",t.dependencies=["xAxis","yAxis","grid"],t.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,boxWidth:[7,50],itemStyle:{color:"#fff",borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2,shadowBlur:5,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0,0,0,0.2)"}},animationDuration:800},t}(mw);qr(BF,RF,!0);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.render=function(e,t,n){var i=e.getData(),a=this.group,r=this._data;this._data||a.removeAll();var o="horizontal"===e.get("layout")?1:0;i.diff(r).add((function(e){if(i.hasValue(e)){var t=VF(i.getItemLayout(e),i,e,o,!0);i.setItemGraphicEl(e,t),a.add(t)}})).update((function(e,t){var n=r.getItemGraphicEl(t);if(i.hasValue(e)){var s=i.getItemLayout(e);n?(gf(n),qF(s,n,i,e)):n=VF(s,i,e,o),a.add(n),i.setItemGraphicEl(e,n)}else a.remove(n)})).remove((function(e){var t=r.getItemGraphicEl(e);t&&a.remove(t)})).execute(),this._data=i},t.prototype.remove=function(e){var t=this.group,n=this._data;this._data=null,n&&n.eachItemGraphicEl((function(e){e&&t.remove(e)}))},t.type="boxplot"}(Tw);var NF=function(){},LF=function(e){function t(t){var n=e.call(this,t)||this;return n.type="boxplotBoxPath",n}return We(t,e),t.prototype.getDefaultShape=function(){return new NF},t.prototype.buildPath=function(e,t){var n=t.points,i=0;for(e.moveTo(n[i][0],n[i][1]),i++;i<4;i++)e.lineTo(n[i][0],n[i][1]);for(e.closePath();i0?"borderColor":"borderColor0"])||n.get(["itemStyle",e>0?"color":"color0"]);0===e&&(a=n.get(["itemStyle","borderColorDoji"]));var r=n.getModel("itemStyle").getItemStyle(zF);t.useStyle(r),t.style.fill=null,t.style.stroke=a}var JF=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.defaultValueDimensions=[{name:"open",defaultTooltip:!0},{name:"close",defaultTooltip:!0},{name:"lowest",defaultTooltip:!0},{name:"highest",defaultTooltip:!0}],n}return We(t,e),t.prototype.getShadowDim=function(){return"open"},t.prototype.brushSelector=function(e,t,n){var i=t.getItemLayout(e);return i&&n.rect(i.brushRect)},t.type="series.candlestick",t.dependencies=["xAxis","yAxis","grid"],t.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,clip:!0,itemStyle:{color:"#eb5454",color0:"#47b262",borderColor:"#eb5454",borderColor0:"#47b262",borderColorDoji:null,borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2}},barMaxWidth:null,barMinWidth:null,barWidth:null,large:!0,largeThreshold:600,progressive:3e3,progressiveThreshold:1e4,progressiveChunkMode:"mod",animationEasing:"linear",animationDuration:300},t}(mw);qr(JF,RF,!0);var eR=["itemStyle","borderColor"],tR=["itemStyle","borderColor0"],nR=["itemStyle","borderColorDoji"],iR=["itemStyle","color"],aR=["itemStyle","color0"];Sw(),Sw();function rR(e,t,n,i,a,r){return n>i?-1:n0?e.get(a,t-1)<=i?1:-1:1}function oR(e,t){var n=t.rippleEffectColor||t.color;e.eachChild((function(e){e.attr({z:t.z,zlevel:t.zlevel,style:{stroke:"stroke"===t.brushType?n:null,fill:"fill"===t.brushType?n:null}})}))}var sR=function(e){function t(t,n){var i=e.call(this)||this,a=new TE(t,n),r=new Rc;return i.add(a),i.add(r),i.updateData(t,n),i}return We(t,e),t.prototype.stopEffectAnimation=function(){this.childAt(1).removeAll()},t.prototype.startEffectAnimation=function(e){for(var t=e.symbolType,n=e.color,i=e.rippleNumber,a=this.childAt(1),r=0;r0&&(r=this._getLineLength(i)/l*1e3),r!==this._period||o!==this._loop||s!==this._roundTrip){i.stopAnimation();var c=void 0;c=Zr(p)?p(n):p,i.__t>0&&(c=-r*i.__t),this._animateSymbol(i,r,c,o,s)}this._period=r,this._loop=o,this._roundTrip=s}},t.prototype._animateSymbol=function(e,t,n,i,a){if(t>0){e.__t=0;var r=this,o=e.animate("",i).when(a?2*t:t,{__t:a?2:1}).delay(n).during((function(){r._updateSymbolPosition(e)}));i||o.done((function(){r.remove(e)})),o.start()}},t.prototype._getLineLength=function(e){return Go(e.__p1,e.__cp1)+Go(e.__cp1,e.__p2)},t.prototype._updateAnimationPoints=function(e,t){e.__p1=t[0],e.__p2=t[1],e.__cp1=t[2]||[(t[0][0]+t[1][0])/2,(t[0][1]+t[1][1])/2]},t.prototype.updateData=function(e,t,n){this.childAt(0).updateData(e,t,n),this._updateEffectSymbol(e,t)},t.prototype._updateSymbolPosition=function(e){var t=e.__p1,n=e.__p2,i=e.__cp1,a=e.__t<1?e.__t:2-e.__t,r=[e.x,e.y],o=r.slice(),s=_l,l=Tl;r[0]=s(t[0],i[0],n[0],a),r[1]=s(t[1],i[1],n[1],a);var p=e.__t<1?l(t[0],i[0],n[0],a):l(n[0],i[0],t[0],1-a),c=e.__t<1?l(t[1],i[1],n[1],a):l(n[1],i[1],t[1],1-a);e.rotation=-Math.atan2(c,p)-Math.PI/2,"line"!==this._symbolType&&"rect"!==this._symbolType&&"roundRect"!==this._symbolType||(void 0!==e.__lastT&&e.__lastT=0&&!(i[r]<=t);r--);r=Math.min(r,a-2)}else{for(r=o;rt);r++);r=Math.min(r-1,a-2)}var s=(t-i[r])/(i[r+1]-i[r]),l=n[r],p=n[r+1];e.x=l[0]*(1-s)+s*p[0],e.y=l[1]*(1-s)+s*p[1];var c=e.__t<1?p[0]-l[0]:l[0]-p[0],d=e.__t<1?p[1]-l[1]:l[1]-p[1];e.rotation=-Math.atan2(d,c)-Math.PI/2,this._lastFrame=r,this._lastFramePercent=t,e.ignore=!1}},t}(lR),dR=function(){this.polyline=!1,this.curveness=0,this.segs=[]},uR=function(e){function t(t){var n=e.call(this,t)||this;return n._off=0,n.hoverDataIdx=-1,n}return We(t,e),t.prototype.reset=function(){this.notClear=!1,this._off=0},t.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},t.prototype.getDefaultShape=function(){return new dR},t.prototype.buildPath=function(e,t){var n,i=t.segs,a=t.curveness;if(t.polyline)for(n=this._off;n0){e.moveTo(i[n++],i[n++]);for(var o=1;o0){var d=(s+p)/2-(l-c)*a,u=(l+c)/2-(p-s)*a;e.quadraticCurveTo(d,u,p,c)}else e.lineTo(p,c)}this.incremental&&(this._off=n,this.notClear=!0)},t.prototype.findDataIndex=function(e,t){var n=this.shape,i=n.segs,a=n.curveness,r=this.style.lineWidth;if(n.polyline)for(var o=0,s=0;s0)for(var p=i[s++],c=i[s++],d=1;d0){if(rm(p,c,(p+u)/2-(c-m)*a,(c+m)/2-(u-p)*a,u,m,r,e,t))return o}else if(im(p,c,u,m,r,e,t))return o;o++}return-1},t.prototype.contain=function(e,t){var n=this.transformCoordToLocal(e,t),i=this.getBoundingRect();return e=n[0],t=n[1],i.contain(e,t)?(this.hoverDataIdx=this.findDataIndex(e,t))>=0:(this.hoverDataIdx=-1,!1)},t.prototype.getBoundingRect=function(){var e=this._rect;if(!e){for(var t=this.shape.segs,n=1/0,i=1/0,a=-1/0,r=-1/0,o=0;o0&&(r.dataIndex=n+e.__startIndex)}))},e.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},e}(),hR={seriesType:"lines",plan:Sw(),reset:function(e){var t=e.coordinateSystem;if(t){var n=e.get("polyline"),i=e.pipelineContext.large;return{progress:function(a,r){var o=[];if(i){var s=void 0,l=a.end-a.start;if(n){for(var p=0,c=a.start;c0&&(l||s.configLayer(r,{motionBlur:!0,lastFrameAlpha:Math.max(Math.min(o/10+.9,1),0)})),a.updateData(i);var p=e.get("clip",!0)&&UE(e.coordinateSystem,!1,e);p?this.group.setClipPath(p):this.group.removeClipPath(),this._lastZlevel=r,this._finished=!0},t.prototype.incrementalPrepareRender=function(e,t,n){var i=e.getData();this._updateLineDraw(i,e).incrementalPrepareUpdate(i),this._clearLayer(n),this._finished=!1},t.prototype.incrementalRender=function(e,t,n){this._lineDraw.incrementalUpdate(e,t.getData()),this._finished=e.end===t.getData().count()},t.prototype.eachRendered=function(e){this._lineDraw&&this._lineDraw.eachRendered(e)},t.prototype.updateTransform=function(e,t,n){var i=e.getData(),a=e.pipelineContext;if(!this._finished||a.large||a.progressiveRender)return{update:!0};var r=hR.reset(e,t,n);r.progress&&r.progress({start:0,end:i.count(),count:i.count()},i),this._lineDraw.updateLayout(),this._clearLayer(n)},t.prototype._updateLineDraw=function(e,t){var n=this._lineDraw,i=this._showEffect(t),a=!!t.get("polyline"),r=t.pipelineContext.large;return n&&i===this._hasEffet&&a===this._isPolyline&&r===this._isLargeDraw||(n&&n.remove(),n=this._lineDraw=r?new mR:new YO(a?i?cR:pR:i?lR:KO),this._hasEffet=i,this._isPolyline=a,this._isLargeDraw=r),this.group.add(n.group),n},t.prototype._showEffect=function(e){return!!e.get(["effect","show"])},t.prototype._clearLayer=function(e){var t=e.getZr();"svg"===t.painter.getType()||null==this._lastZlevel||t.painter.getLayer(this._lastZlevel).clear(!0)},t.prototype.remove=function(e,t){this._lineDraw&&this._lineDraw.remove(),this._lineDraw=null,this._clearLayer(t)},t.prototype.dispose=function(e,t){this.remove(e,t)},t.type="lines"}(Tw),"undefined"==typeof Uint32Array?Array:Uint32Array),fR="undefined"==typeof Float64Array?Array:Float64Array;function yR(e){var t=e.data;t&&t[0]&&t[0][0]&&t[0][0].coord&&(e.data=Ur(t,(function(e){var t={coords:[e[0].coord,e[1].coord]};return e[0].name&&(t.fromName=e[0].name),e[1].name&&(t.toName=e[1].name),Rr([t,e[0],e[1]])})))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.visualStyleAccessPath="lineStyle",n.visualDrawType="stroke",n}We(t,e),t.prototype.init=function(t){t.data=t.data||[],yR(t);var n=this._processFlatCoordsArray(t.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(t.data=new Float32Array(n.count)),e.prototype.init.apply(this,arguments)},t.prototype.mergeOption=function(t){if(yR(t),t.data){var n=this._processFlatCoordsArray(t.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(t.data=new Float32Array(n.count))}e.prototype.mergeOption.apply(this,arguments)},t.prototype.appendData=function(e){var t=this._processFlatCoordsArray(e.data);t.flatCoords&&(this._flatCoords?(this._flatCoords=_o(this._flatCoords,t.flatCoords),this._flatCoordsOffset=_o(this._flatCoordsOffset,t.flatCoordsOffset)):(this._flatCoords=t.flatCoords,this._flatCoordsOffset=t.flatCoordsOffset),e.data=new Float32Array(t.count)),this.getRawData().appendData(e.data)},t.prototype._getCoordsFromItemModel=function(e){var t=this.getData().getItemModel(e),n=t.option instanceof Array?t.option:t.getShallow("coords");return n},t.prototype.getLineCoordsCount=function(e){return this._flatCoordsOffset?this._flatCoordsOffset[2*e+1]:this._getCoordsFromItemModel(e).length},t.prototype.getLineCoords=function(e,t){if(this._flatCoordsOffset){for(var n=this._flatCoordsOffset[2*e],i=this._flatCoordsOffset[2*e+1],a=0;a ")})},t.prototype.preventIncremental=function(){return!!this.get(["effect","show"])},t.prototype.getProgressive=function(){var e=this.option.progressive;return null==e?this.option.large?1e4:this.get("progressive"):e},t.prototype.getProgressiveThreshold=function(){var e=this.option.progressiveThreshold;return null==e?this.option.large?2e4:this.get("progressiveThreshold"):e},t.prototype.getZLevelKey=function(){var e=this.getModel("effect"),t=e.get("trailLength");return this.getData().count()>this.getProgressiveThreshold()?this.id:e.get("show")&&t>0?t+"":""},t.type="series.lines",t.dependencies=["grid","polar","geo","calendar"],t.defaultOption={coordinateSystem:"geo",z:2,legendHoverLink:!0,xAxisIndex:0,yAxisIndex:0,symbol:["none","none"],symbolSize:[10,10],geoIndex:0,effect:{show:!1,period:4,constantSpeed:0,symbol:"circle",symbolSize:3,loop:!0,trailLength:.2},large:!1,largeThreshold:2e3,polyline:!1,clip:!0,label:{show:!1,position:"end"},lineStyle:{opacity:.5}}}(mw);var vR=function(){function e(){this.blurSize=30,this.pointSize=20,this.maxOpacity=1,this.minOpacity=0,this._gradientPixels={inRange:null,outOfRange:null};var e=vr.createCanvas();this.canvas=e}return e.prototype.update=function(e,t,n,i,a,r){var o=this._getBrush(),s=this._getGradient(a,"inRange"),l=this._getGradient(a,"outOfRange"),p=this.pointSize+this.blurSize,c=this.canvas,d=c.getContext("2d"),u=e.length;c.width=t,c.height=n;for(var m=0;m0){var T=r(y)?s:l;y>0&&(y=y*C+S),x[b++]=T[_],x[b++]=T[_+1],x[b++]=T[_+2],x[b++]=T[_+3]*y*256}else b+=4}return d.putImageData(v,0,0),c},e.prototype._getBrush=function(){var e=this._brushCanvas||(this._brushCanvas=vr.createCanvas()),t=this.pointSize+this.blurSize,n=2*t;e.width=n,e.height=n;var i=e.getContext("2d");return i.clearRect(0,0,n,n),i.shadowOffsetX=n,i.shadowBlur=this.blurSize,i.shadowColor="#000",i.beginPath(),i.arc(-t,t,this.pointSize,0,2*Math.PI,!0),i.closePath(),i.fill(),e},e.prototype._getGradient=function(e,t){for(var n=this._gradientPixels,i=n[t]||(n[t]=new Uint8ClampedArray(1024)),a=[0,0,0,0],r=0,o=0;o<256;o++)e[t](o/255,!0,a),i[r++]=a[0],i[r++]=a[1],i[r++]=a[2],i[r++]=a[3];return i},e}();function xR(e){var t=e.dimensions;return"lng"===t[0]&&"lat"===t[1]}(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.render=function(e,t,n){var i;t.eachComponent("visualMap",(function(t){t.eachTargetSeries((function(n){n===e&&(i=t)}))})),this._progressiveEls=null,this.group.removeAll();var a=e.coordinateSystem;"cartesian2d"===a.type||"calendar"===a.type?this._renderOnCartesianAndCalendar(e,n,0,e.getData().count()):xR(a)&&this._renderOnGeo(a,e,i,n)},t.prototype.incrementalPrepareRender=function(e,t,n){this.group.removeAll()},t.prototype.incrementalRender=function(e,t,n,i){var a=t.coordinateSystem;a&&(xR(a)?this.render(t,n,i):(this._progressiveEls=[],this._renderOnCartesianAndCalendar(t,i,e.start,e.end,!0)))},t.prototype.eachRendered=function(e){jf(this._progressiveEls||this.group,e)},t.prototype._renderOnCartesianAndCalendar=function(e,t,n,i,a){var r,o,s,l,p=e.coordinateSystem,c=jE(p,"cartesian2d");if(c){var d=p.getAxis("x"),u=p.getAxis("y");0,r=d.getBandWidth()+.5,o=u.getBandWidth()+.5,s=d.scale.getExtent(),l=u.scale.getExtent()}for(var m=this.group,h=e.getData(),g=e.getModel(["emphasis","itemStyle"]).getItemStyle(),f=e.getModel(["blur","itemStyle"]).getItemStyle(),y=e.getModel(["select","itemStyle"]).getItemStyle(),v=e.get(["itemStyle","borderRadius"]),x=Xf(e),b=e.getModel("emphasis"),w=b.get("focus"),S=b.get("blurScope"),C=b.get("disabled"),_=c?[h.mapDimension("x"),h.mapDimension("y"),h.mapDimension("value")]:[h.mapDimension("time"),h.mapDimension("value")],T=n;Ts[1]||kl[1])continue;var P=p.dataToPoint([M,k]);I=new Fm({shape:{x:P[0]-r/2,y:P[1]-o/2,width:r,height:o},style:E})}else{if(isNaN(h.get(_[1],T)))continue;I=new Fm({z2:1,shape:p.dataToRect([h.get(_[0],T)]).contentShape,style:E})}if(h.hasItemOption){var D=h.getItemModel(T),O=D.getModel("emphasis");g=O.getModel("itemStyle").getItemStyle(),f=D.getModel(["blur","itemStyle"]).getItemStyle(),y=D.getModel(["select","itemStyle"]).getItemStyle(),v=D.get(["itemStyle","borderRadius"]),w=O.get("focus"),S=O.get("blurScope"),C=O.get("disabled"),x=Xf(D)}I.shape.r=v;var A=e.getRawValue(T),F="-";A&&null!=A[2]&&(F=A[2]+""),Yf(I,x,{labelFetcher:e,labelDataIndex:T,defaultOpacity:E.opacity,defaultText:F}),I.ensureState("emphasis").style=g,I.ensureState("blur").style=f,I.ensureState("select").style=y,Vh(I,w,S,C),I.incremental=a,a&&(I.states.emphasis.hoverLayer=!0),m.add(I),h.setItemGraphicEl(T,I),this._progressiveEls&&this._progressiveEls.push(I)}},t.prototype._renderOnGeo=function(e,t,n,i){var a=n.targetVisuals.inRange,r=n.targetVisuals.outOfRange,o=t.getData(),s=this._hmLayer||this._hmLayer||new vR;s.blurSize=t.get("blurSize"),s.pointSize=t.get("pointSize"),s.minOpacity=t.get("minOpacity"),s.maxOpacity=t.get("maxOpacity");var l=e.getViewRect().clone(),p=e.getRoamTransform();l.applyTransform(p);var c=Math.max(l.x,0),d=Math.max(l.y,0),u=Math.min(l.width+l.x,i.getWidth()),m=Math.min(l.height+l.y,i.getHeight()),h=u-c,g=m-d,f=[o.mapDimension("lng"),o.mapDimension("lat"),o.mapDimension("value")],y=o.mapArray(f,(function(t,n,i){var a=e.dataToPoint([t,n]);return a[0]-=c,a[1]-=d,a.push(i),a})),v=n.getExtent(),x="visualMap.continuous"===n.type?function(e,t){var n=e[1]-e[0];return t=[(t[0]-e[0])/n,(t[1]-e[0])/n],function(e){return e>=t[0]&&e<=t[1]}}(v,n.option.range):function(e,t,n){var i=e[1]-e[0],a=(t=Ur(t,(function(t){return{interval:[(t.interval[0]-e[0])/i,(t.interval[1]-e[0])/i]}}))).length,r=0;return function(e){var i;for(i=r;i=0;i--){var o;if((o=t[i].interval)[0]<=e&&e<=o[1]){r=i;break}}return i>=0&&i0?1:-1}(n,r,a,i,d),function(e,t,n,i,a,r,o,s,l,p){var c,d=l.valueDim,u=l.categoryDim,m=Math.abs(n[u.wh]),h=e.getItemVisual(t,"symbolSize");c=Xr(h)?h.slice():null==h?["100%","100%"]:[h,h];c[u.index]=Wc(c[u.index],m),c[d.index]=Wc(c[d.index],i?m:Math.abs(r)),p.symbolSize=c;var g=p.symbolScale=[c[0]/s,c[1]/s];g[d.index]*=(l.isHorizontal?-1:1)*o}(e,t,a,r,0,d.boundingLength,d.pxSign,p,i,d),function(e,t,n,i,a){var r=e.get(bR)||0;r&&(SR.attr({scaleX:t[0],scaleY:t[1],rotation:n}),SR.updateTransform(),r/=SR.getLineScale(),r*=t[i.valueDim.index]);a.valueLineWidth=r||0}(n,d.symbolScale,l,i,d);var u=d.symbolSize,m=VS(n.get("symbolOffset"),u);return function(e,t,n,i,a,r,o,s,l,p,c,d){var u=c.categoryDim,m=c.valueDim,h=d.pxSign,g=Math.max(t[m.index]+s,0),f=g;if(i){var y=Math.abs(l),v=po(e.get("symbolMargin"),"15%")+"",x=!1;v.lastIndexOf("!")===v.length-1&&(x=!0,v=v.slice(0,v.length-1));var b=Wc(v,t[m.index]),w=Math.max(g+2*b,0),S=x?0:2*b,C=pd(i),_=C?i:VR((y+S)/w);w=g+2*(b=(y-_*g)/2/(x?_:Math.max(_-1,1))),S=x?0:2*b,C||"fixed"===i||(_=p?VR((Math.abs(p)+S)/w):0),f=_*w-S,d.repeatTimes=_,d.symbolMargin=b}var T=h*(f/2),I=d.pathPosition=[];I[u.index]=n[u.wh]/2,I[m.index]="start"===o?T:"end"===o?l-T:l/2,r&&(I[0]+=r[0],I[1]+=r[1]);var E=d.bundlePosition=[];E[u.index]=n[u.xy],E[m.index]=n[m.xy];var M=d.barRectShape=Br({},n);M[m.wh]=h*Math.max(Math.abs(n[m.wh]),Math.abs(I[m.index]+T)),M[u.wh]=n[u.wh];var k=d.clipShape={};k[u.xy]=-n[u.xy],k[u.wh]=c.ecSize[u.wh],k[m.xy]=0,k[m.wh]=n[m.wh]}(n,u,a,r,0,m,s,d.valueLineWidth,d.boundingLength,d.repeatCutLength,i,d),d}function _R(e,t){return e.toGlobalCoord(e.dataToCoord(e.scale.parse(t)))}function TR(e){var t=e.symbolPatternSize,n=NS(e.symbolType,-t/2,-t/2,t,t);return n.attr({culling:!0}),"image"!==n.type&&n.setStyle({strokeNoScale:!0}),n}function IR(e,t,n,i){var a=e.__pictorialBundle,r=n.symbolSize,o=n.valueLineWidth,s=n.pathPosition,l=t.valueDim,p=n.repeatTimes||0,c=0,d=r[t.valueDim.index]+o+2*n.symbolMargin;for(BR(e,(function(e){e.__pictorialAnimationIndex=c,e.__pictorialRepeatTimes=p,c0:i<0)&&(a=p-1-e),t[l.index]=d*(a-p/2+.5)+s[l.index],{x:t[0],y:t[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation}}}function ER(e,t,n,i){var a=e.__pictorialBundle,r=e.__pictorialMainPath;r?NR(r,null,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation},n,i):(r=e.__pictorialMainPath=TR(n),a.add(r),NR(r,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:0,scaleY:0,rotation:n.rotation},{scaleX:n.symbolScale[0],scaleY:n.symbolScale[1]},n,i))}function MR(e,t,n){var i=Br({},t.barRectShape),a=e.__pictorialBarRect;a?NR(a,null,{shape:i},t,n):((a=e.__pictorialBarRect=new Fm({z2:2,shape:i,silent:!0,style:{stroke:"transparent",fill:"transparent",lineWidth:0}})).disableMorphing=!0,e.add(a))}function kR(e,t,n,i){if(n.symbolClip){var a=e.__pictorialClipPath,r=Br({},n.clipShape),o=t.valueDim,s=n.animationModel,l=n.dataIndex;if(a)pf(a,{shape:r},s,l);else{r[o.wh]=0,a=new Fm({shape:r}),e.__pictorialBundle.setClipPath(a),e.__pictorialClipPath=a;var p={};p[o.wh]=n.clipShape[o.wh],Hf[i?"updateProps":"initProps"](a,{shape:p},s,l)}}}function PR(e,t){var n=e.getItemModel(t);return n.getAnimationDelayParams=DR,n.isAnimationEnabled=OR,n}function DR(e){return{index:e.__pictorialAnimationIndex,count:e.__pictorialRepeatTimes}}function OR(){return this.parentModel.isAnimationEnabled()&&!!this.getShallow("animation")}function AR(e,t,n,i){var a=new Rc,r=new Rc;return a.add(r),a.__pictorialBundle=r,r.x=n.bundlePosition[0],r.y=n.bundlePosition[1],n.symbolRepeat?IR(a,t,n):ER(a,0,n),MR(a,n,i),kR(a,t,n,i),a.__pictorialShapeStr=RR(e,n),a.__pictorialSymbolMeta=n,a}function FR(e,t,n,i){var a=i.__pictorialBarRect;a&&a.removeTextContent();var r=[];BR(i,(function(e){r.push(e)})),i.__pictorialMainPath&&r.push(i.__pictorialMainPath),i.__pictorialClipPath&&(n=null),zr(r,(function(e){uf(e,{scaleX:0,scaleY:0},n,t,(function(){i.parent&&i.parent.remove(i)}))})),e.setItemGraphicEl(t,null)}function RR(e,t){return[e.getItemVisual(t.dataIndex,"symbol")||"none",!!t.symbolRepeat,!!t.symbolClip].join(":")}function BR(e,t,n){zr(e.__pictorialBundle.children(),(function(i){i!==e.__pictorialBarRect&&t.call(n,i)}))}function NR(e,t,n,i,a,r){t&&e.attr(t),i.symbolClip&&!a?n&&e.attr(n):n&&Hf[a?"updateProps":"initProps"](e,n,i.animationModel,i.dataIndex,r)}function LR(e,t,n){var i=n.dataIndex,a=n.itemModel,r=a.getModel("emphasis"),o=r.getModel("itemStyle").getItemStyle(),s=a.getModel(["blur","itemStyle"]).getItemStyle(),l=a.getModel(["select","itemStyle"]).getItemStyle(),p=a.getShallow("cursor"),c=r.get("focus"),d=r.get("blurScope"),u=r.get("scale");BR(e,(function(e){if(e instanceof Em){var t=e.style;e.useStyle(Br({image:t.image,x:t.x,y:t.y,width:t.width,height:t.height},n.style))}else e.useStyle(n.style);var i=e.ensureState("emphasis");i.style=o,u&&(i.scaleX=1.1*e.scaleX,i.scaleY=1.1*e.scaleY),e.ensureState("blur").style=s,e.ensureState("select").style=l,p&&(e.cursor=p),e.z2=n.z2}));var m=t.valueDim.posDesc[+(n.boundingLength>0)],h=e.__pictorialBarRect;h.ignoreClip=!0,Yf(h,Xf(a),{labelFetcher:t.seriesModel,labelDataIndex:i,defaultText:CE(t.seriesModel.getData(),i),inheritColor:n.style.fill,defaultOpacity:n.style.opacity,defaultOutsidePosition:m}),Vh(e,c,d,r.get("disabled"))}function VR(e){var t=Math.round(e);return Math.abs(e-t)<1e-4?t:Math.ceil(e)}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n.defaultSymbol="roundRect",n}We(t,e),t.prototype.getInitialData=function(t){return t.stack=null,e.prototype.getInitialData.apply(this,arguments)},t.type="series.pictorialBar",t.dependencies=["grid"],t.defaultOption=Sy(sM.defaultOption,{symbol:"circle",symbolSize:null,symbolRotate:null,symbolPosition:null,symbolOffset:null,symbolMargin:null,symbolRepeat:!1,symbolRepeatDirection:"end",symbolClip:!1,symbolBoundingData:null,symbolPatternSize:400,barGap:"-100%",clip:!1,progressive:0,emphasis:{scale:!1},select:{itemStyle:{borderColor:"#212121"}}})}(sM);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._layers=[],n}We(t,e),t.prototype.render=function(e,t,n){var i=e.getData(),a=this,r=this.group,o=e.getLayerSeries(),s=i.getLayout("layoutInfo"),l=s.rect,p=s.boundaryGap;function c(e){return e.name}r.x=0,r.y=l.y+p[0];var d=new S_(this._layersSeries||[],o,c,c),u=[];function m(t,n,s){var l=a._layers;if("remove"!==t){for(var p,c,d=[],m=[],h=o[n].indices,g=0;gT&&!td(E-T)&&E0?(a.virtualPiece?a.virtualPiece.updateData(!1,i,e,t,n):(a.virtualPiece=new qR(i,e,t,n),l.add(a.virtualPiece)),r.piece.off("click"),a.virtualPiece.on("click",(function(e){a._rootToNode(r.parentNode)}))):a.virtualPiece&&(l.remove(a.virtualPiece),a.virtualPiece=null)}(o,s),this._initEvents(),this._oldChildren=c},t.prototype._initEvents=function(){var e=this;this.group.off("click"),this.group.on("click",(function(t){var n=!1;e.seriesModel.getViewRoot().eachNode((function(i){if(!n&&i.piece&&i.piece===t.target){var a=i.getModel().get("nodeClick");if("rootToNode"===a)e._rootToNode(i);else if("link"===a){var r=i.getModel(),o=r.get("link");if(o)fv(o,r.get("target",!0)||"_blank")}n=!0}}))}))},t.prototype._rootToNode=function(e){e!==this.seriesModel.getViewRoot()&&this.api.dispatchAction({type:GR,from:this.uid,seriesId:this.seriesModel.id,targetNode:e})},t.prototype.containPoint=function(e,t){var n=t.getData().getItemLayout(0);if(n){var i=e[0]-n.cx,a=e[1]-n.cy,r=Math.sqrt(i*i+a*a);return r<=n.r&&r>=n.r0}},t.type="sunburst"})(Tw),function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.ignoreStyleOnData=!0,n}We(t,e),t.prototype.getInitialData=function(e,t){var n={name:e.name,children:e.data};zR(n);var i=this._levelModels=Ur(e.levels||[],(function(e){return new xy(e,this,t)}),this),a=jD.createTree(n,this,(function(e){e.wrapMethod("getItemModel",(function(e,t){var n=a.getNodeByDataIndex(t),r=i[n.depth];return r&&(e.parentModel=r),e}))}));return a.data},t.prototype.optionUpdated=function(){this.resetViewRoot()},t.prototype.getDataParams=function(t){var n=e.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return n.treePathInfo=KD(i,this),n},t.prototype.getLevelModel=function(e){return this._levelModels&&this._levelModels[e.depth]},t.prototype.getViewRoot=function(){return this._viewRoot},t.prototype.resetViewRoot=function(e){e?this._viewRoot=e:e=this._viewRoot;var t=this.getRawData().tree.root;e&&(e===t||t.contains(e))||(this._viewRoot=t)},t.prototype.enableAriaDecal=function(){YD(this)},t.type="series.sunburst",t.defaultOption={z:2,center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,stillShowZeroSum:!0,nodeClick:"rootToNode",renderLabelForZeroData:!1,label:{rotate:"radial",show:!0,opacity:1,align:"center",position:"inside",distance:5,silent:!0},itemStyle:{borderWidth:1,borderColor:"white",borderType:"solid",shadowBlur:0,shadowColor:"rgba(0, 0, 0, 0.2)",shadowOffsetX:0,shadowOffsetY:0,opacity:1},emphasis:{focus:"descendant"},blur:{itemStyle:{opacity:.2},label:{opacity:.1}},animationType:"expansion",animationDuration:1e3,animationDurationUpdate:500,data:[],sort:"desc"}}(mw);function zR(e){var t=0;zr(e.children,(function(e){zR(e);var n=e.value;Xr(n)&&(n=n[0]),t+=n}));var n=e.value;Xr(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=t),n<0&&(n=0),Xr(e.value)?e.value[0]=n:e.value=n}Math.PI;var UR={color:"fill",borderColor:"stroke"},jR={symbol:1,symbolSize:1,symbolKeepAspect:1,legendIcon:1,visualMeta:1,liftZ:1,decal:1},HR=Pd(),WR=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.optionUpdated=function(){this.currentZLevel=this.get("zlevel",!0),this.currentZ=this.get("z",!0)},t.prototype.getInitialData=function(e,t){return tT(null,this)},t.prototype.getDataParams=function(t,n,i){var a=e.prototype.getDataParams.call(this,t,n);return i&&(a.info=HR(i).info),a},t.type="series.custom",t.dependencies=["grid","polar","geo","singleAxis","calendar"],t.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,clip:!1},t}(mw);function $R(e,t){return t=t||[0,0],Ur(["x","y"],(function(n,i){var a=this.getAxis(n),r=t[i],o=e[i]/2;return"category"===a.type?a.getBandWidth():Math.abs(a.dataToCoord(r-o)-a.dataToCoord(r+o))}),this)}function KR(e,t){return t=t||[0,0],Ur([0,1],(function(n){var i=t[n],a=e[n]/2,r=[],o=[];return r[n]=i-a,o[n]=i+a,r[1-n]=o[1-n]=t[1-n],Math.abs(this.dataToPoint(r)[n]-this.dataToPoint(o)[n])}),this)}function YR(e,t){var n=this.getAxis(),i=t instanceof Array?t[0]:t,a=(e instanceof Array?e[0]:e)/2;return"category"===n.type?n.getBandWidth():Math.abs(n.dataToCoord(i-a)-n.dataToCoord(i+a))}function XR(e,t){return t=t||[0,0],Ur(["Radius","Angle"],(function(n,i){var a=this["get"+n+"Axis"](),r=t[i],o=e[i]/2,s="category"===a.type?a.getBandWidth():Math.abs(a.dataToCoord(r-o)-a.dataToCoord(r+o));return"Angle"===n&&(s=s*Math.PI/180),s}),this)}function ZR(e,t,n,i){return e&&(e.legacy||!1!==e.legacy&&!n&&!i&&"tspan"!==t&&("text"===t||Eo(e,"text")))}function QR(e,t,n){var i,a,r,o=e;if("text"===t)r=o;else{r={},Eo(o,"text")&&(r.text=o.text),Eo(o,"rich")&&(r.rich=o.rich),Eo(o,"textFill")&&(r.fill=o.textFill),Eo(o,"textStroke")&&(r.stroke=o.textStroke),Eo(o,"fontFamily")&&(r.fontFamily=o.fontFamily),Eo(o,"fontSize")&&(r.fontSize=o.fontSize),Eo(o,"fontStyle")&&(r.fontStyle=o.fontStyle),Eo(o,"fontWeight")&&(r.fontWeight=o.fontWeight),a={type:"text",style:r,silent:!0},i={};var s=Eo(o,"textPosition");n?i.position=s?o.textPosition:"inside":s&&(i.position=o.textPosition),Eo(o,"textPosition")&&(i.position=o.textPosition),Eo(o,"textOffset")&&(i.offset=o.textOffset),Eo(o,"textRotation")&&(i.rotation=o.textRotation),Eo(o,"textDistance")&&(i.distance=o.textDistance)}return JR(r,e),zr(r.rich,(function(e){JR(e,e)})),{textConfig:i,textContent:a}}function JR(e,t){t&&(t.font=t.textFont||t.font,Eo(t,"textStrokeWidth")&&(e.lineWidth=t.textStrokeWidth),Eo(t,"textAlign")&&(e.align=t.textAlign),Eo(t,"textVerticalAlign")&&(e.verticalAlign=t.textVerticalAlign),Eo(t,"textLineHeight")&&(e.lineHeight=t.textLineHeight),Eo(t,"textWidth")&&(e.width=t.textWidth),Eo(t,"textHeight")&&(e.height=t.textHeight),Eo(t,"textBackgroundColor")&&(e.backgroundColor=t.textBackgroundColor),Eo(t,"textPadding")&&(e.padding=t.textPadding),Eo(t,"textBorderColor")&&(e.borderColor=t.textBorderColor),Eo(t,"textBorderWidth")&&(e.borderWidth=t.textBorderWidth),Eo(t,"textBorderRadius")&&(e.borderRadius=t.textBorderRadius),Eo(t,"textBoxShadowColor")&&(e.shadowColor=t.textBoxShadowColor),Eo(t,"textBoxShadowBlur")&&(e.shadowBlur=t.textBoxShadowBlur),Eo(t,"textBoxShadowOffsetX")&&(e.shadowOffsetX=t.textBoxShadowOffsetX),Eo(t,"textBoxShadowOffsetY")&&(e.shadowOffsetY=t.textBoxShadowOffsetY))}function eB(e,t,n){var i=e;i.textPosition=i.textPosition||n.position||"inside",null!=n.offset&&(i.textOffset=n.offset),null!=n.rotation&&(i.textRotation=n.rotation),null!=n.distance&&(i.textDistance=n.distance);var a=i.textPosition.indexOf("inside")>=0,r=e.fill||"#000";tB(i,t);var o=null==i.textFill;return a?o&&(i.textFill=n.insideFill||"#fff",!i.textStroke&&n.insideStroke&&(i.textStroke=n.insideStroke),!i.textStroke&&(i.textStroke=r),null==i.textStrokeWidth&&(i.textStrokeWidth=2)):(o&&(i.textFill=e.fill||n.outsideFill||"#000"),!i.textStroke&&n.outsideStroke&&(i.textStroke=n.outsideStroke)),i.text=t.text,i.rich=t.rich,zr(t.rich,(function(e){tB(e,e)})),i}function tB(e,t){t&&(Eo(t,"fill")&&(e.textFill=t.fill),Eo(t,"stroke")&&(e.textStroke=t.fill),Eo(t,"lineWidth")&&(e.textStrokeWidth=t.lineWidth),Eo(t,"font")&&(e.font=t.font),Eo(t,"fontStyle")&&(e.fontStyle=t.fontStyle),Eo(t,"fontWeight")&&(e.fontWeight=t.fontWeight),Eo(t,"fontSize")&&(e.fontSize=t.fontSize),Eo(t,"fontFamily")&&(e.fontFamily=t.fontFamily),Eo(t,"align")&&(e.textAlign=t.align),Eo(t,"verticalAlign")&&(e.textVerticalAlign=t.verticalAlign),Eo(t,"lineHeight")&&(e.textLineHeight=t.lineHeight),Eo(t,"width")&&(e.textWidth=t.width),Eo(t,"height")&&(e.textHeight=t.height),Eo(t,"backgroundColor")&&(e.textBackgroundColor=t.backgroundColor),Eo(t,"padding")&&(e.textPadding=t.padding),Eo(t,"borderColor")&&(e.textBorderColor=t.borderColor),Eo(t,"borderWidth")&&(e.textBorderWidth=t.borderWidth),Eo(t,"borderRadius")&&(e.textBorderRadius=t.borderRadius),Eo(t,"shadowColor")&&(e.textBoxShadowColor=t.shadowColor),Eo(t,"shadowBlur")&&(e.textBoxShadowBlur=t.shadowBlur),Eo(t,"shadowOffsetX")&&(e.textBoxShadowOffsetX=t.shadowOffsetX),Eo(t,"shadowOffsetY")&&(e.textBoxShadowOffsetY=t.shadowOffsetY),Eo(t,"textShadowColor")&&(e.textShadowColor=t.textShadowColor),Eo(t,"textShadowBlur")&&(e.textShadowBlur=t.textShadowBlur),Eo(t,"textShadowOffsetX")&&(e.textShadowOffsetX=t.textShadowOffsetX),Eo(t,"textShadowOffsetY")&&(e.textShadowOffsetY=t.textShadowOffsetY))}var nB={position:["x","y"],scale:["scaleX","scaleY"],origin:["originX","originY"]},iB=$r(nB),aB=(jr(hc,(function(e,t){return e[t]=1,e}),{}),hc.join(", "),["","style","shape","extra"]),rB=Pd();function oB(e,t,n,i,a){var r=e+"Animation",o=sf(e,i,a)||{},s=rB(t).userDuring;return o.duration>0&&(o.during=s?Kr(mB,{el:t,userDuring:s}):null,o.setToFinal=!0,o.scope=e),Br(o,n[r]),o}function sB(e,t,n,i){var a=(i=i||{}).dataIndex,r=i.isInit,o=i.clearStyle,s=n.isAnimationEnabled(),l=rB(e),p=t.style;l.userDuring=t.during;var c={},d={};if(function(e,t,n){for(var i=0;i=0)){var d=e.getAnimationStyleProps(),u=d?d.style:null;if(u){!a&&(a=i.style={});var m=$r(n);for(p=0;p0&&e.animateFrom(u,m)}else!function(e,t,n,i,a){if(a){var r=oB("update",e,t,i,n);r.duration>0&&e.animateFrom(a,r)}}(e,t,a||0,n,c);lB(e,t),p?e.dirty():e.markRedraw()}function lB(e,t){for(var n=rB(e).leaveToProps,i=0;i=0){!r&&(r=i[e]={});var u=$r(o);for(c=0;ci[1]&&i.reverse(),{coordSys:{type:"polar",cx:e.cx,cy:e.cy,r:i[1],r0:i[0]},api:{coord:function(i){var a=t.dataToRadius(i[0]),r=n.dataToAngle(i[1]),o=e.coordToPoint([a,r]);return o.push(a,r*Math.PI/180),o},size:Kr(XR,e)}}},calendar:function(e){var t=e.getRect(),n=e.getRangeInfo();return{coordSys:{type:"calendar",x:t.x,y:t.y,width:t.width,height:t.height,cellWidth:e.getCellWidth(),cellHeight:e.getCellHeight(),rangeInfo:{start:n.start,end:n.end,weeks:n.weeks,dayCount:n.allDay}},api:{coord:function(t,n){return e.dataToPoint(t,n)}}}}};function DB(e){return e instanceof Sm}function OB(e){return e instanceof bu}var AB=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.render=function(e,t,n,i){this._progressiveEls=null;var a=this._data,r=e.getData(),o=this.group,s=LB(e,r,t,n);a||o.removeAll(),r.diff(a).add((function(t){qB(n,null,t,s(t,i),e,o,r)})).remove((function(t){var n=a.getItemGraphicEl(t);n&&pB(n,HR(n).option,e)})).update((function(t,l){var p=a.getItemGraphicEl(l);qB(n,p,t,s(t,i),e,o,r)})).execute();var l=e.get("clip",!0)?UE(e.coordinateSystem,!1,e):null;l?o.setClipPath(l):o.removeClipPath(),this._data=r},t.prototype.incrementalPrepareRender=function(e,t,n){this.group.removeAll(),this._data=null},t.prototype.incrementalRender=function(e,t,n,i,a){var r=t.getData(),o=LB(t,r,n,i),s=this._progressiveEls=[];function l(e){e.isGroup||(e.incremental=!0,e.ensureState("emphasis").hoverLayer=!0)}for(var p=e.start;p=0?t.getStore().get(a,n):void 0}var r=t.get(i.name,n),o=i&&i.ordinalMeta;return o?o.categories[r]:r},styleEmphasis:function(n,i){0;null==i&&(i=s);var a=v(i,wB).getItemStyle(),r=x(i,wB),o=Zf(r,null,null,!0,!0);o.text=r.getShallow("show")?uo(e.getFormattedLabel(i,wB),e.getFormattedLabel(i,SB),CE(t,i)):null;var l=Qf(r,null,!0);return w(n,a),a=eB(a,o,l),n&&b(a,n),a.legacy=!0,a},visual:function(e,n){if(null==n&&(n=s),Eo(UR,e)){var i=t.getItemVisual(n,"style");return i?i[UR[e]]:null}if(Eo(jR,e))return t.getItemVisual(n,e)},barLayout:function(e){if("cartesian2d"===r.type){return function(e){var t=[],n=e.axis,i="axis0";if("category"===n.type){for(var a=n.getBandWidth(),r=0;r=d;h--){var g=t.childAt(h);WB(t,g,a)}}(e,d,n,i,a),o>=0?r.replaceAt(d,o):r.add(d),d}function zB(e,t,n){var i,a=HR(e),r=t.type,o=t.shape,s=t.style;return n.isUniversalTransitionEnabled()||null!=r&&r!==a.customGraphicType||"path"===r&&((i=o)&&(Eo(i,"pathData")||Eo(i,"d")))&&XB(o)!==a.customPathData||"image"===r&&Eo(s,"image")&&s.image!==a.customImagePath}function UB(e,t,n){var i=t?jB(e,t):e,a=t?HB(e,i,wB):e.style,r=e.type,o=i?i.textConfig:null,s=e.textContent,l=s?t?jB(s,t):s:null;if(a&&(n.isLegacy||ZR(a,r,!!o,!!l))){n.isLegacy=!0;var p=QR(a,r,!t);!o&&p.textConfig&&(o=p.textConfig),!l&&p.textContent&&(l=p.textContent)}if(!t&&l){var c=l;!c.type&&(c.type="text")}var d=t?n[t]:n.normal;d.cfg=o,d.conOpt=l}function jB(e,t){return t?e?e[t]:null:e}function HB(e,t,n){var i=t&&t.style;return null==i&&n===wB&&e&&(i=e.styleEmphasis),i}function WB(e,t,n){t&&pB(t,HR(e).option,n)}function $B(e,t){var n=e&&e.name;return null!=n?n:"e\0\0"+t}function KB(e,t){var n=this.context,i=null!=e?n.newChildren[e]:null,a=null!=t?n.oldChildren[t]:null;GB(n.api,a,n.dataIndex,i,n.seriesModel,n.group)}function YB(e){var t=this.context,n=t.oldChildren[e];n&&pB(n,HR(n).option,t.seriesModel)}function XB(e){return e&&(e.pathData||e.d)}function ZB(e){e.registerChartView(AB),e.registerSeriesModel(WR)}var QB=Pd(),JB=Ar,eN=Kr,tN=function(){function e(){this._dragging=!1,this.animationThreshold=15}return e.prototype.render=function(e,t,n,i){var a=t.get("value"),r=t.get("status");if(this._axisModel=e,this._axisPointerModel=t,this._api=n,i||this._lastValue!==a||this._lastStatus!==r){this._lastValue=a,this._lastStatus=r;var o=this._group,s=this._handle;if(!r||"hide"===r)return o&&o.hide(),void(s&&s.hide());o&&o.show(),s&&s.show();var l={};this.makeElOption(l,a,e,t,n);var p=l.graphicKey;p!==this._lastGraphicKey&&this.clear(n),this._lastGraphicKey=p;var c=this._moveAnimation=this.determineAnimation(e,t);if(o){var d=Yr(nN,t,c);this.updatePointerEl(o,l,d),this.updateLabelEl(o,l,d,t)}else o=this._group=new Rc,this.createPointerEl(o,l,e,t),this.createLabelEl(o,l,e,t),n.getZr().add(o);oN(o,t,!0),this._renderHandle(a)}},e.prototype.remove=function(e){this.clear(e)},e.prototype.dispose=function(e){this.clear(e)},e.prototype.determineAnimation=function(e,t){var n=t.get("animation"),i=e.axis,a="category"===i.type,r=t.get("snap");if(!r&&!a)return!1;if("auto"===n||null==n){var o=this.animationThreshold;if(a&&i.getBandWidth()>o)return!0;if(r){var s=Ok(e).seriesDataCount,l=i.getExtent();return Math.abs(l[0]-l[1])/s>o}return!1}return!0===n},e.prototype.makeElOption=function(e,t,n,i,a){},e.prototype.createPointerEl=function(e,t,n,i){var a=t.pointer;if(a){var r=QB(e).pointerEl=new Hf[a.type](JB(t.pointer));e.add(r)}},e.prototype.createLabelEl=function(e,t,n,i){if(t.label){var a=QB(e).labelEl=new Nm(JB(t.label));e.add(a),aN(a,i)}},e.prototype.updatePointerEl=function(e,t,n){var i=QB(e).pointerEl;i&&t.pointer&&(i.setStyle(t.pointer.style),n(i,{shape:t.pointer.shape}))},e.prototype.updateLabelEl=function(e,t,n,i){var a=QB(e).labelEl;a&&(a.setStyle(t.label.style),n(a,{x:t.label.x,y:t.label.y}),aN(a,i))},e.prototype._renderHandle=function(e){if(!this._dragging&&this.updateHandleTransform){var t,n=this._axisPointerModel,i=this._api.getZr(),a=this._handle,r=n.getModel("handle"),o=n.get("status");if(!r.get("show")||!o||"hide"===o)return a&&i.remove(a),void(this._handle=null);this._handle||(t=!0,a=this._handle=Lf(r.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(e){gs(e.event)},onmousedown:eN(this._onHandleDragMove,this,0,0),drift:eN(this._onHandleDragMove,this),ondragend:eN(this._onHandleDragEnd,this)}),i.add(a)),oN(a,n,!1),a.setStyle(r.getItemStyle(null,["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]));var s=r.get("size");Xr(s)||(s=[s,s]),a.scaleX=s[0]/2,a.scaleY=s[1]/2,Rw(this,"_doDispatchAxisPointer",r.get("throttle")||0,"fixRate"),this._moveHandleToValue(e,t)}},e.prototype._moveHandleToValue=function(e,t){nN(this._axisPointerModel,!t&&this._moveAnimation,this._handle,rN(this.getHandleTransform(e,this._axisModel,this._axisPointerModel)))},e.prototype._onHandleDragMove=function(e,t){var n=this._handle;if(n){this._dragging=!0;var i=this.updateHandleTransform(rN(n),[e,t],this._axisModel,this._axisPointerModel);this._payloadInfo=i,n.stopAnimation(),n.attr(rN(i)),QB(n).lastProp=null,this._doDispatchAxisPointer()}},e.prototype._doDispatchAxisPointer=function(){if(this._handle){var e=this._payloadInfo,t=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:e.cursorPoint[0],y:e.cursorPoint[1],tooltipOption:e.tooltipOption,axesInfo:[{axisDim:t.axis.dim,axisIndex:t.componentIndex}]})}},e.prototype._onHandleDragEnd=function(){if(this._dragging=!1,this._handle){var e=this._axisPointerModel.get("value");this._moveHandleToValue(e),this._api.dispatchAction({type:"hideTip"})}},e.prototype.clear=function(e){this._lastValue=null,this._lastStatus=null;var t=e.getZr(),n=this._group,i=this._handle;t&&n&&(this._lastGraphicKey=null,n&&t.remove(n),i&&t.remove(i),this._group=null,this._handle=null,this._payloadInfo=null),Bw(this,"_doDispatchAxisPointer")},e.prototype.doClear=function(){},e.prototype.buildLabel=function(e,t,n){return{x:e[n=n||0],y:e[1-n],width:t[n],height:t[1-n]}},e}();function nN(e,t,n,i){iN(QB(n).lastProp,i)||(QB(n).lastProp=i,t?pf(n,i,e):(n.stopAnimation(),n.attr(i)))}function iN(e,t){if(to(e)&&to(t)){var n=!0;return zr(t,(function(t,i){n=n&&iN(e[i],t)})),!!n}return e===t}function aN(e,t){e[t.get(["label","show"])?"show":"hide"]()}function rN(e){return{x:e.x||0,y:e.y||0,rotation:e.rotation||0}}function oN(e,t,n){var i=t.get("z"),a=t.get("zlevel");e&&e.traverse((function(e){"group"!==e.type&&(null!=i&&(e.z=i),null!=a&&(e.zlevel=a),e.silent=n)}))}function sN(e){var t,n=e.get("type"),i=e.getModel(n+"Style");return"line"===n?(t=i.getLineStyle()).fill=null:"shadow"===n&&((t=i.getAreaStyle()).stroke=null),t}function lN(e,t,n,i,a){var r=pN(n.get("value"),t.axis,t.ecModel,n.get("seriesDataIndices"),{precision:n.get(["label","precision"]),formatter:n.get(["label","formatter"])}),o=n.getModel("label"),s=pv(o.get("padding")||0),l=o.getFont(),p=xc(r,l),c=a.position,d=p.width+s[1]+s[3],u=p.height+s[0]+s[2],m=a.align;"right"===m&&(c[0]-=d),"center"===m&&(c[0]-=d/2);var h=a.verticalAlign;"bottom"===h&&(c[1]-=u),"middle"===h&&(c[1]-=u/2),function(e,t,n,i){var a=i.getWidth(),r=i.getHeight();e[0]=Math.min(e[0]+t,a)-t,e[1]=Math.min(e[1]+n,r)-n,e[0]=Math.max(e[0],0),e[1]=Math.max(e[1],0)}(c,d,u,i);var g=o.get("backgroundColor");g&&"auto"!==g||(g=t.get(["axisLine","lineStyle","color"])),e.label={x:c[0],y:c[1],style:Zf(o,{text:r,font:l,fill:o.getTextColor(),padding:s,backgroundColor:g}),z2:10}}function pN(e,t,n,i,a){e=t.scale.parse(e);var r=t.scale.getLabel({value:e},{precision:a.precision}),o=a.formatter;if(o){var s={value:iI(t,{value:e}),axisDimension:t.dim,axisIndex:t.index,seriesData:[]};zr(i,(function(e){var t=n.getSeriesByIndex(e.seriesIndex),i=e.dataIndexInside,a=t&&t.getDataParams(i);a&&s.seriesData.push(a)})),Qr(o)?r=o.replace("{value}",r):Zr(o)&&(r=o(s))}return r}function cN(e,t,n){var i=[1,0,0,1,0,0];return _s(i,i,n.rotation),Cs(i,i,n.position),Of([e.dataToCoord(t),(n.labelOffset||0)+(n.labelDirection||1)*(n.labelMargin||0)],i)}function dN(e,t,n,i,a,r){var o=_k.innerTextLayout(n.rotation,0,n.labelDirection);n.labelMargin=a.get(["label","margin"]),lN(t,i,a,r,{position:cN(i.axis,e,n),align:o.textAlign,verticalAlign:o.textVerticalAlign})}function uN(e,t,n){return{x1:e[n=n||0],y1:e[1-n],x2:t[n],y2:t[1-n]}}function mN(e,t,n){return{x:e[n=n||0],y:e[1-n],width:t[n],height:t[1-n]}}function hN(e,t,n,i,a,r){return{cx:e,cy:t,r0:n,r:i,startAngle:a,endAngle:r,clockwise:!0}}var gN=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.makeElOption=function(e,t,n,i,a){var r=n.axis,o=r.grid,s=i.get("type"),l=fN(o,r).getOtherAxis(r).getGlobalExtent(),p=r.toGlobalCoord(r.dataToCoord(t,!0));if(s&&"none"!==s){var c=sN(i),d=yN[s](r,p,l);d.style=c,e.graphicKey=d.type,e.pointer=d}dN(t,e,hk(o.model,n),n,i,a)},t.prototype.getHandleTransform=function(e,t,n){var i=hk(t.axis.grid.model,t,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var a=cN(t.axis,e,i);return{x:a[0],y:a[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},t.prototype.updateHandleTransform=function(e,t,n,i){var a=n.axis,r=a.grid,o=a.getGlobalExtent(!0),s=fN(r,a).getOtherAxis(a).getGlobalExtent(),l="x"===a.dim?0:1,p=[e.x,e.y];p[l]+=t[l],p[l]=Math.min(o[1],p[l]),p[l]=Math.max(o[0],p[l]);var c=(s[1]+s[0])/2,d=[c,c];d[l]=p[l];return{x:p[0],y:p[1],rotation:e.rotation,cursorPoint:d,tooltipOption:[{verticalAlign:"middle"},{align:"center"}][l]}},t}(tN);function fN(e,t){var n={};return n[t.dim+"AxisIndex"]=t.index,e.getCartesian(n)}var yN={line:function(e,t,n){return{type:"Line",subPixelOptimize:!0,shape:uN([t,n[0]],[t,n[1]],vN(e))}},shadow:function(e,t,n){var i=Math.max(1,e.getBandWidth()),a=n[1]-n[0];return{type:"Rect",shape:mN([t-i/2,n[0]],[i,a],vN(e))}}};function vN(e){return"x"===e.dim?0:1}var xN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="axisPointer",t.defaultOption={show:"auto",z:50,type:"line",snap:!1,triggerTooltip:!0,triggerEmphasis:!0,value:null,status:null,link:[],animation:null,animationDurationUpdate:200,lineStyle:{color:"#B9BEC9",width:1,type:"dashed"},shadowStyle:{color:"rgba(210,219,238,0.2)"},label:{show:!0,formatter:null,precision:"auto",margin:3,color:"#fff",padding:[5,7,5,7],backgroundColor:"auto",borderColor:null,borderWidth:0,borderRadius:3},handle:{show:!1,icon:"M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z",size:45,margin:50,color:"#333",shadowBlur:3,shadowColor:"#aaa",shadowOffsetX:0,shadowOffsetY:2,throttle:40}},t}(kv),bN=Pd(),wN=zr;function SN(e,t,n){if(!ur.node){var i=t.getZr();bN(i).records||(bN(i).records={}),function(e,t){if(bN(e).initialized)return;function n(n,i){e.on(n,(function(n){var a=function(e){var t={showTip:[],hideTip:[]},n=function(i){var a=t[i.type];a?a.push(i):(i.dispatchAction=n,e.dispatchAction(i))};return{dispatchAction:n,pendings:t}}(t);wN(bN(e).records,(function(e){e&&i(e,n,a.dispatchAction)})),function(e,t){var n,i=e.showTip.length,a=e.hideTip.length;i?n=e.showTip[i-1]:a&&(n=e.hideTip[a-1]);n&&(n.dispatchAction=null,t.dispatchAction(n))}(a.pendings,t)}))}bN(e).initialized=!0,n("click",Yr(_N,"click")),n("mousemove",Yr(_N,"mousemove")),n("globalout",CN)}(i,t),(bN(i).records[e]||(bN(i).records[e]={})).handler=n}}function CN(e,t,n){e.handler("leave",null,n)}function _N(e,t,n,i){t.handler(e,n,i)}function TN(e,t){if(!ur.node){var n=t.getZr();(bN(n).records||{})[e]&&(bN(n).records[e]=null)}}var IN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.render=function(e,t,n){var i=t.getComponent("tooltip"),a=e.get("triggerOn")||i&&i.get("triggerOn")||"mousemove|click";SN("axisPointer",n,(function(e,t,n){"none"!==a&&("leave"===e||a.indexOf(e)>=0)&&n({type:"updateAxisPointer",currTrigger:e,x:t&&t.offsetX,y:t&&t.offsetY})}))},t.prototype.remove=function(e,t){TN("axisPointer",t)},t.prototype.dispose=function(e,t){TN("axisPointer",t)},t.type="axisPointer",t}(ww);function EN(e,t){var n,i=[],a=e.seriesIndex;if(null==a||!(n=t.getSeriesByIndex(a)))return{point:[]};var r=n.getData(),o=kd(r,e);if(null==o||o<0||Xr(o))return{point:[]};var s=r.getItemGraphicEl(o),l=n.coordinateSystem;if(n.getTooltipPosition)i=n.getTooltipPosition(o)||[];else if(l&&l.dataToPoint)if(e.isStacked){var p=l.getBaseAxis(),c=l.getOtherAxis(p).dim,d=p.dim,u="x"===c||"radius"===c?1:0,m=r.mapDimension(d),h=[];h[u]=r.get(m,o),h[1-u]=r.get(r.getCalculationInfo("stackResultDimension"),o),i=l.dataToPoint(h)||[]}else i=l.dataToPoint(r.getValues(Ur(l.dimensions,(function(e){return r.mapDimension(e)})),o))||[];else if(s){var g=s.getBoundingRect().clone();g.applyTransform(s.transform),i=[g.x+g.width/2,g.y+g.height/2]}return{point:i,el:s}}var MN=Pd();function kN(e,t,n){var i=e.currTrigger,a=[e.x,e.y],r=e,o=e.dispatchAction||Kr(n.dispatchAction,n),s=t.getComponent("axisPointer").coordSysAxesInfo;if(s){FN(a)&&(a=EN({seriesIndex:r.seriesIndex,dataIndex:r.dataIndex},t).point);var l=FN(a),p=r.axesInfo,c=s.axesInfo,d="leave"===i||FN(a),u={},m={},h={list:[],map:{}},g={showPointer:Yr(DN,m),showTooltip:Yr(ON,h)};zr(s.coordSysMap,(function(e,t){var n=l||e.containPoint(a);zr(s.coordSysAxesInfo[t],(function(e,t){var i=e.axis,r=function(e,t){for(var n=0;n<(e||[]).length;n++){var i=e[n];if(t.axis.dim===i.axisDim&&t.axis.model.componentIndex===i.axisIndex)return i}}(p,e);if(!d&&n&&(!p||r)){var o=r&&r.value;null!=o||l||(o=i.pointToData(a)),null!=o&&PN(e,o,g,!1,u)}}))}));var f={};return zr(c,(function(e,t){var n=e.linkGroup;n&&!m[t]&&zr(n.axesInfo,(function(t,i){var a=m[i];if(t!==e&&a){var r=a.value;n.mapper&&(r=e.axis.scale.parse(n.mapper(r,AN(t),AN(e)))),f[e.key]=r}}))})),zr(f,(function(e,t){PN(c[t],e,g,!0,u)})),function(e,t,n){var i=n.axesInfo=[];zr(t,(function(t,n){var a=t.axisPointerModel.option,r=e[n];r?(!t.useHandle&&(a.status="show"),a.value=r.value,a.seriesDataIndices=(r.payloadBatch||[]).slice()):!t.useHandle&&(a.status="hide"),"show"===a.status&&i.push({axisDim:t.axis.dim,axisIndex:t.axis.model.componentIndex,value:a.value})}))}(m,c,u),function(e,t,n,i){if(FN(t)||!e.list.length)return void i({type:"hideTip"});var a=((e.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};i({type:"showTip",escapeConnect:!0,x:t[0],y:t[1],tooltipOption:n.tooltipOption,position:n.position,dataIndexInside:a.dataIndexInside,dataIndex:a.dataIndex,seriesIndex:a.seriesIndex,dataByCoordSys:e.list})}(h,a,e,o),function(e,t,n){var i=n.getZr(),a="axisPointerLastHighlights",r=MN(i)[a]||{},o=MN(i)[a]={};zr(e,(function(e,t){var n=e.axisPointerModel.option;"show"===n.status&&e.triggerEmphasis&&zr(n.seriesDataIndices,(function(e){var t=e.seriesIndex+" | "+e.dataIndex;o[t]=e}))}));var s=[],l=[];zr(r,(function(e,t){!o[t]&&l.push(e)})),zr(o,(function(e,t){!r[t]&&s.push(e)})),l.length&&n.dispatchAction({type:"downplay",escapeConnect:!0,notBlur:!0,batch:l}),s.length&&n.dispatchAction({type:"highlight",escapeConnect:!0,notBlur:!0,batch:s})}(c,0,n),u}}function PN(e,t,n,i,a){var r=e.axis;if(!r.scale.isBlank()&&r.containData(t))if(e.involveSeries){var o=function(e,t){var n=t.axis,i=n.dim,a=e,r=[],o=Number.MAX_VALUE,s=-1;return zr(t.seriesModels,(function(t,l){var p,c,d=t.getData().mapDimensionsAll(i);if(t.getAxisTooltipData){var u=t.getAxisTooltipData(d,e,n);c=u.dataIndices,p=u.nestestValue}else{if(!(c=t.getData().indicesOfNearest(d[0],e,"category"===n.type?.5:null)).length)return;p=t.getData().get(d[0],c[0])}if(null!=p&&isFinite(p)){var m=e-p,h=Math.abs(m);h<=o&&((h=0&&s<0)&&(o=h,s=m,a=p,r.length=0),zr(c,(function(e){r.push({seriesIndex:t.seriesIndex,dataIndexInside:e,dataIndex:t.getData().getRawIndex(e)})})))}})),{payloadBatch:r,snapToValue:a}}(t,e),s=o.payloadBatch,l=o.snapToValue;s[0]&&null==a.seriesIndex&&Br(a,s[0]),!i&&e.snap&&r.containData(l)&&null!=l&&(t=l),n.showPointer(e,t,s),n.showTooltip(e,o,l)}else n.showPointer(e,t)}function DN(e,t,n,i){e[t.key]={value:n,payloadBatch:i}}function ON(e,t,n,i){var a=n.payloadBatch,r=t.axis,o=r.model,s=t.axisPointerModel;if(t.triggerTooltip&&a.length){var l=t.coordSys.model,p=Fk(l),c=e.map[p];c||(c=e.map[p]={coordSysId:l.id,coordSysIndex:l.componentIndex,coordSysType:l.type,coordSysMainType:l.mainType,dataByAxis:[]},e.list.push(c)),c.dataByAxis.push({axisDim:r.dim,axisIndex:o.componentIndex,axisType:o.type,axisId:o.id,value:i,valueLabelOpt:{precision:s.get(["label","precision"]),formatter:s.get(["label","formatter"])},seriesDataIndices:a.slice()})}}function AN(e){var t=e.axis.model,n={},i=n.axisDim=e.axis.dim;return n.axisIndex=n[i+"AxisIndex"]=t.componentIndex,n.axisName=n[i+"AxisName"]=t.name,n.axisId=n[i+"AxisId"]=t.id,n}function FN(e){return!e||null==e[0]||isNaN(e[0])||null==e[1]||isNaN(e[1])}function RN(e){Bk.registerAxisPointerClass("CartesianAxisPointer",gN),e.registerComponentModel(xN),e.registerComponentView(IN),e.registerPreprocessor((function(e){if(e){(!e.axisPointer||0===e.axisPointer.length)&&(e.axisPointer={});var t=e.axisPointer.link;t&&!Xr(t)&&(e.axisPointer.link=[t])}})),e.registerProcessor(e.PRIORITY.PROCESSOR.STATISTIC,(function(e,t){e.getComponent("axisPointer").coordSysAxesInfo=Pk(e,t)})),e.registerAction({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},kN)}function BN(e){dI(Kk),dI(RN)}var NN=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.makeElOption=function(e,t,n,i,a){var r=n.axis;"angle"===r.dim&&(this.animationThreshold=Math.PI/18);var o=r.polar,s=o.getOtherAxis(r).getExtent(),l=r.dataToCoord(t),p=i.get("type");if(p&&"none"!==p){var c=sN(i),d=LN[p](r,o,l,s);d.style=c,e.graphicKey=d.type,e.pointer=d}var u=function(e,t,n,i,a){var r=t.axis,o=r.dataToCoord(e),s=i.getAngleAxis().getExtent()[0];s=s/180*Math.PI;var l,p,c,d=i.getRadiusAxis().getExtent();if("radius"===r.dim){var u=[1,0,0,1,0,0];_s(u,u,s),Cs(u,u,[i.cx,i.cy]),l=Of([o,-a],u);var m=t.getModel("axisLabel").get("rotate")||0,h=_k.innerTextLayout(s,m*Math.PI/180,-1);p=h.textAlign,c=h.textVerticalAlign}else{var g=d[1];l=i.coordToPoint([g+a,o]);var f=i.cx,y=i.cy;p=Math.abs(l[0]-f)/g<.3?"center":l[0]>f?"left":"right",c=Math.abs(l[1]-y)/g<.3?"middle":l[1]>y?"top":"bottom"}return{position:l,align:p,verticalAlign:c}}(t,n,0,o,i.get(["label","margin"]));lN(e,n,i,a,u)},t}(tN);var LN={line:function(e,t,n,i){return"angle"===e.dim?{type:"Line",shape:uN(t.coordToPoint([i[0],n]),t.coordToPoint([i[1],n]))}:{type:"Circle",shape:{cx:t.cx,cy:t.cy,r:n}}},shadow:function(e,t,n,i){var a=Math.max(1,e.getBandWidth()),r=Math.PI/180;return"angle"===e.dim?{type:"Sector",shape:hN(t.cx,t.cy,i[0],i[1],(-n-a/2)*r,(a/2-n)*r)}:{type:"Sector",shape:hN(t.cx,t.cy,n-a/2,n+a/2,0,2*Math.PI)}}},VN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.findAxisModel=function(e){var t;return this.ecModel.eachComponent(e,(function(e){e.getCoordSysModel()===this&&(t=e)}),this),t},t.type="polar",t.dependencies=["radiusAxis","angleAxis"],t.defaultOption={z:0,center:["50%","50%"],radius:"80%"},t}(kv),qN=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return We(t,e),t.prototype.getCoordSysModel=function(){return this.getReferringComponents("polar",Fd).models[0]},t.type="polarAxis",t}(kv);qr(qN,lI);var GN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="angleAxis",t}(qN),zN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="radiusAxis",t}(qN),UN=function(e){function t(t,n){return e.call(this,"radius",t,n)||this}return We(t,e),t.prototype.pointToData=function(e,t){return this.polar.pointToData(e,t)["radius"===this.dim?0:1]},t}(RI);UN.prototype.dataToRadius=RI.prototype.dataToCoord,UN.prototype.radiusToData=RI.prototype.coordToData;var jN=Pd(),HN=function(e){function t(t,n){return e.call(this,"angle",t,n||[0,360])||this}return We(t,e),t.prototype.pointToData=function(e,t){return this.polar.pointToData(e,t)["radius"===this.dim?0:1]},t.prototype.calculateCategoryInterval=function(){var e=this,t=e.getLabelModel(),n=e.scale,i=n.getExtent(),a=n.count();if(i[1]-i[0]<1)return 0;var r=i[0],o=e.dataToCoord(r+1)-e.dataToCoord(r),s=Math.abs(o),l=xc(null==r?"":r+"",t.getFont(),"center","top"),p=Math.max(l.height,7)/s;isNaN(p)&&(p=1/0);var c=Math.max(0,Math.floor(p)),d=jN(e.model),u=d.lastAutoInterval,m=d.lastTickCount;return null!=u&&null!=m&&Math.abs(u-c)<=1&&Math.abs(m-a)<=1&&u>c?c=u:(d.lastTickCount=a,d.lastAutoInterval=c),c},t}(RI);HN.prototype.dataToAngle=RI.prototype.dataToCoord,HN.prototype.angleToData=RI.prototype.coordToData;var WN=["radius","angle"],$N=function(){function e(e){this.dimensions=WN,this.type="polar",this.cx=0,this.cy=0,this._radiusAxis=new UN,this._angleAxis=new HN,this.axisPointerEnabled=!0,this.name=e||"",this._radiusAxis.polar=this._angleAxis.polar=this}return e.prototype.containPoint=function(e){var t=this.pointToCoord(e);return this._radiusAxis.contain(t[0])&&this._angleAxis.contain(t[1])},e.prototype.containData=function(e){return this._radiusAxis.containData(e[0])&&this._angleAxis.containData(e[1])},e.prototype.getAxis=function(e){return this["_"+e+"Axis"]},e.prototype.getAxes=function(){return[this._radiusAxis,this._angleAxis]},e.prototype.getAxesByScale=function(e){var t=[],n=this._angleAxis,i=this._radiusAxis;return n.scale.type===e&&t.push(n),i.scale.type===e&&t.push(i),t},e.prototype.getAngleAxis=function(){return this._angleAxis},e.prototype.getRadiusAxis=function(){return this._radiusAxis},e.prototype.getOtherAxis=function(e){var t=this._angleAxis;return e===t?this._radiusAxis:t},e.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAngleAxis()},e.prototype.getTooltipAxes=function(e){var t=null!=e&&"auto"!==e?this.getAxis(e):this.getBaseAxis();return{baseAxes:[t],otherAxes:[this.getOtherAxis(t)]}},e.prototype.dataToPoint=function(e,t){return this.coordToPoint([this._radiusAxis.dataToRadius(e[0],t),this._angleAxis.dataToAngle(e[1],t)])},e.prototype.pointToData=function(e,t){var n=this.pointToCoord(e);return[this._radiusAxis.radiusToData(n[0],t),this._angleAxis.angleToData(n[1],t)]},e.prototype.pointToCoord=function(e){var t=e[0]-this.cx,n=e[1]-this.cy,i=this.getAngleAxis(),a=i.getExtent(),r=Math.min(a[0],a[1]),o=Math.max(a[0],a[1]);i.inverse?r=o-360:o=r+360;var s=Math.sqrt(t*t+n*n);t/=s,n/=s;for(var l=Math.atan2(-n,t)/Math.PI*180,p=lo;)l+=360*p;return[s,l]},e.prototype.coordToPoint=function(e){var t=e[0],n=e[1]/180*Math.PI;return[Math.cos(n)*t+this.cx,-Math.sin(n)*t+this.cy]},e.prototype.getArea=function(){var e=this.getAngleAxis(),t=this.getRadiusAxis().getExtent().slice();t[0]>t[1]&&t.reverse();var n=e.getExtent(),i=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:t[0],r:t[1],startAngle:-n[0]*i,endAngle:-n[1]*i,clockwise:e.inverse,contain:function(e,t){var n=e-this.cx,i=t-this.cy,a=n*n+i*i-1e-4,r=this.r,o=this.r0;return a<=r*r&&a>=o*o}}},e.prototype.convertToPixel=function(e,t,n){return KN(t)===this?this.dataToPoint(n):null},e.prototype.convertFromPixel=function(e,t,n){return KN(t)===this?this.pointToData(n):null},e}();function KN(e){var t=e.seriesModel,n=e.polarModel;return n&&n.coordinateSystem||t&&t.coordinateSystem}function YN(e,t){var n=this,i=n.getAngleAxis(),a=n.getRadiusAxis();if(i.scale.setExtent(1/0,-1/0),a.scale.setExtent(1/0,-1/0),e.eachSeries((function(e){if(e.coordinateSystem===n){var t=e.getData();zr(sI(t,"radius"),(function(e){a.scale.unionExtentFromData(t,e)})),zr(sI(t,"angle"),(function(e){i.scale.unionExtentFromData(t,e)}))}})),eI(i.scale,i.model),eI(a.scale,a.model),"category"===i.type&&!i.onBand){var r=i.getExtent(),o=360/i.scale.count();i.inverse?r[1]+=o:r[1]-=o,i.setExtent(r[0],r[1])}}function XN(e,t){var n;if(e.type=t.get("type"),e.scale=tI(t),e.onBand=t.get("boundaryGap")&&"category"===e.type,e.inverse=t.get("inverse"),function(e){return"angleAxis"===e.mainType}(t)){e.inverse=e.inverse!==t.get("clockwise");var i=t.get("startAngle"),a=null!==(n=t.get("endAngle"))&&void 0!==n?n:i+(e.inverse?-360:360);e.setExtent(i,a)}t.axis=e,e.model=t}var ZN={dimensions:WN,create:function(e,t){var n=[];return e.eachComponent("polar",(function(e,i){var a=new $N(i+"");a.update=YN;var r=a.getRadiusAxis(),o=a.getAngleAxis(),s=e.findAxisModel("radiusAxis"),l=e.findAxisModel("angleAxis");XN(r,s),XN(o,l),function(e,t,n){var i=t.get("center"),a=n.getWidth(),r=n.getHeight();e.cx=Wc(i[0],a),e.cy=Wc(i[1],r);var o=e.getRadiusAxis(),s=Math.min(a,r)/2,l=t.get("radius");null==l?l=[0,"100%"]:Xr(l)||(l=[0,l]);var p=[Wc(l[0],s),Wc(l[1],s)];o.inverse?o.setExtent(p[1],p[0]):o.setExtent(p[0],p[1])}(a,e,t),n.push(a),e.coordinateSystem=a,a.model=e})),e.eachSeries((function(e){if("polar"===e.get("coordinateSystem")){var t=e.getReferringComponents("polar",Fd).models[0];0,e.coordinateSystem=t.coordinateSystem}})),n}},QN=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function JN(e,t,n){t[1]>t[0]&&(t=t.slice().reverse());var i=e.coordToPoint([t[0],n]),a=e.coordToPoint([t[1],n]);return{x1:i[0],y1:i[1],x2:a[0],y2:a[1]}}function eL(e){return e.getRadiusAxis().inverse?0:1}function tL(e){var t=e[0],n=e[e.length-1];t&&n&&Math.abs(Math.abs(t.coord-n.coord)-360)<1e-4&&e.pop()}var nL=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.axisPointerClass="PolarAxisPointer",n}return We(t,e),t.prototype.render=function(e,t){if(this.group.removeAll(),e.get("show")){var n=e.axis,i=n.polar,a=i.getRadiusAxis().getExtent(),r=n.getTicksCoords(),o=n.getMinorTicksCoords(),s=Ur(n.getViewLabels(),(function(e){e=Ar(e);var t=n.scale,i="ordinal"===t.type?t.getRawOrdinalNumber(e.tickValue):e.tickValue;return e.coord=n.dataToCoord(i),e}));tL(s),tL(r),zr(QN,(function(t){!e.get([t,"show"])||n.scale.isBlank()&&"axisLine"!==t||iL[t](this.group,e,i,r,o,a,s)}),this)}},t.type="angleAxis",t}(Bk),iL={axisLine:function(e,t,n,i,a,r){var o,s=t.getModel(["axisLine","lineStyle"]),l=n.getAngleAxis(),p=Math.PI/180,c=l.getExtent(),d=eL(n),u=d?0:1,m=360===Math.abs(c[1]-c[0])?"Circle":"Arc";(o=0===r[u]?new Hf[m]({shape:{cx:n.cx,cy:n.cy,r:r[d],startAngle:-c[0]*p,endAngle:-c[1]*p,clockwise:l.inverse},style:s.getLineStyle(),z2:1,silent:!0}):new Ag({shape:{cx:n.cx,cy:n.cy,r:r[d],r0:r[u]},style:s.getLineStyle(),z2:1,silent:!0})).style.fill=null,e.add(o)},axisTick:function(e,t,n,i,a,r){var o=t.getModel("axisTick"),s=(o.get("inside")?-1:1)*o.get("length"),l=r[eL(n)],p=Ur(i,(function(e){return new Gg({shape:JN(n,[l,l+s],e.coord)})}));e.add(Ef(p,{style:Nr(o.getModel("lineStyle").getLineStyle(),{stroke:t.get(["axisLine","lineStyle","color"])})}))},minorTick:function(e,t,n,i,a,r){if(a.length){for(var o=t.getModel("axisTick"),s=t.getModel("minorTick"),l=(o.get("inside")?-1:1)*s.get("length"),p=r[eL(n)],c=[],d=0;dh?"left":"right",y=Math.abs(m[1]-g)/u<.3?"middle":m[1]>g?"top":"bottom";if(s&&s[d]){var v=s[d];to(v)&&v.textStyle&&(o=new xy(v.textStyle,l,l.ecModel))}var x=new Nm({silent:_k.isLabelSilent(t),style:Zf(o,{x:m[0],y:m[1],fill:o.getTextColor()||t.get(["axisLine","lineStyle","color"]),text:i.formattedLabel,align:f,verticalAlign:y})});if(e.add(x),c){var b=_k.makeAxisEventDataBase(t);b.targetType="axisLabel",b.value=i.rawLabel,Xm(x).eventData=b}}),this)},splitLine:function(e,t,n,i,a,r){var o=t.getModel("splitLine").getModel("lineStyle"),s=o.get("color"),l=0;s=s instanceof Array?s:[s];for(var p=[],c=0;c=0?"p":"n",I=b;v&&(i[s][_]||(i[s][_]={p:b,n:b}),I=i[s][_][T]);var E=void 0,M=void 0,k=void 0,P=void 0;if("radius"===d.dim){var D=d.dataToCoord(C)-b,O=r.dataToCoord(_);Math.abs(D)=P})}}}))}var dL={startAngle:90,clockwise:!0,splitNumber:12,axisLabel:{rotate:0}},uL={splitNumber:5},mL=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="polar",t}(ww);function hL(e){dI(RN),Bk.registerAxisPointerClass("PolarAxisPointer",NN),e.registerCoordinateSystem("polar",ZN),e.registerComponentModel(VN),e.registerComponentView(mL),sk(e,"angle",GN,dL),sk(e,"radius",zN,uL),e.registerComponentView(nL),e.registerComponentView(oL),e.registerLayout(Yr(cL,"bar"))}function gL(e,t){t=t||{};var n=e.coordinateSystem,i=e.axis,a={},r=i.position,o=i.orient,s=n.getRect(),l=[s.x,s.x+s.width,s.y,s.y+s.height],p={horizontal:{top:l[2],bottom:l[3]},vertical:{left:l[0],right:l[1]}};a.position=["vertical"===o?p.vertical[r]:l[0],"horizontal"===o?p.horizontal[r]:l[3]];a.rotation=Math.PI/2*{horizontal:0,vertical:1}[o];a.labelDirection=a.tickDirection=a.nameDirection={top:-1,bottom:1,right:1,left:-1}[r],e.get(["axisTick","inside"])&&(a.tickDirection=-a.tickDirection),po(t.labelInside,e.get(["axisLabel","inside"]))&&(a.labelDirection=-a.labelDirection);var c=t.rotate;return null==c&&(c=e.get(["axisLabel","rotate"])),a.labelRotation="top"===r?-c:c,a.z2=1,a}var fL=["axisLine","axisTickLabel","axisName"],yL=["splitArea","splitLine"],vL=(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.axisPointerClass="SingleAxisPointer",n}We(t,e),t.prototype.render=function(t,n,i,a){var r=this.group;r.removeAll();var o=this._axisGroup;this._axisGroup=new Rc;var s=gL(t),l=new _k(t,s);zr(fL,l.add,l),r.add(this._axisGroup),r.add(l.getGroup()),zr(yL,(function(e){t.get([e,"show"])&&vL[e](this,this.group,this._axisGroup,t)}),this),Rf(o,this._axisGroup,t),e.prototype.render.call(this,t,n,i,a)},t.prototype.remove=function(){Vk(this)},t.type="singleAxis"}(Bk),{splitLine:function(e,t,n,i){var a=i.axis;if(!a.scale.isBlank()){var r=i.getModel("splitLine"),o=r.getModel("lineStyle"),s=o.get("color");s=s instanceof Array?s:[s];for(var l=o.get("width"),p=i.coordinateSystem.getRect(),c=a.isHorizontal(),d=[],u=0,m=a.getTicksCoords({tickModel:r}),h=[],g=[],f=0;f=t.y&&e[1]<=t.y+t.height:n.contain(n.toLocalCoord(e[1]))&&e[0]>=t.y&&e[0]<=t.y+t.height},e.prototype.pointToData=function(e){var t=this.getAxis();return[t.coordToData(t.toLocalCoord(e["horizontal"===t.orient?0:1]))]},e.prototype.dataToPoint=function(e){var t=this.getAxis(),n=this.getRect(),i=[],a="horizontal"===t.orient?0:1;return e instanceof Array&&(e=e[0]),i[a]=t.toGlobalCoord(t.dataToCoord(+e)),i[1-a]=0===a?n.y+n.height/2:n.x+n.width/2,i},e.prototype.convertToPixel=function(e,t,n){return SL(t)===this?this.dataToPoint(n):null},e.prototype.convertFromPixel=function(e,t,n){return SL(t)===this?this.pointToData(n):null}}();function SL(e){var t=e.seriesModel,n=e.singleAxisModel;return n&&n.coordinateSystem||t&&t.coordinateSystem}var CL=["x","y"],_L=["width","height"],TL=(function(e){function t(){return null!==e&&e.apply(this,arguments)||this}We(t,e),t.prototype.makeElOption=function(e,t,n,i,a){var r=n.axis,o=r.coordinateSystem,s=EL(o,1-IL(r)),l=o.dataToPoint(t)[0],p=i.get("type");if(p&&"none"!==p){var c=sN(i),d=TL[p](r,l,s);d.style=c,e.graphicKey=d.type,e.pointer=d}dN(t,e,gL(n),n,i,a)},t.prototype.getHandleTransform=function(e,t,n){var i=gL(t,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var a=cN(t.axis,e,i);return{x:a[0],y:a[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},t.prototype.updateHandleTransform=function(e,t,n,i){var a=n.axis,r=a.coordinateSystem,o=IL(a),s=EL(r,o),l=[e.x,e.y];l[o]+=t[o],l[o]=Math.min(s[1],l[o]),l[o]=Math.max(s[0],l[o]);var p=EL(r,1-o),c=(p[1]+p[0])/2,d=[c,c];return d[o]=l[o],{x:l[0],y:l[1],rotation:e.rotation,cursorPoint:d,tooltipOption:{verticalAlign:"middle"}}}}(tN),{line:function(e,t,n){return{type:"Line",subPixelOptimize:!0,shape:uN([t,n[0]],[t,n[1]],IL(e))}},shadow:function(e,t,n){var i=e.getBandWidth(),a=n[1]-n[0];return{type:"Rect",shape:mN([t-i/2,n[0]],[i,a],IL(e))}}});function IL(e){return e.isHorizontal()?0:1}function EL(e,t){var n=e.getRect();return[n[CL[t]],n[CL[t]]+n[_L[t]]]}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.type="single"}(ww);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.init=function(t,n,i){var a=Iv(t);e.prototype.init.apply(this,arguments),ML(t,a)},t.prototype.mergeOption=function(t){e.prototype.mergeOption.apply(this,arguments),ML(this.option,t)},t.prototype.getCellSize=function(){return this.option.cellSize},t.type="calendar",t.defaultOption={z:2,left:80,top:60,cellSize:20,orient:"horizontal",splitLine:{show:!0,lineStyle:{color:"#000",width:1,type:"solid"}},itemStyle:{color:"#fff",borderWidth:1,borderColor:"#ccc"},dayLabel:{show:!0,firstDay:0,position:"start",margin:"50%",color:"#000"},monthLabel:{show:!0,position:"start",margin:5,align:"center",formatter:null,color:"#000"},yearLabel:{show:!0,position:null,margin:30,formatter:null,color:"#ccc",fontFamily:"sans-serif",fontWeight:"bolder",fontSize:20}}}(kv);function ML(e,t){var n,i=e.cellSize;1===(n=Xr(i)?i:e.cellSize=[i,i]).length&&(n[1]=n[0]);var a=Ur([0,1],(function(e){return function(e,t){return null!=e[xv[t][0]]||null!=e[xv[t][1]]&&null!=e[xv[t][2]]}(t,e)&&(n[e]="auto"),null!=n[e]&&"auto"!==n[e]}));Tv(e,t,{type:"box",ignoreSize:a})}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.render=function(e,t,n){var i=this.group;i.removeAll();var a=e.coordinateSystem,r=a.getRangeInfo(),o=a.getOrient(),s=t.getLocaleModel();this._renderDayRect(e,r,i),this._renderLines(e,r,o,i),this._renderYearText(e,r,o,i),this._renderMonthText(e,s,o,i),this._renderWeekText(e,s,r,o,i)},t.prototype._renderDayRect=function(e,t,n){for(var i=e.coordinateSystem,a=e.getModel("itemStyle").getItemStyle(),r=i.getCellWidth(),o=i.getCellHeight(),s=t.start.time;s<=t.end.time;s=i.getNextNDay(s,1).time){var l=i.dataToRect([s],!1).tl,p=new Fm({shape:{x:l[0],y:l[1],width:r,height:o},cursor:"default",style:a});n.add(p)}},t.prototype._renderLines=function(e,t,n,i){var a=this,r=e.coordinateSystem,o=e.getModel(["splitLine","lineStyle"]).getLineStyle(),s=e.get(["splitLine","show"]),l=o.lineWidth;this._tlpoints=[],this._blpoints=[],this._firstDayOfMonth=[],this._firstDayPoints=[];for(var p=t.start,c=0;p.time<=t.end.time;c++){u(p.formatedDate),0===c&&(p=r.getDateInfo(t.start.y+"-"+t.start.m));var d=p.date;d.setMonth(d.getMonth()+1),p=r.getDateInfo(d)}function u(t){a._firstDayOfMonth.push(r.getDateInfo(t)),a._firstDayPoints.push(r.dataToRect([t],!1).tl);var l=a._getLinePointsOfOneWeek(e,t,n);a._tlpoints.push(l[0]),a._blpoints.push(l[l.length-1]),s&&a._drawSplitline(l,o,i)}u(r.getNextNDay(t.end.time,1).formatedDate),s&&this._drawSplitline(a._getEdgesPoints(a._tlpoints,l,n),o,i),s&&this._drawSplitline(a._getEdgesPoints(a._blpoints,l,n),o,i)},t.prototype._getEdgesPoints=function(e,t,n){var i=[e[0].slice(),e[e.length-1].slice()],a="horizontal"===n?0:1;return i[0][a]=i[0][a]-t/2,i[1][a]=i[1][a]+t/2,i},t.prototype._drawSplitline=function(e,t,n){var i=new Lg({z2:20,shape:{points:e},style:t});n.add(i)},t.prototype._getLinePointsOfOneWeek=function(e,t,n){for(var i=e.coordinateSystem,a=i.getDateInfo(t),r=[],o=0;o<7;o++){var s=i.getNextNDay(a.time,o),l=i.dataToRect([s.time],!1);r[2*s.day]=l.tl,r[2*s.day+1]=l["horizontal"===n?"bl":"tr"]}return r},t.prototype._formatterLabel=function(e,t){return Qr(e)&&e?(n=e,zr(t,(function(e,t){n=n.replace("{"+t+"}",i?os(e):e)})),n):Zr(e)?e(t):t.nameMap;var n,i},t.prototype._yearTextPositionControl=function(e,t,n,i,a){var r=t[0],o=t[1],s=["center","bottom"];"bottom"===i?(o+=a,s=["center","top"]):"left"===i?r-=a:"right"===i?(r+=a,s=["center","top"]):o-=a;var l=0;return"left"!==i&&"right"!==i||(l=Math.PI/2),{rotation:l,x:r,y:o,style:{align:s[0],verticalAlign:s[1]}}},t.prototype._renderYearText=function(e,t,n,i){var a=e.getModel("yearLabel");if(a.get("show")){var r=a.get("margin"),o=a.get("position");o||(o="horizontal"!==n?"top":"left");var s=[this._tlpoints[this._tlpoints.length-1],this._blpoints[0]],l=(s[0][0]+s[1][0])/2,p=(s[0][1]+s[1][1])/2,c="horizontal"===n?0:1,d={top:[l,s[c][1]],bottom:[l,s[1-c][1]],left:[s[1-c][0],p],right:[s[c][0],p]},u=t.start.y;+t.end.y>+t.start.y&&(u=u+"-"+t.end.y);var m=a.get("formatter"),h={start:t.start.y,end:t.end.y,nameMap:u},g=this._formatterLabel(m,h),f=new Nm({z2:30,style:Zf(a,{text:g})});f.attr(this._yearTextPositionControl(f,d[o],n,o,r)),i.add(f)}},t.prototype._monthTextPositionControl=function(e,t,n,i,a){var r="left",o="top",s=e[0],l=e[1];return"horizontal"===n?(l+=a,t&&(r="center"),"start"===i&&(o="bottom")):(s+=a,t&&(o="middle"),"start"===i&&(r="right")),{x:s,y:l,align:r,verticalAlign:o}},t.prototype._renderMonthText=function(e,t,n,i){var a=e.getModel("monthLabel");if(a.get("show")){var r=a.get("nameMap"),o=a.get("margin"),s=a.get("position"),l=a.get("align"),p=[this._tlpoints,this._blpoints];r&&!Qr(r)||(r&&(t=Py(r)||t),r=t.get(["time","monthAbbr"])||[]);var c="start"===s?0:1,d="horizontal"===n?0:1;o="start"===s?-o:o;for(var u="center"===l,m=0;m=i.start.time&&n.timeo.end.time&&e.reverse(),e},e.prototype._getRangeInfo=function(e){var t,n=[this.getDateInfo(e[0]),this.getDateInfo(e[1])];n[0].time>n[1].time&&(t=!0,n.reverse());var i=Math.floor(n[1].time/kL)-Math.floor(n[0].time/kL)+1,a=new Date(n[0].time),r=a.getDate(),o=n[1].date.getDate();a.setDate(r+i-1);var s=a.getDate();if(s!==o)for(var l=a.getTime()-n[1].time>0?1:-1;(s=a.getDate())!==o&&(a.getTime()-n[1].time)*l>0;)i-=l,a.setDate(s-l);var p=Math.floor((i+n[0].day+6)/7),c=t?1-p:p-1;return t&&n.reverse(),{range:[n[0].formatedDate,n[1].formatedDate],start:n[0],end:n[1],allDay:i,weeks:p,nthWeek:c,fweek:n[0].day,lweek:n[1].day}},e.prototype._getDateByWeeksAndDay=function(e,t,n){var i=this._getRangeInfo(n);if(e>i.weeks||0===e&&ti.lweek)return null;var a=7*(e-1)-i.fweek+t,r=new Date(i.start.time);return r.setDate(+i.start.d+a),this.getDateInfo(r)},e.create=function(t,n){var i=[];return t.eachComponent("calendar",(function(a){var r=new e(a,t,n);i.push(r),a.coordinateSystem=r})),t.eachSeries((function(e){"calendar"===e.get("coordinateSystem")&&(e.coordinateSystem=i[e.get("calendarIndex")||0])})),i},e.dimensions=["time","value"]}();function PL(e){var t=e.calendarModel,n=e.seriesModel;return t?t.coordinateSystem:n?n.coordinateSystem:null}function DL(e,t){var n;return zr(t,(function(t){null!=e[t]&&"auto"!==e[t]&&(n=!0)})),n}var OL=["transition","enterFrom","leaveTo"],AL=OL.concat(["enterAnimation","updateAnimation","leaveAnimation"]);function FL(e,t,n){if(n&&(!e[n]&&t[n]&&(e[n]={}),e=e[n],t=t[n]),e&&t)for(var i=n?OL:AL,a=0;a=0;l--){var u,m,h;if(h=null!=(m=Id((u=n[l]).id,null))?a.get(m):null){var g=h.parent,f=(d=BL(g),{}),y=Cv(h,u,g===i?{width:r,height:o}:{width:d.width,height:d.height},null,{hv:u.hv,boundingMode:u.bounding},f);if(!BL(h).isNew&&y){for(var v=u.transition,x={},b=0;b=0)?x[w]=S:h[w]=S}pf(h,x,e,0)}else h.attr(f)}}},t.prototype._clear=function(){var e=this,t=this._elMap;t.each((function(n){VL(n,BL(n).option,t,e._lastGraphicModel)})),this._elMap=Co()},t.prototype.dispose=function(){this._clear()},t.type="graphic"}(ww);function NL(e){var t=Eo(RL,e)?RL[e]:Cf(e);var n=new t({});return BL(n).type=e,n}function LL(e,t,n,i){var a=NL(n);return t.add(a),i.set(e,a),BL(a).id=e,BL(a).isNew=!0,a}function VL(e,t,n,i){e&&e.parent&&("group"===e.type&&e.traverse((function(e){VL(e,t,n,i)})),pB(e,t,i),n.removeKey(BL(e).id))}function qL(e,t,n,i){e.isGroup||zr([["cursor",bu.prototype.cursor],["zlevel",i||0],["z",n||0],["z2",0]],(function(n){var i=n[0];Eo(t,i)?e[i]=co(t[i],n[1]):null==e[i]&&(e[i]=n[1])})),zr($r(t),(function(n){if(0===n.indexOf("on")){var i=t[n];e[n]=Zr(i)?i:null}})),Eo(t,"draggable")&&(e.draggable=t.draggable),null!=t.name&&(e.name=t.name),null!=t.id&&(e.id=t.id)}var GL=["x","y","radius","angle","single"],zL=["cartesian2d","polar","singleAxis"];function UL(e){return e+"Axis"}function jL(e,t){var n,i=Co(),a=[],r=Co();e.eachComponent({mainType:"dataZoom",query:t},(function(e){r.get(e.uid)||s(e)}));do{n=!1,e.eachComponent("dataZoom",o)}while(n);function o(e){!r.get(e.uid)&&function(e){var t=!1;return e.eachTargetAxis((function(e,n){var a=i.get(e);a&&a[n]&&(t=!0)})),t}(e)&&(s(e),n=!0)}function s(e){r.set(e.uid,!0),a.push(e),e.eachTargetAxis((function(e,t){(i.get(e)||i.set(e,[]))[t]=!0}))}return a}function HL(e){var t=e.ecModel,n={infoList:[],infoMap:Co()};return e.eachTargetAxis((function(e,i){var a=t.getComponent(UL(e),i);if(a){var r=a.getCoordSysModel();if(r){var o=r.uid,s=n.infoMap.get(o);s||(s={model:r,axisModels:[]},n.infoList.push(s),n.infoMap.set(o,s)),s.axisModels.push(a)}}})),n}var WL=function(){function e(){this.indexList=[],this.indexMap=[]}return e.prototype.add=function(e){this.indexMap[e]||(this.indexList.push(e),this.indexMap[e]=!0)},e}(),$L=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._autoThrottle=!0,n._noTarget=!0,n._rangePropMode=["percent","percent"],n}return We(t,e),t.prototype.init=function(e,t,n){var i=KL(e);this.settledOption=i,this.mergeDefaultAndTheme(e,n),this._doInit(i)},t.prototype.mergeOption=function(e){var t=KL(e);Fr(this.option,e,!0),Fr(this.settledOption,t,!0),this._doInit(t)},t.prototype._doInit=function(e){var t=this.option;this._setDefaultThrottle(e),this._updateRangeUse(e);var n=this.settledOption;zr([["start","startValue"],["end","endValue"]],(function(e,i){"value"===this._rangePropMode[i]&&(t[e[0]]=n[e[0]]=null)}),this),this._resetTarget()},t.prototype._resetTarget=function(){var e=this.get("orient",!0),t=this._targetAxisInfoMap=Co();this._fillSpecifiedTargetAxis(t)?this._orient=e||this._makeAutoOrientByTargetAxis():(this._orient=e||"horizontal",this._fillAutoTargetAxisByOrient(t,this._orient)),this._noTarget=!0,t.each((function(e){e.indexList.length&&(this._noTarget=!1)}),this)},t.prototype._fillSpecifiedTargetAxis=function(e){var t=!1;return zr(GL,(function(n){var i=this.getReferringComponents(UL(n),Rd);if(i.specified){t=!0;var a=new WL;zr(i.models,(function(e){a.add(e.componentIndex)})),e.set(n,a)}}),this),t},t.prototype._fillAutoTargetAxisByOrient=function(e,t){var n=this.ecModel,i=!0;if(i){var a="vertical"===t?"y":"x";r(n.findComponents({mainType:a+"Axis"}),a)}i&&r(n.findComponents({mainType:"singleAxis",filter:function(e){return e.get("orient",!0)===t}}),"single");function r(t,n){var a=t[0];if(a){var r=new WL;if(r.add(a.componentIndex),e.set(n,r),i=!1,"x"===n||"y"===n){var o=a.getReferringComponents("grid",Fd).models[0];o&&zr(t,(function(e){a.componentIndex!==e.componentIndex&&o===e.getReferringComponents("grid",Fd).models[0]&&r.add(e.componentIndex)}))}}}i&&zr(GL,(function(t){if(i){var a=n.findComponents({mainType:UL(t),filter:function(e){return"category"===e.get("type",!0)}});if(a[0]){var r=new WL;r.add(a[0].componentIndex),e.set(t,r),i=!1}}}),this)},t.prototype._makeAutoOrientByTargetAxis=function(){var e;return this.eachTargetAxis((function(t){!e&&(e=t)}),this),"y"===e?"vertical":"horizontal"},t.prototype._setDefaultThrottle=function(e){if(e.hasOwnProperty("throttle")&&(this._autoThrottle=!1),this._autoThrottle){var t=this.ecModel.option;this.option.throttle=t.animation&&t.animationDurationUpdate>0?100:20}},t.prototype._updateRangeUse=function(e){var t=this._rangePropMode,n=this.get("rangeMode");zr([["start","startValue"],["end","endValue"]],(function(i,a){var r=null!=e[i[0]],o=null!=e[i[1]];r&&!o?t[a]="percent":!r&&o?t[a]="value":n?t[a]=n[a]:r&&(t[a]="percent")}))},t.prototype.noTarget=function(){return this._noTarget},t.prototype.getFirstTargetAxisModel=function(){var e;return this.eachTargetAxis((function(t,n){null==e&&(e=this.ecModel.getComponent(UL(t),n))}),this),e},t.prototype.eachTargetAxis=function(e,t){this._targetAxisInfoMap.each((function(n,i){zr(n.indexList,(function(n){e.call(t,i,n)}))}))},t.prototype.getAxisProxy=function(e,t){var n=this.getAxisModel(e,t);if(n)return n.__dzAxisProxy},t.prototype.getAxisModel=function(e,t){var n=this._targetAxisInfoMap.get(e);if(n&&n.indexMap[t])return this.ecModel.getComponent(UL(e),t)},t.prototype.setRawRange=function(e){var t=this.option,n=this.settledOption;zr([["start","startValue"],["end","endValue"]],(function(i){null==e[i[0]]&&null==e[i[1]]||(t[i[0]]=n[i[0]]=e[i[0]],t[i[1]]=n[i[1]]=e[i[1]])}),this),this._updateRangeUse(e)},t.prototype.setCalculatedRange=function(e){var t=this.option;zr(["start","startValue","end","endValue"],(function(n){t[n]=e[n]}))},t.prototype.getPercentRange=function(){var e=this.findRepresentativeAxisProxy();if(e)return e.getDataPercentWindow()},t.prototype.getValueRange=function(e,t){var n;if(null==e&&null==t){if(n=this.findRepresentativeAxisProxy())return n.getDataValueWindow()}else if(n=this.getAxisProxy(e,t))return n.getDataValueWindow()},t.prototype.findRepresentativeAxisProxy=function(e){if(e)return e.__dzAxisProxy;for(var t,n=this._targetAxisInfoMap.keys(),i=0;i=0}(t)){var n=UL(this._dimName),i=t.getReferringComponents(n,Fd).models[0];i&&this._axisIndex===i.componentIndex&&e.push(t)}}),this),e},e.prototype.getAxisModel=function(){return this.ecModel.getComponent(this._dimName+"Axis",this._axisIndex)},e.prototype.getMinMaxSpan=function(){return Ar(this._minMaxSpan)},e.prototype.calculateDataWindow=function(e){var t,n=this._dataExtent,i=this.getAxisModel().axis.scale,a=this._dataZoomModel.getRangePropMode(),r=[0,100],o=[],s=[];XL(["start","end"],(function(l,p){var c=e[l],d=e[l+"Value"];"percent"===a[p]?(null==c&&(c=r[p]),d=i.parse(Hc(c,r,n))):(t=!0,c=Hc(d=null==d?n[p]:i.parse(d),n,r)),s[p]=null==d||isNaN(d)?n[p]:d,o[p]=null==c||isNaN(c)?r[p]:c})),ZL(s),ZL(o);var l=this._minMaxSpan;function p(e,t,n,a,r){var o=r?"Span":"ValueSpan";EA(0,e,n,"all",l["min"+o],l["max"+o]);for(var s=0;s<2;s++)t[s]=Hc(e[s],n,a,!0),r&&(t[s]=i.parse(t[s]))}return t?p(s,o,n,r,!1):p(o,s,r,n,!0),{valueWindow:s,percentWindow:o}},e.prototype.reset=function(e){if(e===this._dataZoomModel){var t=this.getTargetSeriesModels();this._dataExtent=function(e,t,n){var i=[1/0,-1/0];XL(n,(function(e){!function(e,t,n){t&&zr(sI(t,n),(function(n){var i=t.getApproximateExtent(n);i[0]e[1]&&(e[1]=i[1])}))}(i,e.getData(),t)}));var a=e.getAxisModel(),r=ZT(a.axis.scale,a,i).calculate();return[r.min,r.max]}(this,this._dimName,t),this._updateMinMaxSpan();var n=this.calculateDataWindow(e.settledOption);this._valueWindow=n.valueWindow,this._percentWindow=n.percentWindow,this._setAxisModel()}},e.prototype.filterData=function(e,t){if(e===this._dataZoomModel){var n=this._dimName,i=this.getTargetSeriesModels(),a=e.get("filterMode"),r=this._valueWindow;"none"!==a&&XL(i,(function(e){var t=e.getData(),i=t.mapDimensionsAll(n);if(i.length){if("weakFilter"===a){var o=t.getStore(),s=Ur(i,(function(e){return t.getDimensionIndex(e)}),t);t.filterSelf((function(e){for(var t,n,a,l=0;lr[1];if(c&&!d&&!u)return!0;c&&(a=!0),d&&(t=!0),u&&(n=!0)}return a&&t&&n}))}else XL(i,(function(n){if("empty"===a)e.setData(t=t.map(n,(function(e){return function(e){return e>=r[0]&&e<=r[1]}(e)?e:NaN})));else{var i={};i[n]=r,t.selectRange(i)}}));XL(i,(function(e){t.setApproximateExtent(r,e)}))}}))}},e.prototype._updateMinMaxSpan=function(){var e=this._minMaxSpan={},t=this._dataZoomModel,n=this._dataExtent;XL(["min","max"],(function(i){var a=t.get(i+"Span"),r=t.get(i+"ValueSpan");null!=r&&(r=this.getAxisModel().axis.scale.parse(r)),null!=r?a=Hc(n[0]+r,n,[0,100],!0):null!=a&&(r=Hc(a,[0,100],n,!0)-n[0]),e[i+"Span"]=a,e[i+"ValueSpan"]=r}),this)},e.prototype._setAxisModel=function(){var e=this.getAxisModel(),t=this._percentWindow,n=this._valueWindow;if(t){var i=Zc(n,[0,500]);i=Math.min(i,20);var a=e.axis.scale.rawExtentInfo;0!==t[0]&&a.setDeterminedMinMax("min",+n[0].toFixed(i)),100!==t[1]&&a.setDeterminedMinMax("max",+n[1].toFixed(i)),a.freeze()}},e}();var JL={getTargetSeries:function(e){function t(t){e.eachComponent("dataZoom",(function(n){n.eachTargetAxis((function(i,a){var r=e.getComponent(UL(i),a);t(i,a,r,n)}))}))}t((function(e,t,n,i){n.__dzAxisProxy=null}));var n=[];t((function(t,i,a,r){a.__dzAxisProxy||(a.__dzAxisProxy=new QL(t,i,r,e),n.push(a.__dzAxisProxy))}));var i=Co();return zr(n,(function(e){zr(e.getTargetSeriesModels(),(function(e){i.set(e.uid,e)}))})),i},overallReset:function(e,t){e.eachComponent("dataZoom",(function(e){e.eachTargetAxis((function(t,n){var i=e.getAxisProxy(t,n);i&&i.reset(e)})),e.eachTargetAxis((function(n,i){var a=e.getAxisProxy(n,i);a&&a.filterData(e,t)}))})),e.eachComponent("dataZoom",(function(e){var t=e.findRepresentativeAxisProxy();if(t){var n=t.getDataPercentWindow(),i=t.getDataValueWindow();e.setCalculatedRange({start:n[0],end:n[1],startValue:i[0],endValue:i[1]})}}))}};var eV=!1;function tV(e){eV||(eV=!0,e.registerProcessor(e.PRIORITY.PROCESSOR.FILTER,JL),function(e){e.registerAction("dataZoom",(function(e,t){zr(jL(t,e),(function(t){t.setRawRange({start:e.start,end:e.end,startValue:e.startValue,endValue:e.endValue})}))}))}(e),e.registerSubTypeDefaulter("dataZoom",(function(){return"slider"})))}var nV=function(){},iV={};function aV(e){return iV[e]}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.optionUpdated=function(){e.prototype.optionUpdated.apply(this,arguments);var t=this.ecModel;zr(this.option.feature,(function(e,n){var i=aV(n);i&&(i.getDefaultOption&&(i.defaultOption=i.getDefaultOption(t)),Fr(e,i.defaultOption))}))},t.type="toolbox",t.layoutMode={type:"box",ignoreSize:!0},t.defaultOption={show:!0,z:6,orient:"horizontal",left:"right",top:"top",backgroundColor:"transparent",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemSize:15,itemGap:8,showTitle:!0,iconStyle:{borderColor:"#666",color:"none"},emphasis:{iconStyle:{borderColor:"#3E98C5"}},tooltip:{show:!1,position:"bottom"}}}(kv);function rV(e,t){var n=pv(t.get("padding")),i=t.getItemStyle(["color","opacity"]);return i.fill=t.get("backgroundColor"),e=new Fm({shape:{x:e.x-n[3],y:e.y-n[0],width:e.width+n[1]+n[3],height:e.height+n[0]+n[2],r:t.get("borderRadius")},style:i,silent:!0,z2:-1})}!function(e){function t(){return null!==e&&e.apply(this,arguments)||this}We(t,e),t.prototype.render=function(e,t,n,i){var a=this.group;if(a.removeAll(),e.get("show")){var r=+e.get("itemSize"),o="vertical"===e.get("orient"),s=e.get("feature")||{},l=this._features||(this._features={}),p=[];zr(s,(function(e,t){p.push(t)})),new S_(this._featureNames||[],p).add(c).update(c).remove(Yr(c,null)).execute(),this._featureNames=p,function(e,t,n){var i=t.getBoxLayoutParams(),a=t.get("padding"),r={width:n.getWidth(),height:n.getHeight()},o=Sv(i,r,a);wv(t.get("orient"),e,t.get("itemGap"),o.width,o.height),Cv(e,i,r,a)}(a,e,n),a.add(rV(a.getBoundingRect(),e)),o||a.eachChild((function(e){var t=e.__title,i=e.ensureState("emphasis"),o=i.textConfig||(i.textConfig={}),s=e.getTextContent(),l=s&&s.ensureState("emphasis");if(l&&!Zr(l)&&t){var p=l.style||(l.style={}),c=xc(t,Nm.makeFont(p)),d=e.x+a.x,u=!1;e.y+a.y+r+c.height>n.getHeight()&&(o.position="top",u=!0);var m=u?-5-c.height:r+10;d+c.width/2>n.getWidth()?(o.position=["100%",m],p.align="right"):d-c.width/2<0&&(o.position=[0,m],p.align="left")}}))}function c(c,d){var u,m=p[c],h=p[d],g=s[m],f=new xy(g,e,e.ecModel);if(i&&null!=i.newTitle&&i.featureName===m&&(g.title=i.newTitle),m&&!h){if(function(e){return 0===e.indexOf("my")}(m))u={onclick:f.option.onclick,featureName:m};else{var y=aV(m);if(!y)return;u=new y}l[m]=u}else if(!(u=l[h]))return;u.uid=wy("toolbox-feature"),u.model=f,u.ecModel=t,u.api=n;var v=u instanceof nV;m||!h?!f.get("show")||v&&u.unusable?v&&u.remove&&u.remove(t,n):(!function(i,s,l){var p,c,d=i.getModel("iconStyle"),u=i.getModel(["emphasis","iconStyle"]),m=s instanceof nV&&s.getIcons?s.getIcons():i.get("icon"),h=i.get("title")||{};Qr(m)?(p={})[l]=m:p=m;Qr(h)?(c={})[l]=h:c=h;var g=i.iconPaths={};zr(p,(function(l,p){var m=Lf(l,{},{x:-r/2,y:-r/2,width:r,height:r});m.setStyle(d.getItemStyle()),m.ensureState("emphasis").style=u.getItemStyle();var h=new Nm({style:{text:c[p],align:u.get("textAlign"),borderRadius:u.get("textBorderRadius"),padding:u.get("textPadding"),fill:null,font:iy({fontStyle:u.get("textFontStyle"),fontFamily:u.get("textFontFamily"),fontSize:u.get("textFontSize"),fontWeight:u.get("textFontWeight")},t)},ignore:!0});m.setTextContent(h),zf({el:m,componentModel:e,itemName:p,formatterParamsExtra:{title:c[p]}}),m.__title=c[p],m.on("mouseover",(function(){var t=u.getItemStyle(),i=o?null==e.get("right")&&"right"!==e.get("left")?"right":"left":null==e.get("bottom")&&"bottom"!==e.get("top")?"bottom":"top";h.setStyle({fill:u.get("textFill")||t.fill||t.stroke||"#000",backgroundColor:u.get("textBackgroundColor")}),m.setTextConfig({position:u.get("textPosition")||i}),h.ignore=!e.get("showTitle"),n.enterEmphasis(this)})).on("mouseout",(function(){"emphasis"!==i.get(["iconStatus",p])&&n.leaveEmphasis(this),h.hide()})),("emphasis"===i.get(["iconStatus",p])?Th:Ih)(m),a.add(m),m.on("click",Kr(s.onclick,s,t,n,p)),g[p]=m}))}(f,u,m),f.setIconStatus=function(e,t){var n=this.option,i=this.iconPaths;n.iconStatus=n.iconStatus||{},n.iconStatus[e]=t,i[e]&&("emphasis"===t?Th:Ih)(i[e])},u instanceof nV&&u.render&&u.render(f,t,n,i)):v&&u.dispose&&u.dispose(t,n)}},t.prototype.updateView=function(e,t,n,i){zr(this._features,(function(e){e instanceof nV&&e.updateView&&e.updateView(e.model,t,n,i)}))},t.prototype.remove=function(e,t){zr(this._features,(function(n){n instanceof nV&&n.remove&&n.remove(e,t)})),this.group.removeAll()},t.prototype.dispose=function(e,t){zr(this._features,(function(n){n instanceof nV&&n.dispose&&n.dispose(e,t)}))},t.type="toolbox"}(ww);!function(e){function t(){return null!==e&&e.apply(this,arguments)||this}We(t,e),t.prototype.onclick=function(e,t){var n=this.model,i=n.get("name")||e.get("title.0.text")||"echarts",a="svg"===t.getZr().painter.getType(),r=a?"svg":n.get("type",!0)||"png",o=t.getConnectedDataURL({type:r,backgroundColor:n.get("backgroundColor",!0)||e.get("backgroundColor")||"#fff",connectedBackgroundColor:n.get("connectedBackgroundColor"),excludeComponents:n.get("excludeComponents"),pixelRatio:n.get("pixelRatio")}),s=ur.browser;if(Zr(MouseEvent)&&(s.newEdge||!s.ie&&!s.edge)){var l=document.createElement("a");l.download=i+"."+r,l.target="_blank",l.href=o;var p=new MouseEvent("click",{view:document.defaultView,bubbles:!0,cancelable:!1});l.dispatchEvent(p)}else if(window.navigator.msSaveOrOpenBlob||a){var c=o.split(","),d=c[0].indexOf("base64")>-1,u=a?decodeURIComponent(c[1]):c[1];d&&(u=window.atob(u));var m=i+"."+r;if(window.navigator.msSaveOrOpenBlob){for(var h=u.length,g=new Uint8Array(h);h--;)g[h]=u.charCodeAt(h);var f=new Blob([g]);window.navigator.msSaveOrOpenBlob(f,m)}else{var y=document.createElement("iframe");document.body.appendChild(y);var v=y.contentWindow,x=v.document;x.open("image/svg+xml","replace"),x.write(u),x.close(),v.focus(),x.execCommand("SaveAs",!0,m),document.body.removeChild(y)}}else{var b=n.get("lang"),w='',S=window.open();S.document.write(w),S.document.title=i}},t.getDefaultOption=function(e){return{show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:e.getLocaleModel().get(["toolbox","saveAsImage","title"]),type:"png",connectedBackgroundColor:"#fff",name:"",excludeComponents:["toolbox"],lang:e.getLocaleModel().get(["toolbox","saveAsImage","lang"])}}}(nV);var oV="__ec_magicType_stack__",sV=[["line","bar"],["stack"]],lV=(function(e){function t(){return null!==e&&e.apply(this,arguments)||this}We(t,e),t.prototype.getIcons=function(){var e=this.model,t=e.get("icon"),n={};return zr(e.get("type"),(function(e){t[e]&&(n[e]=t[e])})),n},t.getDefaultOption=function(e){return{show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z"},title:e.getLocaleModel().get(["toolbox","magicType","title"]),option:{},seriesIndex:{}}},t.prototype.onclick=function(e,t,n){var i=this.model,a=i.get(["seriesIndex",n]);if(lV[n]){var r,o={series:[]};zr(sV,(function(e){Lr(e,n)>=0&&zr(e,(function(e){i.setIconStatus(e,"normal")}))})),i.setIconStatus(n,"emphasis"),e.eachComponent({mainType:"series",query:null==a?null:{seriesIndex:a}},(function(e){var t=e.subType,a=e.id,r=lV[n](t,a,e,i);r&&(Nr(r,e.option),o.series.push(r));var s=e.coordinateSystem;if(s&&"cartesian2d"===s.type&&("line"===n||"bar"===n)){var l=s.getAxesByScale("ordinal")[0];if(l){var p=l.dim+"Axis",c=e.getReferringComponents(p,Fd).models[0].componentIndex;o[p]=o[p]||[];for(var d=0;d<=c;d++)o[p][c]=o[p][c]||{};o[p][c].boundaryGap="bar"===n}}}));var s=n;"stack"===n&&(r=Fr({stack:i.option.title.tiled,tiled:i.option.title.stack},i.option.title),"emphasis"!==i.get(["iconStatus",n])&&(s="tiled")),t.dispatchAction({type:"changeMagicType",currentType:s,newOption:o,newTitle:r,featureName:"magicType"})}}}(nV),{line:function(e,t,n,i){if("bar"===e)return Fr({id:t,type:"line",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","line"])||{},!0)},bar:function(e,t,n,i){if("line"===e)return Fr({id:t,type:"bar",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","bar"])||{},!0)},stack:function(e,t,n,i){var a=n.get("stack")===oV;if("line"===e||"bar"===e)return i.setIconStatus("stack",a?"normal":"emphasis"),Fr({id:t,stack:a?"":oV},i.get(["option","stack"])||{},!0)}});h_({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},(function(e,t){t.mergeOption(e.newOption)}));var pV=new Array(60).join("-"),cV="\t";function dV(e){return e.replace(/^\s\s*/,"").replace(/\s\s*$/,"")}var uV=new RegExp("[\t]+","g");function mV(e,t){var n=e.split(new RegExp("\n*"+pV+"\n*","g")),i={series:[]};return zr(n,(function(e,n){if(function(e){if(e.slice(0,e.indexOf("\n")).indexOf(cV)>=0)return!0}(e)){var a=function(e){for(var t=e.split(/\n+/g),n=[],i=Ur(dV(t.shift()).split(uV),(function(e){return{name:e,data:[]}})),a=0;a=0)&&e(a,i._targetInfoList)}))}return e.prototype.setOutputRanges=function(e,t){return this.matchOutputRanges(e,t,(function(e,t,n){if((e.coordRanges||(e.coordRanges=[])).push(t),!e.coordRange){e.coordRange=t;var i=TV[e.brushType](0,n,t);e.__rangeOffset={offset:EV[e.brushType](i.values,e.range,[1,1]),xyMinMax:i.xyMinMax}}})),e},e.prototype.matchOutputRanges=function(e,t,n){zr(e,(function(e){var i=this.findTargetInfo(e,t);i&&!0!==i&&zr(i.coordSyses,(function(i){var a=TV[e.brushType](1,i,e.range,!0);n(e,a.values,i,t)}))}),this)},e.prototype.setInputRanges=function(e,t){zr(e,(function(e){var n,i,a,r,o,s=this.findTargetInfo(e,t);if(e.range=e.range||[],s&&!0!==s){e.panelId=s.panelId;var l=TV[e.brushType](0,s.coordSys,e.coordRange),p=e.__rangeOffset;e.range=p?EV[e.brushType](l.values,p.offset,(n=l.xyMinMax,i=p.xyMinMax,a=kV(n),r=kV(i),o=[a[0]/r[0],a[1]/r[1]],isNaN(o[0])&&(o[0]=1),isNaN(o[1])&&(o[1]=1),o)):l.values}}),this)},e.prototype.makePanelOpts=function(e,t){return Ur(this._targetInfoList,(function(n){var i=n.getPanelRect();return{panelId:n.panelId,defaultBrushType:t?t(n):null,clipPath:EF(i),isTargetByCursor:kF(i,e,n.coordSysModel),getLinearBrushOtherExtent:MF(i)}}))},e.prototype.controlSeries=function(e,t,n){var i=this.findTargetInfo(e,n);return!0===i||i&&Lr(i.coordSyses,t.coordinateSystem)>=0},e.prototype.findTargetInfo=function(e,t){for(var n=this._targetInfoList,i=wV(t,e),a=0;ae[1]&&e.reverse(),e}function wV(e,t){return Od(e,t,{includeMainTypes:vV})}var SV={grid:function(e,t){var n=e.xAxisModels,i=e.yAxisModels,a=e.gridModels,r=Co(),o={},s={};(n||i||a)&&(zr(n,(function(e){var t=e.axis.grid.model;r.set(t.id,t),o[t.id]=!0})),zr(i,(function(e){var t=e.axis.grid.model;r.set(t.id,t),s[t.id]=!0})),zr(a,(function(e){r.set(e.id,e),o[e.id]=!0,s[e.id]=!0})),r.each((function(e){var a=e.coordinateSystem,r=[];zr(a.getCartesians(),(function(e,t){(Lr(n,e.getAxis("x").model)>=0||Lr(i,e.getAxis("y").model)>=0)&&r.push(e)})),t.push({panelId:"grid--"+e.id,gridModel:e,coordSysModel:e,coordSys:r[0],coordSyses:r,getPanelRect:_V.grid,xAxisDeclared:o[e.id],yAxisDeclared:s[e.id]})})))},geo:function(e,t){zr(e.geoModels,(function(e){var n=e.coordinateSystem;t.push({panelId:"geo--"+e.id,geoModel:e,coordSysModel:e,coordSys:n,coordSyses:[n],getPanelRect:_V.geo})}))}},CV=[function(e,t){var n=e.xAxisModel,i=e.yAxisModel,a=e.gridModel;return!a&&n&&(a=n.axis.grid.model),!a&&i&&(a=i.axis.grid.model),a&&a===t.gridModel},function(e,t){var n=e.geoModel;return n&&n===t.geoModel}],_V={grid:function(){return this.coordSys.master.getRect().clone()},geo:function(){var e=this.coordSys,t=e.getBoundingRect().clone();return t.applyTransform(Df(e)),t}},TV={lineX:Yr(IV,0),lineY:Yr(IV,1),rect:function(e,t,n,i){var a=e?t.pointToData([n[0][0],n[1][0]],i):t.dataToPoint([n[0][0],n[1][0]],i),r=e?t.pointToData([n[0][1],n[1][1]],i):t.dataToPoint([n[0][1],n[1][1]],i),o=[bV([a[0],r[0]]),bV([a[1],r[1]])];return{values:o,xyMinMax:o}},polygon:function(e,t,n,i){var a=[[1/0,-1/0],[1/0,-1/0]];return{values:Ur(n,(function(n){var r=e?t.pointToData(n,i):t.dataToPoint(n,i);return a[0][0]=Math.min(a[0][0],r[0]),a[1][0]=Math.min(a[1][0],r[1]),a[0][1]=Math.max(a[0][1],r[0]),a[1][1]=Math.max(a[1][1],r[1]),r})),xyMinMax:a}}};function IV(e,t,n,i){var a=n.getAxis(["x","y"][e]),r=bV(Ur([0,1],(function(e){return t?a.coordToData(a.toLocalCoord(i[e]),!0):a.toGlobalCoord(a.dataToCoord(i[e]))}))),o=[];return o[e]=r,o[1-e]=[NaN,NaN],{values:r,xyMinMax:o}}var EV={lineX:Yr(MV,0),lineY:Yr(MV,1),rect:function(e,t,n){return[[e[0][0]-n[0]*t[0][0],e[0][1]-n[0]*t[0][1]],[e[1][0]-n[1]*t[1][0],e[1][1]-n[1]*t[1][1]]]},polygon:function(e,t,n){return Ur(e,(function(e,i){return[e[0]-n[0]*t[i][0],e[1]-n[1]*t[i][1]]}))}};function MV(e,t,n,i){return[t[0]-i[e]*n[0],t[1]-i[e]*n[1]]}function kV(e){return e?[e[0][1]-e[0][0],e[1][1]-e[1][0]]:[NaN,NaN]}var PV,DV,OV=zr,AV=yd+"toolbox-dataZoom_",FV=(function(e){function t(){return null!==e&&e.apply(this,arguments)||this}We(t,e),t.prototype.render=function(e,t,n,i){this._brushController||(this._brushController=new XA(n.getZr()),this._brushController.on("brush",Kr(this._onBrush,this)).mount()),function(e,t,n,i,a){var r=n._isZoomActive;i&&"takeGlobalCursor"===i.type&&(r="dataZoomSelect"===i.key&&i.dataZoomSelectActive);n._isZoomActive=r,e.setIconStatus("zoom",r?"emphasis":"normal");var o=new xV(RV(e),t,{include:["grid"]}),s=o.makePanelOpts(a,(function(e){return e.xAxisDeclared&&!e.yAxisDeclared?"lineX":!e.xAxisDeclared&&e.yAxisDeclared?"lineY":"rect"}));n._brushController.setPanels(s).enableBrush(!(!r||!s.length)&&{brushType:"auto",brushStyle:e.getModel("brushStyle").getItemStyle()})}(e,t,this,i,n),function(e,t){e.setIconStatus("back",function(e){return yV(e).length}(t)>1?"emphasis":"normal")}(e,t)},t.prototype.onclick=function(e,t,n){FV[n].call(this)},t.prototype.remove=function(e,t){this._brushController&&this._brushController.unmount()},t.prototype.dispose=function(e,t){this._brushController&&this._brushController.dispose()},t.prototype._onBrush=function(e){var t=e.areas;if(e.isEnd&&t.length){var n={},i=this.ecModel;this._brushController.updateCovers([]),new xV(RV(this.model),i,{include:["grid"]}).matchOutputRanges(t,i,(function(e,t,n){if("cartesian2d"===n.type){var i=e.brushType;"rect"===i?(a("x",n,t[0]),a("y",n,t[1])):a({lineX:"x",lineY:"y"}[i],n,t)}})),function(e,t){var n=yV(e);gV(t,(function(t,i){for(var a=n.length-1;a>=0&&!n[a][i];a--);if(a<0){var r=e.queryComponents({mainType:"dataZoom",subType:"select",id:i})[0];if(r){var o=r.getPercentRange();n[0][i]={dataZoomId:i,start:o[0],end:o[1]}}}})),n.push(t)}(i,n),this._dispatchZoomAction(n)}function a(e,t,a){var r=t.getAxis(e),o=r.model,s=function(e,t,n){var i;return n.eachComponent({mainType:"dataZoom",subType:"select"},(function(n){n.getAxisModel(e,t.componentIndex)&&(i=n)})),i}(e,o,i),l=s.findRepresentativeAxisProxy(o);if(l){var p=l.getMinMaxSpan();null==p.minValueSpan&&null==p.maxValueSpan||(a=EA(0,a.slice(),r.scale.getExtent(),0,p.minValueSpan,p.maxValueSpan))}s&&(n[s.id]={dataZoomId:s.id,startValue:a[0],endValue:a[1]})}},t.prototype._dispatchZoomAction=function(e){var t=[];OV(e,(function(e,n){t.push(Ar(e))})),t.length&&this.api.dispatchAction({type:"dataZoom",from:this.uid,batch:t})},t.getDefaultOption=function(e){return{show:!0,filterMode:"filter",icon:{zoom:"M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1",back:"M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26"},title:e.getLocaleModel().get(["toolbox","dataZoom","title"]),brushStyle:{borderWidth:0,color:"rgba(210,219,238,0.2)"}}}}(nV),{zoom:function(){var e=!this._isZoomActive;this.api.dispatchAction({type:"takeGlobalCursor",key:"dataZoomSelect",dataZoomSelectActive:e})},back:function(){this._dispatchZoomAction(function(e){var t=yV(e),n=t[t.length-1];t.length>1&&t.pop();var i={};return gV(n,(function(e,n){for(var a=t.length-1;a>=0;a--)if(e=t[a][n]){i[n]=e;break}})),i}(this.ecModel))}});function RV(e){var t={xAxisIndex:e.get("xAxisIndex",!0),yAxisIndex:e.get("yAxisIndex",!0),xAxisId:e.get("xAxisId",!0),yAxisId:e.get("yAxisId",!0)};return null==t.xAxisIndex&&null==t.xAxisId&&(t.xAxisIndex="all"),null==t.yAxisIndex&&null==t.yAxisId&&(t.yAxisIndex="all"),t}PV="dataZoom",DV=function(e){var t=e.getComponent("toolbox",0),n=["feature","dataZoom"];if(t&&null!=t.get(n)){var i=t.getModel(n),a=[],r=Od(e,RV(i));return OV(r.xAxisModels,(function(e){return o(e,"xAxis","xAxisIndex")})),OV(r.yAxisModels,(function(e){return o(e,"yAxis","yAxisIndex")})),a}function o(e,t,n){var r=e.componentIndex,o={type:"select",$fromToolbox:!0,filterMode:i.get("filterMode",!0)||"filter",id:AV+t+r};o[n]=r,a.push(o)}},go(null==Zv.get(PV)&&DV),Zv.set(PV,DV);var BV=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="tooltip",t.dependencies=["axisPointer"],t.defaultOption={z:60,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",renderMode:"auto",confine:null,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"#fff",shadowBlur:10,shadowColor:"rgba(0, 0, 0, .2)",shadowOffsetX:1,shadowOffsetY:2,borderRadius:4,borderWidth:1,padding:null,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#666",fontSize:14}},t}(kv);function NV(e){var t=e.get("confine");return null!=t?!!t:"richText"===e.get("renderMode")}function LV(e){if(ur.domSupported)for(var t=document.documentElement.style,n=0,i=e.length;n-1?(p+="top:50%",c+="translateY(-50%) rotate("+(o="left"===s?-225:-45)+"deg)"):(p+="left:50%",c+="translateX(-50%) rotate("+(o="top"===s?225:45)+"deg)");var d=o*Math.PI/180,u=l+a,m=u*Math.abs(Math.cos(d))+u*Math.abs(Math.sin(d)),h=t+" solid "+a+"px;";return'
'}(n,i,a)),Qr(e))r.innerHTML=e+o;else if(e){r.innerHTML="",Xr(e)||(e=[e]);for(var s=0;s=0?this._tryShow(n,i):"leave"===t&&this._hide(i))}),this))},t.prototype._keepShow=function(){var e=this._tooltipModel,t=this._ecModel,n=this._api,i=e.get("triggerOn");if(null!=this._lastX&&null!=this._lastY&&"none"!==i&&"click"!==i){var a=this;clearTimeout(this._refreshUpdateTimeout),this._refreshUpdateTimeout=setTimeout((function(){!n.isDisposed()&&a.manuallyShowTip(e,t,n,{x:a._lastX,y:a._lastY,dataByCoordSys:a._lastDataByCoordSys})}))}},t.prototype.manuallyShowTip=function(e,t,n,i){if(i.from!==this.uid&&!ur.node&&n.getDom()){var a=tq(i,n);this._ticket="";var r=i.dataByCoordSys,o=function(e,t,n){var i=Ad(e).queryOptionMap,a=i.keys()[0];if(!a||"series"===a)return;var r=Bd(t,a,i.get(a),{useDefault:!1,enableAll:!1,enableNone:!1}),o=r.models[0];if(!o)return;var s,l=n.getViewOfComponentModel(o);if(l.group.traverse((function(t){var n=Xm(t).tooltipConfig;if(n&&n.name===e.name)return s=t,!0})),s)return{componentMainType:a,componentIndex:o.componentIndex,el:s}}(i,t,n);if(o){var s=o.el.getBoundingRect().clone();s.applyTransform(o.el.transform),this._tryShow({offsetX:s.x+s.width/2,offsetY:s.y+s.height/2,target:o.el,position:i.position,positionDefault:"bottom"},a)}else if(i.tooltip&&null!=i.x&&null!=i.y){var l=QV;l.x=i.x,l.y=i.y,l.update(),Xm(l).tooltipConfig={name:null,option:i.tooltip},this._tryShow({offsetX:i.x,offsetY:i.y,target:l},a)}else if(r)this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,dataByCoordSys:r,tooltipOption:i.tooltipOption},a);else if(null!=i.seriesIndex){if(this._manuallyAxisShowTip(e,t,n,i))return;var p=EN(i,t),c=p.point[0],d=p.point[1];null!=c&&null!=d&&this._tryShow({offsetX:c,offsetY:d,target:p.el,position:i.position,positionDefault:"bottom"},a)}else null!=i.x&&null!=i.y&&(n.dispatchAction({type:"updateAxisPointer",x:i.x,y:i.y}),this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,target:n.getZr().findHover(i.x,i.y).target},a))}},t.prototype.manuallyHideTip=function(e,t,n,i){var a=this._tooltipContent;this._tooltipModel&&a.hideLater(this._tooltipModel.get("hideDelay")),this._lastX=this._lastY=this._lastDataByCoordSys=null,i.from!==this.uid&&this._hide(tq(i,n))},t.prototype._manuallyAxisShowTip=function(e,t,n,i){var a=i.seriesIndex,r=i.dataIndex,o=t.getComponent("axisPointer").coordSysAxesInfo;if(null!=a&&null!=r&&null!=o){var s=t.getSeriesByIndex(a);if(s)if("axis"===eq([s.getData().getItemModel(r),s,(s.coordinateSystem||{}).model],this._tooltipModel).get("trigger"))return n.dispatchAction({type:"updateAxisPointer",seriesIndex:a,dataIndex:r,position:i.position}),!0}},t.prototype._tryShow=function(e,t){var n=e.target;if(this._tooltipModel){this._lastX=e.offsetX,this._lastY=e.offsetY;var i=e.dataByCoordSys;if(i&&i.length)this._showAxisTooltip(i,e);else if(n){var a,r;if("legend"===Xm(n).ssrType)return;this._lastDataByCoordSys=null,TS(n,(function(e){return null!=Xm(e).dataIndex?(a=e,!0):null!=Xm(e).tooltipConfig?(r=e,!0):void 0}),!0),a?this._showSeriesItemTooltip(e,a,t):r?this._showComponentItemTooltip(e,r,t):this._hide(t)}else this._lastDataByCoordSys=null,this._hide(t)}},t.prototype._showOrMove=function(e,t){var n=e.get("showDelay");t=Kr(t,this),clearTimeout(this._showTimout),n>0?this._showTimout=setTimeout(t,n):t()},t.prototype._showAxisTooltip=function(e,t){var n=this._ecModel,i=this._tooltipModel,a=[t.offsetX,t.offsetY],r=eq([t.tooltipOption],i),o=this._renderMode,s=[],l=Zb("section",{blocks:[],noHeader:!0}),p=[],c=new lw;zr(e,(function(e){zr(e.dataByAxis,(function(e){var t=n.getComponent(e.axisDim+"Axis",e.axisIndex),a=e.value;if(t&&t.axis&&null!=a){var r=pN(a,t.axis,n,e.seriesDataIndices,e.valueLabelOpt),d=Zb("section",{header:r,noHeader:!fo(r),sortBlocks:!0,blocks:[]});l.blocks.push(d),zr(e.seriesDataIndices,(function(l){var u=n.getSeriesByIndex(l.seriesIndex),m=l.dataIndexInside,h=u.getDataParams(m);if(!(h.dataIndex<0)){h.axisDim=e.axisDim,h.axisIndex=e.axisIndex,h.axisType=e.axisType,h.axisId=e.axisId,h.axisValue=iI(t.axis,{value:a}),h.axisValueLabel=r,h.marker=c.makeTooltipMarker("item",gv(h.color),o);var g=mb(u.formatTooltip(m,!0,null)),f=g.frag;if(f){var y=eq([u],i).get("valueFormatter");d.blocks.push(y?Br({valueFormatter:y},f):f)}g.text&&p.push(g.text),s.push(h)}}))}}))})),l.blocks.reverse(),p.reverse();var d=t.position,u=r.get("order"),m=iw(l,c,o,u,n.get("useUTC"),r.get("textStyle"));m&&p.unshift(m);var h="richText"===o?"\n\n":"
",g=p.join(h);this._showOrMove(r,(function(){this._updateContentNotChangedOnAxis(e,s)?this._updatePosition(r,d,a[0],a[1],this._tooltipContent,s):this._showTooltipContent(r,g,s,Math.random()+"",a[0],a[1],d,null,c)}))},t.prototype._showSeriesItemTooltip=function(e,t,n){var i=this._ecModel,a=Xm(t),r=a.seriesIndex,o=i.getSeriesByIndex(r),s=a.dataModel||o,l=a.dataIndex,p=a.dataType,c=s.getData(p),d=this._renderMode,u=e.positionDefault,m=eq([c.getItemModel(l),s,o&&(o.coordinateSystem||{}).model],this._tooltipModel,u?{position:u}:null),h=m.get("trigger");if(null==h||"item"===h){var g=s.getDataParams(l,p),f=new lw;g.marker=f.makeTooltipMarker("item",gv(g.color),d);var y=mb(s.formatTooltip(l,!1,p)),v=m.get("order"),x=m.get("valueFormatter"),b=y.frag,w=b?iw(x?Br({valueFormatter:x},b):b,f,d,v,i.get("useUTC"),m.get("textStyle")):y.text,S="item_"+s.name+"_"+l;this._showOrMove(m,(function(){this._showTooltipContent(m,w,g,S,e.offsetX,e.offsetY,e.position,e.target,f)})),n({type:"showTip",dataIndexInside:l,dataIndex:c.getRawIndex(l),seriesIndex:r,from:this.uid})}},t.prototype._showComponentItemTooltip=function(e,t,n){var i=Xm(t),a=i.tooltipConfig.option||{};if(Qr(a)){a={content:a,formatter:a}}var r=[a],o=this._ecModel.getComponent(i.componentMainType,i.componentIndex);o&&r.push(o),r.push({formatter:a.content});var s=e.positionDefault,l=eq(r,this._tooltipModel,s?{position:s}:null),p=l.get("content"),c=Math.random()+"",d=new lw;this._showOrMove(l,(function(){var n=Ar(l.get("formatterParams")||{});this._showTooltipContent(l,p,n,c,e.offsetX,e.offsetY,e.position,t,d)})),n({type:"showTip",from:this.uid})},t.prototype._showTooltipContent=function(e,t,n,i,a,r,o,s,l){if(this._ticket="",e.get("showContent")&&e.get("show")){var p=this._tooltipContent;p.setEnterable(e.get("enterable"));var c=e.get("formatter");o=o||e.get("position");var d=t,u=this._getNearestPoint([a,r],n,e.get("trigger"),e.get("borderColor")).color;if(c)if(Qr(c)){var m=e.ecModel.get("useUTC"),h=Xr(n)?n[0]:n;d=c,h&&h.axisType&&h.axisType.indexOf("time")>=0&&(d=jy(h.axisValue,d,m)),d=mv(d,n,!0)}else if(Zr(c)){var g=Kr((function(t,i){t===this._ticket&&(p.setContent(i,l,e,u,o),this._updatePosition(e,o,a,r,p,n,s))}),this);this._ticket=i,d=c(n,i,g)}else d=c;p.setContent(d,l,e,u,o),p.show(e,u),this._updatePosition(e,o,a,r,p,n,s)}},t.prototype._getNearestPoint=function(e,t,n,i){return"axis"===n||Xr(t)?{color:i||("html"===this._renderMode?"#fff":"none")}:Xr(t)?void 0:{color:i||t.color||t.borderColor}},t.prototype._updatePosition=function(e,t,n,i,a,r,o){var s=this._api.getWidth(),l=this._api.getHeight();t=t||e.get("position");var p=a.getSize(),c=e.get("align"),d=e.get("verticalAlign"),u=o&&o.getBoundingRect().clone();if(o&&u.applyTransform(o.transform),Zr(t)&&(t=t([n,i],r,a.el,u,{viewSize:[s,l],contentSize:p.slice()})),Xr(t))n=Wc(t[0],s),i=Wc(t[1],l);else if(to(t)){var m=t;m.width=p[0],m.height=p[1];var h=Sv(m,{width:s,height:l});n=h.x,i=h.y,c=null,d=null}else if(Qr(t)&&o){var g=function(e,t,n,i){var a=n[0],r=n[1],o=Math.ceil(Math.SQRT2*i)+8,s=0,l=0,p=t.width,c=t.height;switch(e){case"inside":s=t.x+p/2-a/2,l=t.y+c/2-r/2;break;case"top":s=t.x+p/2-a/2,l=t.y-r-o;break;case"bottom":s=t.x+p/2-a/2,l=t.y+c+o;break;case"left":s=t.x-a-o,l=t.y+c/2-r/2;break;case"right":s=t.x+p+o,l=t.y+c/2-r/2}return[s,l]}(t,u,p,e.get("borderWidth"));n=g[0],i=g[1]}else{g=function(e,t,n,i,a,r,o){var s=n.getSize(),l=s[0],p=s[1];null!=r&&(e+l+r+2>i?e-=l+r:e+=r);null!=o&&(t+p+o>a?t-=p+o:t+=o);return[e,t]}(n,i,a,s,l,c?null:20,d?null:20);n=g[0],i=g[1]}if(c&&(n-=nq(c)?p[0]/2:"right"===c?p[0]:0),d&&(i-=nq(d)?p[1]/2:"bottom"===d?p[1]:0),NV(e)){g=function(e,t,n,i,a){var r=n.getSize(),o=r[0],s=r[1];return e=Math.min(e+o,i)-o,t=Math.min(t+s,a)-s,e=Math.max(e,0),t=Math.max(t,0),[e,t]}(n,i,a,s,l);n=g[0],i=g[1]}a.moveTo(n,i)},t.prototype._updateContentNotChangedOnAxis=function(e,t){var n=this._lastDataByCoordSys,i=this._cbParamsList,a=!!n&&n.length===e.length;return a&&zr(n,(function(n,r){var o=n.dataByAxis||[],s=(e[r]||{}).dataByAxis||[];(a=a&&o.length===s.length)&&zr(o,(function(e,n){var r=s[n]||{},o=e.seriesDataIndices||[],l=r.seriesDataIndices||[];(a=a&&e.value===r.value&&e.axisType===r.axisType&&e.axisId===r.axisId&&o.length===l.length)&&zr(o,(function(e,t){var n=l[t];a=a&&e.seriesIndex===n.seriesIndex&&e.dataIndex===n.dataIndex})),i&&zr(e.seriesDataIndices,(function(e){var n=e.seriesIndex,r=t[n],o=i[n];r&&o&&o.data!==r.data&&(a=!1)}))}))})),this._lastDataByCoordSys=e,this._cbParamsList=t,!!a},t.prototype._hide=function(e){this._lastDataByCoordSys=null,e({type:"hideTip",from:this.uid})},t.prototype.dispose=function(e,t){!ur.node&&t.getDom()&&(Bw(this,"_updatePosition"),this._tooltipContent.dispose(),TN("itemTooltip",t))},t.type="tooltip",t}(ww);function eq(e,t,n){var i,a=t.ecModel;n?(i=new xy(n,a,a),i=new xy(t.option,i,a)):i=t;for(var r=e.length-1;r>=0;r--){var o=e[r];o&&(o instanceof xy&&(o=o.get("tooltip",!0)),Qr(o)&&(o={formatter:o}),o&&(i=new xy(o,i,a)))}return i}function tq(e,t){return e.dispatchAction||Kr(t.dispatchAction,t)}function nq(e){return"center"===e||"middle"===e}function iq(e){dI(RN),e.registerComponentModel(BV),e.registerComponentView(JV),e.registerAction({type:"showTip",event:"showTip",update:"tooltip:manuallyShowTip"},Mo),e.registerAction({type:"hideTip",event:"hideTip",update:"tooltip:manuallyHideTip"},Mo)}var aq=zr;function rq(e){if(e)for(var t in e)if(e.hasOwnProperty(t))return!0}function oq(e,t,n){var i={};return aq(t,(function(t){var a,r=i[t]=((a=function(){}).prototype.__hidden=a.prototype,new a);aq(e[t],(function(e,i){if(dO.isValidType(i)){var a={type:i,visual:e};n&&n(a,t),r[i]=new dO(a),"opacity"===i&&((a=Ar(a)).type="colorAlpha",r.__hidden.__alphaForOpacity=new dO(a))}}))})),i}function sq(e,t,n){var i;zr(n,(function(e){t.hasOwnProperty(e)&&rq(t[e])&&(i=!0)})),i&&zr(n,(function(n){t.hasOwnProperty(n)&&rq(t[n])?e[n]=Ar(t[n]):delete e[n]}))}lq(0),lq(1);function lq(e){var t=["x","y"],n=["width","height"];return{point:function(t,n,i){if(t){var a=i.range;return pq(t[e],a)}},rect:function(i,a,r){if(i){var o=r.range,s=[i[t[e]],i[t[e]]+i[n[e]]];return s[1]=t&&(e=t-1),e<0&&(e=0)),this.option.currentIndex=e},t.prototype.getCurrentIndex=function(){return this.option.currentIndex},t.prototype.isIndexMax=function(){return this.getCurrentIndex()>=this._data.count()-1},t.prototype.setPlayState=function(e){this.option.autoPlay=!!e},t.prototype.getPlayState=function(){return!!this.option.autoPlay},t.prototype._initData=function(){var e,t=this.option,n=t.data||[],i=t.axisType,a=this._names=[];"category"===i?(e=[],zr(n,(function(t,n){var i,r=Id(wd(t),"");to(t)?(i=Ar(t)).value=n:i=n,e.push(i),a.push(r)}))):e=n;var r={category:"ordinal",time:"time",value:"number"}[i]||"number";(this._data=new W_([{name:"value",type:r}],this)).initData(e,a)},t.prototype.getData=function(){return this._data},t.prototype.getCategories=function(){if("category"===this.get("axisType"))return this._names.slice()},t.type="timeline",t.defaultOption={z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},t}(kv),hq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="timeline.slider",t.defaultOption=Sy(mq.defaultOption,{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"circle",symbolSize:12,lineStyle:{show:!0,width:2,color:"#DAE1F5"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#A4B1D7"},itemStyle:{color:"#A4B1D7",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:15,color:"#316bf3",borderColor:"#fff",borderWidth:2,shadowBlur:2,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0, 0, 0, 0.3)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:24,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z",prevIcon:"M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z",prevBtnSize:18,nextBtnSize:18,color:"#A4B1D7",borderColor:"#A4B1D7",borderWidth:1},emphasis:{label:{show:!0,color:"#6f778d"},itemStyle:{color:"#316BF3"},controlStyle:{color:"#316BF3",borderColor:"#316BF3",borderWidth:2}},progress:{lineStyle:{color:"#316BF3"},itemStyle:{color:"#316BF3"},label:{color:"#6f778d"}},data:[]}),t}(mq);qr(hq,ub.prototype);var gq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="timeline",t}(ww),fq=function(e){function t(t,n,i,a){var r=e.call(this,t,n,i)||this;return r.type=a||"value",r}return We(t,e),t.prototype.getLabelModel=function(){return this.model.getModel("label")},t.prototype.isHorizontal=function(){return"horizontal"===this.model.get("orient")},t}(RI),yq=Math.PI,vq=Pd();!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.init=function(e,t){this.api=t},t.prototype.render=function(e,t,n){if(this.model=e,this.api=n,this.ecModel=t,this.group.removeAll(),e.get("show",!0)){var i=this._layout(e,n),a=this._createGroup("_mainGroup"),r=this._createGroup("_labelGroup"),o=this._axis=this._createAxis(i,e);e.formatTooltip=function(e){return Zb("nameValue",{noName:!0,value:o.scale.getLabel({value:e})})},zr(["AxisLine","AxisTick","Control","CurrentPointer"],(function(t){this["_render"+t](i,a,o,e)}),this),this._renderAxisLabel(i,r,o,e),this._position(i,e)}this._doPlayStop(),this._updateTicksStatus()},t.prototype.remove=function(){this._clearTimer(),this.group.removeAll()},t.prototype.dispose=function(){this._clearTimer()},t.prototype._layout=function(e,t){var n,i,a,r,o=e.get(["label","position"]),s=e.get("orient"),l=function(e,t){return Sv(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()},e.get("padding"))}(e,t),p={horizontal:"center",vertical:(n=null==o||"auto"===o?"horizontal"===s?l.y+l.height/2=0||"+"===n?"left":"right"},c={horizontal:n>=0||"+"===n?"top":"bottom",vertical:"middle"},d={horizontal:0,vertical:yq/2},u="vertical"===s?l.height:l.width,m=e.getModel("controlStyle"),h=m.get("show",!0),g=h?m.get("itemSize"):0,f=h?m.get("itemGap"):0,y=g+f,v=e.get(["label","rotate"])||0;v=v*yq/180;var x=m.get("position",!0),b=h&&m.get("showPlayBtn",!0),w=h&&m.get("showPrevBtn",!0),S=h&&m.get("showNextBtn",!0),C=0,_=u;"left"===x||"bottom"===x?(b&&(i=[0,0],C+=y),w&&(a=[C,0],C+=y),S&&(r=[_-g,0],_-=y)):(b&&(i=[_-g,0],_-=y),w&&(a=[0,0],C+=y),S&&(r=[_-g,0],_-=y));var T=[C,_];return e.get("inverse")&&T.reverse(),{viewRect:l,mainLength:u,orient:s,rotation:d[s],labelRotation:v,labelPosOpt:n,labelAlign:e.get(["label","align"])||p[s],labelBaseline:e.get(["label","verticalAlign"])||e.get(["label","baseline"])||c[s],playPosition:i,prevBtnPosition:a,nextBtnPosition:r,axisExtent:T,controlSize:g,controlGap:f}},t.prototype._position=function(e,t){var n=this._mainGroup,i=this._labelGroup,a=e.viewRect;if("vertical"===e.orient){var r=[1,0,0,1,0,0],o=a.x,s=a.y+a.height;Cs(r,r,[-o,-s]),_s(r,r,-yq/2),Cs(r,r,[o,s]),(a=a.clone()).applyTransform(r)}var l=f(a),p=f(n.getBoundingRect()),c=f(i.getBoundingRect()),d=[n.x,n.y],u=[i.x,i.y];u[0]=d[0]=l[0][0];var m,h=e.labelPosOpt;null==h||Qr(h)?(y(d,p,l,1,m="+"===h?0:1),y(u,c,l,1,1-m)):(y(d,p,l,1,m=h>=0?0:1),u[1]=d[1]+h);function g(e){e.originX=l[0][0]-e.x,e.originY=l[1][0]-e.y}function f(e){return[[e.x,e.x+e.width],[e.y,e.y+e.height]]}function y(e,t,n,i,a){e[i]+=n[i][a]-t[i][a]}n.setPosition(d),i.setPosition(u),n.rotation=i.rotation=e.rotation,g(n),g(i)},t.prototype._createAxis=function(e,t){var n=t.getData(),i=t.get("axisType"),a=function(e,t){if(t=t||e.get("type"),t)switch(t){case"category":return new hT({ordinalMeta:e.getCategories(),extent:[1/0,-1/0]});case"time":return new PT({locale:e.ecModel.getLocaleModel(),useUTC:e.ecModel.get("useUTC")});default:return new fT}}(t,i);a.getTicks=function(){return n.mapArray(["value"],(function(e){return{value:e}}))};var r=n.getDataExtent("value");a.setExtent(r[0],r[1]),a.calcNiceTicks();var o=new fq("value",a,e.axisExtent,i);return o.model=t,o},t.prototype._createGroup=function(e){var t=this[e]=new Rc;return this.group.add(t),t},t.prototype._renderAxisLine=function(e,t,n,i){var a=n.getExtent();if(i.get(["lineStyle","show"])){var r=new Gg({shape:{x1:a[0],y1:0,x2:a[1],y2:0},style:Br({lineCap:"round"},i.getModel("lineStyle").getLineStyle()),silent:!0,z2:1});t.add(r);var o=this._progressLine=new Gg({shape:{x1:a[0],x2:this._currentPointer?this._currentPointer.x:a[0],y1:0,y2:0},style:Nr({lineCap:"round",lineWidth:r.style.lineWidth},i.getModel(["progress","lineStyle"]).getLineStyle()),silent:!0,z2:1});t.add(o)}},t.prototype._renderAxisTick=function(e,t,n,i){var a=this,r=i.getData(),o=n.scale.getTicks();this._tickSymbols=[],zr(o,(function(e){var o=n.dataToCoord(e.value),s=r.getItemModel(e.value),l=s.getModel("itemStyle"),p=s.getModel(["emphasis","itemStyle"]),c=s.getModel(["progress","itemStyle"]),d={x:o,y:0,onclick:Kr(a._changeTimeline,a,e.value)},u=xq(s,l,t,d);u.ensureState("emphasis").style=p.getItemStyle(),u.ensureState("progress").style=c.getItemStyle(),Lh(u);var m=Xm(u);s.get("tooltip")?(m.dataIndex=e.value,m.dataModel=i):m.dataIndex=m.dataModel=null,a._tickSymbols.push(u)}))},t.prototype._renderAxisLabel=function(e,t,n,i){var a=this;if(n.getLabelModel().get("show")){var r=i.getData(),o=n.getViewLabels();this._tickLabels=[],zr(o,(function(i){var o=i.tickValue,s=r.getItemModel(o),l=s.getModel("label"),p=s.getModel(["emphasis","label"]),c=s.getModel(["progress","label"]),d=n.dataToCoord(i.tickValue),u=new Nm({x:d,y:0,rotation:e.labelRotation-e.rotation,onclick:Kr(a._changeTimeline,a,o),silent:!1,style:Zf(l,{text:i.formattedLabel,align:e.labelAlign,verticalAlign:e.labelBaseline})});u.ensureState("emphasis").style=Zf(p),u.ensureState("progress").style=Zf(c),t.add(u),Lh(u),vq(u).dataIndex=o,a._tickLabels.push(u)}))}},t.prototype._renderControl=function(e,t,n,i){var a=e.controlSize,r=e.rotation,o=i.getModel("controlStyle").getItemStyle(),s=i.getModel(["emphasis","controlStyle"]).getItemStyle(),l=i.getPlayState(),p=i.get("inverse",!0);function c(e,n,l,p){if(e){var c=Cc(co(i.get(["controlStyle",n+"BtnSize"]),a),a),d=function(e,t,n,i){var a=i.style,r=Lf(e.get(["controlStyle",t]),i||{},new Ns(n[0],n[1],n[2],n[3]));a&&r.setStyle(a);return r}(i,n+"Icon",[0,-c/2,c,c],{x:e[0],y:e[1],originX:a/2,originY:0,rotation:p?-r:0,rectHover:!0,style:o,onclick:l});d.ensureState("emphasis").style=s,t.add(d),Lh(d)}}c(e.nextBtnPosition,"next",Kr(this._changeTimeline,this,p?"-":"+")),c(e.prevBtnPosition,"prev",Kr(this._changeTimeline,this,p?"+":"-")),c(e.playPosition,l?"stop":"play",Kr(this._handlePlayClick,this,!l),!0)},t.prototype._renderCurrentPointer=function(e,t,n,i){var a=i.getData(),r=i.getCurrentIndex(),o=a.getItemModel(r).getModel("checkpointStyle"),s=this,l={onCreate:function(e){e.draggable=!0,e.drift=Kr(s._handlePointerDrag,s),e.ondragend=Kr(s._handlePointerDragend,s),bq(e,s._progressLine,r,n,i,!0)},onUpdate:function(e){bq(e,s._progressLine,r,n,i)}};this._currentPointer=xq(o,o,this._mainGroup,{},this._currentPointer,l)},t.prototype._handlePlayClick=function(e){this._clearTimer(),this.api.dispatchAction({type:"timelinePlayChange",playState:e,from:this.uid})},t.prototype._handlePointerDrag=function(e,t,n){this._clearTimer(),this._pointerChangeTimeline([n.offsetX,n.offsetY])},t.prototype._handlePointerDragend=function(e){this._pointerChangeTimeline([e.offsetX,e.offsetY],!0)},t.prototype._pointerChangeTimeline=function(e,t){var n=this._toAxisCoord(e)[0],i=Kc(this._axis.getExtent().slice());n>i[1]&&(n=i[1]),n=0&&(o[r]=+o[r].toFixed(d)),[o,c]}var Eq={min:Yr(Iq,"min"),max:Yr(Iq,"max"),average:Yr(Iq,"average"),median:Yr(Iq,"median")};function Mq(e,t){if(t){var n=e.getData(),i=e.coordinateSystem,a=i&&i.dimensions;if(!function(e){return!isNaN(parseFloat(e.x))&&!isNaN(parseFloat(e.y))}(t)&&!Xr(t.coord)&&Xr(a)){var r=kq(t,n,i,e);if((t=Ar(t)).type&&Eq[t.type]&&r.baseAxis&&r.valueAxis){var o=Lr(a,r.baseAxis.dim),s=Lr(a,r.valueAxis.dim),l=Eq[t.type](n,r.baseDataDim,r.valueDataDim,o,s);t.coord=l[0],t.value=l[1]}else t.coord=[null!=t.xAxis?t.xAxis:t.radiusAxis,null!=t.yAxis?t.yAxis:t.angleAxis]}if(null!=t.coord&&Xr(a))for(var p=t.coord,c=0;c<2;c++)Eq[p[c]]&&(p[c]=Oq(n,n.mapDimension(a[c]),p[c]));else t.coord=[];return t}}function kq(e,t,n,i){var a={};return null!=e.valueIndex||null!=e.valueDim?(a.valueDataDim=null!=e.valueIndex?t.getDimension(e.valueIndex):e.valueDim,a.valueAxis=n.getAxis(function(e,t){var n=e.getData().getDimensionInfo(t);return n&&n.coordDim}(i,a.valueDataDim)),a.baseAxis=n.getOtherAxis(a.valueAxis),a.baseDataDim=t.mapDimension(a.baseAxis.dim)):(a.baseAxis=i.getBaseAxis(),a.valueAxis=n.getOtherAxis(a.baseAxis),a.baseDataDim=t.mapDimension(a.baseAxis.dim),a.valueDataDim=t.mapDimension(a.valueAxis.dim)),a}function Pq(e,t){return!(e&&e.containData&&t.coord&&!Tq(t))||e.containData(t.coord)}function Dq(e,t){return e?function(e,n,i,a){return yb(a<2?e.coord&&e.coord[a]:e.value,t[a])}:function(e,n,i,a){return yb(e.value,t[a])}}function Oq(e,t,n){if("average"===n){var i=0,a=0;return e.each(t,(function(e,t){isNaN(e)||(i+=e,a++)})),i/a}return"median"===n?e.getMedian(t):e.getDataExtent(t)["max"===n?1:0]}var Aq=Pd(),Fq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.init=function(){this.markerGroupMap=Co()},t.prototype.render=function(e,t,n){var i=this,a=this.markerGroupMap;a.each((function(e){Aq(e).keep=!1})),t.eachSeries((function(e){var a=_q.getMarkerModelFromSeries(e,i.type);a&&i.renderSeries(e,a,t,n)})),a.each((function(e){!Aq(e).keep&&i.group.remove(e.group)}))},t.prototype.markKeep=function(e){Aq(e).keep=!0},t.prototype.toggleBlurSeries=function(e,t){var n=this;zr(e,(function(e){var i=_q.getMarkerModelFromSeries(e,n.type);i&&i.getData().eachItemGraphicEl((function(e){e&&(t?Eh(e):Mh(e))}))}))},t.type="marker",t}(ww);function Rq(e,t,n){var i=t.coordinateSystem;e.each((function(a){var r,o=e.getItemModel(a),s=Wc(o.get("x"),n.getWidth()),l=Wc(o.get("y"),n.getHeight());if(isNaN(s)||isNaN(l)){if(t.getMarkerPosition)r=t.getMarkerPosition(e.getValues(e.dimensions,a));else if(i){var p=e.get(i.dimensions[0],a),c=e.get(i.dimensions[1],a);r=i.dataToPoint([p,c])}}else r=[s,l];isNaN(s)||(r[0]=s),isNaN(l)||(r[1]=l),e.setItemLayout(a,r)}))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.updateTransform=function(e,t,n){t.eachSeries((function(e){var t=_q.getMarkerModelFromSeries(e,"markPoint");t&&(Rq(t.getData(),e,n),this.markerGroupMap.get(e.id).updateLayout())}),this)},t.prototype.renderSeries=function(e,t,n,i){var a=e.coordinateSystem,r=e.id,o=e.getData(),s=this.markerGroupMap,l=s.get(r)||s.set(r,new PE),p=function(e,t,n){var i;i=e?Ur(e&&e.dimensions,(function(e){return Br(Br({},t.getData().getDimensionInfo(t.getData().mapDimension(e))||{}),{name:e,ordinalMeta:null})})):[{name:"value",type:"float"}];var a=new W_(i,n),r=Ur(n.get("data"),Yr(Mq,t));e&&(r=Hr(r,Yr(Pq,e)));var o=Dq(!!e,i);return a.initData(r,null,o),a}(a,e,t);t.setData(p),Rq(t.getData(),e,i),p.each((function(e){var n=p.getItemModel(e),i=n.getShallow("symbol"),a=n.getShallow("symbolSize"),r=n.getShallow("symbolRotate"),s=n.getShallow("symbolOffset"),l=n.getShallow("symbolKeepAspect");if(Zr(i)||Zr(a)||Zr(r)||Zr(s)){var c=t.getRawValue(e),d=t.getDataParams(e);Zr(i)&&(i=i(c,d)),Zr(a)&&(a=a(c,d)),Zr(r)&&(r=r(c,d)),Zr(s)&&(s=s(c,d))}var u=n.getModel("itemStyle").getItemStyle(),m=wS(o,"color");u.fill||(u.fill=m),p.setItemVisual(e,{symbol:i,symbolSize:a,symbolRotate:r,symbolOffset:s,symbolKeepAspect:l,style:u})})),l.updateData(p),this.group.add(l.group),p.eachItemGraphicEl((function(e){e.traverse((function(e){Xm(e).dataModel=t}))})),this.markKeep(l),l.group.silent=t.get("silent")||e.get("silent")},t.type="markPoint"}(Fq);var Bq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.createMarkerModelFromSeries=function(e,n,i){return new t(e,n,i)},t.type="markLine",t.defaultOption={z:5,symbol:["circle","arrow"],symbolSize:[8,16],symbolOffset:0,precision:2,tooltip:{trigger:"item"},label:{show:!0,position:"end",distance:5},lineStyle:{type:"dashed"},emphasis:{label:{show:!0},lineStyle:{width:3}},animationEasing:"linear"},t}(_q),Nq=Pd(),Lq=function(e,t,n,i){var a,r=e.getData();if(Xr(i))a=i;else{var o=i.type;if("min"===o||"max"===o||"average"===o||"median"===o||null!=i.xAxis||null!=i.yAxis){var s=void 0,l=void 0;if(null!=i.yAxis||null!=i.xAxis)s=t.getAxis(null!=i.yAxis?"y":"x"),l=po(i.yAxis,i.xAxis);else{var p=kq(i,r,t,e);s=p.valueAxis,l=Oq(r,eT(r,p.valueDataDim),o)}var c="x"===s.dim?0:1,d=1-c,u=Ar(i),m={coord:[]};u.type=null,u.coord=[],u.coord[d]=-1/0,m.coord[d]=1/0;var h=n.get("precision");h>=0&&eo(l)&&(l=+l.toFixed(Math.min(h,20))),u.coord[c]=m.coord[c]=l,a=[u,m,{type:o,valueIndex:i.valueIndex,value:l}]}else a=[]}var g=[Mq(e,a[0]),Mq(e,a[1]),Br({},a[2])];return g[2].type=g[2].type||null,Fr(g[2],g[0]),Fr(g[2],g[1]),g};function Vq(e){return!isNaN(e)&&!isFinite(e)}function qq(e,t,n,i){var a=1-e,r=i.dimensions[e];return Vq(t[a])&&Vq(n[a])&&t[e]===n[e]&&i.getAxis(r).containData(t[e])}function Gq(e,t){if("cartesian2d"===e.type){var n=t[0].coord,i=t[1].coord;if(n&&i&&(qq(1,n,i,e)||qq(0,n,i,e)))return!0}return Pq(e,t[0])&&Pq(e,t[1])}function zq(e,t,n,i,a){var r,o=i.coordinateSystem,s=e.getItemModel(t),l=Wc(s.get("x"),a.getWidth()),p=Wc(s.get("y"),a.getHeight());if(isNaN(l)||isNaN(p)){if(i.getMarkerPosition)r=i.getMarkerPosition(e.getValues(e.dimensions,t));else{var c=o.dimensions,d=e.get(c[0],t),u=e.get(c[1],t);r=o.dataToPoint([d,u])}if(jE(o,"cartesian2d")){var m=o.getAxis("x"),h=o.getAxis("y");c=o.dimensions;Vq(e.get(c[0],t))?r[0]=m.toGlobalCoord(m.getExtent()[n?0:1]):Vq(e.get(c[1],t))&&(r[1]=h.toGlobalCoord(h.getExtent()[n?0:1]))}isNaN(l)||(r[0]=l),isNaN(p)||(r[1]=p)}else r=[l,p];e.setItemLayout(t,r)}var Uq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.updateTransform=function(e,t,n){t.eachSeries((function(e){var t=_q.getMarkerModelFromSeries(e,"markLine");if(t){var i=t.getData(),a=Nq(t).from,r=Nq(t).to;a.each((function(t){zq(a,t,!0,e,n),zq(r,t,!1,e,n)})),i.each((function(e){i.setItemLayout(e,[a.getItemLayout(e),r.getItemLayout(e)])})),this.markerGroupMap.get(e.id).updateLayout()}}),this)},t.prototype.renderSeries=function(e,t,n,i){var a=e.coordinateSystem,r=e.id,o=e.getData(),s=this.markerGroupMap,l=s.get(r)||s.set(r,new YO);this.group.add(l.group);var p=function(e,t,n){var i;i=e?Ur(e&&e.dimensions,(function(e){return Br(Br({},t.getData().getDimensionInfo(t.getData().mapDimension(e))||{}),{name:e,ordinalMeta:null})})):[{name:"value",type:"float"}];var a=new W_(i,n),r=new W_(i,n),o=new W_([],n),s=Ur(n.get("data"),Yr(Lq,t,e,n));e&&(s=Hr(s,Yr(Gq,e)));var l=Dq(!!e,i);return a.initData(Ur(s,(function(e){return e[0]})),null,l),r.initData(Ur(s,(function(e){return e[1]})),null,l),o.initData(Ur(s,(function(e){return e[2]}))),o.hasItemOption=!0,{from:a,to:r,line:o}}(a,e,t),c=p.from,d=p.to,u=p.line;Nq(t).from=c,Nq(t).to=d,t.setData(u);var m=t.get("symbol"),h=t.get("symbolSize"),g=t.get("symbolRotate"),f=t.get("symbolOffset");function y(t,n,a){var r=t.getItemModel(n);zq(t,n,a,e,i);var s=r.getModel("itemStyle").getItemStyle();null==s.fill&&(s.fill=wS(o,"color")),t.setItemVisual(n,{symbolKeepAspect:r.get("symbolKeepAspect"),symbolOffset:co(r.get("symbolOffset",!0),f[a?0:1]),symbolRotate:co(r.get("symbolRotate",!0),g[a?0:1]),symbolSize:co(r.get("symbolSize"),h[a?0:1]),symbol:co(r.get("symbol",!0),m[a?0:1]),style:s})}Xr(m)||(m=[m,m]),Xr(h)||(h=[h,h]),Xr(g)||(g=[g,g]),Xr(f)||(f=[f,f]),p.from.each((function(e){y(c,e,!0),y(d,e,!1)})),u.each((function(e){var t=u.getItemModel(e).getModel("lineStyle").getLineStyle();u.setItemLayout(e,[c.getItemLayout(e),d.getItemLayout(e)]),null==t.stroke&&(t.stroke=c.getItemVisual(e,"style").fill),u.setItemVisual(e,{fromSymbolKeepAspect:c.getItemVisual(e,"symbolKeepAspect"),fromSymbolOffset:c.getItemVisual(e,"symbolOffset"),fromSymbolRotate:c.getItemVisual(e,"symbolRotate"),fromSymbolSize:c.getItemVisual(e,"symbolSize"),fromSymbol:c.getItemVisual(e,"symbol"),toSymbolKeepAspect:d.getItemVisual(e,"symbolKeepAspect"),toSymbolOffset:d.getItemVisual(e,"symbolOffset"),toSymbolRotate:d.getItemVisual(e,"symbolRotate"),toSymbolSize:d.getItemVisual(e,"symbolSize"),toSymbol:d.getItemVisual(e,"symbol"),style:t})})),l.updateData(u),p.line.eachItemGraphicEl((function(e){Xm(e).dataModel=t,e.traverse((function(e){Xm(e).dataModel=t}))})),this.markKeep(l),l.group.silent=t.get("silent")||e.get("silent")},t.type="markLine",t}(Fq);function jq(e){e.registerComponentModel(Bq),e.registerComponentView(Uq),e.registerPreprocessor((function(e){wq(e.series,"markLine")&&(e.markLine=e.markLine||{})}))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.createMarkerModelFromSeries=function(e,n,i){return new t(e,n,i)},t.type="markArea",t.defaultOption={z:1,tooltip:{trigger:"item"},animation:!1,label:{show:!0,position:"top"},itemStyle:{borderWidth:0},emphasis:{label:{show:!0,position:"top"}}}}(_q);var Hq=Pd(),Wq=function(e,t,n,i){var a=i[0],r=i[1];if(a&&r){var o=Mq(e,a),s=Mq(e,r),l=o.coord,p=s.coord;l[0]=po(l[0],-1/0),l[1]=po(l[1],-1/0),p[0]=po(p[0],1/0),p[1]=po(p[1],1/0);var c=Rr([{},o,s]);return c.coord=[o.coord,s.coord],c.x0=o.x,c.y0=o.y,c.x1=s.x,c.y1=s.y,c}};function $q(e){return!isNaN(e)&&!isFinite(e)}function Kq(e,t,n,i){var a=1-e;return $q(t[a])&&$q(n[a])}function Yq(e,t){var n=t.coord[0],i=t.coord[1],a={coord:n,x:t.x0,y:t.y0},r={coord:i,x:t.x1,y:t.y1};return jE(e,"cartesian2d")?!(!n||!i||!Kq(1,n,i)&&!Kq(0,n,i))||function(e,t,n){return!(e&&e.containZone&&t.coord&&n.coord&&!Tq(t)&&!Tq(n))||e.containZone(t.coord,n.coord)}(e,a,r):Pq(e,a)||Pq(e,r)}function Xq(e,t,n,i,a){var r,o=i.coordinateSystem,s=e.getItemModel(t),l=Wc(s.get(n[0]),a.getWidth()),p=Wc(s.get(n[1]),a.getHeight());if(isNaN(l)||isNaN(p)){if(i.getMarkerPosition){var c=e.getValues(["x0","y0"],t),d=e.getValues(["x1","y1"],t),u=o.clampData(c),m=o.clampData(d),h=[];"x0"===n[0]?h[0]=u[0]>m[0]?d[0]:c[0]:h[0]=u[0]>m[0]?c[0]:d[0],"y0"===n[1]?h[1]=u[1]>m[1]?d[1]:c[1]:h[1]=u[1]>m[1]?c[1]:d[1],r=i.getMarkerPosition(h,n,!0)}else{var g=[v=e.get(n[0],t),x=e.get(n[1],t)];o.clampData&&o.clampData(g,g),r=o.dataToPoint(g,!0)}if(jE(o,"cartesian2d")){var f=o.getAxis("x"),y=o.getAxis("y"),v=e.get(n[0],t),x=e.get(n[1],t);$q(v)?r[0]=f.toGlobalCoord(f.getExtent()["x0"===n[0]?0:1]):$q(x)&&(r[1]=y.toGlobalCoord(y.getExtent()["y0"===n[1]?0:1]))}isNaN(l)||(r[0]=l),isNaN(p)||(r[1]=p)}else r=[l,p];return r}var Zq=[["x0","y0"],["x1","y0"],["x1","y1"],["x0","y1"]];!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}We(t,e),t.prototype.updateTransform=function(e,t,n){t.eachSeries((function(e){var t=_q.getMarkerModelFromSeries(e,"markArea");if(t){var i=t.getData();i.each((function(t){var a=Ur(Zq,(function(a){return Xq(i,t,a,e,n)}));i.setItemLayout(t,a),i.getItemGraphicEl(t).setShape("points",a)}))}}),this)},t.prototype.renderSeries=function(e,t,n,i){var a=e.coordinateSystem,r=e.id,o=e.getData(),s=this.markerGroupMap,l=s.get(r)||s.set(r,{group:new Rc});this.group.add(l.group),this.markKeep(l);var p=function(e,t,n){var i,a,r=["x0","y0","x1","y1"];if(e){var o=Ur(e&&e.dimensions,(function(e){var n=t.getData();return Br(Br({},n.getDimensionInfo(n.mapDimension(e))||{}),{name:e,ordinalMeta:null})}));a=Ur(r,(function(e,t){return{name:e,type:o[t%2].type}})),i=new W_(a,n)}else i=new W_(a=[{name:"value",type:"float"}],n);var s=Ur(n.get("data"),Yr(Wq,t,e,n));e&&(s=Hr(s,Yr(Yq,e)));var l=e?function(e,t,n,i){return yb(e.coord[Math.floor(i/2)][i%2],a[i])}:function(e,t,n,i){return yb(e.value,a[i])};return i.initData(s,null,l),i.hasItemOption=!0,i}(a,e,t);t.setData(p),p.each((function(t){var n=Ur(Zq,(function(n){return Xq(p,t,n,e,i)})),r=a.getAxis("x").scale,s=a.getAxis("y").scale,l=r.getExtent(),c=s.getExtent(),d=[r.parse(p.get("x0",t)),r.parse(p.get("x1",t))],u=[s.parse(p.get("y0",t)),s.parse(p.get("y1",t))];Kc(d),Kc(u);var m=!!(l[0]>d[1]||l[1]u[1]||c[1]=0},t.prototype.getOrient=function(){return"vertical"===this.get("orient")?{index:1,name:"vertical"}:{index:0,name:"horizontal"}},t.type="legend.plain",t.dependencies=["series"],t.defaultOption={z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,symbolRotate:"inherit",symbolKeepAspect:!0,inactiveColor:"#ccc",inactiveBorderColor:"#ccc",inactiveBorderWidth:"auto",itemStyle:{color:"inherit",opacity:"inherit",borderColor:"inherit",borderWidth:"auto",borderCap:"inherit",borderJoin:"inherit",borderDashOffset:"inherit",borderMiterLimit:"inherit"},lineStyle:{width:"auto",color:"inherit",inactiveColor:"#ccc",inactiveWidth:2,opacity:"inherit",type:"inherit",cap:"inherit",join:"inherit",dashOffset:"inherit",miterLimit:"inherit"},textStyle:{color:"#333"},selectedMode:!0,selector:!1,selectorLabel:{show:!0,borderRadius:10,padding:[3,5,3,5],fontSize:12,fontFamily:"sans-serif",color:"#666",borderWidth:1,borderColor:"#666"},emphasis:{selectorLabel:{show:!0,color:"#eee",backgroundColor:"#666"}},selectorPosition:"auto",selectorItemGap:7,selectorButtonGap:10,tooltip:{show:!1}},t}(kv),Jq=Yr,eG=zr,tG=Rc,nG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.newlineDisabled=!1,n}return We(t,e),t.prototype.init=function(){this.group.add(this._contentGroup=new tG),this.group.add(this._selectorGroup=new tG),this._isFirstRender=!0},t.prototype.getContentGroup=function(){return this._contentGroup},t.prototype.getSelectorGroup=function(){return this._selectorGroup},t.prototype.render=function(e,t,n){var i=this._isFirstRender;if(this._isFirstRender=!1,this.resetInner(),e.get("show",!0)){var a=e.get("align"),r=e.get("orient");a&&"auto"!==a||(a="right"===e.get("left")&&"vertical"===r?"right":"left");var o=e.get("selector",!0),s=e.get("selectorPosition",!0);!o||s&&"auto"!==s||(s="horizontal"===r?"end":"start"),this.renderInner(a,e,t,n,o,r,s);var l=e.getBoxLayoutParams(),p={width:n.getWidth(),height:n.getHeight()},c=e.get("padding"),d=Sv(l,p,c),u=this.layoutInner(e,a,d,i,o,s),m=Sv(Nr({width:u.width,height:u.height},l),p,c);this.group.x=m.x-u.x,this.group.y=m.y-u.y,this.group.markRedraw(),this.group.add(this._backgroundEl=rV(u,e))}},t.prototype.resetInner=function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl),this.getSelectorGroup().removeAll()},t.prototype.renderInner=function(e,t,n,i,a,r,o){var s=this.getContentGroup(),l=Co(),p=t.get("selectedMode"),c=[];n.eachRawSeries((function(e){!e.get("legendHoverLink")&&c.push(e.id)})),eG(t.getData(),(function(a,r){var o=a.get("name");if(!this.newlineDisabled&&(""===o||"\n"===o)){var d=new tG;return d.newline=!0,void s.add(d)}var u=n.getSeriesByName(o)[0];if(!l.get(o)){if(u){var m=u.getData(),h=m.getVisual("legendLineStyle")||{},g=m.getVisual("legendIcon"),f=m.getVisual("style"),y=this._createItem(u,o,r,a,t,e,h,f,g,p,i);y.on("click",Jq(iG,o,null,i,c)).on("mouseover",Jq(rG,u.name,null,i,c)).on("mouseout",Jq(oG,u.name,null,i,c)),n.ssr&&y.eachChild((function(e){var t=Xm(e);t.seriesIndex=u.seriesIndex,t.dataIndex=r,t.ssrType="legend"})),l.set(o,!0)}else n.eachRawSeries((function(s){if(!l.get(o)&&s.legendVisualProvider){var d=s.legendVisualProvider;if(!d.containName(o))return;var u=d.indexOfName(o),m=d.getItemVisual(u,"style"),h=d.getItemVisual(u,"legendIcon"),g=Kl(m.fill);g&&0===g[3]&&(g[3]=.2,m=Br(Br({},m),{fill:tp(g,"rgba")}));var f=this._createItem(s,o,r,a,t,e,{},m,h,p,i);f.on("click",Jq(iG,null,o,i,c)).on("mouseover",Jq(rG,null,o,i,c)).on("mouseout",Jq(oG,null,o,i,c)),n.ssr&&f.eachChild((function(e){var t=Xm(e);t.seriesIndex=s.seriesIndex,t.dataIndex=r,t.ssrType="legend"})),l.set(o,!0)}}),this);0}}),this),a&&this._createSelector(a,t,i,r,o)},t.prototype._createSelector=function(e,t,n,i,a){var r=this.getSelectorGroup();eG(e,(function(e){var i=e.type,a=new Nm({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){n.dispatchAction({type:"all"===i?"legendAllSelect":"legendInverseSelect"})}});r.add(a),Yf(a,{normal:t.getModel("selectorLabel"),emphasis:t.getModel(["emphasis","selectorLabel"])},{defaultText:e.title}),Lh(a)}))},t.prototype._createItem=function(e,t,n,i,a,r,o,s,l,p,c){var d=e.visualDrawType,u=a.get("itemWidth"),m=a.get("itemHeight"),h=a.isSelected(t),g=i.get("symbolRotate"),f=i.get("symbolKeepAspect"),y=i.get("icon"),v=function(e,t,n,i,a,r,o){function s(e,t){"auto"===e.lineWidth&&(e.lineWidth=t.lineWidth>0?2:0),eG(e,(function(n,i){"inherit"===e[i]&&(e[i]=t[i])}))}var l=t.getModel("itemStyle"),p=l.getItemStyle(),c=0===e.lastIndexOf("empty",0)?"fill":"stroke",d=l.getShallow("decal");p.decal=d&&"inherit"!==d?cC(d,o):i.decal,"inherit"===p.fill&&(p.fill=i[a]);"inherit"===p.stroke&&(p.stroke=i[c]);"inherit"===p.opacity&&(p.opacity=("fill"===a?i:n).opacity);s(p,i);var u=t.getModel("lineStyle"),m=u.getLineStyle();if(s(m,n),"auto"===p.fill&&(p.fill=i.fill),"auto"===p.stroke&&(p.stroke=i.fill),"auto"===m.stroke&&(m.stroke=i.fill),!r){var h=t.get("inactiveBorderWidth"),g=p[c];p.lineWidth="auto"===h?i.lineWidth>0&&g?2:0:p.lineWidth,p.fill=t.get("inactiveColor"),p.stroke=t.get("inactiveBorderColor"),m.stroke=u.get("inactiveColor"),m.lineWidth=u.get("inactiveWidth")}return{itemStyle:p,lineStyle:m}}(l=y||l||"roundRect",i,o,s,d,h,c),x=new tG,b=i.getModel("textStyle");if(!Zr(e.getLegendIcon)||y&&"inherit"!==y){var w="inherit"===y&&e.getData().getVisual("symbol")?"inherit"===g?e.getData().getVisual("symbolRotate"):g:0;x.add(function(e){var t=e.icon||"roundRect",n=NS(t,0,0,e.itemWidth,e.itemHeight,e.itemStyle.fill,e.symbolKeepAspect);n.setStyle(e.itemStyle),n.rotation=(e.iconRotate||0)*Math.PI/180,n.setOrigin([e.itemWidth/2,e.itemHeight/2]),t.indexOf("empty")>-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2);return n}({itemWidth:u,itemHeight:m,icon:l,iconRotate:w,itemStyle:v.itemStyle,lineStyle:v.lineStyle,symbolKeepAspect:f}))}else x.add(e.getLegendIcon({itemWidth:u,itemHeight:m,icon:l,iconRotate:g,itemStyle:v.itemStyle,lineStyle:v.lineStyle,symbolKeepAspect:f}));var S="left"===r?u+5:-5,C=r,_=a.get("formatter"),T=t;Qr(_)&&_?T=_.replace("{name}",null!=t?t:""):Zr(_)&&(T=_(t));var I=h?b.getTextColor():i.get("inactiveColor");x.add(new Nm({style:Zf(b,{text:T,x:S,y:m/2,fill:I,align:C,verticalAlign:"middle"},{inheritColor:I})}));var E=new Fm({shape:x.getBoundingRect(),style:{fill:"transparent"}}),M=i.getModel("tooltip");return M.get("show")&&zf({el:E,componentModel:a,itemName:t,itemTooltipOption:M.option}),x.add(E),x.eachChild((function(e){e.silent=!0})),E.silent=!p,this.getContentGroup().add(x),Lh(x),x.__legendDataIndex=n,x},t.prototype.layoutInner=function(e,t,n,i,a,r){var o=this.getContentGroup(),s=this.getSelectorGroup();wv(e.get("orient"),o,e.get("itemGap"),n.width,n.height);var l=o.getBoundingRect(),p=[-l.x,-l.y];if(s.markRedraw(),o.markRedraw(),a){wv("horizontal",s,e.get("selectorItemGap",!0));var c=s.getBoundingRect(),d=[-c.x,-c.y],u=e.get("selectorButtonGap",!0),m=e.getOrient().index,h=0===m?"width":"height",g=0===m?"height":"width",f=0===m?"y":"x";"end"===r?d[m]+=l[h]+u:p[m]+=c[h]+u,d[1-m]+=l[g]/2-c[g]/2,s.x=d[0],s.y=d[1],o.x=p[0],o.y=p[1];var y={x:0,y:0};return y[h]=l[h]+u+c[h],y[g]=Math.max(l[g],c[g]),y[f]=Math.min(0,c[f]+d[1-m]),y}return o.x=p[0],o.y=p[1],this.group.getBoundingRect()},t.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},t.type="legend.plain",t}(ww);function iG(e,t,n,i){oG(e,t,n,i),n.dispatchAction({type:"legendToggleSelect",name:null!=e?e:t}),rG(e,t,n,i)}function aG(e){for(var t,n=e.getZr().storage.getDisplayList(),i=0,a=n.length;in[a],h=[-d.x,-d.y];t||(h[i]=l[s]);var g=[0,0],f=[-u.x,-u.y],y=co(e.get("pageButtonGap",!0),e.get("itemGap",!0));m&&("end"===e.get("pageButtonPosition",!0)?f[i]+=n[a]-u[a]:g[i]+=u[a]+y);f[1-i]+=d[r]/2-u[r]/2,l.setPosition(h),p.setPosition(g),c.setPosition(f);var v={x:0,y:0};if(v[a]=m?n[a]:d[a],v[r]=Math.max(d[r],u[r]),v[o]=Math.min(0,u[o]+f[1-i]),p.__rectSize=n[a],m){var x={x:0,y:0};x[a]=Math.max(n[a]-u[a]-y,0),x[r]=v[r],p.setClipPath(new Fm({shape:x})),p.__rectSize=x[a]}else c.eachChild((function(e){e.attr({invisible:!0,silent:!0})}));var b=this._getPageInfo(e);return null!=b.pageIndex&&pf(l,{x:b.contentPosition[0],y:b.contentPosition[1]},m?e:null),this._updatePageInfoView(e,b),v},t.prototype._pageGo=function(e,t,n){var i=this._getPageInfo(t)[e];null!=i&&n.dispatchAction({type:"legendScroll",scrollDataIndex:i,legendId:t.id})},t.prototype._updatePageInfoView=function(e,t){var n=this._controllerGroup;zr(["pagePrev","pageNext"],(function(i){var a=null!=t[i+"DataIndex"],r=n.childOfName(i);r&&(r.setStyle("fill",a?e.get("pageIconColor",!0):e.get("pageIconInactiveColor",!0)),r.cursor=a?"pointer":"default")}));var i=n.childOfName("pageText"),a=e.get("pageFormatter"),r=t.pageIndex,o=null!=r?r+1:0,s=t.pageCount;i&&a&&i.setStyle("text",Qr(a)?a.replace("{current}",null==o?"":o+"").replace("{total}",null==s?"":s+""):a({current:o,total:s}))},t.prototype._getPageInfo=function(e){var t=e.get("scrollDataIndex",!0),n=this.getContentGroup(),i=this._containerGroup.__rectSize,a=e.getOrient().index,r=pG[a],o=cG[a],s=this._findTargetItemIndex(t),l=n.children(),p=l[s],c=l.length,d=c?1:0,u={contentPosition:[n.x,n.y],pageCount:d,pageIndex:d-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!p)return u;var m=v(p);u.contentPosition[a]=-m.s;for(var h=s+1,g=m,f=m,y=null;h<=c;++h)(!(y=v(l[h]))&&f.e>g.s+i||y&&!x(y,g.s))&&(g=f.i>g.i?f:y)&&(null==u.pageNextDataIndex&&(u.pageNextDataIndex=g.i),++u.pageCount),f=y;for(h=s-1,g=m,f=m,y=null;h>=-1;--h)(y=v(l[h]))&&x(f,y.s)||!(g.i=t&&e.s<=t+i}},t.prototype._findTargetItemIndex=function(e){return this._showController?(this.getContentGroup().eachChild((function(i,a){var r=i.__legendDataIndex;null==n&&null!=r&&(n=a),r===e&&(t=a)})),null!=t?t:n):0;var t,n},t.type="legend.scroll"}(nG);var dG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="dataZoom.inside",t.defaultOption=Sy($L.defaultOption,{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),t}($L),uG=Pd();function mG(e,t){if(t){e.removeKey(t.model.uid);var n=t.controller;n&&n.dispose()}}function hG(e,t){e.isDisposed()||e.dispatchAction({type:"dataZoom",animation:{easing:"cubicOut",duration:100},batch:t})}function gG(e,t,n,i){return e.coordinateSystem.containPoint([n,i])}function fG(e){e.registerProcessor(e.PRIORITY.PROCESSOR.FILTER,(function(e,t){var n=uG(t),i=n.coordSysRecordMap||(n.coordSysRecordMap=Co());i.each((function(e){e.dataZoomInfoMap=null})),e.eachComponent({mainType:"dataZoom",subType:"inside"},(function(e){zr(HL(e).infoList,(function(n){var a=n.model.uid,r=i.get(a)||i.set(a,function(e,t){var n={model:t,containsPoint:Yr(gG,t),dispatchAction:Yr(hG,e),dataZoomInfoMap:null,controller:null},i=n.controller=new mP(e.getZr());return zr(["pan","zoom","scrollMove"],(function(e){i.on(e,(function(t){var i=[];n.dataZoomInfoMap.each((function(a){if(t.isAvailableBehavior(a.model.option)){var r=(a.getRange||{})[e],o=r&&r(a.dzReferCoordSysInfo,n.model.mainType,n.controller,t);!a.model.get("disabled",!0)&&o&&i.push({dataZoomId:a.model.id,start:o[0],end:o[1]})}})),i.length&&n.dispatchAction(i)}))})),n}(t,n.model));(r.dataZoomInfoMap||(r.dataZoomInfoMap=Co())).set(e.uid,{dzReferCoordSysInfo:n,model:e,getRange:null})}))})),i.each((function(e){var t,n=e.controller,a=e.dataZoomInfoMap;if(a){var r=a.keys()[0];null!=r&&(t=a.get(r))}if(t){var o=function(e){var t,n="type_",i={type_true:2,type_move:1,type_false:0,type_undefined:-1},a=!0;return e.each((function(e){var r=e.model,o=!r.get("disabled",!0)&&(!r.get("zoomLock",!0)||"move");i[n+o]>i[n+t]&&(t=o),a=a&&r.get("preventDefaultMouseMove",!0)})),{controlType:t,opt:{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!0,preventDefaultMouseMove:!!a}}}(a);n.enable(o.controlType,o.opt),n.setPointerChecker(e.containsPoint),Rw(e,"dispatchAction",t.model.get("throttle",!0),"fixRate")}else mG(i,e)}))}))}var yG=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type="dataZoom.inside",t}return We(t,e),t.prototype.render=function(t,n,i){e.prototype.render.apply(this,arguments),t.noTarget()?this._clear():(this.range=t.getPercentRange(),function(e,t,n){uG(e).coordSysRecordMap.each((function(e){var i=e.dataZoomInfoMap.get(t.uid);i&&(i.getRange=n)}))}(i,t,{pan:Kr(vG.pan,this),zoom:Kr(vG.zoom,this),scrollMove:Kr(vG.scrollMove,this)}))},t.prototype.dispose=function(){this._clear(),e.prototype.dispose.apply(this,arguments)},t.prototype._clear=function(){!function(e,t){for(var n=uG(e).coordSysRecordMap,i=n.keys(),a=0;a0?s.pixelStart+s.pixelLength-s.pixel:s.pixel-s.pixelStart)/s.pixelLength*(r[1]-r[0])+r[0],p=Math.max(1/i.scale,0);r[0]=(r[0]-l)*p+l,r[1]=(r[1]-l)*p+l;var c=this.dataZoomModel.findRepresentativeAxisProxy();if(c){var d=c.getMinMaxSpan();if(EA(0,r,[0,100],0,d.minSpan,d.maxSpan),this.range=r,a[0]!==r[0]||a[1]!==r[1])return r}}},pan:xG((function(e,t,n,i,a,r){var o=bG[i]([r.oldX,r.oldY],[r.newX,r.newY],t,a,n);return o.signal*(e[1]-e[0])*o.pixel/o.pixelLength})),scrollMove:xG((function(e,t,n,i,a,r){return bG[i]([0,0],[r.scrollDelta,r.scrollDelta],t,a,n).signal*(e[1]-e[0])*r.scrollDelta}))};function xG(e){return function(t,n,i,a){var r=this.range,o=r.slice(),s=t.axisModels[0];if(s)return EA(e(o,s,t,n,i,a),o,[0,100],"all"),this.range=o,r[0]!==o[0]||r[1]!==o[1]?o:void 0}}var bG={grid:function(e,t,n,i,a){var r=n.axis,o={},s=a.model.coordinateSystem.getRect();return e=e||[0,0],"x"===r.dim?(o.pixel=t[0]-e[0],o.pixelLength=s.width,o.pixelStart=s.x,o.signal=r.inverse?1:-1):(o.pixel=t[1]-e[1],o.pixelLength=s.height,o.pixelStart=s.y,o.signal=r.inverse?-1:1),o},polar:function(e,t,n,i,a){var r=n.axis,o={},s=a.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),p=s.getAngleAxis().getExtent();return e=e?s.pointToCoord(e):[0,0],t=s.pointToCoord(t),"radiusAxis"===n.mainType?(o.pixel=t[0]-e[0],o.pixelLength=l[1]-l[0],o.pixelStart=l[0],o.signal=r.inverse?1:-1):(o.pixel=t[1]-e[1],o.pixelLength=p[1]-p[0],o.pixelStart=p[0],o.signal=r.inverse?-1:1),o},singleAxis:function(e,t,n,i,a){var r=n.axis,o=a.model.coordinateSystem.getRect(),s={};return e=e||[0,0],"horizontal"===r.orient?(s.pixel=t[0]-e[0],s.pixelLength=o.width,s.pixelStart=o.x,s.signal=r.inverse?1:-1):(s.pixel=t[1]-e[1],s.pixelLength=o.height,s.pixelStart=o.y,s.signal=r.inverse?-1:1),s}};function wG(e){tV(e),e.registerComponentModel(dG),e.registerComponentView(yG),fG(e)}var SG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.type="dataZoom.slider",t.layoutMode="box",t.defaultOption=Sy($L.defaultOption,{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,borderColor:"#d2dbee",borderRadius:3,backgroundColor:"rgba(47,69,84,0)",dataBackground:{lineStyle:{color:"#d2dbee",width:.5},areaStyle:{color:"#d2dbee",opacity:.2}},selectedDataBackground:{lineStyle:{color:"#8fb0f7",width:.5},areaStyle:{color:"#8fb0f7",opacity:.2}},fillerColor:"rgba(135,175,274,0.2)",handleIcon:"path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z",handleSize:"100%",handleStyle:{color:"#fff",borderColor:"#ACB8D1"},moveHandleSize:7,moveHandleIcon:"path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z",moveHandleStyle:{color:"#D2DBEE",opacity:.7},showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:"#6E7079"},brushSelect:!0,brushStyle:{color:"rgba(135,175,274,0.15)"},emphasis:{handleStyle:{borderColor:"#8FB0F7"},moveHandleStyle:{color:"#8FB0F7"}}}),t}($L),CG=Fm,_G="horizontal",TG="vertical",IG=["line","bar","candlestick","scatter","custom"],EG={easing:"cubicOut",duration:100,delay:0},MG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._displayables={},n}return We(t,e),t.prototype.init=function(e,t){this.api=t,this._onBrush=Kr(this._onBrush,this),this._onBrushEnd=Kr(this._onBrushEnd,this)},t.prototype.render=function(t,n,i,a){if(e.prototype.render.apply(this,arguments),Rw(this,"_dispatchZoomAction",t.get("throttle"),"fixRate"),this._orient=t.getOrient(),!1!==t.get("show")){if(t.noTarget())return this._clear(),void this.group.removeAll();a&&"dataZoom"===a.type&&a.from===this.uid||this._buildView(),this._updateView()}else this.group.removeAll()},t.prototype.dispose=function(){this._clear(),e.prototype.dispose.apply(this,arguments)},t.prototype._clear=function(){Bw(this,"_dispatchZoomAction");var e=this.api.getZr();e.off("mousemove",this._onBrush),e.off("mouseup",this._onBrushEnd)},t.prototype._buildView=function(){var e=this.group;e.removeAll(),this._brushing=!1,this._displayables.brushRect=null,this._resetLocation(),this._resetInterval();var t=this._displayables.sliderGroup=new Rc;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),e.add(t),this._positionGroup()},t.prototype._resetLocation=function(){var e=this.dataZoomModel,t=this.api,n=e.get("brushSelect")?7:0,i=this._findCoordRect(),a={width:t.getWidth(),height:t.getHeight()},r=this._orient===_G?{right:a.width-i.x-i.width,top:a.height-30-7-n,width:i.width,height:30}:{right:7,top:i.y,width:30,height:i.height},o=Iv(e.option);zr(["right","top","width","height"],(function(e){"ph"===o[e]&&(o[e]=r[e])}));var s=Sv(o,a);this._location={x:s.x,y:s.y},this._size=[s.width,s.height],this._orient===TG&&this._size.reverse()},t.prototype._positionGroup=function(){var e=this.group,t=this._location,n=this._orient,i=this.dataZoomModel.getFirstTargetAxisModel(),a=i&&i.get("inverse"),r=this._displayables.sliderGroup,o=(this._dataShadowInfo||{}).otherAxisInverse;r.attr(n!==_G||a?n===_G&&a?{scaleY:o?1:-1,scaleX:-1}:n!==TG||a?{scaleY:o?-1:1,scaleX:-1,rotation:Math.PI/2}:{scaleY:o?-1:1,scaleX:1,rotation:Math.PI/2}:{scaleY:o?1:-1,scaleX:1});var s=e.getBoundingRect([r]);e.x=t.x-s.x,e.y=t.y-s.y,e.markRedraw()},t.prototype._getViewExtent=function(){return[0,this._size[0]]},t.prototype._renderBackground=function(){var e=this.dataZoomModel,t=this._size,n=this._displayables.sliderGroup,i=e.get("brushSelect");n.add(new CG({silent:!0,shape:{x:0,y:0,width:t[0],height:t[1]},style:{fill:e.get("backgroundColor")},z2:-40}));var a=new CG({shape:{x:0,y:0,width:t[0],height:t[1]},style:{fill:"transparent"},z2:0,onclick:Kr(this._onClickPanel,this)}),r=this.api.getZr();i?(a.on("mousedown",this._onBrushStart,this),a.cursor="crosshair",r.on("mousemove",this._onBrush),r.on("mouseup",this._onBrushEnd)):(r.off("mousemove",this._onBrush),r.off("mouseup",this._onBrushEnd)),n.add(a)},t.prototype._renderDataShadow=function(){var e=this._dataShadowInfo=this._prepareDataShadowInfo();if(this._displayables.dataShadowSegs=[],e){var t=this._size,n=this._shadowSize||[],i=e.series,a=i.getRawData(),r=i.getShadowDim&&i.getShadowDim(),o=r&&a.getDimensionInfo(r)?i.getShadowDim():e.otherDim;if(null!=o){var s=this._shadowPolygonPts,l=this._shadowPolylinePts;if(a!==this._shadowData||o!==this._shadowDim||t[0]!==n[0]||t[1]!==n[1]){var p=a.getDataExtent(o),c=.3*(p[1]-p[0]);p=[p[0]-c,p[1]+c];var d,u=[0,t[1]],m=[0,t[0]],h=[[t[0],0],[0,0]],g=[],f=m[1]/(a.count()-1),y=0,v=Math.round(a.count()/t[0]);a.each([o],(function(e,t){if(v>0&&t%v)y+=f;else{var n=null==e||isNaN(e)||""===e,i=n?0:Hc(e,p,u,!0);n&&!d&&t?(h.push([h[h.length-1][0],0]),g.push([g[g.length-1][0],0])):!n&&d&&(h.push([y,0]),g.push([y,0])),h.push([y,i]),g.push([y,i]),y+=f,d=n}})),s=this._shadowPolygonPts=h,l=this._shadowPolylinePts=g}this._shadowData=a,this._shadowDim=o,this._shadowSize=[t[0],t[1]];for(var x=this.dataZoomModel,b=0;b<3;b++){var w=S(1===b);this._displayables.sliderGroup.add(w),this._displayables.dataShadowSegs.push(w)}}}function S(e){var t=x.getModel(e?"selectedDataBackground":"dataBackground"),n=new Rc,i=new Bg({shape:{points:s},segmentIgnoreThreshold:1,style:t.getModel("areaStyle").getAreaStyle(),silent:!0,z2:-20}),a=new Lg({shape:{points:l},segmentIgnoreThreshold:1,style:t.getModel("lineStyle").getLineStyle(),silent:!0,z2:-19});return n.add(i),n.add(a),n}},t.prototype._prepareDataShadowInfo=function(){var e=this.dataZoomModel,t=e.get("showDataShadow");if(!1!==t){var n,i=this.ecModel;return e.eachTargetAxis((function(a,r){var o=e.getAxisProxy(a,r);o&&zr(o.getTargetSeriesModels(),(function(e){if(!(n||!0!==t&&Lr(IG,e.get("type"))<0)){var o,s=i.getComponent(UL(a),r).axis,l=function(e){var t={x:"y",y:"x",radius:"angle",angle:"radius"};return t[e]}(a),p=e.coordinateSystem;null!=l&&p.getOtherAxis&&(o=p.getOtherAxis(s).inverse),l=e.getData().mapDimension(l),n={thisAxis:s,series:e,thisDim:a,otherDim:l,otherAxisInverse:o}}}),this)}),this),n}},t.prototype._renderHandle=function(){var e=this.group,t=this._displayables,n=t.handles=[null,null],i=t.handleLabels=[null,null],a=this._displayables.sliderGroup,r=this._size,o=this.dataZoomModel,s=this.api,l=o.get("borderRadius")||0,p=o.get("brushSelect"),c=t.filler=new CG({silent:p,style:{fill:o.get("fillerColor")},textConfig:{position:"inside"}});a.add(c),a.add(new CG({silent:!0,subPixelOptimize:!0,shape:{x:0,y:0,width:r[0],height:r[1],r:l},style:{stroke:o.get("dataBackgroundColor")||o.get("borderColor"),lineWidth:1,fill:"rgba(0,0,0,0)"}})),zr([0,1],(function(t){var r=o.get("handleIcon");!FS[r]&&r.indexOf("path://")<0&&r.indexOf("image://")<0&&(r="path://"+r);var s=NS(r,-1,0,2,2,null,!0);s.attr({cursor:kG(this._orient),draggable:!0,drift:Kr(this._onDragMove,this,t),ondragend:Kr(this._onDragEnd,this),onmouseover:Kr(this._showDataInfo,this,!0),onmouseout:Kr(this._showDataInfo,this,!1),z2:5});var l=s.getBoundingRect(),p=o.get("handleSize");this._handleHeight=Wc(p,this._size[1]),this._handleWidth=l.width/l.height*this._handleHeight,s.setStyle(o.getModel("handleStyle").getItemStyle()),s.style.strokeNoScale=!0,s.rectHover=!0,s.ensureState("emphasis").style=o.getModel(["emphasis","handleStyle"]).getItemStyle(),Lh(s);var c=o.get("handleColor");null!=c&&(s.style.fill=c),a.add(n[t]=s);var d=o.getModel("textStyle");e.add(i[t]=new Nm({silent:!0,invisible:!0,style:Zf(d,{x:0,y:0,text:"",verticalAlign:"middle",align:"center",fill:d.getTextColor(),font:d.getFont()}),z2:10}))}),this);var d=c;if(p){var u=Wc(o.get("moveHandleSize"),r[1]),m=t.moveHandle=new Fm({style:o.getModel("moveHandleStyle").getItemStyle(),silent:!0,shape:{r:[0,0,2,2],y:r[1]-.5,height:u}}),h=.8*u,g=t.moveHandleIcon=NS(o.get("moveHandleIcon"),-h/2,-h/2,h,h,"#fff",!0);g.silent=!0,g.y=r[1]+u/2-.5,m.ensureState("emphasis").style=o.getModel(["emphasis","moveHandleStyle"]).getItemStyle();var f=Math.min(r[1]/2,Math.max(u,10));(d=t.moveZone=new Fm({invisible:!0,shape:{y:r[1]-f,height:u+f}})).on("mouseover",(function(){s.enterEmphasis(m)})).on("mouseout",(function(){s.leaveEmphasis(m)})),a.add(m),a.add(g),a.add(d)}d.attr({draggable:!0,cursor:kG(this._orient),drift:Kr(this._onDragMove,this,"all"),ondragstart:Kr(this._showDataInfo,this,!0),ondragend:Kr(this._onDragEnd,this),onmouseover:Kr(this._showDataInfo,this,!0),onmouseout:Kr(this._showDataInfo,this,!1)})},t.prototype._resetInterval=function(){var e=this._range=this.dataZoomModel.getPercentRange(),t=this._getViewExtent();this._handleEnds=[Hc(e[0],[0,100],t,!0),Hc(e[1],[0,100],t,!0)]},t.prototype._updateInterval=function(e,t){var n=this.dataZoomModel,i=this._handleEnds,a=this._getViewExtent(),r=n.findRepresentativeAxisProxy();if(r){var o=r.getMinMaxSpan(),s=[0,100];EA(t,i,a,n.get("zoomLock")?"all":e,null!=o.minSpan?Hc(o.minSpan,s,a,!0):null,null!=o.maxSpan?Hc(o.maxSpan,s,a,!0):null);var l=this._range,p=this._range=Kc([Hc(i[0],a,s,!0),Hc(i[1],a,s,!0)]);return!l||l[0]!==p[0]||l[1]!==p[1]}return!1},t.prototype._updateView=function(e){var t=this._displayables,n=this._handleEnds,i=Kc(n.slice()),a=this._size;zr([0,1],(function(e){var i=t.handles[e],r=this._handleHeight;i.attr({scaleX:r/2,scaleY:r/2,x:n[e]+(e?-1:1),y:a[1]/2-r/2})}),this),t.filler.setShape({x:i[0],y:0,width:i[1]-i[0],height:a[1]});var r={x:i[0],width:i[1]-i[0]};t.moveHandle&&(t.moveHandle.setShape(r),t.moveZone.setShape(r),t.moveZone.getBoundingRect(),t.moveHandleIcon&&t.moveHandleIcon.attr("x",r.x+r.width/2));for(var o=t.dataShadowSegs,s=[0,i[0],i[1],a[0]],l=0;lt[0]||n[1]<0||n[1]>t[1])){var i=this._handleEnds,a=(i[0]+i[1])/2,r=this._updateInterval("all",n[0]-a);this._updateView(),r&&this._dispatchZoomAction(!1)}},t.prototype._onBrushStart=function(e){var t=e.offsetX,n=e.offsetY;this._brushStart=new Ms(t,n),this._brushing=!0,this._brushStartTime=+new Date},t.prototype._onBrushEnd=function(e){if(this._brushing){var t=this._displayables.brushRect;if(this._brushing=!1,t){t.attr("ignore",!0);var n=t.shape;if(!(+new Date-this._brushStartTime<200&&Math.abs(n.width)<5)){var i=this._getViewExtent(),a=[0,100];this._range=Kc([Hc(n.x,i,a,!0),Hc(n.x+n.width,i,a,!0)]),this._handleEnds=[n.x,n.x+n.width],this._updateView(),this._dispatchZoomAction(!1)}}}},t.prototype._onBrush=function(e){this._brushing&&(gs(e.event),this._updateBrushRect(e.offsetX,e.offsetY))},t.prototype._updateBrushRect=function(e,t){var n=this._displayables,i=this.dataZoomModel,a=n.brushRect;a||(a=n.brushRect=new CG({silent:!0,style:i.getModel("brushStyle").getItemStyle()}),n.sliderGroup.add(a)),a.attr("ignore",!1);var r=this._brushStart,o=this._displayables.sliderGroup,s=o.transformCoordToLocal(e,t),l=o.transformCoordToLocal(r.x,r.y),p=this._size;s[0]=Math.max(Math.min(p[0],s[0]),0),a.setShape({x:l[0],y:0,width:s[0]-l[0],height:p[1]})},t.prototype._dispatchZoomAction=function(e){var t=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,animation:e?EG:null,start:t[0],end:t[1]})},t.prototype._findCoordRect=function(){var e,t=HL(this.dataZoomModel).infoList;if(!e&&t.length){var n=t[0].model.coordinateSystem;e=n.getRect&&n.getRect()}if(!e){var i=this.api.getWidth(),a=this.api.getHeight();e={x:.2*i,y:.2*a,width:.6*i,height:.6*a}}return e},t.type="dataZoom.slider",t}(YL);function kG(e){return"vertical"===e?"ns-resize":"ew-resize"}function PG(e){e.registerComponentModel(SG),e.registerComponentView(MG),tV(e)}function DG(e){dI(wG),dI(PG)}var OG=function(e,t,n){var i=Ar((AG[e]||{})[t]);return n&&Xr(i)?i[i.length-1]:i},AG={color:{active:["#006edd","#e0ffff"],inactive:["rgba(0,0,0,0)"]},colorHue:{active:[0,360],inactive:[0,0]},colorSaturation:{active:[.3,1],inactive:[0,0]},colorLightness:{active:[.9,.5],inactive:[0,0]},colorAlpha:{active:[.3,1],inactive:[0,0]},opacity:{active:[.3,1],inactive:[0,0]},symbol:{active:["circle","roundRect","diamond"],inactive:["none"]},symbolSize:{active:[10,50],inactive:[0,0]}},FG=dO.mapVisual,RG=dO.eachVisual,BG=Xr,NG=zr,LG=Kc,VG=Hc,qG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.stateList=["inRange","outOfRange"],n.replacableOptionKeys=["inRange","outOfRange","target","controller","color"],n.layoutMode={type:"box",ignoreSize:!0},n.dataBound=[-1/0,1/0],n.targetVisuals={},n.controllerVisuals={},n}return We(t,e),t.prototype.init=function(e,t,n){this.mergeDefaultAndTheme(e,n)},t.prototype.optionUpdated=function(e,t){var n=this.option;!t&&sq(n,e,this.replacableOptionKeys),this.textStyleModel=this.getModel("textStyle"),this.resetItemSize(),this.completeVisualOption()},t.prototype.resetVisual=function(e){var t=this.stateList;e=Kr(e,this),this.controllerVisuals=oq(this.option.controller,t,e),this.targetVisuals=oq(this.option.target,t,e)},t.prototype.getItemSymbol=function(){return null},t.prototype.getTargetSeriesIndices=function(){var e=this.option.seriesIndex,t=[];return null==e||"all"===e?this.ecModel.eachSeries((function(e,n){t.push(n)})):t=vd(e),t},t.prototype.eachTargetSeries=function(e,t){zr(this.getTargetSeriesIndices(),(function(n){var i=this.ecModel.getSeriesByIndex(n);i&&e.call(t,i)}),this)},t.prototype.isTargetSeries=function(e){var t=!1;return this.eachTargetSeries((function(n){n===e&&(t=!0)})),t},t.prototype.formatValueText=function(e,t,n){var i,a=this.option,r=a.precision,o=this.dataBound,s=a.formatter;n=n||["<",">"],Xr(e)&&(e=e.slice(),i=!0);var l=t?e:i?[p(e[0]),p(e[1])]:p(e);return Qr(s)?s.replace("{value}",i?l[0]:l).replace("{value2}",i?l[1]:l):Zr(s)?i?s(e[0],e[1]):s(e):i?e[0]===o[0]?n[0]+" "+l[1]:e[1]===o[1]?n[1]+" "+l[0]:l[0]+" - "+l[1]:l;function p(e){return e===o[0]?"min":e===o[1]?"max":(+e).toFixed(Math.min(r,20))}},t.prototype.resetExtent=function(){var e=this.option,t=LG([e.min,e.max]);this._dataExtent=t},t.prototype.getDataDimensionIndex=function(e){var t=this.option.dimension;if(null!=t)return e.getDimensionIndex(t);for(var n=e.dimensions,i=n.length-1;i>=0;i--){var a=n[i],r=e.getDimensionInfo(a);if(!r.isCalculationCoord)return r.storeDimIndex}},t.prototype.getExtent=function(){return this._dataExtent.slice()},t.prototype.completeVisualOption=function(){var e=this.ecModel,t=this.option,n={inRange:t.inRange,outOfRange:t.outOfRange},i=t.target||(t.target={}),a=t.controller||(t.controller={});Fr(i,n),Fr(a,n);var r=this.isCategory();function o(n){BG(t.color)&&!n.inRange&&(n.inRange={color:t.color.slice().reverse()}),n.inRange=n.inRange||{color:e.get("gradientColor")}}o.call(this,i),o.call(this,a),function(e,t,n){var i=e[t],a=e[n];i&&!a&&(a=e[n]={},NG(i,(function(e,t){if(dO.isValidType(t)){var n=OG(t,"inactive",r);null!=n&&(a[t]=n,"color"!==t||a.hasOwnProperty("opacity")||a.hasOwnProperty("colorAlpha")||(a.opacity=[0,0]))}})))}.call(this,i,"inRange","outOfRange"),function(e){var t=(e.inRange||{}).symbol||(e.outOfRange||{}).symbol,n=(e.inRange||{}).symbolSize||(e.outOfRange||{}).symbolSize,i=this.get("inactiveColor"),a=this.getItemSymbol()||"roundRect";NG(this.stateList,(function(o){var s=this.itemSize,l=e[o];l||(l=e[o]={color:r?i:[i]}),null==l.symbol&&(l.symbol=t&&Ar(t)||(r?a:[a])),null==l.symbolSize&&(l.symbolSize=n&&Ar(n)||(r?s[0]:[s[0],s[0]])),l.symbol=FG(l.symbol,(function(e){return"none"===e?a:e}));var p=l.symbolSize;if(null!=p){var c=-1/0;RG(p,(function(e){e>c&&(c=e)})),l.symbolSize=FG(p,(function(e){return VG(e,[0,c],[0,s[0]],!0)}))}}),this)}.call(this,a)},t.prototype.resetItemSize=function(){this.itemSize=[parseFloat(this.get("itemWidth")),parseFloat(this.get("itemHeight"))]},t.prototype.isCategory=function(){return!!this.option.categories},t.prototype.setSelected=function(e){},t.prototype.getSelected=function(){return null},t.prototype.getValueState=function(e){return null},t.prototype.getVisualMeta=function(e){return null},t.type="visualMap",t.dependencies=["series"],t.defaultOption={show:!0,z:4,seriesIndex:"all",min:0,max:200,left:0,right:null,top:null,bottom:0,itemWidth:null,itemHeight:null,inverse:!1,orient:"vertical",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",contentColor:"#5793f3",inactiveColor:"#aaa",borderWidth:0,padding:5,textGap:10,precision:0,textStyle:{color:"#333"}},t}(kv),GG=[20,140],zG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.optionUpdated=function(t,n){e.prototype.optionUpdated.apply(this,arguments),this.resetExtent(),this.resetVisual((function(e){e.mappingMethod="linear",e.dataExtent=this.getExtent()})),this._resetRange()},t.prototype.resetItemSize=function(){e.prototype.resetItemSize.apply(this,arguments);var t=this.itemSize;(null==t[0]||isNaN(t[0]))&&(t[0]=GG[0]),(null==t[1]||isNaN(t[1]))&&(t[1]=GG[1])},t.prototype._resetRange=function(){var e=this.getExtent(),t=this.option.range;!t||t.auto?(e.auto=1,this.option.range=e):Xr(t)&&(t[0]>t[1]&&t.reverse(),t[0]=Math.max(t[0],e[0]),t[1]=Math.min(t[1],e[1]))},t.prototype.completeVisualOption=function(){e.prototype.completeVisualOption.apply(this,arguments),zr(this.stateList,(function(e){var t=this.option.controller[e].symbolSize;t&&t[0]!==t[1]&&(t[0]=t[1]/3)}),this)},t.prototype.setSelected=function(e){this.option.range=e.slice(),this._resetRange()},t.prototype.getSelected=function(){var e=this.getExtent(),t=Kc((this.get("range")||[]).slice());return t[0]>e[1]&&(t[0]=e[1]),t[1]>e[1]&&(t[1]=e[1]),t[0]=n[1]||e<=t[1])?"inRange":"outOfRange"},t.prototype.findTargetDataIndices=function(e){var t=[];return this.eachTargetSeries((function(n){var i=[],a=n.getData();a.each(this.getDataDimensionIndex(a),(function(t,n){e[0]<=t&&t<=e[1]&&i.push(n)}),this),t.push({seriesId:n.id,dataIndex:i})}),this),t},t.prototype.getVisualMeta=function(e){var t=UG(this,"outOfRange",this.getExtent()),n=UG(this,"inRange",this.option.range.slice()),i=[];function a(t,n){i.push({value:t,color:e(t,n)})}for(var r=0,o=0,s=n.length,l=t.length;oe[1])break;n.push({color:this.getControllerVisual(r,"color",t),offset:a/100})}return n.push({color:this.getControllerVisual(e[1],"color",t),offset:1}),n},t.prototype._createBarPoints=function(e,t){var n=this.visualMapModel.itemSize;return[[n[0]-t[0],e[0]],[n[0],e[0]],[n[0],e[1]],[n[0]-t[1],e[1]]]},t.prototype._createBarGroup=function(e){var t=this._orient,n=this.visualMapModel.get("inverse");return new Rc("horizontal"!==t||n?"horizontal"===t&&n?{scaleX:"bottom"===e?-1:1,rotation:-Math.PI/2}:"vertical"!==t||n?{scaleX:"left"===e?1:-1}:{scaleX:"left"===e?1:-1,scaleY:-1}:{scaleX:"bottom"===e?1:-1,rotation:Math.PI/2})},t.prototype._updateHandle=function(e,t){if(this._useHandle){var n=this._shapes,i=this.visualMapModel,a=n.handleThumbs,r=n.handleLabels,o=i.itemSize,s=i.getExtent();YG([0,1],(function(l){var p=a[l];p.setStyle("fill",t.handlesColor[l]),p.y=e[l];var c=KG(e[l],[0,o[1]],s,!0),d=this.getControllerVisual(c,"symbolSize");p.scaleX=p.scaleY=d/o[0],p.x=o[0]-d/2;var u=Of(n.handleLabelPoints[l],Df(p,this.group));r[l].setStyle({x:u[0],y:u[1],text:i.formatValueText(this._dataInterval[l]),verticalAlign:"middle",align:"vertical"===this._orient?this._applyTransform("left",n.mainGroup):"center"})}),this)}},t.prototype._showIndicator=function(e,t,n,i){var a=this.visualMapModel,r=a.getExtent(),o=a.itemSize,s=[0,o[1]],l=this._shapes,p=l.indicator;if(p){p.attr("invisible",!1);var c=this.getControllerVisual(e,"color",{convertOpacityToAlpha:!0}),d=this.getControllerVisual(e,"symbolSize"),u=KG(e,r,s,!0),m=o[0]-d/2,h={x:p.x,y:p.y};p.y=u,p.x=m;var g=Of(l.indicatorLabelPoint,Df(p,this.group)),f=l.indicatorLabel;f.attr("invisible",!1);var y=this._applyTransform("left",l.mainGroup),v="horizontal"===this._orient;f.setStyle({text:(n||"")+a.formatValueText(t),verticalAlign:v?y:"middle",align:v?"center":y});var x={x:m,y:u,style:{fill:c}},b={style:{x:g[0],y:g[1]}};if(a.ecModel.isAnimationEnabled()&&!this._firstShowIndicator){var w={duration:100,easing:"cubicInOut",additive:!0};p.x=h.x,p.y=h.y,p.animateTo(x,w),f.animateTo(b,w)}else p.attr(x),f.attr(b);this._firstShowIndicator=!1;var S=this._shapes.handleLabels;if(S)for(var C=0;Ca[1]&&(p[1]=1/0),t&&(p[0]===-1/0?this._showIndicator(l,p[1],"< ",o):p[1]===1/0?this._showIndicator(l,p[0],"> ",o):this._showIndicator(l,l,"≈ ",o));var c=this._hoverLinkDataIndices,d=[];(t||ez(n))&&(d=this._hoverLinkDataIndices=n.findTargetDataIndices(p));var u=function(e,t){var n={},i={};return a(e||[],n),a(t||[],i,n),[r(n),r(i)];function a(e,t,n){for(var i=0,a=e.length;i=0&&(a.dimension=r,i.push(a))}})),e.getData().setVisual("visualMeta",i)}}];function rz(e,t,n,i){for(var a=t.targetVisuals[i],r=dO.prepareVisualTypes(a),o={color:wS(e.getData(),"color")},s=0,l=r.length;s0:e.splitNumber>0)&&!e.calculable?"piecewise":"continuous"})),e.registerAction(nz,iz),zr(az,(function(t){e.registerVisual(e.PRIORITY.VISUAL.COMPONENT,t)})),e.registerPreprocessor(sz))}function dz(e){e.registerComponentModel(zG),e.registerComponentView(QG),cz(e)}var uz=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._pieceList=[],n}return We(t,e),t.prototype.optionUpdated=function(t,n){e.prototype.optionUpdated.apply(this,arguments),this.resetExtent();var i=this._mode=this._determineMode();this._pieceList=[],mz[this._mode].call(this,this._pieceList),this._resetSelected(t,n);var a=this.option.categories;this.resetVisual((function(e,t){"categories"===i?(e.mappingMethod="category",e.categories=Ar(a)):(e.dataExtent=this.getExtent(),e.mappingMethod="piecewise",e.pieceList=Ur(this._pieceList,(function(e){return e=Ar(e),"inRange"!==t&&(e.visual=null),e})))}))},t.prototype.completeVisualOption=function(){var t=this.option,n={},i=dO.listVisualTypes(),a=this.isCategory();function r(e,t,n){return e&&e[t]&&e[t].hasOwnProperty(n)}zr(t.pieces,(function(e){zr(i,(function(t){e.hasOwnProperty(t)&&(n[t]=1)}))})),zr(n,(function(e,n){var i=!1;zr(this.stateList,(function(e){i=i||r(t,e,n)||r(t.target,e,n)}),this),!i&&zr(this.stateList,(function(e){(t[e]||(t[e]={}))[n]=OG(n,"inRange"===e?"active":"inactive",a)}))}),this),e.prototype.completeVisualOption.apply(this,arguments)},t.prototype._resetSelected=function(e,t){var n=this.option,i=this._pieceList,a=(t?n:e).selected||{};if(n.selected=a,zr(i,(function(e,t){var n=this.getSelectedMapKey(e);a.hasOwnProperty(n)||(a[n]=!0)}),this),"single"===n.selectedMode){var r=!1;zr(i,(function(e,t){var n=this.getSelectedMapKey(e);a[n]&&(r?a[n]=!1:r=!0)}),this)}},t.prototype.getItemSymbol=function(){return this.get("itemSymbol")},t.prototype.getSelectedMapKey=function(e){return"categories"===this._mode?e.value+"":e.index+""},t.prototype.getPieceList=function(){return this._pieceList},t.prototype._determineMode=function(){var e=this.option;return e.pieces&&e.pieces.length>0?"pieces":this.option.categories?"categories":"splitNumber"},t.prototype.setSelected=function(e){this.option.selected=Ar(e)},t.prototype.getValueState=function(e){var t=dO.findPieceIndex(e,this._pieceList);return null!=t&&this.option.selected[this.getSelectedMapKey(this._pieceList[t])]?"inRange":"outOfRange"},t.prototype.findTargetDataIndices=function(e){var t=[],n=this._pieceList;return this.eachTargetSeries((function(i){var a=[],r=i.getData();r.each(this.getDataDimensionIndex(r),(function(t,i){dO.findPieceIndex(t,n)===e&&a.push(i)}),this),t.push({seriesId:i.id,dataIndex:a})}),this),t},t.prototype.getRepresentValue=function(e){var t;if(this.isCategory())t=e.value;else if(null!=e.value)t=e.value;else{var n=e.interval||[];t=n[0]===-1/0&&n[1]===1/0?0:(n[0]+n[1])/2}return t},t.prototype.getVisualMeta=function(e){if(!this.isCategory()){var t=[],n=["",""],i=this,a=this._pieceList.slice();if(a.length){var r=a[0].interval[0];r!==-1/0&&a.unshift({interval:[-1/0,r]}),(r=a[a.length-1].interval[1])!==1/0&&a.push({interval:[r,1/0]})}else a.push({interval:[-1/0,1/0]});var o=-1/0;return zr(a,(function(e){var t=e.interval;t&&(t[0]>o&&s([o,t[0]],"outOfRange"),s(t.slice()),o=t[1])}),this),{stops:t,outerColors:n}}function s(a,r){var o=i.getRepresentValue({interval:a});r||(r=i.getValueState(o));var s=e(o,r);a[0]===-1/0?n[0]=s:a[1]===1/0?n[1]=s:t.push({value:a[0],color:s},{value:a[1],color:s})}},t.type="visualMap.piecewise",t.defaultOption=Sy(qG.defaultOption,{selected:null,minOpen:!1,maxOpen:!1,align:"auto",itemWidth:20,itemHeight:14,itemSymbol:"roundRect",pieces:null,categories:null,splitNumber:5,selectedMode:"multiple",itemGap:10,hoverLink:!0}),t}(qG),mz={splitNumber:function(e){var t=this.option,n=Math.min(t.precision,20),i=this.getExtent(),a=t.splitNumber;a=Math.max(parseInt(a,10),1),t.splitNumber=a;for(var r=(i[1]-i[0])/a;+r.toFixed(n)!==r&&n<5;)n++;t.precision=n,r=+r.toFixed(n),t.minOpen&&e.push({interval:[-1/0,i[0]],close:[0,0]});for(var o=0,s=i[0];o","≥"][t[0]]];e.text=e.text||this.formatValueText(null!=e.value?e.value:e.interval,!1,n)}),this)}};function hz(e,t){var n=e.inverse;("vertical"===e.orient?!n:n)&&t.reverse()}var gz=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return We(t,e),t.prototype.doRender=function(){var e=this.group;e.removeAll();var t=this.visualMapModel,n=t.get("textGap"),i=t.textStyleModel,a=i.getFont(),r=i.getTextColor(),o=this._getItemAlign(),s=t.itemSize,l=this._getViewData(),p=l.endsText,c=po(t.get("showLabel",!0),!p);p&&this._renderEndsText(e,p[0],s,c,o),zr(l.viewPieceList,(function(i){var l=i.piece,p=new Rc;p.onclick=Kr(this._onItemClick,this,l),this._enableHoverLink(p,i.indexInModelPieceList);var d=t.getRepresentValue(l);if(this._createItemSymbol(p,d,[0,0,s[0],s[1]]),c){var u=this.visualMapModel.getValueState(d);p.add(new Nm({style:{x:"right"===o?-n:s[0]+n,y:s[1]/2,text:l.text,verticalAlign:"middle",align:o,font:a,fill:r,opacity:"outOfRange"===u?.5:1}}))}e.add(p)}),this),p&&this._renderEndsText(e,p[1],s,c,o),wv(t.get("orient"),e,t.get("itemGap")),this.renderBackground(e),this.positionGroup(e)},t.prototype._enableHoverLink=function(e,t){var n=this;e.on("mouseover",(function(){return i("highlight")})).on("mouseout",(function(){return i("downplay")}));var i=function(e){var i=n.visualMapModel;i.option.hoverLink&&n.api.dispatchAction({type:e,batch:$G(i.findTargetDataIndices(t),i)})}},t.prototype._getItemAlign=function(){var e=this.visualMapModel,t=e.option;if("vertical"===t.orient)return WG(e,this.api,e.itemSize);var n=t.align;return n&&"auto"!==n||(n="left"),n},t.prototype._renderEndsText=function(e,t,n,i,a){if(t){var r=new Rc,o=this.visualMapModel.textStyleModel;r.add(new Nm({style:Zf(o,{x:i?"right"===a?n[0]:0:n[0]/2,y:n[1]/2,verticalAlign:"middle",align:i?a:"center",text:t})})),e.add(r)}},t.prototype._getViewData=function(){var e=this.visualMapModel,t=Ur(e.getPieceList(),(function(e,t){return{piece:e,indexInModelPieceList:t}})),n=e.get("text"),i=e.get("orient"),a=e.get("inverse");return("horizontal"===i?a:!a)?t.reverse():n&&(n=n.slice().reverse()),{viewPieceList:t,endsText:n}},t.prototype._createItemSymbol=function(e,t,n){e.add(NS(this.getControllerVisual(t,"symbol"),n[0],n[1],n[2],n[3],this.getControllerVisual(t,"color")))},t.prototype._onItemClick=function(e){var t=this.visualMapModel,n=t.option,i=n.selectedMode;if(i){var a=Ar(n.selected),r=t.getSelectedMapKey(e);"single"===i||!0===i?(a[r]=!0,zr(a,(function(e,t){a[t]=t===r}))):a[r]=!a[r],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:a})}},t.type="visualMap.piecewise",t}(jG);function fz(e){e.registerComponentModel(uz),e.registerComponentView(gz),cz(e)}function yz(e){dI(dz),dI(fz)}Pd();var vz={value:"eq","<":"lt","<=":"lte",">":"gt",">=":"gte","=":"eq","!=":"ne","<>":"ne"},xz=function(){function e(e){if(null==(this._condVal=Qr(e)?new RegExp(e):so(e)?e:null)){var t="";0,hd(t)}}return e.prototype.evaluate=function(e){var t=typeof e;return Qr(t)?this._condVal.test(e):!!eo(t)&&this._condVal.test(e+"")},e}(),bz=function(){function e(){}return e.prototype.evaluate=function(){return this.value},e}(),wz=function(){function e(){}return e.prototype.evaluate=function(){for(var e=this.children,t=0;t=Dz:-l>=Dz),u=l>0?l%Dz:l%Dz+Dz,m=!1;m=!!d||!lp(c)&&u>=Pz==!!p;var h=e+n*kz(r),g=t+i*Mz(r);this._start&&this._add("M",h,g);var f=Math.round(a*Oz);if(d){var y=1/this._p,v=(p?1:-1)*(Dz-y);this._add("A",n,i,f,1,+p,e+n*kz(r+v),t+i*Mz(r+v)),y>.01&&this._add("A",n,i,f,0,+p,h,g)}else{var x=e+n*kz(o),b=t+i*Mz(o);this._add("A",n,i,f,+m,+p,x,b)}},e.prototype.rect=function(e,t,n,i){this._add("M",e,t),this._add("l",n,0),this._add("l",0,i),this._add("l",-n,0),this._add("Z")},e.prototype.closePath=function(){this._d.length>0&&this._add("Z")},e.prototype._add=function(e,t,n,i,a,r,o,s,l){for(var p=[],c=this._p,d=1;d"}(a,r)+("style"!==a?os(o):o||"")+(i?""+n+Ur(i,(function(t){return e(t)})).join(n)+n:"")+("")}(e)}function Hz(e){return{zrId:e,shadowCache:{},patternCache:{},gradientCache:{},clipPathCache:{},defs:{},cssNodes:{},cssAnims:{},cssStyleCache:{},cssAnimIdx:0,shadowIdx:0,gradientIdx:0,patternIdx:0,clipPathIdx:0}}function Wz(e,t,n,i){return Uz("svg","root",{width:e,height:t,xmlns:Vz,"xmlns:xlink":qz,version:"1.1",baseProfile:"full",viewBox:!!i&&"0 0 "+e+" "+t},n)}var $z=0;function Kz(){return $z++}var Yz={cubicIn:"0.32,0,0.67,0",cubicOut:"0.33,1,0.68,1",cubicInOut:"0.65,0,0.35,1",quadraticIn:"0.11,0,0.5,0",quadraticOut:"0.5,1,0.89,1",quadraticInOut:"0.45,0,0.55,1",quarticIn:"0.5,0,0.75,0",quarticOut:"0.25,1,0.5,1",quarticInOut:"0.76,0,0.24,1",quinticIn:"0.64,0,0.78,0",quinticOut:"0.22,1,0.36,1",quinticInOut:"0.83,0,0.17,1",sinusoidalIn:"0.12,0,0.39,0",sinusoidalOut:"0.61,1,0.88,1",sinusoidalInOut:"0.37,0,0.63,1",exponentialIn:"0.7,0,0.84,0",exponentialOut:"0.16,1,0.3,1",exponentialInOut:"0.87,0,0.13,1",circularIn:"0.55,0,1,0.45",circularOut:"0,0.55,0.45,1",circularInOut:"0.85,0,0.15,1"},Xz="transform-origin";function Zz(e,t,n){var i=Br({},e.shape);Br(i,t),e.buildPath(n,i);var a=new Az;return a.reset(vp(e)),n.rebuildPath(a,1),a.generateStr(),a.getStr()}function Qz(e,t){var n=t.originX,i=t.originY;(n||i)&&(e[Xz]=n+"px "+i+"px")}var Jz={fill:"fill",opacity:"opacity",lineWidth:"stroke-width",lineDashOffset:"stroke-dashoffset"};function eU(e,t){var n=t.zrId+"-ani-"+t.cssAnimIdx++;return t.cssAnims[n]=e,n}function tU(e){return Qr(e)?Yz[e]?"cubic-bezier("+Yz[e]+")":Dl(e)?e:"":""}function nU(e,t,n,i){var a=e.animators,r=a.length,o=[];if(e instanceof Kg){var s=function(e,t,n){var i,a,r=e.shape.paths,o={};if(zr(r,(function(e){var t=Hz(n.zrId);t.animation=!0,nU(e,{},t,!0);var r=t.cssAnims,s=t.cssNodes,l=$r(r),p=l.length;if(p){var c=r[a=l[p-1]];for(var d in c){var u=c[d];o[d]=o[d]||{d:""},o[d].d+=u.d||""}for(var m in s){var h=s[m].animation;h.indexOf(a)>=0&&(i=h)}}})),i){t.d=!1;var s=eU(o,n);return i.replace(a,s)}}(e,t,n);if(s)o.push(s);else if(!r)return}else if(!r)return;for(var l={},p=0;p0})).length)return eU(c,n)+" "+a[0]+" both"}for(var f in l){(s=g(l[f]))&&o.push(s)}if(o.length){var y=n.zrId+"-cls-"+Kz();n.cssNodes["."+y]={animation:o.join(",")},t.class=y}}function iU(e,t,n,i){var a=JSON.stringify(e),r=n.cssStyleCache[a];r||(r=n.zrId+"-cls-"+Kz(),n.cssStyleCache[a]=r,n.cssNodes["."+r+(i?":hover":"")]=e),t.class=t.class?t.class+" "+r:r}var aU=Math.round;function rU(e){return e&&Qr(e.src)}function oU(e){return e&&Zr(e.toDataURL)}function sU(e,t,n,i){Lz((function(a,r){var o="fill"===a||"stroke"===a;o&&fp(r)?vU(t,e,a,i):o&&mp(r)?xU(n,e,a,i):e[a]=o&&"none"===r?"transparent":r}),t,n,!1),function(e,t,n){var i=e.style;if(function(e){return e&&(e.shadowBlur||e.shadowOffsetX||e.shadowOffsetY)}(i)){var a=function(e){var t=e.style,n=e.getGlobalScale();return[t.shadowColor,(t.shadowBlur||0).toFixed(2),(t.shadowOffsetX||0).toFixed(2),(t.shadowOffsetY||0).toFixed(2),n[0],n[1]].join(",")}(e),r=n.shadowCache,o=r[a];if(!o){var s=e.getGlobalScale(),l=s[0],p=s[1];if(!l||!p)return;var c=i.shadowOffsetX||0,d=i.shadowOffsetY||0,u=i.shadowBlur,m=op(i.shadowColor),h=m.opacity,g=m.color,f=u/2/l+" "+u/2/p;o=n.zrId+"-s"+n.shadowIdx++,n.defs[o]=Uz("filter",o,{id:o,x:"-100%",y:"-100%",width:"300%",height:"300%"},[Uz("feDropShadow","",{dx:c/l,dy:d/p,stdDeviation:f,"flood-color":g,"flood-opacity":h})]),r[a]=o}t.filter=yp(o)}}(n,e,i)}function lU(e,t){var n=zc(t);n&&(n.each((function(t,n){null!=t&&(e[(Gz+n).toLowerCase()]=t+"")})),t.isSilent()&&(e[Gz+"silent"]="true"))}function pU(e){return lp(e[0]-1)&&lp(e[1])&&lp(e[2])&&lp(e[3]-1)}function cU(e,t,n){if(t&&(!function(e){return lp(e[4])&&lp(e[5])}(t)||!pU(t))){var i=n?10:1e4;e.transform=pU(t)?"translate("+aU(t[4]*i)/i+" "+aU(t[5]*i)/i+")":function(e){return"matrix("+pp(e[0])+","+pp(e[1])+","+pp(e[2])+","+pp(e[3])+","+cp(e[4])+","+cp(e[5])+")"}(t)}}function dU(e,t,n){for(var i=e.points,a=[],r=0;r=0&&o||r;s&&(a=ap(s))}var l=i.lineWidth;l&&(l/=!i.strokeNoScale&&e.transform?e.transform[0]:1);var p={cursor:"pointer"};a&&(p.fill=a),i.stroke&&(p.stroke=i.stroke),l&&(p["stroke-width"]=l),iU(p,t,n,!0)}}(e,r,t),Uz(s,e.id+"",r)}function yU(e,t){return e instanceof Sm?fU(e,t):e instanceof Em?function(e,t){var n=e.style,i=n.image;if(i&&!Qr(i)&&(rU(i)?i=i.src:oU(i)&&(i=i.toDataURL())),i){var a=n.x||0,r=n.y||0,o={href:i,width:n.width,height:n.height};return a&&(o.x=a),r&&(o.y=r),cU(o,e.transform),sU(o,n,e,t),lU(o,e),t.animation&&nU(e,o,t),Uz("image",e.id+"",o)}}(e,t):e instanceof _m?function(e,t){var n=e.style,i=n.text;if(null!=i&&(i+=""),i&&!isNaN(n.x)&&!isNaN(n.y)){var a=n.font||hr,r=n.x||0,o=function(e,t,n){return"top"===n?e+=t/2:"bottom"===n&&(e-=t/2),e}(n.y||0,Sc(a),n.textBaseline),s={"dominant-baseline":"central","text-anchor":dp[n.textAlign]||n.textAlign};if(Um(n)){var l="",p=n.fontStyle,c=Gm(n.fontSize);if(!parseFloat(c))return;var d=n.fontFamily||mr,u=n.fontWeight;l+="font-size:"+c+";font-family:"+d+";",p&&"normal"!==p&&(l+="font-style:"+p+";"),u&&"normal"!==u&&(l+="font-weight:"+u+";"),s.style=l}else s.style="font: "+a;return i.match(/\s/)&&(s["xml:space"]="preserve"),r&&(s.x=r),o&&(s.y=o),cU(s,e.transform),sU(s,n,e,t),lU(s,e),t.animation&&nU(e,s,t),Uz("text",e.id+"",s,void 0,i)}}(e,t):void 0}function vU(e,t,n,i){var a,r=e[n],o={gradientUnits:r.global?"userSpaceOnUse":"objectBoundingBox"};if(hp(r))a="linearGradient",o.x1=r.x,o.y1=r.y,o.x2=r.x2,o.y2=r.y2;else{if(!gp(r))return void 0;a="radialGradient",o.cx=co(r.x,.5),o.cy=co(r.y,.5),o.r=co(r.r,.5)}for(var s=r.colorStops,l=[],p=0,c=s.length;pl?FU(e,null==n[d+1]?null:n[d+1].elm,n,s,d):RU(e,t,o,l))}(n,i,a):PU(a)?(PU(e.text)&&EU(n,""),FU(n,null,a,0,a.length-1)):PU(i)?RU(n,i,0,i.length-1):PU(e.text)&&EU(n,""):e.text!==t.text&&(PU(i)&&RU(n,i,0,i.length-1),EU(n,t.text)))}var LU=0,VU=function(){function e(e,t,n){if(this.type="svg",this.refreshHover=qU("refreshHover"),this.configLayer=qU("configLayer"),this.storage=t,this._opts=n=Br({},n),this.root=e,this._id="zr"+LU++,this._oldVNode=Wz(n.width,n.height),e&&!n.ssr){var i=this._viewport=document.createElement("div");i.style.cssText="position:relative;overflow:hidden";var a=this._svgDom=this._oldVNode.elm=zz("svg");BU(null,this._oldVNode),i.appendChild(a),e.appendChild(i)}this.resize(n.width,n.height)}return e.prototype.getType=function(){return this.type},e.prototype.getViewportRoot=function(){return this._viewport},e.prototype.getViewportRootOffset=function(){var e=this.getViewportRoot();if(e)return{offsetLeft:e.offsetLeft||0,offsetTop:e.offsetTop||0}},e.prototype.getSvgDom=function(){return this._svgDom},e.prototype.refresh=function(){if(this.root){var e=this.renderToVNode({willUpdate:!0});e.attrs.style="position:absolute;left:0;top:0;user-select:none",function(e,t){if(OU(e,t))NU(e,t);else{var n=e.elm,i=TU(n);AU(t),null!==i&&(SU(i,t.elm,IU(n)),RU(i,[e],0,0))}}(this._oldVNode,e),this._oldVNode=e}},e.prototype.renderOneToVNode=function(e){return yU(e,Hz(this._id))},e.prototype.renderToVNode=function(e){e=e||{};var t=this.storage.getDisplayList(!0),n=this._width,i=this._height,a=Hz(this._id);a.animation=e.animation,a.willUpdate=e.willUpdate,a.compress=e.compress,a.emphasis=e.emphasis;var r=[],o=this._bgVNode=function(e,t,n,i){var a;if(n&&"none"!==n)if(a=Uz("rect","bg",{width:e,height:t,x:"0",y:"0"}),fp(n))vU({fill:n},a.attrs,"fill",i);else if(mp(n))xU({style:{fill:n},dirty:Mo,getBoundingRect:function(){return{width:e,height:t}}},a.attrs,"fill",i);else{var r=op(n),o=r.color,s=r.opacity;a.attrs.fill=o,s<1&&(a.attrs["fill-opacity"]=s)}return a}(n,i,this._backgroundColor,a);o&&r.push(o);var s=e.compress?null:this._mainVNode=Uz("g","main",{},[]);this._paintList(t,a,s?s.children:r),s&&r.push(s);var l=Ur($r(a.defs),(function(e){return a.defs[e]}));if(l.length&&r.push(Uz("defs","defs",{},l)),e.animation){var p=function(e,t,n){var i=(n=n||{}).newline?"\n":"",a=" {"+i,r=i+"}",o=Ur($r(e),(function(t){return t+a+Ur($r(e[t]),(function(n){return n+":"+e[t][n]+";"})).join(i)+r})).join(i),s=Ur($r(t),(function(e){return"@keyframes "+e+a+Ur($r(t[e]),(function(n){return n+a+Ur($r(t[e][n]),(function(i){var a=t[e][n][i];return"d"===i&&(a='path("'+a+'")'),i+":"+a+";"})).join(i)+r})).join(i)+r})).join(i);return o||s?[""].join(i):""}(a.cssNodes,a.cssAnims,{newline:!0});if(p){var c=Uz("style","stl",{},[],p);r.push(c)}}return Wz(n,i,r,e.useViewBox)},e.prototype.renderToString=function(e){return e=e||{},jz(this.renderToVNode({animation:co(e.cssAnimation,!0),emphasis:co(e.cssEmphasis,!0),willUpdate:!1,compress:!0,useViewBox:co(e.useViewBox,!0)}),{newline:!0})},e.prototype.setBackgroundColor=function(e){this._backgroundColor=e},e.prototype.getSvgRoot=function(){return this._mainVNode&&this._mainVNode.elm},e.prototype._paintList=function(e,t,n){for(var i,a,r=e.length,o=[],s=0,l=0,p=0;p=0&&(!d||!a||d[h]!==a[h]);h--);for(var g=m-1;g>h;g--)i=o[--s-1];for(var f=h+1;f=o)}}for(var c=this.__startIndex;c15)break}n.prevElClipPaths&&d.restore()};if(m)if(0===m.length)s=l.__endIndex;else for(var b=u.dpr,w=0;w0&&e>i[0]){for(s=0;se);s++);o=n[i[s]]}if(i.splice(s+1,0,e),n[e]=t,!t.virtual)if(o){var l=o.dom;l.nextSibling?r.insertBefore(t.dom,l.nextSibling):r.appendChild(t.dom)}else r.firstChild?r.insertBefore(t.dom,r.firstChild):r.appendChild(t.dom);t.painter||(t.painter=this)}},e.prototype.eachLayer=function(e,t){for(var n=this._zlevelList,i=0;i0?WU:0),this._needsManuallyCompositing),p.__builtin__||Or("ZLevel "+l+" has been used by unkown layer "+p.id),p!==r&&(p.__used=!0,p.__startIndex!==a&&(p.__dirty=!0),p.__startIndex=a,p.incremental?p.__drawIndex=-1:p.__drawIndex=a,t(a),r=p),1&s.__dirty&&!s.__inHover&&(p.__dirty=!0,p.incremental&&p.__drawIndex<0&&(p.__drawIndex=a))}t(a),this.eachBuiltinLayer((function(e,t){!e.__used&&e.getElementCount()>0&&(e.__dirty=!0,e.__startIndex=e.__endIndex=e.__drawIndex=0),e.__dirty&&e.__drawIndex<0&&(e.__drawIndex=e.__startIndex)}))},e.prototype.clear=function(){return this.eachBuiltinLayer(this._clearLayer),this},e.prototype._clearLayer=function(e){e.clear()},e.prototype.setBackgroundColor=function(e){this._backgroundColor=e,zr(this._layers,(function(e){e.setUnpainted()}))},e.prototype.configLayer=function(e,t){if(t){var n=this._layerConfig;n[e]?Fr(n[e],t,!0):n[e]=t;for(var i=0;i({legendKey:e,left:!0}),ZU=e=>({background:e});function QU(e,n){1&e&&t.ɵɵelementContainer(0)}function JU(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,QU,1,0,"ng-container",8),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e.widgetTitlePanel)}}function ej(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Min")))}function tj(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Max")))}function nj(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Avg")))}function ij(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Total")))}function aj(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Latest")))}function rj(e,n){1&e&&t.ɵɵelementContainer(0)}function oj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].min,"html"),t.ɵɵsanitizeHtml)}}function sj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].max,"html"),t.ɵɵsanitizeHtml)}}function lj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].avg,"html"),t.ɵɵsanitizeHtml)}}function pj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].total,"html"),t.ɵɵsanitizeHtml)}}function cj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].latest,"html"),t.ɵɵsanitizeHtml)}}function dj(e,n){if(1&e&&(t.ɵɵelementStart(0,"tr")(1,"th"),t.ɵɵtemplate(2,rj,1,0,"ng-container",14),t.ɵɵelementEnd(),t.ɵɵtemplate(3,oj,2,4,"td",15)(4,sj,2,4,"td",15)(5,lj,2,4,"td",15)(6,pj,2,4,"td",15)(7,cj,2,4,"td",15),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2),a=t.ɵɵreference(8);t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",a)("ngTemplateOutletContext",t.ɵɵpureFunction1(7,XU,e)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showMin),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showMax),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showAvg),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showTotal),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showLatest)}}function uj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"table",10)(2,"thead")(3,"tr"),t.ɵɵelement(4,"th"),t.ɵɵtemplate(5,ej,3,3,"th",11)(6,tj,3,3,"th",11)(7,nj,3,3,"th",11)(8,ij,3,3,"th",11)(9,aj,3,3,"th",11),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"tbody"),t.ɵɵtemplate(11,dj,8,9,"tr",12),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngIf",!0===e.legendConfig.showMin),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showMax),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showAvg),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showTotal),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showLatest),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",e.legendKeys)}}function mj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",18),t.ɵɵelement(2,"div",19)(3,"div",20),t.ɵɵpipe(4,"safe"),t.ɵɵelementEnd()()),2&e){const e=n.legendKey,i=n.left;t.ɵɵclassProp("left",i),t.ɵɵadvance(2),t.ɵɵstyleMap(t.ɵɵpureFunction1(8,ZU,e.dataKey.color)),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(4,5,e.dataKey.label,"html"),t.ɵɵsanitizeHtml)}}class hj{constructor(e,t,n){this.renderer=e,this.sanitizer=t,this.widgetComponent=n,this.updateXAxisTimeWindow=(e,t)=>{e.min=t.minTime,e.max=t.maxTime}}ngOnInit(){this.initEchart(),this.initLegend()}ngAfterViewInit(){this.myChart=function(e,t,n){var i=!(n&&n.ssr);if(i){var a=p_(e);if(a)return a}var r=new KC(e,t,n);return r.id="ec_"+s_++,r_[r.id]=r,i&&Nd(e,l_,r.id),UC(r),hC.trigger("afterinit",r),r}(this.echartContainer.nativeElement,null,{renderer:"svg"}),this.initResize(),this.xAxis=this.setupXAxis(),this.yAxis=this.setupYAxis(),this.option={...this.setupAnimationSettings(),formatter:e=>this.setupTooltipElement(e),backgroundColor:"transparent",darkMode:!1,tooltip:{show:!0,trigger:"axis",confine:!0,padding:[8,12],appendTo:"body",textStyle:{fontFamily:"Roboto",fontSize:12,fontWeight:"normal",lineHeight:16}},grid:[{backgroundColor:null,borderColor:"#ccc",borderWidth:1,bottom:45,left:5,right:5,show:!1,top:10}],xAxis:[this.xAxis],yAxis:[this.yAxis],series:this.setupChartLines(),dataZoom:[{type:"inside",disabled:!1,realtime:!0,filterMode:"none"},{type:"slider",show:!0,showDetail:!1,realtime:!0,filterMode:"none",bottom:5}]},this.myChart.setOption(this.option),this.updateAxisOffset(!1)}onDataUpdated(){const e=[];this.onResize(),this.updateXAxisTimeWindow(this.xAxis,this.ctx.defaultSubscription.timeWindow);for(const t in this.ctx.data){e[t]=[];for(const[n,i]of this.ctx.data[t].data)e[t].push({name:n,value:[n,i]})}const t=[];for(const n of e)t.push({data:n});this.option.series=t,this.myChart.setOption(this.option),this.updateAxisOffset()}updateAxisOffset(e=!0){const t=Qe(this.myChart,this.yAxis.mainType,this.yAxis.id),n=Je(this.myChart,this.yAxis.mainType,this.yAxis.id,this.yAxis.name),i=Qe(this.myChart,this.xAxis.mainType,this.xAxis.id),a=t+n,r=i+Je(this.myChart,this.yAxis.mainType,this.yAxis.id,this.yAxis.name)+35;this.option.grid[0].left===a&&this.option.grid[0].bottom===r||(this.option.grid[0].left=a,this.yAxis.nameGap=t,this.option.grid[0].bottom=r,this.xAxis.nameGap=i,this.myChart.setOption(this.option,{replaceMerge:["yAxis","xAxis","grid"],lazyUpdate:e}))}initEchart(){dI([iq,BN,yz,DG,jq,hL,lP,oM,OM,XM,pP,ZB,wE,KU,GU])}initLegend(){this.showLegend=this.ctx.settings.showLegend,this.showLegend&&(this.legendConfig=this.ctx.settings.legendConfig,this.legendData=this.ctx.defaultSubscription.legendData,this.legendKeys=this.legendData.keys,this.legendClass=`legend-${this.legendConfig.position}`,this.legendConfig.sortDataKeys?this.legendKeys=this.legendData.keys.sort(((e,t)=>e.dataKey.label.localeCompare(t.dataKey.label))):this.legendKeys=this.legendData.keys)}initResize(){this.shapeResize$=new ResizeObserver((()=>{this.onResize()})),this.shapeResize$.observe(this.echartContainer.nativeElement)}onResize(){this.myChart.resize()}setupTooltipElement(e){const t=this.renderer.createElement("div");if(this.renderer.setStyle(t,"display","flex"),this.renderer.setStyle(t,"flex-direction","column"),this.renderer.setStyle(t,"align-items","flex-start"),this.renderer.setStyle(t,"gap","16px"),e.length){const n=this.renderer.createElement("div");this.renderer.setStyle(n,"display","flex"),this.renderer.setStyle(n,"flex-direction","column"),this.renderer.setStyle(n,"align-items","flex-start"),this.renderer.setStyle(n,"gap","4px"),this.renderer.appendChild(n,this.setTooltipDate(e));for(const[t,i]of e.entries())this.renderer.appendChild(n,this.constructTooltipSeriesElement(i,t));this.renderer.appendChild(t,n)}return t}constructTooltipSeriesElement(e,t){const n=this.renderer.createElement("div");this.renderer.setStyle(n,"display","flex"),this.renderer.setStyle(n,"flex-direction","row"),this.renderer.setStyle(n,"align-items","center"),this.renderer.setStyle(n,"align-self","stretch"),this.renderer.setStyle(n,"gap","12px");const i=this.renderer.createElement("div");this.renderer.setStyle(i,"display","flex"),this.renderer.setStyle(i,"align-items","center"),this.renderer.setStyle(i,"gap","8px"),this.renderer.appendChild(n,i);const a=this.renderer.createElement("div");this.renderer.setStyle(a,"width","8px"),this.renderer.setStyle(a,"height","8px"),this.renderer.setStyle(a,"border-radius","50%"),this.renderer.setStyle(a,"background",e.color),this.renderer.appendChild(i,a);const r=this.renderer.createElement("div");this.renderer.setProperty(r,"innerHTML",this.sanitizer.sanitize(g.HTML,e.seriesName)),this.renderer.setStyle(r,"font-family","Roboto"),this.renderer.setStyle(r,"font-size","12px"),this.renderer.setStyle(r,"font-style","normal"),this.renderer.setStyle(r,"font-weight",400),this.renderer.setStyle(r,"line-height","16px"),this.renderer.setStyle(r,"color","rgba(0, 0, 0, 0.76)"),this.renderer.appendChild(i,r);const o=J(this.ctx.data[t].dataKey.decimals)?this.ctx.data[t].dataKey.decimals:this.ctx.decimals,s=J(this.ctx.data[t].dataKey.units)?this.ctx.data[t].dataKey.units:this.ctx.units,l=ee(e.value[1],o,s,!1),p=this.renderer.createElement("div");return this.renderer.setProperty(p,"innerHTML",this.sanitizer.sanitize(g.HTML,l)),this.renderer.setStyle(p,"flex","1"),this.renderer.setStyle(p,"text-align","end"),this.renderer.setStyle(p,"font-family","Roboto"),this.renderer.setStyle(p,"font-size","12px"),this.renderer.setStyle(p,"font-style","normal"),this.renderer.setStyle(p,"font-weight",500),this.renderer.setStyle(p,"line-height","16px"),this.renderer.setStyle(p,"color","rgba(0, 0, 0, 0.76)"),this.renderer.appendChild(n,p),n}setTooltipDate(e){const t=this.renderer.createElement("div");return this.renderer.appendChild(t,this.renderer.createText(new Date(e[0].value[0]).toLocaleString("en-GB"))),this.renderer.setStyle(t,"font-family","Roboto"),this.renderer.setStyle(t,"font-size","11px"),this.renderer.setStyle(t,"font-style","normal"),this.renderer.setStyle(t,"font-weight","400"),this.renderer.setStyle(t,"line-height","16px"),this.renderer.setStyle(t,"color","rgba(0, 0, 0, 0.76)"),t}setupAnimationSettings(){return{animation:!0,animationDelay:0,animationDelayUpdate:0,animationDuration:500,animationDurationUpdate:300,animationEasing:"cubicOut",animationEasingUpdate:"cubicOut",animationThreshold:2e3}}setupChartLines(){const e=[];for(const[t,n]of this.ctx.datasources[0].dataKeys.entries())e.push({id:t,name:n.label,type:"line",showSymbol:!1,smooth:!1,step:!1,stackStrategy:"all",data:[],lineStyle:{color:n.color},itemStyle:{color:n.color}});return e}setupYAxis(){return{type:"value",position:"left",mainType:"yAxis",id:"yAxis",offset:0,nameLocation:"middle",nameRotate:90,alignTicks:!0,scale:!0,show:!0,axisLabel:{color:"rgba(0, 0, 0, 0.54)",fontFamily:"Roboto",fontSize:12,fontStyle:"normal",fontWeight:400,show:!0,formatter:e=>ee(e,this.ctx.decimals,this.ctx.units,!1)},splitLine:{show:!0},axisLine:{show:!0,lineStyle:{color:"rgba(0, 0, 0, 0.54)"}},axisTick:{lineStyle:{color:"rgba(0, 0, 0, 0.54)"},show:!0},nameTextStyle:{color:"rgba(0, 0, 0, 0.54)",fontFamily:"Roboto",fontSize:12,fontStyle:"normal",fontWeight:600}}}setupXAxis(){return{id:"xAxis",mainType:"xAxis",show:!0,type:"time",position:"bottom",offset:0,nameLocation:"middle",max:this.ctx.defaultSubscription.timeWindow.maxTime,min:this.ctx.defaultSubscription.timeWindow.minTime,nameTextStyle:{color:"rgba(0, 0, 0, 0.54)",fontStyle:"normal",fontWeight:600,fontFamily:"Roboto",fontSize:12},axisPointer:{shadowStyle:{color:"rgba(210,219,238,0.2)"}},splitLine:{show:!0},axisTick:{show:!0,lineStyle:{color:"rgba(0, 0, 0, 0.54)"}},axisLine:{onZero:!1,show:!0,lineStyle:{color:"rgba(0, 0, 0, 0.54)"}},axisLabel:{color:"rgba(0, 0, 0, 0.54)",fontFamily:"Roboto",fontSize:10,fontStyle:"normal",fontWeight:400,show:!0,hideOverlap:!0}}}static{this.ɵfac=function(e){return new(e||hj)(t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(et.DomSanitizer),t.ɵɵdirectiveInject(tt.WidgetComponent))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:hj,selectors:[["tb-gateway-statistics-chart"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(YU,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.echartContainer=e.first)}},inputs:{ctx:"ctx",widgetTitlePanel:"widgetTitlePanel"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:9,vars:4,consts:[["echartContainer",""],["legendItem",""],[1,"tb-time-series-chart-panel"],[1,"tb-time-series-chart-overlay"],[4,"ngIf"],[1,"tb-time-series-chart-content"],[1,"tb-time-series-chart-shape"],["class","tb-time-series-chart-legend",4,"ngIf"],[4,"ngTemplateOutlet"],[1,"tb-time-series-chart-legend"],[1,"tb-time-series-chart-legend-table","vertical"],["class","tb-time-series-chart-legend-type-label right legend legend-row-color",4,"ngIf"],[4,"ngFor","ngForOf"],[1,"tb-time-series-chart-legend-type-label","right","legend","legend-row-color"],[4,"ngTemplateOutlet","ngTemplateOutletContext"],["class","tb-time-series-chart-legend-value legend",3,"innerHTML",4,"ngIf"],[1,"tb-time-series-chart-legend-value","legend",3,"innerHTML"],[1,"tb-time-series-chart-legend-item"],[1,"tb-time-series-chart-legend-item-label"],[1,"tb-time-series-chart-legend-item-label-circle"],[1,"legend","legend-label-color",3,"innerHTML"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",2),t.ɵɵelement(1,"div",3),t.ɵɵtemplate(2,JU,2,1,"ng-container",4),t.ɵɵelementStart(3,"div",5),t.ɵɵelement(4,"div",6,0),t.ɵɵtemplate(6,uj,12,6,"div",7)(7,mj,5,10,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.widgetComponent.dashboardWidget.showWidgetTitlePanel),t.ɵɵadvance(),t.ɵɵclassMap(n.legendClass),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.showLegend))},dependencies:t.ɵɵgetComponentDepsFactory(hj,[se,_]),styles:['@charset "UTF-8";.tb-time-series-chart-panel[_ngcontent-%COMP%]{width:100%;height:100%;position:relative;display:flex;flex-direction:column;gap:8px;padding:12px}.tb-time-series-chart-panel[_ngcontent-%COMP%] > div[_ngcontent-%COMP%]:not(.tb-time-series-chart-overlay){z-index:1}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-overlay[_ngcontent-%COMP%]{position:absolute;inset:12px}.tb-time-series-chart-panel[_ngcontent-%COMP%] div.tb-widget-title[_ngcontent-%COMP%]{padding:0}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;gap:8px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%]{flex-direction:column-reverse}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-right[_ngcontent-%COMP%]{flex-direction:row}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-left[_ngcontent-%COMP%]{flex-direction:row-reverse}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-shape[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;display:flex;align-items:center;justify-content:center}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-right[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-left[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]{display:inline-grid;grid-auto-flow:column;grid-template-rows:repeat(auto-fit,minmax(16px,min-content));max-width:calc(25% - 8px);height:fit-content;max-height:100%}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-bottom[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]{align-self:center}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%] .tb-time-series-chart-legend.tb-simple-legend[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-bottom[_ngcontent-%COMP%] .tb-time-series-chart-legend.tb-simple-legend[_ngcontent-%COMP%]{justify-content:center}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]:not(.tb-simple-legend), .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-bottom[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]:not(.tb-simple-legend){width:100%}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]{display:flex;align-items:flex-start;align-self:stretch;column-gap:16px;row-gap:8px;flex-wrap:wrap;overflow:auto;width:fit-content;max-width:100%;max-height:calc(35% - 8px)}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%]{border-spacing:0;table-layout:fixed}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table.vertical[_ngcontent-%COMP%]{width:100%;table-layout:auto}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table.vertical[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] th[_ngcontent-%COMP%]{width:95%}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%]:not(:last-child), .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%]:not(:last-child){padding-right:16px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] th[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] td[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:not(:last-child) th[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:not(:last-child) td[_ngcontent-%COMP%]{padding-bottom:8px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%]{align-items:flex-end}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] .tb-time-series-chart-legend-item.left[_ngcontent-%COMP%]{align-items:flex-start}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%]{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;-webkit-user-select:none;user-select:none}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%] .tb-time-series-chart-legend-item-label[_ngcontent-%COMP%]{display:flex;align-items:center;gap:4px;color:#ccc;white-space:nowrap;cursor:pointer}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%] .tb-time-series-chart-legend-item-label[_ngcontent-%COMP%] .tb-time-series-chart-legend-item-label-circle[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:50%;background-color:#ccc}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-type-label[_ngcontent-%COMP%]{white-space:nowrap;text-align:left}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-type-label.right[_ngcontent-%COMP%]{text-align:right}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-value[_ngcontent-%COMP%]{white-space:nowrap;text-align:right}.tb-time-series-chart-panel[_ngcontent-%COMP%] .legend[_ngcontent-%COMP%]{font-size:12px;font-style:normal;font-weight:500;letter-spacing:normal;line-height:16px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .legend.legend-row-color[_ngcontent-%COMP%]{color:#00000061}.tb-time-series-chart-panel[_ngcontent-%COMP%] .legend.legend-label-color[_ngcontent-%COMP%]{color:#000}']})}}const gj=["statisticChart"];function fj(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",12),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.openEditCommandDialog())})),t.ɵɵelementStart(2,"mat-icon",13),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",12),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.onDeleteClick())})),t.ɵɵelementStart(6,"mat-icon",13),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function yj(e,n){if(1&e&&t.ɵɵelement(0,"tb-gateway-statistics-chart",14,0),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("ctx",e.ctx)}}function vj(e,n){if(1&e&&t.ɵɵelement(0,"tb-custom-statistics-table",15),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("data",e.subscriptionData)}}function xj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10),t.ɵɵtemplate(1,yj,2,1,"tb-gateway-statistics-chart",14)(2,vj,1,1,"tb-custom-statistics-table",15),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵconditional(e.isNumericData?1:2)}}function bj(e,n){1&e&&(t.ɵɵelementStart(0,"div",11),t.ɵɵelement(1,"div",16),t.ɵɵelementStart(2,"div",17),t.ɵɵtext(3,"attribute.no-telemetry-text"),t.ɵɵelementEnd()())}class wj{constructor(e,t,n,i,a,r){this.fb=e,this.attributeService=t,this.destroyRef=n,this.dialog=i,this.dialogService=a,this.utils=r,this.subscriptionData=[],this.statisticForm=this.fb.group({command:[]}),this.isNumericData=!1,this.commands=[],this.subscribed=!1,this.dataTypeDefined=!1,this.subscriptionOptions={callbacks:{onDataUpdated:()=>this.ctx.ngZone.run((()=>{this.isDataOnlyNumbers(),this.isNumericData&&this.statisticChart?.onDataUpdated()})),onDataUpdateError:(e,t)=>this.ctx.ngZone.run((()=>{this.onDataUpdateError(t)}))},useDashboardTimewindow:!1,legendConfig:F(R.timeseries)},this.statisticForm.get("command").valueChanges.pipe(In()).subscribe((e=>{this.subscribed=!1,this.subscriptionInfo&&e?.attributeOnGateway&&this.createSubscription(this.ctx.defaultSubscription.datasources[0].entity,e.attributeOnGateway)}))}ngAfterViewInit(){if(this.ctx.defaultSubscription.datasources.length){const e=this.ctx.defaultSubscription.datasources[0].entity;if(e.id.id===B)return;this.getGatewayGeneralConfig().pipe(In(this.destroyRef)).subscribe((t=>{this.commands=t?.statistics.commands.reverse()??[],this.commands.length&&(this.statisticForm.get("command").setValue(this.commands[0]),this.createSubscription(e,this.commands[0].attributeOnGateway))}))}}openEditCommandDialog(){const e=this.statisticForm.get("command").value,t="string"==typeof e||!e,n="string"==typeof e?{attributeOnGateway:e}:e;let i;this.dialog.open(cr,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{titleText:t?"gateway.statistics.create-command":"gateway.statistics.edit-command",buttonText:t?"action.add":"action.apply",command:n,existingCommands:this.commands.map((e=>e.attributeOnGateway))}}).afterClosed().pipe(Ve((e=>Te(this.getGatewayGeneralConfig(),_e(e)))),Ve((([e,t])=>(this.commands=[...e?.statistics.commands.filter((e=>e.attributeOnGateway!==t?.prev?.attributeOnGateway))??[],...t?.current?[{...t.current}]:[]],i=t?.current,this.updateStatisticsCommands(e,this.commands)))),In(this.destroyRef)).subscribe((()=>{i&&this.statisticForm.get("command").patchValue(i)}))}onDeleteClick(){const e=this.statisticForm.get("command").value.attributeOnGateway;this.dialogService.confirm(this.ctx.translate.instant("gateway.statistics.delete-command",{command:e}),this.ctx.translate.instant("gateway.statistics.delete-command-data"),this.ctx.translate.instant("action.cancel"),this.ctx.translate.instant("action.confirm")).pipe(Le(Boolean),Ve((()=>this.getGatewayGeneralConfig())),Ve((t=>(this.commands=[...t.statistics.commands.filter((t=>t.attributeOnGateway!==e))],this.updateStatisticsCommands(t,this.commands)))),In(this.destroyRef)).subscribe()}getGatewayGeneralConfig(){const e=this.ctx.defaultSubscription.datasources[0].entity;return e.id.id===B?_e(null):this.attributeService.getEntityAttributes(e.id,D.SHARED_SCOPE,["general_configuration"]).pipe(ke((e=>e[0]?.value)))}updateStatisticsCommands(e,t){const n=this.ctx.defaultSubscription.datasources[0].entity;return n.id.id!==B&&e?this.attributeService.saveEntityAttributes(n.id,D.SHARED_SCOPE,[{key:"general_configuration",value:{...e,statistics:{...e.statistics,commands:t}}}]):_e(null)}createSubscription(e,t){const n=[{type:N.entity,entityType:L.DEVICE,entityId:e.id.id,entityName:e.name,timeseries:[]}];n[0].timeseries=[{name:t,label:t,settings:{}}],this.subscriptionInfo=n,this.changeSubscription(n)}onDataUpdateError(e){const t=this.utils.parseException(e);let n=t.name;t.message&&(n+=": "+t.message),console.error(n)}isDataOnlyNumbers(){this.subscriptionData=this.ctx.defaultSubscription.data[0]?.data??[],this.subscriptionData.length&&!this.dataTypeDefined&&(this.isNumericData=this.subscriptionData.every((e=>!isNaN(+e[1]))),this.dataTypeDefined=!0),this.ctx.detectChanges()}changeSubscription(e){this.ctx.defaultSubscription?.unsubscribe(),this.ctx.datasources[0].entity&&this.ctx.subscriptionApi.createSubscriptionFromInfo(R.timeseries,e,this.subscriptionOptions,!1,!0).pipe(In(this.destroyRef)).subscribe((e=>{this.dataTypeDefined=!1,this.ctx.defaultSubscription=e,this.ctx.settings.showLegend=!1,this.ctx.data=e.data,this.ctx.datasources=e.datasources,this.isDataOnlyNumbers(),this.subscribed=!0}))}static{this.ɵfac=function(e){return new(e||wj)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(K.AttributeService),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(K.DialogService),t.ɵɵdirectiveInject(K.UtilsService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:wj,selectors:[["tb-gateway-statistics"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(gj,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.statisticChart=e.first)}},inputs:{ctx:"ctx"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:18,vars:13,consts:[["statisticChart",""],[1,"flex","flex-1","flex-col","max-h-full"],[1,"entry-container","flex","items-center"],[1,"tb-form-panel","stroked","w-full","flex-1",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"entry-container","flex","w-full","gap-2"],["formControlName","command",1,"flex-1",3,"onCreateNewClicked","commands"],["appearance","outline",1,"flex-1"],["matInput","","disabled","",3,"tbTruncateWithTooltip","value"],[1,"actions-container","flex","flex-col","p-2","min-w-16"],[1,"chart-box","flex","flex-1","flex-col","overflow-auto"],[1,"tb-no-data-available","h-full"],["type","button","matSuffix","","mat-icon-button","","aria-label","Edit","matTooltipPosition","above",1,"action-button",3,"click","matTooltip"],[1,"material-icons"],[1,"flex-1",3,"ctx"],[1,"h-full","flex-1",3,"data"],[1,"tb-no-data-bg"],["translate","",1,"tb-no-data-text"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"div",2)(2,"div",3)(3,"div",4),t.ɵɵtext(4,"gateway.statistics.entry"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",5)(6,"tb-statistics-commands-autocomplete",6),t.ɵɵlistener("onCreateNewClicked",(function(){return n.openEditCommandDialog()})),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-form-field",7)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",8),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(14,"div",9),t.ɵɵtemplate(15,fj,8,6),t.ɵɵelementEnd()(),t.ɵɵtemplate(16,xj,3,1,"div",10)(17,bj,4,0,"div",11),t.ɵɵelementEnd()),2&e){let e,i,a,r;t.ɵɵadvance(2),t.ɵɵproperty("formGroup",n.statisticForm),t.ɵɵadvance(4),t.ɵɵproperty("commands",n.commands),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,7,"gateway.statistics.command")),t.ɵɵadvance(2),t.ɵɵproperty("tbTruncateWithTooltip",null!==(e=null==(e=n.statisticForm.get("command").value)?null:e.command)&&void 0!==e?e:t.ɵɵpipeBind1(12,9,"gateway.statistics.no-config-commands-found"))("value",null!==(i=null==(i=n.statisticForm.get("command").value)?null:i.command)&&void 0!==i?i:t.ɵɵpipeBind1(13,11,"gateway.statistics.no-config-commands-found")),t.ɵɵadvance(4),t.ɵɵconditional(null!=(a=n.statisticForm.get("command").value)&&a.attributeOnGateway?15:-1),t.ɵɵadvance(),t.ɵɵconditional(null!=(r=n.statisticForm.get("command").value)&&r.attributeOnGateway&&n.subscriptionData.length&&n.subscribed?16:17)}},dependencies:t.ɵɵgetComponentDepsFactory(wj,[se,_,Nn,Hn,hj]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;padding:4px;display:flex;flex-direction:column}[_nghost-%COMP%] .action-button[_ngcontent-%COMP%]{opacity:.7}@media screen and (max-width: 599px){[_nghost-%COMP%] .entry-container[_ngcontent-%COMP%]{flex-direction:column}[_nghost-%COMP%] .actions-container[_ngcontent-%COMP%]{flex-direction:row}}']})}}var Sj;e("GatewayStatisticsComponent",wj),e("BACnetRequestTypes",Sj),function(e){e.WriteProperty="writeProperty",e.ReadProperty="readProperty"}(Sj||e("BACnetRequestTypes",Sj={}));const Cj=e("BACnetRequestTypesTranslates",new Map([[Sj.WriteProperty,"gateway.rpc.write-property"],[Sj.ReadProperty,"gateway.rpc.read-property"]]));var _j;e("BACnetObjectTypes",_j),function(e){e.BinaryInput="binaryInput",e.BinaryOutput="binaryOutput",e.AnalogInput="analogInput",e.AnalogOutput="analogOutput",e.BinaryValue="binaryValue",e.AnalogValue="analogValue"}(_j||e("BACnetObjectTypes",_j={}));const Tj=e("BACnetObjectTypesTranslates",new Map([[_j.AnalogOutput,"gateway.rpc.analog-output"],[_j.AnalogInput,"gateway.rpc.analog-input"],[_j.BinaryOutput,"gateway.rpc.binary-output"],[_j.BinaryInput,"gateway.rpc.binary-input"],[_j.BinaryValue,"gateway.rpc.binary-value"],[_j.AnalogValue,"gateway.rpc.analog-value"]]));var Ij;e("BLEMethods",Ij),function(e){e.WRITE="write",e.READ="read",e.SCAN="scan"}(Ij||e("BLEMethods",Ij={}));const Ej=e("BLEMethodsTranslates",new Map([[Ij.WRITE,"gateway.rpc.write"],[Ij.READ,"gateway.rpc.read"],[Ij.SCAN,"gateway.rpc.scan"]]));var Mj,kj;e("CANByteOrders",Mj),function(e){e.LITTLE="LITTLE",e.BIG="BIG"}(Mj||e("CANByteOrders",Mj={})),e("SocketMethodProcessings",kj),function(e){e.WRITE="write",e.READ="read"}(kj||e("SocketMethodProcessings",kj={}));const Pj=e("SocketMethodProcessingsTranslates",new Map([[kj.WRITE,"gateway.rpc.write"],[kj.READ,"gateway.rpc.read"]]));var Dj;e("SNMPMethods",Dj),function(e){e.SET="set",e.MULTISET="multiset",e.GET="get",e.BULKWALK="bulkwalk",e.TABLE="table",e.MULTIGET="multiget",e.GETNEXT="getnext",e.BULKGET="bulkget",e.WALKS="walk"}(Dj||e("SNMPMethods",Dj={}));const Oj=e("SNMPMethodsTranslations",new Map([[Dj.SET,"gateway.rpc.set"],[Dj.MULTISET,"gateway.rpc.multiset"],[Dj.GET,"gateway.rpc.get"],[Dj.BULKWALK,"gateway.rpc.bulk-walk"],[Dj.TABLE,"gateway.rpc.table"],[Dj.MULTIGET,"gateway.rpc.multi-get"],[Dj.GETNEXT,"gateway.rpc.get-next"],[Dj.BULKGET,"gateway.rpc.bulk-get"],[Dj.WALKS,"gateway.rpc.walk"]]));var Aj,Fj;e("SocketEncodings",Aj),function(e){e.UTF_8="utf-8"}(Aj||e("SocketEncodings",Aj={})),e("RestSecurityType",Fj),function(e){e.ANONYMOUS="anonymous",e.BASIC="basic"}(Fj||e("RestSecurityType",Fj={}));const Rj=e("RestSecurityTypeTranslationsMap",new Map([[Fj.ANONYMOUS,"gateway.broker.security-types.anonymous"],[Fj.BASIC,"gateway.broker.security-types.basic"]]));class Bj{transform(e){return e.map((e=>(e?.value??e).toString())).join(", ")}static{this.ɵfac=function(e){return new(e||Bj)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"getRpcTemplateArrayView",type:Bj,pure:!0,standalone:!0})}}e("RpcTemplateArrayViewPipe",Bj);class Nj{constructor(){this.differs=i(f),this.keyValues=[]}transform(e){if(!e||!(e instanceof Map)&&"object"!=typeof e)return null;this.differ??=this.differs.find(e).create();const t=this.differ.diff(e);return t&&(this.keyValues=[],t.forEachItem((e=>{J(e.currentValue)&&this.keyValues.push(this.makeKeyValuePair(e.key,e.currentValue))}))),this.keyValues}makeKeyValuePair(e,t){return{key:e,value:t}}static{this.ɵfac=function(e){return new(e||Nj)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"keyValueIsNotEmpty",type:Nj,pure:!1,standalone:!0})}}e("KeyValueIsNotEmptyPipe",Nj);const Lj=e=>({$implicit:e,innerValue:!1}),Vj=e=>({"padding-left":e}),qj=(e,t)=>({"boolean-true":e,"boolean-false":t}),Gj=e=>({$implicit:e,innerValue:!0});function zj(e,n){if(1&e&&t.ɵɵelementContainer(0,13),2&e){const e=n.$implicit;t.ɵɵnextContext();const i=t.ɵɵreference(15);t.ɵɵproperty("ngTemplateOutlet",i)("ngTemplateOutletContext",t.ɵɵpureFunction1(2,Lj,e))}}function Uj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",19),t.ɵɵtext(1),t.ɵɵpipe(2,"getRpcTemplateArrayView"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,e.value)," ")}}function jj(e,n){if(1&e&&t.ɵɵelementContainer(0,20),2&e){t.ɵɵnextContext();const e=t.ɵɵreference(12);t.ɵɵproperty("ngTemplateOutlet",e)}}function Hj(e,n){if(1&e&&t.ɵɵelementContainer(0,20),2&e){t.ɵɵnextContext(2);const e=t.ɵɵreference(10);t.ɵɵproperty("ngTemplateOutlet",e)}}function Wj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div"),t.ɵɵtemplate(1,Hj,1,1,"ng-container",21),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵreference(8),i=t.ɵɵnextContext().$implicit,a=t.ɵɵnextContext();t.ɵɵclassMap(t.ɵɵpureFunction2(4,qj,!0===e.value,!1===e.value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",i.type===a.ConnectorType.SNMP&&"method"===e.key)("ngIfElse",n)}}function $j(e,n){if(1&e&&t.ɵɵtext(0),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵtextInterpolate(e.value)}}function Kj(e,n){if(1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵtextInterpolate(t.ɵɵpipeBind1(1,1,n.SNMPMethodsTranslations.get(e.value)))}}function Yj(e,n){if(1&e&&t.ɵɵelementContainer(0,13),2&e){const e=n.$implicit;t.ɵɵnextContext(3);const i=t.ɵɵreference(15);t.ɵɵproperty("ngTemplateOutlet",i)("ngTemplateOutletContext",t.ɵɵpureFunction1(2,Gj,e))}}function Xj(e,n){if(1&e&&(t.ɵɵtemplate(0,Yj,1,4,"ng-container",12),t.ɵɵpipe(1,"keyvalue")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("ngForOf",t.ɵɵpipeBind2(1,1,e.value,n.originalOrder))}}function Zj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",14)(1,"div",15),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,Uj,3,3,"div",16)(5,jj,1,1,"ng-container",17)(6,Wj,2,7,"div",18)(7,$j,1,1,"ng-template",null,1,t.ɵɵtemplateRefExtractor)(9,Kj,2,3,"ng-template",null,2,t.ɵɵtemplateRefExtractor)(11,Xj,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=n.innerValue,a=t.ɵɵnextContext(2);t.ɵɵstyleMap(t.ɵɵpureFunction1(10,Vj,i?"16px":"0")),t.ɵɵclassMap(a.getRpcParamsRowClasses(e.value)),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",i?e.key:t.ɵɵpipeBind1(3,8,"gateway.rpc."+e.key)," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",a.isArray(e.value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",a.isObject(e.value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!a.isObject(e.value)&&!a.isArray(e.value))}}function Qj(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-expansion-panel",6)(1,"mat-expansion-panel-header")(2,"mat-panel-title",7)(3,"span",8),t.ɵɵtext(4),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"mat-panel-description")(6,"button",9),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteTemplate(n,i))})),t.ɵɵelementStart(7,"mat-icon",10),t.ɵɵtext(8,"delete"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",11),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.applyTemplate(n,i))})),t.ɵɵelementStart(10,"mat-icon",10),t.ɵɵtext(11,"play_arrow"),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(12,zj,1,4,"ng-container",12),t.ɵɵpipe(13,"keyValueIsNotEmpty"),t.ɵɵtemplate(14,Zj,13,12,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()}if(2&e){const e=n.$implicit;t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",e.name),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(13,3,e.config))}}class Jj{constructor(e){this.attributeService=e,this.saveTemplate=new u,this.useTemplate=new u,this.ConnectorType=dt,this.originalOrder=()=>0,this.isObject=e=>H(e),this.isArray=e=>Array.isArray(e),this.SNMPMethodsTranslations=Oj}applyTemplate(e,t){e.stopPropagation(),this.useTemplate.emit(t)}deleteTemplate(e,t){e.stopPropagation();const n=this.rpcTemplates.findIndex((e=>e.name==t.name));this.rpcTemplates.splice(n,1);const i=`${this.connectorType}_template`;this.attributeService.saveEntityAttributes({id:this.ctx.defaultSubscription.targetDeviceId,entityType:L.DEVICE},D.SERVER_SCOPE,[{key:i,value:this.rpcTemplates}]).subscribe((()=>{}))}getRpcParamsRowClasses(e){return this.isObject(e)?"flex-col":"flex-row justify-between items-center"}static{this.ɵfac=function(e){return new(e||Jj)(t.ɵɵdirectiveInject(K.AttributeService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Jj,selectors:[["tb-gateway-service-rpc-connector-templates"]],inputs:{connectorType:"connectorType",ctx:"ctx",rpcTemplates:"rpcTemplates"},outputs:{saveTemplate:"saveTemplate",useTemplate:"useTemplate"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:4,vars:4,consts:[["RPCTemplateRef",""],["value",""],["SNMPMethod",""],["RPCObjectRow",""],[1,"mat-subtitle-1","title"],["hideToggle","",4,"ngFor","ngForOf"],["hideToggle",""],[1,"template-name"],["matTooltipPosition","above",3,"matTooltip"],["mat-icon-button","","matTooltip","Delete",3,"click"],[1,"material-icons"],["mat-icon-button","","matTooltip","Use",3,"click"],[3,"ngTemplateOutlet","ngTemplateOutletContext",4,"ngFor","ngForOf"],[3,"ngTemplateOutlet","ngTemplateOutletContext"],[1,"rpc-params-row","flex"],[1,"template-key"],["tbTruncateWithTooltip","","class","array-value",4,"ngIf"],[3,"ngTemplateOutlet",4,"ngIf"],[3,"class",4,"ngIf"],["tbTruncateWithTooltip","",1,"array-value"],[3,"ngTemplateOutlet"],[3,"ngTemplateOutlet",4,"ngIf","ngIfElse"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",4),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(3,Qj,16,5,"mat-expansion-panel",5)),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,"gateway.rpc.templates-title")),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.rpcTemplates))},dependencies:t.ɵɵgetComponentDepsFactory(Jj,[se,_,Bj,Nj]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex;flex-direction:column;padding:0}[_nghost-%COMP%] .title[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .template-key[_ngcontent-%COMP%]{color:#00000061;height:32px;line-height:32px}[_nghost-%COMP%] .boolean-true[_ngcontent-%COMP%], [_nghost-%COMP%] .boolean-false[_ngcontent-%COMP%]{border-radius:3px;height:32px;line-height:32px;padding:0 12px;width:fit-content;font-size:14px;text-transform:capitalize}[_nghost-%COMP%] .boolean-false[_ngcontent-%COMP%]{color:#d12730;background-color:#d1273014}[_nghost-%COMP%] .boolean-true[_ngcontent-%COMP%]{color:#198038;background-color:#19803814}[_nghost-%COMP%] mat-expansion-panel[_ngcontent-%COMP%]{margin-top:10px;overflow:visible}[_nghost-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{flex-direction:row-reverse;align-items:center;margin-right:0;flex:0}[_nghost-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%] > mat-icon[_ngcontent-%COMP%]{margin-left:15px;color:#00000061}[_nghost-%COMP%] .mat-expansion-panel-header[_ngcontent-%COMP%]{padding:0 0 0 12px}[_nghost-%COMP%] .mat-expansion-panel-header.mat-expansion-panel-header.mat-expanded[_ngcontent-%COMP%]{height:48px}[_nghost-%COMP%] .mat-expansion-panel-header[_ngcontent-%COMP%] .mat-content.mat-content-hide-toggle[_ngcontent-%COMP%]{margin-right:0}[_nghost-%COMP%] .rpc-params-row[_ngcontent-%COMP%]{overflow:hidden;white-space:nowrap}[_nghost-%COMP%] .rpc-params-row[_ngcontent-%COMP%] [_ngcontent-%COMP%]:not(:first-child){white-space:pre;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .template-name[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;display:block}[_nghost-%COMP%] .mat-content{align-items:center}[_nghost-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex:1;margin:0}[_nghost-%COMP%] .array-value[_ngcontent-%COMP%]{margin-left:10px}']})}}function eH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rpc.template-name-required")," "))}function tH(e,n){1&e&&(t.ɵɵelementStart(0,"div",12),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rpc.template-name-duplicate")," "))}e("GatewayServiceRPCConnectorTemplatesComponent",Jj);class nH extends A{constructor(e,t,n,i,a){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.config=this.data.config,this.templates=this.data.templates,this.templateNameCtrl=this.fb.control("",[me.required])}validateDuplicateName(e){const t=e.value.trim();return!!this.templates.find((e=>e.name===t))}close(){this.dialogRef.close()}save(){this.templateNameCtrl.setValue(this.templateNameCtrl.value.trim()),this.templateNameCtrl.valid&&this.dialogRef.close(this.templateNameCtrl.value)}static{this.ɵfac=function(e){return new(e||nH)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:nH,selectors:[["tb-gateway-service-rpc-connector-template-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:20,vars:10,consts:[["color","primary",1,"justify-between"],["translate",""],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"mat-content","flex","flex-col",2,"width","600px"],[1,"mat-block","tb-value-type",2,"flex-grow","0"],["matInput","","required","",3,"formControl"],[4,"ngIf"],["class","mat-mdc-form-field-error","style","margin-top: -15px; padding-left: 10px; font-size: 14px;",4,"ngIf"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"mat-mdc-form-field-error",2,"margin-top","-15px","padding-left","10px","font-size","14px"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-toolbar",0)(1,"h2",1),t.ɵɵtext(2,"gateway.rpc.save-template"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"button",2),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵelementStart(4,"mat-icon",3),t.ɵɵtext(5,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(6,"div",4)(7,"mat-form-field",5)(8,"mat-label",1),t.ɵɵtext(9,"gateway.rpc.template-name"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",6),t.ɵɵtemplate(11,eH,3,3,"mat-error",7),t.ɵɵelementEnd(),t.ɵɵtemplate(12,tH,3,3,"div",8),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",9)(14,"button",10),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵlistener("click",(function(){return n.save()})),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(10),t.ɵɵproperty("formControl",n.templateNameCtrl),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.templateNameCtrl.hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.validateDuplicateName(n.templateNameCtrl)),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(16,6,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",!n.templateNameCtrl.valid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(19,8,"action.save")," "))},dependencies:t.ɵɵgetComponentDepsFactory(nH,[se,_]),encapsulation:2})}}function iH(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",6),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SecurityTypeTranslationsMap.get(e))," ")}}function aH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.username-required"))}function rH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.password-required"))}function oH(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",7)(2,"div",8),t.ɵɵtext(3,"gateway.username"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",9)(5,"mat-form-field",10),t.ɵɵelement(6,"input",11),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,aH,3,3,"mat-icon",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",7)(10,"div",8),t.ɵɵtext(11,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"div",9)(13,"mat-form-field",10),t.ɵɵelement(14,"input",13),t.ɵɵpipe(15,"translate"),t.ɵɵtemplate(16,rH,3,3,"mat-icon",12),t.ɵɵelementStart(17,"div",14),t.ɵɵelement(18,"tb-toggle-password",15),t.ɵɵelementEnd()()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,6,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("username").hasError("required")&&e.securityFormGroup.get("username").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("password").hasError("required")&&e.securityFormGroup.get("password").touched),t.ɵɵadvance(),t.ɵɵclassProp("hide-toggle",e.securityFormGroup.get("password").hasError("required"))}}e("GatewayServiceRPCConnectorTemplateDialogComponent",nH);class sH{constructor(e){this.fb=e,this.BrokerSecurityType=Fj,this.securityTypes=Object.values(Fj),this.SecurityTypeTranslationsMap=Rj,this.destroy$=new we,this.propagateChange=e=>{},this.securityFormGroup=this.fb.group({type:[Fj.ANONYMOUS,[]],username:["",[me.required,me.pattern(pn)]],password:["",[me.required,me.pattern(pn)]]}),this.observeSecurityForm()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}writeValue(e){e.type||(e.type=Fj.ANONYMOUS),this.securityFormGroup.reset(e),this.updateView(e)}validate(){return this.securityFormGroup.valid?null:{securityForm:{valid:!1}}}updateView(e){this.propagateChange(e)}updateValidators(e){e===Fj.BASIC?(this.securityFormGroup.get("username").enable({emitEvent:!1}),this.securityFormGroup.get("password").enable({emitEvent:!1})):(this.securityFormGroup.get("username").disable({emitEvent:!1}),this.securityFormGroup.get("password").disable({emitEvent:!1}))}observeSecurityForm(){this.securityFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.updateView(e))),this.securityFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.updateValidators(e)))}static{this.ɵfac=function(e){return new(e||sH)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:sH,selectors:[["tb-rest-connector-security"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>sH)),multi:!0},{provide:he,useExisting:c((()=>sH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:7,vars:3,consts:[[1,"tb-form-row","space-between","same-padding","tb-flex","column",3,"formGroup"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fields-label"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[4,"ngIf"],[3,"value"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["translate","",1,"fixed-title-width"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","username",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","password","name","value","formControlName","password",3,"placeholder"],["matSuffix","",1,"tb-flex","no-gap","align-center","fill-height"],[1,"tb-flex","align-center","fill-height"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.security"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"tb-toggle-select",3),t.ɵɵtemplate(5,iH,3,4,"tb-toggle-option",4),t.ɵɵelementEnd()(),t.ɵɵtemplate(6,oH,19,10,"ng-container",5),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.securityFormGroup),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.securityTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.securityFormGroup.get("type").value===n.BrokerSecurityType.BASIC))},dependencies:t.ɵɵgetComponentDepsFactory(sH,[_,se]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block;margin-bottom:10px}[_nghost-%COMP%] .fields-label[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .hide-toggle[_ngcontent-%COMP%]{display:none}'],changeDetection:d.OnPush})}}e("RestConnectorSecurityComponent",sH);const lH=e=>({type:e});function pH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.bACnetRequestTypesTranslates.get(e))," ")}}function cH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.bACnetObjectTypesTranslates.get(e))," ")}}function dH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",9),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",10)(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",11),t.ɵɵtemplate(10,pH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field")(12,"mat-label"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",13),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",14)(17,"mat-form-field",15)(18,"mat-label"),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-select",16),t.ɵɵtemplate(22,cH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"mat-form-field",15)(24,"mat-label"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",17),t.ɵɵelementEnd()(),t.ɵɵelementStart(28,"mat-form-field",10)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",18),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,8,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,10,"gateway.rpc.requestType")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.bACnetRequestTypes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,12,"gateway.rpc.requestTimeout")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,14,"gateway.rpc.objectType")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.bACnetObjectTypes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(26,16,"gateway.rpc.identifier")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,18,"gateway.rpc.propertyId"))}}function uH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.bLEMethodsTranslates.get(e))," ")}}function mH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",20),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",21),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-form-field",10)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-select",22),t.ɵɵtemplate(15,uH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"mat-slide-toggle",23),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,5,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,7,"gateway.rpc.characteristicUUID")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,9,"gateway.rpc.methodProcessing")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.bLEMethods),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,11,"gateway.rpc.withResponse")," ")}}function hH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e)," ")}}function gH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",24),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",25),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-slide-toggle",26),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-slide-toggle",27),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-slide-toggle",28),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",14)(20,"mat-form-field",15)(21,"mat-label"),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",29),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",15)(26,"mat-label"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-select",30),t.ɵɵtemplate(30,hH,3,4,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(31,"div",14)(32,"mat-form-field",15)(33,"mat-label"),t.ɵɵtext(34),t.ɵɵpipe(35,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(36,"input",31),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"mat-form-field",15)(38,"mat-label"),t.ɵɵtext(39),t.ɵɵpipe(40,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(41,"input",32),t.ɵɵelementEnd()(),t.ɵɵelementStart(42,"mat-form-field")(43,"mat-label"),t.ɵɵtext(44),t.ɵɵpipe(45,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(46,"input",33),t.ɵɵelementEnd(),t.ɵɵelementStart(47,"mat-form-field")(48,"mat-label"),t.ɵɵtext(49),t.ɵɵpipe(50,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(51,"input",34),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,12,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,14,"gateway.rpc.nodeID")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,16,"gateway.rpc.isExtendedID")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,18,"gateway.rpc.isFD")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,20,"gateway.rpc.bitrateSwitch")," "),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(23,22,"gateway.rpc.dataLength")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,24,"gateway.rpc.dataByteorder")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.cANByteOrders),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(35,26,"gateway.rpc.dataBefore")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(40,28,"gateway.rpc.dataAfter")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(45,30,"gateway.rpc.dataInHEX")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(50,32,"gateway.rpc.dataExpression"))}}function fH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",35),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",36),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,2,"gateway.rpc.methodFilter")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,4,"gateway.rpc.valueExpression")))}function yH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",37),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",36),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-slide-toggle",38),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,5,"gateway.rpc.valueExpression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,7,"gateway.rpc.withResponse")," "))}function vH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",37),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",36),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-slide-toggle",38),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,5,"gateway.rpc.valueExpression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,7,"gateway.rpc.withResponse")," "))}function xH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SNMPMethodsTranslations.get(e))," ")}}function bH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",45)(1,"mat-form-field",46),t.ɵɵelement(2,"input",47),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-icon",48),t.ɵɵpipe(4,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext(3);return t.ɵɵresetView(i.removeSNMPoid(n))})),t.ɵɵtext(5,"delete "),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵproperty("formControl",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(4,2,"gateway.rpc.remove"))}}function wH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",39),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",10)(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",40),t.ɵɵtemplate(10,xH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-slide-toggle",38),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"fieldset",41)(15,"span",42),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(18,bH,6,4,"div",43),t.ɵɵelementStart(19,"button",44),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.addSNMPoid())})),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,7,"gateway.rpc.requestFilter")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,9,"gateway.rpc.method")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.sNMPMethods),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,11,"gateway.rpc.withResponse")," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(17,13,"gateway.rpc.oids"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",e.getFormArrayControls("oid")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(21,15,"gateway.rpc.add-oid")," ")}}function SH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function CH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",59),t.ɵɵelementContainerStart(1,63),t.ɵɵelementStart(2,"mat-form-field",64),t.ɵɵelement(3,"input",65),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",64),t.ɵɵelement(5,"input",66),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-icon",67),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext(4);return t.ɵɵresetView(i.removeHTTPHeader(n))})),t.ɵɵtext(8,"delete "),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()}if(2&e){const e=n.index;t.ɵɵadvance(),t.ɵɵproperty("formGroupName",e),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,2,"gateway.rpc.remove"))}}function _H(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",58)(1,"div",59)(2,"span",60),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"span",60),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"span",61),t.ɵɵelementEnd(),t.ɵɵelement(9,"mat-divider"),t.ɵɵtemplate(10,CH,9,4,"div",62),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rpc.header-name")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,5,"gateway.rpc.value")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.getFormArrayControls("httpHeaders"))}}function TH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",49),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",14)(6,"mat-form-field",50)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-select",51),t.ɵɵtemplate(11,SH,2,2,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",15)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",52),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",14)(18,"mat-form-field",15)(19,"mat-label"),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(22,"input",53),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",15)(24,"mat-label"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",54),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-form-field",15)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",55),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"mat-form-field")(34,"mat-label"),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",36),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"fieldset",56)(39,"span",42),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(42,_H,11,7,"div",57),t.ɵɵelementStart(43,"button",44),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.addHTTPHeader())})),t.ɵɵtext(44),t.ɵɵpipe(45,"translate"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,11,"gateway.rpc.methodFilter")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,13,"gateway.rpc.httpMethod")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.hTTPMethods),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,15,"gateway.rpc.requestUrlExpression")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(21,17,"gateway.rpc.responseTimeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(26,19,"gateway.rpc.timeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,21,"gateway.rpc.tries")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,23,"gateway.rpc.valueExpression")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,25,"gateway.rpc.httpHeaders")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.getFormArrayControls("httpHeaders").length),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(45,27,"gateway.rpc.add-header")," ")}}function IH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function EH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",59),t.ɵɵelementContainerStart(1,63),t.ɵɵelementStart(2,"mat-form-field",64),t.ɵɵelement(3,"input",73),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",64),t.ɵɵelement(6,"input",74),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-icon",67),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext(4);return t.ɵɵresetView(i.removeHTTPHeader(n))})),t.ɵɵtext(9,"delete "),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()}if(2&e){const e=n.index;t.ɵɵadvance(),t.ɵɵproperty("formGroupName",e),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(4,3,"gateway.rpc.set")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,5,"gateway.rpc.remove"))}}function MH(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",58)(1,"div",59)(2,"span",60),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"span",60),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"span",61),t.ɵɵelementEnd(),t.ɵɵelement(9,"mat-divider"),t.ɵɵtemplate(10,EH,10,7,"div",62),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rpc.header-name")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,5,"gateway.rpc.value")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.getFormArrayControls("httpHeaders"))}}function kH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",68),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",59)(6,"mat-form-field",50)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-select",51),t.ɵɵtemplate(11,IH,2,2,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",15)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",52),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",59)(18,"mat-form-field",15)(19,"mat-label"),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(22,"input",53),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",15)(24,"mat-label"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",69),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-form-field",15)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",70),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"mat-form-field")(34,"mat-label"),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",71),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"mat-form-field")(39,"mat-label"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(42,"input",72),t.ɵɵelementEnd(),t.ɵɵelementStart(43,"fieldset",56)(44,"span",42),t.ɵɵtext(45),t.ɵɵpipe(46,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(47,MH,11,7,"div",57),t.ɵɵelementStart(48,"button",44),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.addHTTPHeader())})),t.ɵɵtext(49),t.ɵɵpipe(50,"translate"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,12,"gateway.rpc.methodFilter")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,14,"gateway.rpc.httpMethod")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.hTTPMethods),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,16,"gateway.rpc.requestUrlExpression")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(21,18,"gateway.rpc.responseTimeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(26,20,"gateway.rpc.timeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,22,"gateway.rpc.tries")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,24,"gateway.rpc.requestValueExpression")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,26,"gateway.rpc.responseValueExpression")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(46,28,"gateway.rpc.httpHeaders")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.getFormArrayControls("httpHeaders").length),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(50,30,"gateway.rpc.add-header")," ")}}function PH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rpc.json-value-invalid")," "))}function DH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",75),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",76),t.ɵɵelementStart(10,"mat-icon",77),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext(2);return t.ɵɵresetView(i.openEditJSONDialog(n))})),t.ɵɵtext(12,"edit "),t.ɵɵelementEnd(),t.ɵɵtemplate(13,PH,3,3,"mat-error",78),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,4,"gateway.statistics.command")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,6,"widget-config.datasource-parameters")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,8,"gateway.rpc-command-edit-params")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.commandForm.get("params").hasError("invalidJSON"))}}function OH(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,6),t.ɵɵtemplate(1,dH,33,20,"ng-template",7)(2,mH,19,13,"ng-template",7)(3,gH,52,34,"ng-template",7)(4,fH,10,6,"ng-template",7)(5,yH,13,9,"ng-template",7)(6,vH,13,9,"ng-template",7)(7,wH,22,17,"ng-template",7)(8,TH,46,29,"ng-template",7)(9,kH,51,32,"ng-template",7)(10,DH,14,10,"ng-template",8),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("ngSwitch",e.connectorType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.BACNET),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.BLE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.CAN),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.FTP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.OCPP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.XMPP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.SNMP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.REST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.REQUEST)}}class AH{constructor(e,t){this.fb=e,this.dialog=t,this.sendCommand=new u,this.saveTemplate=new u,this.ConnectorType=dt,this.bACnetRequestTypes=Object.values(Sj),this.bACnetObjectTypes=Object.values(_j),this.bLEMethods=Object.values(Ij),this.cANByteOrders=Object.values(Mj),this.sNMPMethods=Object.values(Dj),this.hTTPMethods=Object.values(gt),this.bACnetRequestTypesTranslates=Cj,this.bACnetObjectTypesTranslates=Tj,this.bLEMethodsTranslates=Ej,this.SNMPMethodsTranslations=Oj,this.gatewayConnectorDefaultTypesTranslates=ut,this.urlPattern=/^[-a-zA-Zd_$:{}?~+=\/.0-9-]*$/,this.numbersOnlyPattern=/^[0-9]*$/,this.hexOnlyPattern=/^[0-9A-Fa-f ]+$/,this.propagateChange=e=>{},this.destroy$=new we}ngOnInit(){this.commandForm=this.connectorParamsFormGroupByType(this.connectorType),this.observeFormChanges()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}connectorParamsFormGroupByType(e){let t;switch(e){case dt.BACNET:t=this.fb.group({method:[null,[me.required,me.pattern(pn)]],requestType:[null,[me.required,me.pattern(pn)]],requestTimeout:[null,[me.required,me.min(10),me.pattern(this.numbersOnlyPattern)]],objectType:[null,[]],identifier:[null,[me.required,me.min(1),me.pattern(this.numbersOnlyPattern)]],propertyId:[null,[me.required,me.pattern(pn)]]});break;case dt.BLE:t=this.fb.group({methodRPC:[null,[me.required,me.pattern(pn)]],characteristicUUID:["00002A00-0000-1000-8000-00805F9B34FB",[me.required,me.pattern(pn)]],methodProcessing:[null,[me.required]],withResponse:[!1,[]]});break;case dt.CAN:t=this.fb.group({method:[null,[me.required,me.pattern(pn)]],nodeID:[null,[me.required,me.min(0),me.pattern(this.numbersOnlyPattern)]],isExtendedID:[!1,[]],isFD:[!1,[]],bitrateSwitch:[!1,[]],dataLength:[null,[me.min(1),me.pattern(this.numbersOnlyPattern)]],dataByteorder:[null,[]],dataBefore:[null,[me.pattern(pn),me.pattern(this.hexOnlyPattern)]],dataAfter:[null,[me.pattern(pn),me.pattern(this.hexOnlyPattern)]],dataInHEX:[null,[me.pattern(pn),me.pattern(this.hexOnlyPattern)]],dataExpression:[null,[me.pattern(pn)]]});break;case dt.FTP:t=this.fb.group({methodFilter:[null,[me.required,me.pattern(pn)]],valueExpression:[null,[me.required,me.pattern(pn)]]});break;case dt.OCPP:case dt.XMPP:t=this.fb.group({methodRPC:[null,[me.required,me.pattern(pn)]],valueExpression:[null,[me.required,me.pattern(pn)]],withResponse:[!1,[]]});break;case dt.SNMP:t=this.fb.group({requestFilter:[null,[me.required,me.pattern(pn)]],method:[null,[me.required]],withResponse:[!1,[]],oid:this.fb.array([],[me.required])});break;case dt.REST:t=this.fb.group({methodFilter:[null,[me.required,me.pattern(pn)]],httpMethod:[null,[me.required]],requestUrlExpression:[null,[me.required,me.pattern(this.urlPattern)]],responseTimeout:[null,[me.required,me.min(10),me.pattern(this.numbersOnlyPattern)]],timeout:[null,[me.required,me.min(10),me.pattern(this.numbersOnlyPattern)]],tries:[null,[me.required,me.min(1),me.pattern(this.numbersOnlyPattern)]],valueExpression:[null,[me.required,me.pattern(pn)]],httpHeaders:this.fb.array([]),security:[{},[me.required]]});break;case dt.REQUEST:t=this.fb.group({methodFilter:[null,[me.required,me.pattern(pn)]],httpMethod:[null,[me.required]],requestUrlExpression:[null,[me.required,me.pattern(this.urlPattern)]],responseTimeout:[null,[me.required,me.min(10),me.pattern(this.numbersOnlyPattern)]],timeout:[null,[me.required,me.min(10),me.pattern(this.numbersOnlyPattern)]],tries:[null,[me.required,me.min(1),me.pattern(this.numbersOnlyPattern)]],requestValueExpression:[null,[me.required,me.pattern(pn)]],responseValueExpression:[null,[me.pattern(pn)]],httpHeaders:this.fb.array([])});break;default:t=this.fb.group({command:[null,[me.required,me.pattern(pn)]],params:[{},[lt]]})}return t}addSNMPoid(e=null){const t=this.commandForm.get("oid");t&&t.push(this.fb.control(e,[me.required,me.pattern(pn)]),{emitEvent:!1})}removeSNMPoid(e){this.commandForm.get("oid").removeAt(e)}addHTTPHeader(e={headerName:null,value:null}){const t=this.commandForm.get("httpHeaders"),n=this.fb.group({headerName:[e.headerName,[me.required,me.pattern(pn)]],value:[e.value,[me.required,me.pattern(pn)]]});t&&t.push(n,{emitEvent:!1})}removeHTTPHeader(e){this.commandForm.get("httpHeaders").removeAt(e)}getFormArrayControls(e){return this.commandForm.get(e).controls}openEditJSONDialog(e){e&&e.stopPropagation(),this.dialog.open(nt,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{jsonValue:this.commandForm.get("params").value,required:!0}}).afterClosed().subscribe((e=>{e&&this.commandForm.get("params").setValue(e)}))}save(){this.saveTemplate.emit()}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}clearFromArrayByName(e){const t=this.commandForm.get(e);for(;0!==t.length;)t.removeAt(0)}writeValue(e){if("object"==typeof e){switch(e=te(e),this.connectorType){case dt.SNMP:this.clearFromArrayByName("oid"),e.oid.forEach((e=>{this.addSNMPoid(e)})),delete e.oid;break;case dt.REQUEST:case dt.REST:this.clearFromArrayByName("httpHeaders"),e.httpHeaders&&Object.entries(e.httpHeaders).forEach((e=>{this.addHTTPHeader({headerName:e[0],value:e[1]})})),delete e.httpHeaders}this.commandForm.patchValue(e,{onlySelf:!1})}}observeFormChanges(){this.commandForm.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.connectorType!==dt.REST&&this.connectorType!==dt.REQUEST||(e.httpHeaders=e.httpHeaders.reduce(((e,t)=>(e[t.headerName]=t.value,e)),{})),this.commandForm.valid&&this.propagateChange({...this.commandForm.value,...e})}))}static{this.ɵfac=function(e){return new(e||AH)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(je.MatDialog))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:AH,selectors:[["tb-gateway-service-rpc-connector"]],inputs:{connectorType:"connectorType"},outputs:{sendCommand:"sendCommand",saveTemplate:"saveTemplate"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>AH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:12,vars:16,consts:[[1,"command-form","flex","flex-col",3,"formGroup"],[1,"mat-subtitle-1","title"],[3,"ngIf"],[1,"template-actions","flex","flex-row","justify-end","gap-2.5"],["mat-raised-button","",3,"click","disabled"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"ngSwitch"],[3,"ngSwitchCase"],["ngSwitchDefault",""],["matInput","","formControlName","method","placeholder","set_state"],[1,"mat-block"],["formControlName","requestType"],[3,"value",4,"ngFor","ngForOf"],["matInput","","formControlName","requestTimeout","type","number","min","10","step","1","placeholder","1000"],[1,"flex","flex-1","flex-row","gap-2.5"],[1,"flex-1"],["formControlName","objectType"],["matInput","","formControlName","identifier","type","number","min","1","step","1","placeholder","1"],["matInput","","formControlName","propertyId","placeholder","presentValue"],[3,"value"],["matInput","","formControlName","methodRPC","placeholder","rpcMethod1"],["matInput","","formControlName","characteristicUUID","placeholder","00002A00-0000-1000-8000-00805F9B34FB"],["formControlName","methodProcessing"],["formControlName","withResponse",1,"mat-slide"],["matInput","","formControlName","method","placeholder","sendSameData"],["matInput","","formControlName","nodeID","type","number","placeholder","4","min","0","step","1"],["formControlName","isExtendedID",1,"mat-slide","margin"],["formControlName","isFD",1,"mat-slide","margin"],["formControlName","bitrateSwitch",1,"mat-slide","margin"],["matInput","","formControlName","dataLength","type","number","placeholder","2","min","1","step","1"],["formControlName","dataByteorder"],["matInput","","formControlName","dataBefore","placeholder","00AA"],["matInput","","formControlName","dataAfter","placeholder","0102"],["matInput","","formControlName","dataInHEX","placeholder","aa bb cc dd ee ff aa bb aa bb cc d ee ff"],["matInput","","formControlName","dataExpression","placeholder","userSpeed if maxAllowedSpeed > userSpeed else maxAllowedSpeed"],["matInput","","formControlName","methodFilter","placeholder","read"],["matInput","","formControlName","valueExpression","placeholder","${params}"],["matInput","","formControlName","methodRPC","placeholder","rpc1"],["formControlName","withResponse",1,"mat-slide","margin"],["matInput","","formControlName","requestFilter","placeholder","setData"],["formControlName","method"],["formArrayName","oid",1,"fields","flex","flex-col","gap-2.5","border"],[1,"fields-label"],["class","flex flex-1 flex-row items-center justify-center gap-2.5",4,"ngFor","ngForOf"],["mat-raised-button","",1,"self-start",3,"click"],[1,"flex","flex-1","flex-row","items-center","justify-center","gap-2.5"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","flex-1"],["matInput","","required","",3,"formControl"],[1,"flex-[1_1_30px]",2,"cursor","pointer","max-width","30px","min-width","30px",3,"click","matTooltip"],["matInput","","formControlName","methodFilter","placeholder","post_attributes"],[1,"max-w-4/12","flex-[1_1_33%]"],["formControlName","httpMethod"],["matInput","","formControlName","requestUrlExpression","placeholder","http://127.0.0.1:5000/my_devices"],["matInput","","formControlName","responseTimeout","type","number","step","1","min","10","placeholder","10"],["matInput","","formControlName","timeout","type","number","step","1","min","10","placeholder","1000"],["matInput","","formControlName","tries","type","number","step","1","min","1","placeholder","3"],["formArrayName","httpHeaders",1,"fields","flex","flex-col","gap-2.5","border"],["class","flex flex-col gap-2.5 border",4,"ngIf"],[1,"flex","flex-col","gap-2.5","border"],[1,"flex","flex-row","items-center","justify-center","gap-2.5"],[1,"title","flex-1"],[2,"width","30px"],["class","flex flex-row items-center justify-center gap-2.5",4,"ngFor","ngForOf"],[3,"formGroupName"],["appearance","outline",1,"flex-1"],["matInput","","formControlName","headerName"],["matInput","","formControlName","value","placeholder","application/json"],[2,"cursor","pointer","width","30px",3,"click","matTooltip"],["matInput","","formControlName","methodFilter","placeholder","echo"],["matInput","","formControlName","timeout","type","number","step","1","min","10","placeholder","10"],["matInput","","formControlName","tries","type","number","step","1","min","1","placeholder","1"],["matInput","","formControlName","requestValueExpression","placeholder","${params}"],["matInput","","formControlName","responseValueExpression","placeholder","${temp}"],["matInput","","formControlName","headerName",3,"placeholder"],["matInput","","formControlName","value"],["matInput","","formControlName","command"],["matInput","","formControlName","params","type","JSON","tb-json-to-string",""],["aria-hidden","false","aria-label","help-icon","matIconSuffix","",1,"material-icons-outlined",2,"cursor","pointer",3,"click","matTooltip"],[4,"ngIf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,OH,11,10,"ng-template",2),t.ɵɵelementStart(5,"div",3)(6,"button",4),t.ɵɵlistener("click",(function(){return n.save()})),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",5),t.ɵɵlistener("click",(function(){return n.sendCommand.emit()})),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.commandForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(3,7,"gateway.rpc.title",t.ɵɵpureFunction1(14,lH,n.gatewayConnectorDefaultTypesTranslates.get(n.connectorType)))),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.connectorType),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.commandForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,10,"gateway.rpc-command-save-template")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.commandForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,12,"gateway.rpc-command-send")," "))},dependencies:t.ɵɵgetComponentDepsFactory(AH,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex;flex-direction:column;padding:0}[_nghost-%COMP%] .title[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%]{flex-wrap:nowrap}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%] > button[_ngcontent-%COMP%]{margin-top:10px}[_nghost-%COMP%] .mat-mdc-slide-toggle.margin[_ngcontent-%COMP%]{margin-bottom:10px;margin-left:10px}[_nghost-%COMP%] .fields[_ngcontent-%COMP%] .fields-label[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .border[_ngcontent-%COMP%]{padding:16px;margin-bottom:10px;box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;border:solid 1px #e0e0e0;border-radius:4px}[_nghost-%COMP%] .border[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{color:#0000008a}[_nghost-%COMP%] .border[_ngcontent-%COMP%] .mat-icon[_ngcontent-%COMP%]{color:#00000061}[_nghost-%COMP%] .border[_ngcontent-%COMP%] .mat-divider[_ngcontent-%COMP%]{margin-left:-16px;margin-right:-16px;margin-bottom:16px}']})}}function FH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",11),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function RH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",11),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.ModbusFunctionCodeTranslationsMap.get(e)))}}function BH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",12),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-required"))}function NH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",12),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function LH(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13)(1,"mat-form-field",3)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",14),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,NH,3,3,"mat-icon",8),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rpc.value")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.rpcParametersFormGroup.get("value").hasError("required")&&e.rpcParametersFormGroup.get("value").touched)}}class VH{constructor(e){this.fb=e,this.ModbusEditableDataTypes=Qt,this.ModbusFunctionCodeTranslationsMap=nn,this.modbusDataTypes=Object.values(Zt),this.writeFunctionCodes=[5,6,15,16],this.defaultFunctionCodes=[3,4,6,16],this.readFunctionCodes=[1,2,3,4],this.bitsFunctionCodes=[...this.readFunctionCodes,...this.writeFunctionCodes],this.destroy$=new we,this.rpcParametersFormGroup=this.fb.group({type:[Zt.BYTES,[me.required]],functionCode:[this.defaultFunctionCodes[0],[me.required]],value:[{value:"",disabled:!0},[me.required,me.pattern(pn)]],address:[null,[me.required]],objectsCount:[1,[me.required]]}),this.updateFunctionCodes(this.rpcParametersFormGroup.get("type").value),this.observeValueChanges(),this.observeKeyDataType(),this.observeFunctionCode()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.rpcParametersFormGroup.valid?null:{rpcParametersFormGroup:{valid:!1}}}writeValue(e){this.rpcParametersFormGroup.patchValue(e,{emitEvent:!1})}observeValueChanges(){this.rpcParametersFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}observeKeyDataType(){this.rpcParametersFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.ModbusEditableDataTypes.includes(e)||this.rpcParametersFormGroup.get("objectsCount").patchValue(Jt[e],{emitEvent:!1}),this.updateFunctionCodes(e)}))}observeFunctionCode(){this.rpcParametersFormGroup.get("functionCode").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.updateValueEnabling(e)))}updateValueEnabling(e){this.writeFunctionCodes.includes(e)?this.rpcParametersFormGroup.get("value").enable({emitEvent:!1}):(this.rpcParametersFormGroup.get("value").setValue(null),this.rpcParametersFormGroup.get("value").disable({emitEvent:!1}))}updateFunctionCodes(e){this.functionCodes=e===Zt.BITS?this.bitsFunctionCodes:this.defaultFunctionCodes,this.functionCodes.includes(this.rpcParametersFormGroup.get("functionCode").value)||this.rpcParametersFormGroup.get("functionCode").patchValue(this.functionCodes[0],{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||VH)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:VH,selectors:[["tb-gateway-modbus-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>VH)),multi:!0},{provide:he,useExisting:c((()=>VH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:35,vars:30,consts:[[3,"formGroup"],[1,"tb-form-hint","tb-primary-fill","no-padding-top","hint-container"],[1,"flex","flex-1","flex-row","gap-2.5"],[1,"flex-1"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["formControlName","functionCode"],["matInput","","type","number","min","0","max","50000","name","value","formControlName","address",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","number","min","1","max","50000","name","value","formControlName","objectsCount",3,"placeholder","readonly"],["class","flex",4,"ngIf"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[1,"flex"],["matInput","","name","value","formControlName","value",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelement(4,"br"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",2)(8,"mat-form-field",3)(9,"mat-label"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-select",4),t.ɵɵtemplate(13,FH,2,2,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-form-field",3)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"mat-select",6),t.ɵɵtemplate(19,RH,3,4,"mat-option",5),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",2)(21,"mat-form-field",3)(22,"mat-label"),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",7),t.ɵɵpipe(26,"translate"),t.ɵɵtemplate(27,BH,3,3,"mat-icon",8),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-form-field",3)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",9),t.ɵɵpipe(33,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(34,LH,8,7,"div",10),t.ɵɵelementContainerEnd()),2&e&&(t.ɵɵproperty("formGroup",n.rpcParametersFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,14,"gateway.rpc.hint.modbus-response-reading"),""),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,16,"gateway.rpc.hint.modbus-writing-functions")," "),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(11,18,"gateway.rpc.type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.modbusDataTypes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(17,20,"gateway.rpc.functionCode")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.functionCodes),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(24,22,"gateway.rpc.address")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(26,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.rpcParametersFormGroup.get("address").hasError("required")&&n.rpcParametersFormGroup.get("address").touched),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,26,"gateway.rpc.objectsCount")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(33,28,"gateway.set")),t.ɵɵproperty("readonly",!n.ModbusEditableDataTypes.includes(n.rpcParametersFormGroup.get("type").value)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.writeFunctionCodes.includes(n.rpcParametersFormGroup.get("functionCode").value)))},dependencies:t.ɵɵgetComponentDepsFactory(VH,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{margin-bottom:12px}'],changeDetection:d.OnPush})}}function qH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",6),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rpc.responseTopicExpression")))}function GH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",7),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rpc.responseTimeout")))}class zH{constructor(e){this.fb=e,this.onChange=e=>{},this.onTouched=()=>{},this.destroy$=new we,this.rpcParametersFormGroup=this.fb.group({methodFilter:[null,[me.required,me.pattern(pn)]],requestTopicExpression:[null,[me.required,me.pattern(pn)]],responseTopicExpression:[{value:null,disabled:!0},[me.required,me.pattern(pn)]],responseTimeout:[{value:null,disabled:!0},[me.min(10),me.pattern(cn)]],valueExpression:[null,[me.required,me.pattern(pn)]],withResponse:[!1,[]]}),this.observeValueChanges(),this.observeWithResponse()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.rpcParametersFormGroup.valid?null:{rpcParametersFormGroup:{valid:!1}}}writeValue(e){this.rpcParametersFormGroup.patchValue(e,{emitEvent:!1}),this.toggleResponseFields(e.withResponse)}observeValueChanges(){this.rpcParametersFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}observeWithResponse(){this.rpcParametersFormGroup.get("withResponse").valueChanges.pipe(Ae((e=>this.toggleResponseFields(e))),Me(this.destroy$)).subscribe()}toggleResponseFields(e){const t=this.rpcParametersFormGroup.get("responseTopicExpression"),n=this.rpcParametersFormGroup.get("responseTimeout");e?(t.enable(),n.enable()):(t.disable(),n.disable())}static{this.ɵfac=function(e){return new(e||zH)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:zH,selectors:[["tb-gateway-mqtt-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>zH)),multi:!0},{provide:he,useExisting:c((()=>zH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:21,vars:15,consts:[[3,"formGroup"],["matInput","","formControlName","methodFilter","placeholder","echo"],["matInput","","formControlName","requestTopicExpression","placeholder","sensor/${deviceName}/request/${methodName}/${requestId}"],["formControlName","withResponse",1,"margin",3,"click"],[4,"ngIf"],["matInput","","formControlName","valueExpression","placeholder","${params}"],["matInput","","formControlName","responseTopicExpression","placeholder","sensor/${deviceName}/response/${methodName}/${requestId}"],["matInput","","formControlName","responseTimeout","type","number","placeholder","10000","min","10","step","1"]],template:function(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"mat-form-field")(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",1),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field")(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",2),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-slide-toggle",3),t.ɵɵlistener("click",(function(e){return e.stopPropagation()})),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(14,qH,5,3,"mat-form-field",4)(15,GH,5,3,"mat-form-field",4),t.ɵɵelementStart(16,"mat-form-field")(17,"mat-label"),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(20,"input",5),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e){let e,i;t.ɵɵproperty("formGroup",n.rpcParametersFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,7,"gateway.rpc.method-name")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,9,"gateway.rpc.requestTopicExpression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,11,"gateway.rpc.withResponse")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",null==(e=n.rpcParametersFormGroup.get("withResponse"))?null:e.value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",null==(i=n.rpcParametersFormGroup.get("withResponse"))?null:i.value),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(19,13,"gateway.rpc.valueExpression"))}},dependencies:t.ɵɵgetComponentDepsFactory(zH,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:flex;flex-direction:column}[_nghost-%COMP%] .mat-mdc-slide-toggle.margin[_ngcontent-%COMP%]{margin-bottom:10px;margin-left:10px}'],changeDetection:d.OnPush})}}function UH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",26),t.ɵɵelement(1,"mat-icon",27),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("svgIcon",i.valueTypes.get(e).icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,i.valueTypes.get(e).name))}}function jH(e,n){1&e&&(t.ɵɵelement(0,"input",28),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function HH(e,n){1&e&&(t.ɵɵelement(0,"input",29),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function WH(e,n){1&e&&(t.ɵɵelement(0,"input",30),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function $H(e,n){1&e&&(t.ɵɵelementStart(0,"mat-select",31)(1,"mat-option",26),t.ɵɵtext(2,"true"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-option",26),t.ɵɵtext(4,"false"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵproperty("value",!0),t.ɵɵadvance(2),t.ɵɵproperty("value",!1))}function KH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",32),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function YH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",8)(1,"div",9)(2,"div",10),t.ɵɵtext(3,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",11)(5,"mat-form-field",12)(6,"mat-select",13)(7,"mat-select-trigger")(8,"div",14),t.ɵɵelement(9,"mat-icon",15),t.ɵɵelementStart(10,"span"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(13,UH,5,5,"mat-option",16),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(14,"div",17)(15,"div",10),t.ɵɵtext(16,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-form-field",18),t.ɵɵelementContainerStart(18,19),t.ɵɵtemplate(19,jH,2,3,"input",20)(20,HH,2,3,"input",21)(21,WH,2,3,"input",22)(22,$H,5,2,"mat-select",23),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(23,KH,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"button",25),t.ɵɵpipe(25,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext();return t.ɵɵresetView(i.removeArgument(n))})),t.ɵɵelementStart(26,"mat-icon"),t.ɵɵtext(27,"delete"),t.ɵɵelementEnd()()()}if(2&e){let e,i;const a=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("formGroup",a),t.ɵɵadvance(9),t.ɵɵproperty("svgIcon",null==(e=r.valueTypes.get(a.get("type").value))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,11,null==(i=r.valueTypes.get(a.get("type").value))?null:i.name)),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",r.valueTypeKeys),t.ɵɵadvance(5),t.ɵɵproperty("ngSwitch",a.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.STRING),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.INTEGER),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.DOUBLE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.BOOLEAN),t.ɵɵadvance(),t.ɵɵproperty("ngIf",a.get(a.get("type").value+"Value").hasError("required")&&a.get(a.get("type").value+"Value").touched),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(25,13,"gateway.rpc.remove"))}}class XH{constructor(e,t){this.fb=e,this.cdr=t,this.valueTypeKeys=Object.values(en),this.MappingValueType=en,this.valueTypes=tn,this.onChange=e=>{},this.onTouched=()=>{},this.destroy$=new we,this.rpcParametersFormGroup=this.fb.group({method:[null,[me.required,me.pattern(pn)]],arguments:this.fb.array([])}),this.observeValueChanges()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.rpcParametersFormGroup.valid?null:{rpcParametersFormGroup:{valid:!1}}}writeValue(e){this.clearArguments(),e.arguments?.map((({type:e,value:t})=>({type:e,[e+"Value"]:t}))).forEach((e=>this.addArgument(e))),this.cdr.markForCheck(),this.rpcParametersFormGroup.get("method").patchValue(e.method)}observeValueChanges(){this.rpcParametersFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{const t=e.arguments.map((({type:e,...t})=>({type:e,value:t[e+"Value"]})));this.onChange({method:e.method,arguments:t}),this.onTouched()}))}removeArgument(e){this.rpcParametersFormGroup.get("arguments").removeAt(e)}addArgument(e={}){const t=this.fb.group({type:[e.type??en.STRING],stringValue:[e.stringValue??{value:"",disabled:!(W(e,{})||e.stringValue)},[me.required,me.pattern(pn)]],integerValue:[{value:e.integerValue??0,disabled:!J(e.integerValue)},[me.required,me.pattern(cn)]],doubleValue:[{value:e.doubleValue??0,disabled:!J(e.doubleValue)},[me.required]],booleanValue:[{value:e.booleanValue??!1,disabled:!J(e.booleanValue)},[me.required]]});this.observeTypeChange(t),this.rpcParametersFormGroup.get("arguments").push(t,{emitEvent:!1})}clearArguments(){const e=this.rpcParametersFormGroup.get("arguments");for(;0!==e.length;)e.removeAt(0)}observeTypeChange(e){e.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((t=>{e.disable({emitEvent:!1}),e.get("type").enable({emitEvent:!1}),e.get(t+"Value").enable({emitEvent:!1})}))}static{this.ɵfac=function(e){return new(e||XH)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:XH,selectors:[["tb-gateway-opc-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>XH)),multi:!0},{provide:he,useExisting:c((()=>XH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:18,vars:14,consts:[[3,"formGroup"],[1,"tb-form-hint","tb-primary-fill","tb-flex","no-padding-top","hint-container"],[1,"tb-flex"],["matInput","","formControlName","method","placeholder","multiply"],["formArrayName","arguments",1,"tb-form-panel","stroked","arguments-container"],[1,"fields-label"],["class","flex flex-1 items-center justify-center gap-2.5",3,"formGroup",4,"ngFor","ngForOf"],["mat-raised-button","",1,"self-start",3,"click"],[1,"flex","flex-1","items-center","justify-center","gap-2.5",3,"formGroup"],[1,"tb-form-row","column-xs","type-container","items-center","justify-between"],["translate","",1,"tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","type"],[1,"tb-flex","align-center"],[1,"tb-mat-18",3,"svgIcon"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-row","column-xs","value-container","item-center","justify-between"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","tb-suffix-absolute","flex"],[3,"ngSwitch"],["matInput","","required","","formControlName","stringValue",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder",4,"ngSwitchCase"],["formControlName","booleanValue",4,"ngSwitchCase"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["mat-icon-button","","matTooltipPosition","above",1,"tb-box-button",3,"click","matTooltip"],[3,"value"],[1,"tb-mat-20",3,"svgIcon"],["matInput","","required","","formControlName","stringValue",3,"placeholder"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder"],["formControlName","booleanValue"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",2)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",3),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"fieldset",4)(10,"strong")(11,"span",5),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(14,YH,28,15,"div",6),t.ɵɵelementStart(15,"button",7),t.ɵɵlistener("click",(function(){return n.addArgument()})),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e&&(t.ɵɵproperty("formGroup",n.rpcParametersFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,6,"gateway.rpc.hint.opc-method")," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,8,"gateway.rpc.method")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,10,"gateway.rpc.arguments")),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.rpcParametersFormGroup.get("arguments").controls),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,12,"gateway.rpc.add-argument")," "))},dependencies:t.ɵɵgetComponentDepsFactory(XH,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .arguments-container[_ngcontent-%COMP%]{margin-bottom:10px}[_nghost-%COMP%] .type-container[_ngcontent-%COMP%]{width:40%}[_nghost-%COMP%] .value-container[_ngcontent-%COMP%]{width:50%}[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{margin-bottom:12px}'],changeDetection:d.OnPush})}}function ZH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SocketMethodProcessingsTranslates.get(e))," ")}}function QH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}class JH extends za{constructor(){super(...arguments),this.SocketMethodProcessingsTranslates=Pj,this.socketMethodProcessings=Object.values(kj),this.socketEncoding=Object.values(ht)}initFormGroup(){return this.fb.group({methodRPC:[null,[me.required,me.pattern(pn)]],methodProcessing:[kj.WRITE,[me.required]],encoding:[Aj.UTF_8,[me.required,me.pattern(pn)]],withResponse:[!1,[]]})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(JH)))(n||JH)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:JH,selectors:[["tb-gateway-socket-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>JH)),multi:!0},{provide:he,useExisting:c((()=>JH)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:21,vars:15,consts:[[3,"formGroup"],[1,"w-full"],["matInput","","formControlName","methodRPC","placeholder","rpcMethod1"],[1,"mat-block"],["formControlName","methodProcessing"],[3,"value",4,"ngFor","ngForOf"],["formControlName","encoding"],["formControlName","withResponse",1,"mat-slide","margin"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"mat-form-field",1)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",2),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",3)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-select",4),t.ɵɵtemplate(11,ZH,3,4,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",3)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-select",6),t.ɵɵtemplate(17,QH,2,2,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"mat-slide-toggle",7),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e&&(t.ɵɵproperty("formGroup",n.formGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,7,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,9,"gateway.rpc.methodProcessing")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.socketMethodProcessings),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,11,"gateway.encoding")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,13,"gateway.rpc.withResponse")," "))},dependencies:t.ɵɵgetComponentDepsFactory(JH,[se,_]),encapsulation:2,changeDetection:d.OnPush})}}const eW=e=>({border:e}),tW=e=>({type:e});function nW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",15),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function iW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-min")," "))}function aW(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"mat-form-field")(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",9),t.ɵɵtemplate(6,nW,2,2,"mat-option",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",11)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",12),t.ɵɵtemplate(12,iW,3,3,"mat-error",13),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"button",14),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.sendCommand())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,6,"gateway.statistics.command")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.RPCCommands),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,8,"gateway.statistics.timeout")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.commandForm.get("time").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("disabled",e.commandForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,10,"gateway.rpc-command-send")," ")}}function rW(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-service-rpc-connector",17),t.ɵɵlistener("sendCommand",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.sendCommand())}))("saveTemplate",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.saveTemplate())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("connectorType",e.connectorType)}}function oW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-modbus-rpc-parameters",24)}function sW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-mqtt-rpc-parameters",24)}function lW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-opc-rpc-parameters",24)}function pW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-socket-rpc-parameters",24)}function cW(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",18)(1,"div",19),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(4,20),t.ɵɵtemplate(5,oW,1,0,"tb-gateway-modbus-rpc-parameters",21)(6,sW,1,0,"tb-gateway-mqtt-rpc-parameters",21)(7,lW,1,0,"tb-gateway-opc-rpc-parameters",21)(8,pW,1,0,"tb-gateway-socket-rpc-parameters",21),t.ɵɵelementContainerEnd(),t.ɵɵelementStart(9,"div",22)(10,"button",23),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.saveTemplate())})),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"button",14),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.sendCommand())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(3,10,"gateway.rpc.title",t.ɵɵpureFunction1(17,tW,e.gatewayConnectorDefaultTypesTranslates.get(e.connectorType)))),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",e.connectorType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MODBUS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MQTT),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.OPCUA),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.SOCKET),t.ɵɵadvance(2),t.ɵɵproperty("disabled",e.commandForm.get("params").invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,13,"gateway.rpc-command-save-template")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",e.commandForm.get("params").invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,15,"gateway.rpc-command-send")," ")}}function dW(e,n){if(1&e&&t.ɵɵtemplate(0,rW,1,1,"tb-gateway-service-rpc-connector",16)(1,cW,16,19,"ng-template",null,1,t.ɵɵtemplateRefExtractor),2&e){const e=t.ɵɵreference(2),n=t.ɵɵnextContext();t.ɵɵproperty("ngIf",!n.typesWithUpdatedParams.has(n.connectorType))("ngIfElse",e)}}function uW(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",25)(1,"mat-icon",26),t.ɵɵtext(2,"schedule"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"span"),t.ɵɵtext(4),t.ɵɵpipe(5,"date"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(5,1,e.resultTime,"yyyy/MM/dd HH:mm:ss"))}}function mW(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-service-rpc-connector-templates",27),t.ɵɵlistener("useTemplate",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.useTemplate(n))})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("rpcTemplates",e.templates)("ctx",e.ctx)("connectorType",e.connectorType)}}class hW{constructor(e,t,n,i,a){this.fb=e,this.dialog=t,this.utils=n,this.cd=i,this.attributeService=a,this.contentTypes=V,this.RPCCommands=["Ping","Stats","Devices","Update","Version","Restart","Reboot"],this.templates=[],this.ConnectorType=dt,this.gatewayConnectorDefaultTypesTranslates=ut,this.typesWithUpdatedParams=new Set([dt.MQTT,dt.OPCUA,dt.MODBUS,dt.SOCKET]),this.modbusReadFunctionCodes=[1,2,3,4],this.subscriptionOptions={callbacks:{onDataUpdated:()=>this.ctx.ngZone.run((()=>{this.updateTemplates()})),onDataUpdateError:(e,t)=>this.ctx.ngZone.run((()=>{this.onDataUpdateError(t)})),dataLoading:()=>{}}},this.commandForm=this.fb.group({command:[null,[me.required]],time:[60,[me.required,me.min(1)]],params:["{}",[lt]],result:[null]})}ngOnInit(){if(this.isConnector=this.ctx.settings.isConnector,this.isConnector){this.connectorType=this.ctx.stateController.getStateParams().connector_rpc.value.type;const e=[{type:N.entity,entityType:L.DEVICE,entityId:this.ctx.defaultSubscription.targetDeviceId,entityName:"Connector",attributes:[{name:`${this.connectorType}_template`}]}];this.ctx.subscriptionApi.createSubscriptionFromInfo(R.latest,e,this.subscriptionOptions,!1,!0).subscribe((e=>{this.subscription=e}))}else this.commandForm.get("command").setValue(this.RPCCommands[0])}sendCommand(e){this.resultTime=null;const t=e||this.commandForm.value,n=this.isConnector?`${this.connectorType}_`:"gateway_",i=this.isConnector?this.getCommandFromParamsByType(t.params):t.command.toLowerCase(),a=this.ctx.stateController.getStateParams().connector_rpc?.value.configurationJson.id,r=a?{...t.params,connectorId:a}:t.params;this.ctx.controlApi.sendTwoWayCommand(n+i,r,t.time).subscribe({next:e=>{this.resultTime=(new Date).getTime(),this.commandForm.get("result").setValue(JSON.stringify(e))},error:e=>{this.resultTime=(new Date).getTime(),console.error(e),this.commandForm.get("result").setValue(JSON.stringify(e.error))}})}getCommandFromParamsByType(e){switch(this.connectorType){case dt.MQTT:case dt.FTP:case dt.SNMP:case dt.REST:case dt.REQUEST:return e.methodFilter;case dt.MODBUS:return this.modbusReadFunctionCodes.includes(e.functionCode)?"get":"set";case dt.BACNET:case dt.CAN:case dt.OPCUA:return e.method;case dt.BLE:case dt.OCPP:case dt.SOCKET:case dt.XMPP:return e.methodRPC;default:return e.command}}saveTemplate(){this.dialog.open(nH,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{config:this.commandForm.value.params,templates:this.templates}}).afterClosed().subscribe((e=>{if(e){const t={name:e,config:this.commandForm.value.params,type:this.connectorType},n=this.templates,i=n.findIndex((e=>e.name==t.name));i>-1&&n.splice(i,1),n.push(t);const a=`${this.connectorType}_template`;this.attributeService.saveEntityAttributes({id:this.ctx.defaultSubscription.targetDeviceId,entityType:L.DEVICE},D.SERVER_SCOPE,[{key:a,value:n}]).subscribe((()=>{this.cd.detectChanges()}))}}))}useTemplate(e){this.commandForm.get("params").patchValue(e.config)}updateTemplates(){this.templates=this.subscription.data[0].data[0][1].length?JSON.parse(this.subscription.data[0].data[0][1]):[],this.cd.detectChanges()}onDataUpdateError(e){const t=this.utils.parseException(e);let n=t.name;t.message&&(n+=": "+t.message),console.error(n)}static{this.ɵfac=function(e){return new(e||hW)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(K.UtilsService),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(K.AttributeService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:hW,selectors:[["tb-gateway-service-rpc"]],inputs:{ctx:"ctx",dialogRef:"dialogRef"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:12,vars:14,consts:[["connectorForm",""],["updatedParameters",""],[1,"flex","flex-1","flex-col"],[1,"command-form","flex","flex-row","gap-2.5","lt-sm:flex-col",3,"formGroup"],[4,"ngIf","ngIfElse"],[1,"result-block",3,"formGroup"],["class","result-time flex flex-1 flex-row items-center justify-center",4,"ngIf"],["readonly","true","formControlName","result",3,"contentType"],["class","border",3,"rpcTemplates","ctx","connectorType","useTemplate",4,"ngIf"],["formControlName","command"],[3,"value",4,"ngFor","ngForOf"],[1,"flex-1"],["matInput","","formControlName","time","type","number","min","1"],[4,"ngIf"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["formControlName","params",3,"connectorType","sendCommand","saveTemplate",4,"ngIf","ngIfElse"],["formControlName","params",3,"sendCommand","saveTemplate","connectorType"],[1,"rpc-parameters","flex","flex-col"],[1,"mat-subtitle-1","tb-form-panel-title"],[3,"ngSwitch"],["formControlName","params",4,"ngSwitchCase"],[1,"fex-row","template-actions","flex","flex-1","items-center","justify-end","gap-2.5"],["mat-raised-button","",3,"click","disabled"],["formControlName","params"],[1,"result-time","flex","flex-1","flex-row","items-center","justify-center"],[1,"material-icons"],[1,"border",3,"useTemplate","rpcTemplates","ctx","connectorType"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",2)(1,"div",3),t.ɵɵtemplate(2,aW,16,12,"ng-container",4)(3,dW,3,2,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"section",5)(6,"span"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,uW,6,4,"div",6),t.ɵɵelementEnd(),t.ɵɵelement(10,"tb-json-content",7),t.ɵɵelementEnd()(),t.ɵɵtemplate(11,mW,1,3,"tb-gateway-service-rpc-connector-templates",8)),2&e){const e=t.ɵɵreference(4);t.ɵɵclassMap(t.ɵɵpureFunction1(12,eW,n.isConnector)),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.commandForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.isConnector)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵproperty("formGroup",n.commandForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(8,10,"gateway.rpc-command-result")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.resultTime),t.ɵɵadvance(),t.ɵɵproperty("contentType",n.contentTypes.JSON),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isConnector)}},dependencies:t.ɵɵgetComponentDepsFactory(hW,[se,_,AH,VH,zH,XH,Jj,JH]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;overflow:auto;display:flex;flex-direction:row;padding:0 5px}[_nghost-%COMP%] > *[_ngcontent-%COMP%]{height:100%;overflow:auto}[_nghost-%COMP%] > tb-gateway-service-rpc-connector-templates[_ngcontent-%COMP%]:last-child{margin-left:10px}[_nghost-%COMP%] tb-gateway-service-rpc-connector-templates[_ngcontent-%COMP%]{flex:1 1 30%;max-width:30%}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%]{flex-wrap:nowrap;padding:0 5px 5px}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%] > button[_ngcontent-%COMP%]{margin-top:10px}[_nghost-%COMP%] .rpc-parameters[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%]{padding:0 5px;display:flex;flex-direction:column;flex:1}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] > span[_ngcontent-%COMP%]{font-weight:600;position:relative;font-size:14px;margin-bottom:10px}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] .result-time[_ngcontent-%COMP%]{font-weight:400;font-size:14px;line-height:32px;position:absolute;left:0;top:25px;z-index:5;color:#0000008a}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] .result-time[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{padding-left:10px}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] tb-json-content[_ngcontent-%COMP%]{flex:1}[_nghost-%COMP%] .border[_ngcontent-%COMP%]{padding:16px;box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;border:solid 1px #e0e0e0;border-radius:4px}']})}}function gW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.configuration-delete-dialog-input-required")," "))}e("GatewayServiceRPCComponent",hW);class fW extends A{constructor(e,t,n,i,a){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.gatewayName=this.data.gatewayName,this.gatewayControl=this.fb.control("")}close(){this.dialogRef.close()}turnOff(){this.dialogRef.close(!0)}static{this.ɵfac=function(e){return new(e||fW)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:fW,selectors:[["tb-gateway-remote-configuration-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:24,vars:14,consts:[["color","warn"],["translate",""],[1,"flex-1"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"mat-content","flex-col",2,"max-width","600px"],[3,"innerHTML"],[1,"mat-block","tb-value-type",2,"flex-grow","0"],["matInput","","required","",3,"formControl"],[4,"ngIf"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","warn","type","button","cdkFocusInitial","",3,"click"],["mat-button","","color","warn","type","button",3,"click","disabled"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-toolbar",0)(1,"mat-icon"),t.ɵɵtext(2,"warning"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"h2",1),t.ɵɵtext(4,"gateway.configuration-delete-dialog-header"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",2),t.ɵɵelementStart(6,"button",3),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵelementStart(7,"mat-icon",4),t.ɵɵtext(8,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",5),t.ɵɵelement(10,"span",6),t.ɵɵpipe(11,"translate"),t.ɵɵelementStart(12,"mat-form-field",7)(13,"mat-label",1),t.ɵɵtext(14,"gateway.configuration-delete-dialog-input"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",8),t.ɵɵtemplate(16,gW,3,3,"mat-error",9),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",10)(18,"button",11),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"button",12),t.ɵɵlistener("click",(function(){return n.turnOff()})),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(10),t.ɵɵpropertyInterpolate2("innerHTML","",t.ɵɵpipeBind1(11,8,"gateway.configuration-delete-dialog-body")," ",n.gatewayName,"",t.ɵɵsanitizeHtml),t.ɵɵadvance(5),t.ɵɵproperty("formControl",n.gatewayControl),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayControl.hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,10,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.gatewayControl.value!==n.gatewayName),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(23,12,"gateway.configuration-delete-dialog-confirm")," "))},dependencies:t.ɵɵgetComponentDepsFactory(fW,[se,_]),encapsulation:2})}}var yW;e("GatewayRemoteConfigurationDialogComponent",fW),function(e){e.tls="tls",e.accessToken="accessToken"}(yW||(yW={}));const vW="configuration_drafts",xW="RemoteLoggingLevel",bW=new Map([[yW.tls,"gateway.security-types.tls"],[yW.accessToken,"gateway.security-types.access-token"]]);var wW,SW;!function(e){e.none="NONE",e.critical="CRITICAL",e.error="ERROR",e.warning="WARNING",e.info="INFO",e.debug="DEBUG"}(wW||(wW={})),function(e){e.memory="memory",e.file="file"}(SW||(SW={}));const CW=new Map([[SW.memory,"gateway.storage-types.memory-storage"],[SW.file,"gateway.storage-types.file-storage"]]);var _W;!function(e){e.mqtt="MQTT",e.modbus="Modbus",e.opcua="OPC-UA",e.ble="BLE",e.request="Request",e.can="CAN",e.bacnet="BACnet",e.custom="Custom"}(_W||(_W={}));const TW={config:{},name:"",configType:null,enabled:!1};function IW(e){return JSON.stringify(e.value)===JSON.stringify({})?{validJSON:!0}:null}function EW(e){return e.replace("_","").replace("-","").replace(/^\s+|\s+/g,"").toLowerCase()+".json"}function MW(e,t){return'[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=converterHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"'.replace(/{ERROR}/g,e).replace(/{.\/logs\/}/g,t)}function kW(e){return{id:e,entityType:L.DEVICE}}function PW(e){const t={};return Object.prototype.hasOwnProperty.call(e,"thingsboard")&&(t.host=e.thingsboard.host,t.port=e.thingsboard.port,t.remoteConfiguration=e.thingsboard.remoteConfiguration,Object.prototype.hasOwnProperty.call(e.thingsboard.security,yW.accessToken)?(t.securityType=yW.accessToken,t.accessToken=e.thingsboard.security.accessToken):(t.securityType=yW.tls,t.caCertPath=e.thingsboard.security.caCert,t.privateKeyPath=e.thingsboard.security.privateKey,t.certPath=e.thingsboard.security.cert)),Object.prototype.hasOwnProperty.call(e,"storage")&&Object.prototype.hasOwnProperty.call(e.storage,"type")&&(e.storage.type===SW.memory?(t.storageType=SW.memory,t.readRecordsCount=e.storage.read_records_count,t.maxRecordsCount=e.storage.max_records_count):e.storage.type===SW.file&&(t.storageType=SW.file,t.dataFolderPath=e.storage.data_folder_path,t.maxFilesCount=e.storage.max_file_count,t.readRecordsCount=e.storage.read_records_count,t.maxRecordsCount=e.storage.max_records_count)),t}function DW(e){const t={};for(const n of e)n.enabled||(t[n.name]={connector:n.configType,config:n.config});return t}function OW(e){const t={thingsboard:AW(e)};return function(e,t){for(const n of t)if(n.enabled){const t=n.configType;Array.isArray(e[t])||(e[t]=[]);const i={name:n.name,config:n.config};e[t].push(i)}}(t,e.connectors),t}function AW(e){let t;t=e.securityType===yW.accessToken?{accessToken:e.accessToken}:{caCert:e.caCertPath,privateKey:e.privateKeyPath,cert:e.certPath};const n={host:e.host,remoteConfiguration:e.remoteConfiguration,port:e.port,security:t};let i;i=e.storageType===SW.memory?{type:SW.memory,read_records_count:e.readRecordsCount,max_records_count:e.maxRecordsCount}:{type:SW.file,data_folder_path:e.dataFolderPath,max_file_count:e.maxFilesCount,max_read_records_count:e.readRecordsCount,max_records_per_file:e.maxRecordsCount};const a=[];for(const t of e.connectors)if(t.enabled){const e={configuration:EW(t.name),name:t.name,type:t.configType};a.push(e)}return{thingsboard:n,connectors:a,storage:i,logs:window.btoa(MW(e.remoteLoggingLevel,e.remoteLoggingPathToLogs))}}const FW=["formContainer"],RW=(e,t,n)=>({"gap-1.25":e,"flex-row":t,"flex-col":n}),BW=(e,t,n)=>({"gap-1.25":e,"flex-row justify-end item-center":t,"flex-col justify-evenly item-center":n});function NW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e.value.toString())," ")}}function LW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-host-required "),t.ɵɵelementEnd())}function VW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-required "),t.ɵɵelementEnd())}function qW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-min "),t.ɵɵelementEnd())}function GW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-max "),t.ɵɵelementEnd())}function zW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-pattern "),t.ɵɵelementEnd())}function UW(e,n){1&e&&(t.ɵɵelementStart(0,"div",16)(1,"mat-form-field")(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",30),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field")(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",31),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field")(12,"mat-label"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",32),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.tls-path-ca-certificate")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,5,"gateway.tls-path-private-key")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,7,"gateway.tls-path-client-certificate")))}function jW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function HW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.path-logs-required "),t.ɵɵelementEnd())}function WW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e.value.toString())," ")}}function $W(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-pack-size-required "),t.ɵɵelementEnd())}function KW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-pack-size-min "),t.ɵɵelementEnd())}function YW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-pack-size-pattern "),t.ɵɵelementEnd())}function XW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-records-required "),t.ɵɵelementEnd())}function ZW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-records-min "),t.ɵɵelementEnd())}function QW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-records-pattern "),t.ɵɵelementEnd())}function JW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-files-required "),t.ɵɵelementEnd())}function e$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-files-min "),t.ɵɵelementEnd())}function t$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-files-pattern "),t.ɵɵelementEnd())}function n$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-path-required "),t.ɵɵelementEnd())}function i$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",5)(1,"mat-form-field",8)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",33),t.ɵɵtemplate(6,JW,2,0,"mat-error",10)(7,e$,2,0,"mat-error",10)(8,t$,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",8)(10,"mat-label"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",34),t.ɵɵtemplate(14,n$,2,0,"mat-error",10),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵclassMap(t.ɵɵpureFunction3(12,RW,e.layoutGap,e.alignment,!e.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,8,"gateway.storage-max-files")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("maxFilesCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("maxFilesCount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("maxFilesCount").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,10,"gateway.storage-path")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("dataFolderPath").hasError("required"))}}function a$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function r$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.connector-type-required "),t.ɵɵelementEnd())}function o$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.connector-name-required "),t.ɵɵelementEnd())}function s$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",35)(1,"div",36)(2,"div",37),t.ɵɵelement(3,"mat-slide-toggle",38),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",39)(5,"mat-form-field",8)(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",40),t.ɵɵlistener("selectionChange",(function(){const n=t.ɵɵrestoreView(e).$implicit,i=t.ɵɵnextContext();return t.ɵɵresetView(i.changeConnectorType(n))})),t.ɵɵtemplate(10,a$,2,2,"mat-option",7),t.ɵɵelementEnd(),t.ɵɵtemplate(11,r$,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",8)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"input",41),t.ɵɵlistener("blur",(function(){const n=t.ɵɵrestoreView(e),i=n.$implicit,a=n.index,r=t.ɵɵnextContext();return t.ɵɵresetView(r.changeConnectorName(i,a))})),t.ɵɵelementEnd(),t.ɵɵtemplate(17,o$,2,0,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",42)(19,"button",43),t.ɵɵpipe(20,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e),a=i.$implicit,r=i.index,o=t.ɵɵnextContext();return t.ɵɵresetView(o.openConfigDialog(n,r,a.get("config").value,a.get("name").value))})),t.ɵɵelementStart(21,"mat-icon"),t.ɵɵtext(22,"more_horiz"),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"button",43),t.ɵɵpipe(24,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext();return t.ɵɵresetView(i.removeConnector(n))})),t.ɵɵelementStart(25,"mat-icon"),t.ɵɵtext(26,"close"),t.ɵɵelementEnd()()()()()}if(2&e){const e=n.$implicit,i=n.index,a=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("formGroupName",i),t.ɵɵadvance(3),t.ɵɵclassMap(t.ɵɵpureFunction3(24,RW,a.layoutGap,a.alignment,!a.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,16,"gateway.connector-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",a.connectorTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("configType").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,18,"gateway.connector-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.get("name").hasError("required")),t.ɵɵadvance(),t.ɵɵclassMap(t.ɵɵpureFunction3(28,BW,a.layoutGap,a.alignment,!a.alignment)),t.ɵɵadvance(),t.ɵɵclassProp("mat-warn",e.get("config").invalid),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(20,20,"gateway.update-config")),t.ɵɵproperty("disabled",a.isReadOnlyForm),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(24,22,"gateway.delete")),t.ɵɵproperty("disabled",a.isReadOnlyForm)}}function l$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",44),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.exportConfig())})),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,3,"gateway.download-tip")),t.ɵɵproperty("disabled",!e.gatewayConfigurationGroup.dirty||e.gatewayConfigurationGroup.invalid),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"action.download")," ")}}function p$(e,n){if(1&e&&(t.ɵɵelementStart(0,"button",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,3,"gateway.save-tip")),t.ɵɵproperty("disabled",!e.gatewayConfigurationGroup.dirty||e.gatewayConfigurationGroup.invalid),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"action.save")," ")}}class c$ extends q{constructor(e,t,n,i,a,r,o,s,l,p,c){super(e),this.store=e,this.elementRef=t,this.utils=n,this.ngZone=i,this.fb=a,this.window=r,this.dialog=o,this.translate=s,this.deviceService=l,this.attributeService=p,this.importExport=c,this.alignment=!0,this.layoutGap=!0,this.securityTypes=bW,this.gatewayLogLevels=Object.keys(wW).map((e=>wW[e])),this.connectorTypes=Object.keys(_W),this.storageTypes=CW,this.toastTargetId="gateway-configuration-widget"+this.utils.guid(),this.isReadOnlyForm=!1}get connectors(){return this.gatewayConfigurationGroup.get("connectors")}ngOnInit(){this.initWidgetSettings(this.ctx.settings),this.ctx.datasources&&this.ctx.datasources.length&&(this.deviceNameForm=this.ctx.datasources[0].name),this.buildForm(),this.ctx.updateWidgetParams(),this.formResize$=new ResizeObserver((()=>{this.resize()})),this.formResize$.observe(this.formContainerRef.nativeElement)}ngOnDestroy(){this.formResize$&&this.formResize$.disconnect(),this.subscribeGateway$.unsubscribe(),this.subscribeStorageType$.unsubscribe()}initWidgetSettings(e){let t;t=e.gatewayTitle&&e.gatewayTitle.length?this.utils.customTranslation(e.gatewayTitle,e.gatewayTitle):this.translate.instant("gateway.gateway"),this.ctx.widgetTitle=t,this.isReadOnlyForm=!!e.readOnly&&e.readOnly,this.archiveFileName=e.archiveFileName?.length?e.archiveFileName:"gatewayConfiguration",this.gatewayType=e.gatewayType?.length?e.gatewayType:"Gateway",this.gatewayNameExists=this.utils.customTranslation(e.gatewayNameExists,e.gatewayNameExists)||this.translate.instant("gateway.gateway-exists"),this.successfulSaved=this.utils.customTranslation(e.successfulSave,e.successfulSave)||this.translate.instant("gateway.gateway-saved"),this.updateWidgetDisplaying()}resize(){this.ngZone.run((()=>{this.updateWidgetDisplaying(),this.ctx.detectChanges()}))}updateWidgetDisplaying(){this.ctx.$container&&this.ctx.$container[0].offsetWidth<=425?(this.layoutGap=!1,this.alignment=!1):(this.layoutGap=!0,this.alignment=!0)}saveAttribute(e,t,n){const i=this.gatewayConfigurationGroup.get("gateway").value,a={key:e,value:t};return this.attributeService.saveEntityAttributes(kW(i),n,[a])}createConnector(e=TW){this.connectors.push(this.fb.group({enabled:[e.enabled],configType:[e.configType,[me.required]],name:[e.name,[me.required]],config:[e.config,[me.nullValidator,IW]]}))}getFormField(e){return this.gatewayConfigurationGroup.get(e)}buildForm(){this.gatewayConfigurationGroup=this.fb.group({gateway:[null,[]],accessToken:[null,[me.required]],securityType:[yW.accessToken],host:[this.window.location.hostname,[me.required]],port:[1883,[me.required,me.min(1),me.max(65535),me.pattern(/^-?[0-9]+$/)]],remoteConfiguration:[!0],caCertPath:["/etc/thingsboard-gateway/ca.pem"],privateKeyPath:["/etc/thingsboard-gateway/privateKey.pem"],certPath:["/etc/thingsboard-gateway/certificate.pem"],remoteLoggingLevel:[wW.debug],remoteLoggingPathToLogs:["./logs/",[me.required]],storageType:[SW.memory],readRecordsCount:[100,[me.required,me.min(1),me.pattern(/^-?[0-9]+$/)]],maxRecordsCount:[1e4,[me.required,me.min(1),me.pattern(/^-?[0-9]+$/)]],maxFilesCount:[5,[me.required,me.min(1),me.pattern(/^-?[0-9]+$/)]],dataFolderPath:["./data/",[me.required]],connectors:this.fb.array([])}),this.isReadOnlyForm&&this.gatewayConfigurationGroup.disable({emitEvent:!1}),this.subscribeStorageType$=this.getFormField("storageType").valueChanges.subscribe((e=>{e===SW.memory?(this.getFormField("maxFilesCount").disable(),this.getFormField("dataFolderPath").disable()):(this.getFormField("maxFilesCount").enable(),this.getFormField("dataFolderPath").enable())})),this.subscribeGateway$=this.getFormField("gateway").valueChanges.subscribe((e=>{null!==e?Ie([this.deviceService.getDeviceCredentials(e).pipe(Ae((e=>{this.getFormField("accessToken").patchValue(e.credentialsId)}))),...this.getAttributes(e)]).subscribe((()=>{this.gatewayConfigurationGroup.markAsPristine(),this.ctx.detectChanges()})):this.getFormField("accessToken").patchValue("")}))}gatewayExist(){this.ctx.showErrorToast(this.gatewayNameExists,"top","left",this.toastTargetId)}exportConfig(){const e=this.gatewayConfigurationGroup.value,t={};var n,i,a;t["tb_gateway.yaml"]=function(e){let t;t="thingsboard:\n",t+=" host: "+e.host+"\n",t+=" remoteConfiguration: "+e.remoteConfiguration+"\n",t+=" port: "+e.port+"\n",t+=" security:\n",e.securityType===yW.accessToken?t+=" access-token: "+e.accessToken+"\n":(t+=" ca_cert: "+e.caCertPath+"\n",t+=" privateKey: "+e.privateKeyPath+"\n",t+=" cert: "+e.certPath+"\n"),t+="storage:\n",e.storageType===SW.memory?(t+=" type: memory\n",t+=" read_records_count: "+e.readRecordsCount+"\n",t+=" max_records_count: "+e.maxRecordsCount+"\n"):(t+=" type: file\n",t+=" data_folder_path: "+e.dataFolderPath+"\n",t+=" max_file_count: "+e.maxFilesCount+"\n",t+=" max_read_records_count: "+e.readRecordsCount+"\n",t+=" max_records_per_file: "+e.maxRecordsCount+"\n"),t+="connectors:\n";for(const n of e.connectors)n.enabled&&(t+=" -\n",t+=" name: "+n.name+"\n",t+=" type: "+n.configType+"\n",t+=" configuration: "+EW(n.name)+"\n");return t}(e),function(e,t){for(const n of t)n.enabled&&(e[EW(n.name)]=JSON.stringify(n.config))}(t,e.connectors),n=t,i=e.remoteLoggingLevel,a=e.remoteLoggingPathToLogs,n["logs.conf"]=MW(i,a),this.importExport.exportJSZip(t,this.archiveFileName),this.saveAttribute(xW,this.gatewayConfigurationGroup.value.remoteLoggingLevel.toUpperCase(),D.SHARED_SCOPE)}addNewConnector(){this.createConnector()}removeConnector(e){e>-1&&(this.connectors.removeAt(e),this.connectors.markAsDirty())}openConfigDialog(e,t,n,i){e&&(e.stopPropagation(),e.preventDefault()),this.dialog.open(nt,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{jsonValue:n,required:!0,title:this.translate.instant("gateway.title-connectors-json",{typeName:i})}}).afterClosed().subscribe((e=>{e&&(this.connectors.at(t).get("config").patchValue(e),this.ctx.detectChanges())}))}createConnectorName(e,t,n=0){const i=n?t+n:t;return-1===e.findIndex((e=>e.name===i))?i:this.createConnectorName(e,t,++n)}validateConnectorName(e,t,n,i=0){for(let a=0;a{this.ctx.showSuccessToast(this.successfulSaved,2e3,"top","left",this.toastTargetId),this.gatewayConfigurationGroup.markAsPristine()}))}getAttributes(e){const t=[];return t.push(Ie([this.getAttribute("current_configuration",D.CLIENT_SCOPE,e),this.getAttribute(vW,D.SERVER_SCOPE,e)]).pipe(Ae((([e,t])=>{this.setFormGatewaySettings(e),this.setFormConnectorsDraft(t),this.isReadOnlyForm&&this.gatewayConfigurationGroup.disable({emitEvent:!1})})))),t.push(this.getAttribute(xW,D.SHARED_SCOPE,e).pipe(Ae((e=>this.processLoggingLevel(e))))),t}getAttribute(e,t,n){return this.attributeService.getEntityAttributes(kW(n),t,[e])}setFormGatewaySettings(e){if(this.connectors.clear(),e.length>0){const t=JSON.parse(window.atob(e[0].value));for(const e of Object.keys(t)){const n=t[e];if("thingsboard"===e)null!==n&&Object.keys(n).length>0&&this.gatewayConfigurationGroup.patchValue(PW(n));else for(const t of Object.keys(n)){let i="No name";Object.prototype.hasOwnProperty.call(n[t],"name")&&(i=n[t].name);const a={enabled:!0,configType:e,config:n[t].config,name:i};this.createConnector(a)}}}}setFormConnectorsDraft(e){if(e.length>0){const t=JSON.parse(window.atob(e[0].value));for(const e of Object.keys(t)){const n={enabled:!1,configType:t[e].connector,config:t[e].config,name:e};this.createConnector(n)}}}processLoggingLevel(e){let t=wW.debug;e.length>0&&wW[e[0].value.toLowerCase()]&&(t=wW[e[0].value.toLowerCase()]),this.getFormField("remoteLoggingLevel").patchValue(t)}static{this.ɵfac=function(e){return new(e||c$)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(t.ElementRef),t.ɵɵdirectiveInject(K.UtilsService),t.ɵɵdirectiveInject(t.NgZone),t.ɵɵdirectiveInject(de.UntypedFormBuilder),t.ɵɵdirectiveInject($),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(K.DeviceService),t.ɵɵdirectiveInject(K.AttributeService),t.ɵɵdirectiveInject(it.ImportExportService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:c$,selectors:[["tb-gateway-form"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(FW,7),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.formContainerRef=e.first)}},inputs:{ctx:"ctx",isStateForm:"isStateForm"},standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:104,vars:104,consts:[["formContainer",""],["tb-toast","",1,"gateway-form",3,"ngSubmit","formGroup","toastTarget"],["multi","true",1,"mat-body-2"],[1,"tb-panel-title"],["formControlName","gateway","required","",3,"gatewayNameExist","deviceName","isStateForm","newGatewayType"],[1,"flex"],["formControlName","securityType"],[3,"value",4,"ngFor","ngForOf"],[1,"flex-1"],["matInput","","type","text","formControlName","host"],["translate","",4,"ngIf"],["matInput","","type","number","formControlName","port"],["class","flex flex-col",4,"ngIf"],["formControlName","remoteConfiguration"],["formControlName","remoteLoggingLevel"],["matInput","","type","text","formControlName","remoteLoggingPathToLogs"],[1,"flex","flex-col"],["formControlName","storageType"],["matInput","","type","number","formControlName","readRecordsCount"],["matInput","","type","number","formControlName","maxRecordsCount"],["class","flex",3,"class",4,"ngIf"],[1,"gateway-config","flex","flex-col"],["formArrayName","connectors",4,"ngFor","ngForOf"],[1,"no-data-found","items-center","justify-center"],["mat-raised-button","","type","button","matTooltipPosition","above",3,"click","matTooltip"],[1,"form-action-buttons","flex","flex-row","items-center","justify-end"],["mat-raised-button","","color","primary","type","button",3,"disabled","matTooltip","click",4,"ngIf"],["mat-raised-button","","color","primary","type","submit",3,"disabled","matTooltip",4,"ngIf"],[3,"value"],["translate",""],["matInput","","type","text","formControlName","caCertPath"],["matInput","","type","text","formControlName","privateKeyPath"],["matInput","","type","text","formControlName","certPath"],["matInput","","type","number","formControlName","maxFilesCount"],["matInput","","type","text","formControlName","dataFolderPath"],["formArrayName","connectors"],[1,"flex","flex-row","items-stretch","justify-between","gap-2",3,"formGroupName"],[1,"flex","flex-col","justify-center"],["formControlName","enabled"],[1,"flex-full","flex"],["formControlName","configType",3,"selectionChange"],["matInput","","type","text","formControlName","name",3,"blur"],[1,"action-buttons","flex"],["mat-icon-button","","matTooltipPosition","above",3,"click","disabled","matTooltip"],["mat-raised-button","","color","primary","type","button",3,"click","disabled","matTooltip"],["mat-raised-button","","color","primary","type","submit",3,"disabled","matTooltip"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"form",1,0),t.ɵɵlistener("ngSubmit",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.save())})),t.ɵɵelementStart(2,"mat-accordion",2)(3,"mat-expansion-panel")(4,"mat-expansion-panel-header")(5,"mat-panel-title")(6,"div",3),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"uppercase"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"tb-entity-gateway-select",4),t.ɵɵlistener("gatewayNameExist",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.gatewayExist())})),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",5)(12,"mat-label"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-select",6),t.ɵɵtemplate(16,NW,3,4,"mat-option",7),t.ɵɵpipe(17,"keyvalue"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",5)(19,"mat-form-field",8)(20,"mat-label"),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(23,"input",9),t.ɵɵtemplate(24,LW,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",8)(26,"mat-label"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(29,"input",11),t.ɵɵtemplate(30,VW,2,0,"mat-error",10)(31,qW,2,0,"mat-error",10)(32,GW,2,0,"mat-error",10)(33,zW,2,0,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵtemplate(34,UW,16,9,"div",12),t.ɵɵelementStart(35,"mat-checkbox",13),t.ɵɵtext(36),t.ɵɵpipe(37,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"div",5)(39,"mat-form-field",8)(40,"mat-label"),t.ɵɵtext(41),t.ɵɵpipe(42,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(43,"mat-select",14),t.ɵɵtemplate(44,jW,2,2,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"mat-form-field",8)(46,"mat-label"),t.ɵɵtext(47),t.ɵɵpipe(48,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(49,"input",15),t.ɵɵtemplate(50,HW,2,0,"mat-error",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(51,"mat-expansion-panel")(52,"mat-expansion-panel-header")(53,"mat-panel-title")(54,"div",3),t.ɵɵtext(55),t.ɵɵpipe(56,"translate"),t.ɵɵpipe(57,"uppercase"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(58,"div",16)(59,"mat-form-field")(60,"mat-label"),t.ɵɵtext(61),t.ɵɵpipe(62,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(63,"mat-select",17),t.ɵɵtemplate(64,WW,3,4,"mat-option",7),t.ɵɵpipe(65,"keyvalue"),t.ɵɵelementEnd()(),t.ɵɵelementStart(66,"div",5)(67,"mat-form-field",8)(68,"mat-label"),t.ɵɵtext(69),t.ɵɵpipe(70,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(71,"input",18),t.ɵɵtemplate(72,$W,2,0,"mat-error",10)(73,KW,2,0,"mat-error",10)(74,YW,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(75,"mat-form-field",8)(76,"mat-label"),t.ɵɵtext(77),t.ɵɵpipe(78,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(79,"input",19),t.ɵɵtemplate(80,XW,2,0,"mat-error",10)(81,ZW,2,0,"mat-error",10)(82,QW,2,0,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵtemplate(83,i$,15,16,"div",20),t.ɵɵelementEnd()(),t.ɵɵelementStart(84,"mat-expansion-panel")(85,"mat-expansion-panel-header")(86,"mat-panel-title")(87,"div",3),t.ɵɵtext(88),t.ɵɵpipe(89,"translate"),t.ɵɵpipe(90,"uppercase"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(91,"div",21),t.ɵɵtemplate(92,s$,27,32,"section",22),t.ɵɵelementStart(93,"span",23),t.ɵɵtext(94),t.ɵɵpipe(95,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(96,"div")(97,"button",24),t.ɵɵpipe(98,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addNewConnector())})),t.ɵɵtext(99),t.ɵɵpipe(100,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(101,"section",25),t.ɵɵtemplate(102,l$,4,7,"button",26)(103,p$,4,7,"button",27),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("toastTarget",n.toastTargetId),t.ɵɵproperty("formGroup",n.gatewayConfigurationGroup),t.ɵɵadvance(7),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,54,t.ɵɵpipeBind1(8,52,"gateway.thingsboard"))),t.ɵɵadvance(3),t.ɵɵproperty("deviceName",n.deviceNameForm)("isStateForm",n.isStateForm)("newGatewayType",n.gatewayType),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,56,"gateway.security-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(17,58,n.securityTypes)),t.ɵɵadvance(2),t.ɵɵclassMap(t.ɵɵpureFunction3(92,RW,n.layoutGap,n.alignment,!n.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,60,"gateway.thingsboard-host")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("host").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,62,"gateway.thingsboard-port")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("max")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("pattern")),t.ɵɵadvance(),t.ɵɵproperty("ngIf","tls"===n.gatewayConfigurationGroup.get("securityType").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(37,64,"gateway.remote")),t.ɵɵadvance(2),t.ɵɵclassMap(t.ɵɵpureFunction3(96,RW,n.layoutGap,n.alignment,!n.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(42,66,"gateway.remote-logging-level")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.gatewayLogLevels),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(48,68,"gateway.path-logs")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("remoteLoggingPathToLogs").hasError("required")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(57,72,t.ɵɵpipeBind1(56,70,"gateway.storage"))),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(62,74,"gateway.storage-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(65,76,n.storageTypes)),t.ɵɵadvance(2),t.ɵɵclassMap(t.ɵɵpureFunction3(100,RW,n.layoutGap,n.alignment,!n.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(70,78,"gateway.storage-pack-size")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("readRecordsCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("readRecordsCount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("readRecordsCount").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(78,80,"file"!==n.gatewayConfigurationGroup.get("storageType").value?"gateway.storage-max-records":"gateway.storage-max-file-records")," "),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("maxRecordsCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("maxRecordsCount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("maxRecordsCount").hasError("pattern")),t.ɵɵadvance(),t.ɵɵproperty("ngIf","file"===n.gatewayConfigurationGroup.get("storageType").value),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(90,84,t.ɵɵpipeBind1(89,82,"gateway.connectors-config"))),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",n.connectors.controls),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.connectors.length),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(95,86,"gateway.no-connectors")),t.ɵɵadvance(3),t.ɵɵclassProp("!hidden",n.isReadOnlyForm),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(98,88,"gateway.connector-add")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(100,90,"action.add")," "),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.isReadOnlyForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.gatewayConfigurationGroup.get("remoteConfiguration").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("remoteConfiguration").value))},dependencies:t.ɵɵgetComponentDepsFactory(c$,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .gateway-form[_ngcontent-%COMP%]{height:100%;padding:5px;background-color:transparent;overflow-y:auto;overflow-x:hidden}[_nghost-%COMP%] .gateway-form[_ngcontent-%COMP%] .form-action-buttons[_ngcontent-%COMP%]{padding-top:8px}[_nghost-%COMP%] .gateway-form[_ngcontent-%COMP%] .gateway-config[_ngcontent-%COMP%] .no-data-found[_ngcontent-%COMP%]{position:relative;display:flex;height:40px}']})}}e("GatewayFormComponent",c$);class d${transform(e,t){return Ya.parseVersion(e)>=Ya.parseVersion(Ai.get(t))}static{this.ɵfac=function(e){return new(e||d$)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"isLatestVersionConfig",type:d$,pure:!0,standalone:!0})}}class u${constructor(e){this.translate=e}transform(e){return e.hasError("required")?this.translate.instant("gateway.port-required"):e.hasError("min")||e.hasError("max")?this.translate.instant("gateway.port-limits-error",{min:Oi.MIN,max:Oi.MAX}):""}static{this.ɵfac=function(e){return new(e||u$)(t.ɵɵdirectiveInject(le.TranslateService,16))}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"getGatewayPortTooltip",type:u$,pure:!0,standalone:!0})}}class m${transform(e,t,n,i){switch(e){case dt.OPCUA:return this.getOpcConnectorHelpLink(t,n);case dt.MQTT:return this.getMqttConnectorHelpLink(t,n,i);case dt.BACNET:return this.getBacnetConnectorHelpLink(t,n);case dt.REST:return this.getRestConnectorHelpLink(n)}}getOpcConnectorHelpLink(e,t){if(t!==Fi.CONST)return`widget/lib/gateway/${e}-${t}_fn`}getMqttConnectorHelpLink(e,t,n){if(t!==oi.CONST)return n?e!==Ui.ATTRIBUTES&&e!==Ui.TIMESERIES||n!==ri.JSON?`widget/lib/gateway/mqtt-${n}-expression_fn`:"widget/lib/gateway/mqtt-json-key-expression_fn":"widget/lib/gateway/mqtt-expression_fn"}getBacnetConnectorHelpLink(e,t){if(t!==Fi.CONST)return`widget/lib/gateway/bacnet-device-${e}-${t}_fn`}getRestConnectorHelpLink(e){if(e!==Si.CONST)return"widget/lib/gateway/rest-json_fn"}static{this.ɵfac=function(e){return new(e||m$)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"getConnectorMappingHelpLink",type:m$,pure:!0,standalone:!0})}}function h$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.gatewayConnectorDefaultTypesTranslatesMap.get(e)," ")}}function g$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",22),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.connectorForm.get("name").hasError("duplicateName")?"gateway.connector-duplicate-name":"gateway.name-required"))}}function f$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"div",9),t.ɵɵtext(2,"gateway.connectors-table-class"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",10),t.ɵɵelement(4,"input",23),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,1,"gateway.set")))}function y$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"div",9),t.ɵɵtext(2,"gateway.connectors-table-key"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",10),t.ɵɵelement(4,"input",24),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,1,"gateway.set")))}function v$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function x$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"mat-slide-toggle",25)(2,"mat-label",26),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.fill-connector-defaults-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.fill-connector-defaults")," "))}function b$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"mat-slide-toggle",27)(2,"mat-label",26),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.send-change-data-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.send-change-data")," "))}class w$ extends A{constructor(e,t,n,i,a,r){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.isLatestVersionConfig=r,this.connectorType=dt,this.gatewayConnectorDefaultTypesTranslatesMap=ut,this.gatewayLogLevel=Object.values(pt),this.submitted=!1,this.destroy$=new we,this.updateConnectorTypesByVersion(),this.connectorForm=this.fb.group({type:[dt.MQTT,[]],name:["",[me.required,this.uniqNameRequired(),me.pattern(pn)]],logLevel:[pt.INFO,[]],useDefaults:[!0,[]],sendDataOnlyOnChange:[!1,[]],class:["",[]],key:["auto",[]]})}ngOnInit(){this.observeTypeChange()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}helpLinkId(){return O+"/docs/iot-gateway/configuration/"}cancel(){this.dialogRef.close(null)}add(){this.submitted=!0;const e=this.connectorForm.getRawValue();if(e.useDefaults){const t=Xt(e.type),n=this.data.gatewayVersion;n&&(e.configVersion=n),e.configurationJson=(this.isLatestVersionConfig.transform(n,e.type)?t[Ai.get(e.type)]:t[ct.Legacy])??t,this.connectorForm.valid&&this.dialogRef.close(e)}else this.connectorForm.valid&&this.dialogRef.close(e)}uniqNameRequired(){return e=>{const t=e.value.trim().toLowerCase();return this.data.dataSourceData.some((({value:{name:e}})=>e.toLowerCase()===t))?{duplicateName:{valid:!1}}:null}}updateConnectorTypesByVersion(){const e=mt.keys();for(let t of e)if(t===ct.Legacy||Ya.parseVersion(this.data.gatewayVersion)>=Ya.parseVersion(t)){this.connectorTypes=mt.get(t);break}}observeTypeChange(){this.connectorForm.get("type").valueChanges.pipe(Ae((e=>{const t=this.connectorForm.get("useDefaults");e===dt.GRPC||e===dt.CUSTOM?t.setValue(!1):t.value||t.setValue(!0)})),Me(this.destroy$)).subscribe()}static{this.ɵfac=function(e){return new(e||w$)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(d$))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:w$,selectors:[["tb-add-connector-dialog"]],standalone:!0,features:[t.ɵɵProvidersFeature([d$]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:43,vars:25,consts:[[1,"add-connector",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","autocomplete","off","name","value","formControlName","name",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf"],["formControlName","logLevel"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","class",3,"placeholder"],["matInput","","name","value","formControlName","key",3,"placeholder"],["formControlName","useDefaults",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"mat-toolbar",1)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",2)(6,"div",3),t.ɵɵelementStart(7,"button",4),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",5),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",6)(11,"div",7)(12,"div",8)(13,"div",9),t.ɵɵtext(14,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-form-field",10)(16,"mat-select",11),t.ɵɵtemplate(17,h$,2,2,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(18,"div",8)(19,"div",13),t.ɵɵtext(20,"gateway.name"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",10),t.ɵɵelement(22,"input",14),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,g$,3,3,"mat-icon",15),t.ɵɵelementEnd()(),t.ɵɵtemplate(25,f$,6,3,"div",16)(26,y$,6,3,"div",16),t.ɵɵelementStart(27,"div",8)(28,"div",9),t.ɵɵtext(29,"gateway.remote-logging-level"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",10)(31,"mat-select",17),t.ɵɵtemplate(32,v$,2,2,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵtemplate(33,x$,6,6,"div",16)(34,b$,6,6,"div",16),t.ɵɵpipe(35,"withReportStrategy"),t.ɵɵelementEnd()(),t.ɵɵelementStart(36,"div",18)(37,"button",19),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(38),t.ɵɵpipe(39,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(40,"button",20),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(41),t.ɵɵpipe(42,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.connectorForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,14,"gateway.add-connector")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.helpLinkId()),t.ɵɵadvance(11),t.ɵɵproperty("ngForOf",n.connectorTypes),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,16,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.connectorForm.get("name").hasError("required")&&n.connectorForm.get("name").touched||n.connectorForm.get("name").hasError("duplicateName")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value===n.connectorType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value===n.connectorType.GRPC),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.gatewayLogLevel),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value!==n.connectorType.GRPC&&n.connectorForm.get("type").value!==n.connectorType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value===n.connectorType.MQTT&&!t.ɵɵpipeBind2(35,18,n.data.gatewayVersion,n.connectorType.MQTT)),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(39,21,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.connectorForm.invalid||!n.connectorForm.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(42,23,"action.add")," "))},dependencies:t.ɵɵgetComponentDepsFactory(w$,[se,_,tr]),styles:['@charset "UTF-8";[_nghost-%COMP%] .add-connector[_ngcontent-%COMP%]{min-width:400px;width:500px}']})}}e("AddConnectorDialogComponent",w$);const S$=()=>({maxWidth:"970px"});function C$(e,n){1&e&&(t.ɵɵelementStart(0,"div",4),t.ɵɵtext(1,"gateway.device-info.source"),t.ɵɵelementEnd())}function _$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function T$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"mat-form-field",18)(2,"mat-select",19),t.ɵɵtemplate(3,_$,3,4,"mat-option",20),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.sourceTypes)}}function I$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",22),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-info.device-name-expression-required"))}function E$(e,n){if(1&e&&(t.ɵɵelement(0,"div",23),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,e.connectorType,"name-field",e.mappingFormGroup.get("deviceNameExpressionSource").value,e.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,S$))}}function M$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function k$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"mat-form-field",18)(2,"mat-select",26),t.ɵɵtemplate(3,M$,3,4,"mat-option",20),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.sourceTypes)}}function P$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",22),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-info.device-profile-expression-required"))}function D$(e,n){if(1&e&&(t.ɵɵelement(0,"div",23),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,e.connectorType,"profile-name",e.mappingFormGroup.get("deviceProfileExpressionSource").value,e.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,S$))}}function O$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",24)(1,"div",9),t.ɵɵtext(2,"gateway.device-info.profile-name"),t.ɵɵelementEnd(),t.ɵɵtemplate(3,k$,4,1,"div",10),t.ɵɵelementStart(4,"div",11)(5,"mat-form-field",12),t.ɵɵelement(6,"input",25),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,P$,3,3,"mat-icon",14)(9,D$,2,8,"div",15),t.ɵɵpipe(10,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.useSource),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,4,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingFormGroup.get("deviceProfileExpression").hasError("required")&&e.mappingFormGroup.get("deviceProfileExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(10,6,e.connectorType,"profile-name",e.mappingFormGroup.get("deviceProfileExpressionSource").value,e.convertorType))}}class A$ extends q{get deviceInfoType(){return this.deviceInfoTypeValue}set deviceInfoType(e){this.deviceInfoTypeValue!==e&&(this.deviceInfoTypeValue=e)}constructor(e,t,n,i){super(e),this.store=e,this.translate=t,this.dialog=n,this.fb=i,this.SourceTypeTranslationsMap=zi,this.DeviceInfoType=Sa,this.useSource=!0,this.required=!1,this.connectorType=dt.MQTT,this.sourceTypes=Object.values(oi),this.destroy$=new we,this.propagateChange=e=>{}}ngOnInit(){this.mappingFormGroup=this.fb.group({deviceNameExpression:["",this.required?[me.required,me.pattern(pn)]:[me.pattern(pn)]]}),this.useSource&&this.mappingFormGroup.addControl("deviceNameExpressionSource",this.fb.control(this.sourceTypes[0],[])),this.deviceInfoType===Sa.FULL&&(this.useSource&&this.mappingFormGroup.addControl("deviceProfileExpressionSource",this.fb.control(this.sourceTypes[0],[])),this.mappingFormGroup.addControl("deviceProfileExpression",this.fb.control("",this.required?[me.required,me.pattern(pn)]:[me.pattern(pn)]))),this.mappingFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.updateView(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}writeValue(e){this.mappingFormGroup.patchValue(e,{emitEvent:!1})}validate(){return this.mappingFormGroup.valid?null:{mappingForm:{valid:!1}}}updateView(e){this.propagateChange(e)}static{this.ɵfac=function(e){return new(e||A$)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:A$,selectors:[["tb-device-info-table"]],inputs:{useSource:"useSource",required:"required",connectorType:"connectorType",convertorType:"convertorType",sourceTypes:"sourceTypes",deviceInfoType:"deviceInfoType"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>A$)),multi:!0},{provide:he,useExisting:c((()=>A$)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:23,vars:18,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-table","no-padding","no-gap"],[1,"tb-form-table-header"],["translate","",1,"tb-form-table-header-cell","w-1/5"],["class","tb-form-table-header-cell w-1/5","translate","",4,"ngIf"],["translate","",1,"tb-form-table-header-cell","w-1/2"],[1,"tb-form-table-body","no-gap"],[1,"tb-form-table-row","tb-form-row","no-border","same-padding","pt-4"],["translate","",1,"tb-required","w-1/5"],["class","no-gap w-1/5",4,"ngIf"],[1,"tb-form-table-row-cell","tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","deviceNameExpression",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style",4,"ngIf"],["class","tb-form-table-row tb-form-row no-border same-padding pb-4",4,"ngIf"],[1,"no-gap","w-1/5"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","!w-full"],["formControlName","deviceNameExpressionSource"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"tb-form-table-row","tb-form-row","no-border","same-padding","pb-4"],["matInput","","name","value","formControlName","deviceProfileExpression",3,"placeholder"],["formControlName","deviceProfileExpressionSource"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2,"device.device"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2)(4,"div",3)(5,"div",4),t.ɵɵtext(6,"gateway.device-info.entity-field"),t.ɵɵelementEnd(),t.ɵɵtemplate(7,C$,2,0,"div",5),t.ɵɵelementStart(8,"div",6),t.ɵɵtext(9," gateway.device-info.expression "),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",7)(11,"div",8)(12,"div",9),t.ɵɵtext(13,"gateway.device-info.name"),t.ɵɵelementEnd(),t.ɵɵtemplate(14,T$,4,1,"div",10),t.ɵɵelementStart(15,"div",11)(16,"mat-form-field",12),t.ɵɵelement(17,"input",13),t.ɵɵpipe(18,"translate"),t.ɵɵtemplate(19,I$,3,3,"mat-icon",14)(20,E$,2,8,"div",15),t.ɵɵpipe(21,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(22,O$,11,11,"div",16),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingFormGroup),t.ɵɵadvance(),t.ɵɵclassProp("tb-required",n.required),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.useSource),t.ɵɵadvance(4),t.ɵɵclassProp("pb-4",n.deviceInfoType!==n.DeviceInfoType.FULL),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.useSource),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(18,11,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.mappingFormGroup.get("deviceNameExpression").hasError("required")&&n.mappingFormGroup.get("deviceNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(21,13,n.connectorType,"name-field",n.mappingFormGroup.get("deviceNameExpressionSource").value,n.convertorType)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceInfoType===n.DeviceInfoType.FULL))},dependencies:t.ɵɵgetComponentDepsFactory(A$,[se,_,m$]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}'],changeDetection:d.OnPush})}}He([I()],A$.prototype,"useSource",void 0),He([I()],A$.prototype,"required",void 0);const F$=()=>({maxWidth:"970px"});function R$(e,n){if(1&e&&t.ɵɵelement(0,"mat-icon",19),2&e){let e;const n=t.ɵɵnextContext();t.ɵɵproperty("svgIcon",null==(e=n.valueTypes.get(n.valueTypeFormGroup.get("type").value))?null:e.icon)}}function B$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵelement(1,"mat-icon",22),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){let e,i;const a=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",a),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("svgIcon",null==(e=r.valueTypes.get(a))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,null==(i=r.valueTypes.get(a))?null:i.name))}}function N$(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,B$,5,5,"mat-option",20),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.valueTypeKeys)}}function L$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-option",23)(1,"span"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.raw")))}function V$(e,n){1&e&&(t.ɵɵelement(0,"input",24),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function q$(e,n){1&e&&(t.ɵɵelement(0,"input",25),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function G$(e,n){1&e&&(t.ɵɵelement(0,"input",26),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function z$(e,n){1&e&&(t.ɵɵelement(0,"input",27),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function U$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-select",28)(1,"mat-option",21),t.ɵɵtext(2,"true"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-option",21),t.ɵɵtext(4,"false"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵproperty("value",!0),t.ɵɵadvance(2),t.ɵɵproperty("value",!1))}function j$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",29),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function H$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",30),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("tb-help-popup",e.helpLink)("tb-help-popup-style",t.ɵɵpureFunction0(2,F$))}}class W${constructor(e){this.fb=e,this.valueTypeKeys=Object.values(en),this.valueTypes=tn,this.MappingValueType=en,this.destroy$=new we,this.onChange=e=>{},this.valueTypeFormGroup=this.fb.group({type:[en.STRING],stringValue:[{value:"",disabled:this.rawData},[me.required,me.pattern(pn)]],integerValue:[{value:0,disabled:!0},[me.required,me.pattern(cn)]],doubleValue:[{value:0,disabled:!0},[me.required]],booleanValue:[{value:!1,disabled:!0},[me.required]],rawValue:[{value:"",disabled:!this.rawData},[me.required,me.pattern(pn)]]}),this.valueTypeFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((({type:e,...t})=>{this.onChange({type:e,value:t[e+"Value"]})})),this.observeTypeChange()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}observeTypeChange(){this.valueTypeFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.toggleTypeInputs(e)))}toggleTypeInputs(e){this.valueTypeFormGroup.disable({emitEvent:!1}),this.valueTypeFormGroup.get("type").enable({emitEvent:!1}),this.valueTypeFormGroup.get(e+"Value").enable({emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){const t=this.getValueType(e?.value),n={stringValue:"",rawValue:"",integerValue:0,doubleValue:0,booleanValue:!1,type:t};n[t+"Value"]=e?.value,this.toggleTypeInputs(t),this.valueTypeFormGroup.patchValue(n,{emitEvent:!1})}validate(){return this.valueTypeFormGroup.valid?null:{valueTypeFormGroup:{valid:!1}}}getValueType(e){if(this.rawData)return"raw";switch(typeof e){case"boolean":return en.BOOLEAN;case"number":return Number.isInteger(e)?en.INTEGER:en.DOUBLE;default:return en.STRING}}static{this.ɵfac=function(e){return new(e||W$)(t.ɵɵdirectiveInject(de.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:W$,selectors:[["tb-type-value-field"]],inputs:{rawData:"rawData",helpLink:"helpLink"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>W$)),multi:!0},{provide:he,useExisting:c((()=>W$)),multi:!0}]),t.ɵɵStandaloneFeature],decls:29,vars:17,consts:[["raw",""],[3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","type"],[1,"tb-flex","align-center"],["class","tb-mat-18",3,"svgIcon",4,"ngIf"],[4,"ngIf","ngIfElse"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","flex","tb-suffix-absolute"],[3,"ngSwitch"],["matInput","","required","","formControlName","stringValue",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","rawValue",3,"placeholder",4,"ngSwitchCase"],["formControlName","booleanValue",4,"ngSwitchCase"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style","click",4,"ngIf"],[1,"tb-mat-18",3,"svgIcon"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],[1,"tb-mat-20",3,"svgIcon"],["value","raw"],["matInput","","required","","formControlName","stringValue",3,"placeholder"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder"],["matInput","","required","","formControlName","rawValue",3,"placeholder"],["formControlName","booleanValue"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"click","tb-help-popup","tb-help-popup-style"]],template:function(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,1),t.ɵɵelementStart(1,"div",2)(2,"div",3),t.ɵɵtext(3,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",4)(5,"mat-form-field",5)(6,"mat-select",6)(7,"mat-select-trigger")(8,"div",7),t.ɵɵtemplate(9,R$,1,1,"mat-icon",8),t.ɵɵelementStart(10,"span"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(14,N$,2,1,"ng-container",9)(15,L$,4,3,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(17,"div",2)(18,"div",3),t.ɵɵtext(19,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"mat-form-field",10),t.ɵɵelementContainerStart(21,11),t.ɵɵtemplate(22,V$,2,3,"input",12)(23,q$,2,3,"input",13)(24,G$,2,3,"input",14)(25,z$,2,3,"input",15)(26,U$,5,2,"mat-select",16),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(27,j$,3,3,"mat-icon",17)(28,H$,1,3,"div",18),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e){let e,i;const a=t.ɵɵreference(16);t.ɵɵproperty("formGroup",n.valueTypeFormGroup),t.ɵɵadvance(9),t.ɵɵproperty("ngIf",!n.rawData),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,13,null==(e=n.valueTypes.get(n.valueTypeFormGroup.get("type").value))?null:e.name)||t.ɵɵpipeBind1(13,15,"gateway.raw")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",!n.rawData)("ngIfElse",a),t.ɵɵadvance(7),t.ɵɵproperty("ngSwitch",n.valueTypeFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.STRING),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.INTEGER),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.DOUBLE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase","raw"),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.BOOLEAN),t.ɵɵadvance(),t.ɵɵproperty("ngIf",(null==(i=n.valueTypeFormGroup.get(n.valueTypeFormGroup.get("type").value))?null:i.hasError("required"))&&n.valueTypeFormGroup.get(n.valueTypeFormGroup.get("type").value).touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.helpLink)}},dependencies:t.ɵɵgetComponentDepsFactory(W$,[_,se]),styles:['@charset "UTF-8";[_nghost-%COMP%]{gap:16px;display:grid;width:100%}']})}}function $$(e,n){if(1&e&&t.ɵɵelement(0,"tb-type-value-field",14),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("helpLink",e.helpLink)}}function K$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",6)(1,"div",7),t.ɵɵelementContainerStart(2,8),t.ɵɵelementStart(3,"mat-expansion-panel",9)(4,"mat-expansion-panel-header",10)(5,"mat-panel-title")(6,"div",11),t.ɵɵtext(7),t.ɵɵelementEnd()()(),t.ɵɵtemplate(8,$$,1,1,"ng-template",12),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",13),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"delete"),t.ɵɵelementEnd()()()}if(2&e){let e;const i=n.$implicit,a=n.last;t.ɵɵadvance(2),t.ɵɵproperty("formGroup",i),t.ɵɵadvance(),t.ɵɵproperty("expanded",a),t.ɵɵadvance(4),t.ɵɵtextInterpolate(null!==(e=null==(e=i.get("typeValue").value)?null:e.value)&&void 0!==e?e:""),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(10,4,"gateway.delete-argument"))}}function Y$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4),t.ɵɵtemplate(1,K$,13,6,"div",5),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.valueListFormArray.controls)("ngForTrackBy",e.trackByKey)}}function X$(e,n){1&e&&(t.ɵɵelementStart(0,"div",15)(1,"span",16),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate("gateway.no-value"))}He([I()],W$.prototype,"rawData",void 0);class Z${constructor(e){this.fb=e,this.destroy$=new we,this.onChange=e=>{}}ngOnInit(){this.valueListFormArray=this.fb.array([]),this.valueListFormArray.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e.map((({typeValue:e})=>({...e}))))}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}trackByKey(e,t){return t}addKey(){const e=this.fb.group({typeValue:[]});this.valueListFormArray.push(e)}deleteKey(e,t){e&&e.stopPropagation(),this.valueListFormArray.removeAt(t),this.valueListFormArray.markAsDirty()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){for(const t of e){const e={typeValue:[t]},n=this.fb.group(e);this.valueListFormArray.push(n)}}validate(){return this.valueListFormArray.valid?null:{valueListForm:{valid:!1}}}static{this.ɵfac=function(e){return new(e||Z$)(t.ɵɵdirectiveInject(de.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Z$,selectors:[["tb-type-value-panel"]],inputs:{helpLink:"helpLink"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>Z$)),multi:!0},{provide:he,useExisting:c((()=>Z$)),multi:!0}]),t.ɵɵStandaloneFeature],decls:8,vars:5,consts:[["noKeys",""],[1,"tb-form-panel","no-border","no-padding"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],["tbTruncateWithTooltip","",1,"title-container"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],["formControlName","typeValue",3,"helpLink"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",1),t.ɵɵtemplate(1,Y$,2,2,"div",2),t.ɵɵelementStart(2,"div")(3,"button",3),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(6,X$,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor)}if(2&e){const e=t.ɵɵreference(7);t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.valueListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,3,"gateway.add-value")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(Z$,[se,_,W$]),styles:['@charset "UTF-8";[_nghost-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw}[_nghost-%COMP%] .key-panel[_ngcontent-%COMP%]{height:250px;overflow:auto}[_nghost-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}']})}}const Q$=()=>({maxWidth:"970px"});function J$(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",19),t.ɵɵtext(2),t.ɵɵelementEnd(),t.ɵɵtext(3),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",e.get("key").value," "),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",":","  ")}}function eK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function tK(e,n){if(1&e&&(t.ɵɵelement(0,"div",41),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext(3).$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,n.connectorType,n.keysType,e.get("type").value,n.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,Q$))}}function nK(e,n){if(1&e&&t.ɵɵelement(0,"mat-icon",44),2&e){let e;const n=t.ɵɵnextContext(4).$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("svgIcon",null==(e=i.valueTypes.get(n.get("type").value))?null:e.icon)}}function iK(e,n){if(1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){let e;const n=t.ɵɵnextContext(4).$implicit,i=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,null!==(e=null==(e=i.valueTypes.get(n.get("type").value))?null:e.name)&&void 0!==e?e:i.valueTypes.get(n.get("type").value))," ")}}function aK(e,n){1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.raw")))}function rK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-select-trigger")(1,"div",42),t.ɵɵtemplate(2,nK,1,1,"mat-icon",43)(3,iK,3,3,"span",36)(4,aK,3,3,"ng-template",null,2,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()),2&e){let e;const n=t.ɵɵreference(5),i=t.ɵɵnextContext(3).$implicit,a=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("ngIf",null==(e=a.valueTypes.get(i.get("type").value))?null:e.icon),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!a.rawData)("ngIfElse",n)}}function oK(e,n){if(1&e&&t.ɵɵelement(0,"mat-icon",48),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(6);t.ɵɵpropertyInterpolate("svgIcon",n.valueTypes.get(e).icon)}}function sK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",46),t.ɵɵtemplate(1,oK,1,1,"mat-icon",47),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){let e,i;const a=n.$implicit,r=t.ɵɵnextContext(6);t.ɵɵproperty("value",a),t.ɵɵadvance(),t.ɵɵproperty("ngIf",null==(e=r.valueTypes.get(a))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,3,null!==(i=null==(i=r.valueTypes.get(a))?null:i.name)&&void 0!==i?i:r.valueTypes.get(a))," ")}}function lK(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,sK,5,5,"mat-option",45),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(5);t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.valueTypeKeys)}}function pK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-option",46)(1,"span"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("value","raw"),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,2,"gateway.raw")))}function cK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function dK(e,n){if(1&e&&(t.ɵɵelement(0,"div",41),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext(3).$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,n.connectorType,n.keysType,e.get("type").value,n.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,Q$))}}function uK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"div",24)(2,"div",25),t.ɵɵtext(3,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",26)(5,"div",27),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",28),t.ɵɵelement(10,"input",29),t.ɵɵpipe(11,"translate"),t.ɵɵtemplate(12,eK,3,3,"mat-icon",30)(13,tK,2,8,"div",31),t.ɵɵpipe(14,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",24)(16,"div",25),t.ɵɵtext(17,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"div",32)(19,"div",33),t.ɵɵtext(20,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",34)(22,"mat-select",35),t.ɵɵtemplate(23,rK,6,3,"mat-select-trigger",18)(24,lK,2,1,"ng-container",36)(25,pK,4,4,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()(),t.ɵɵelementStart(27,"div",37)(28,"div",27),t.ɵɵpipe(29,"translate"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(32,"mat-form-field",38),t.ɵɵelement(33,"input",39),t.ɵɵpipe(34,"translate"),t.ɵɵtemplate(35,cK,3,3,"mat-icon",30)(36,dK,2,8,"div",31),t.ɵɵpipe(37,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵreference(26),n=t.ɵɵnextContext(2).$implicit,i=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,13,"gateway.JSONPath-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,15,"gateway.key")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(11,17,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.get("key").hasError("required")&&n.get("key").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(14,19,i.connectorType,i.keysType,n.get("type").value,i.convertorType)),t.ɵɵadvance(10),t.ɵɵproperty("ngIf",!i.rawData),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!i.rawData)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(29,24,"gateway.JSONPath-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(31,26,"gateway.value")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(34,28,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.get("value").hasError("required")&&n.get("value").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(37,30,i.connectorType,i.keysType,n.get("type").value,i.convertorType))}}function mK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function hK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function gK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"div",26)(2,"div",33),t.ɵɵtext(3,"gateway.key"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",28),t.ɵɵelement(5,"input",29),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,mK,3,3,"mat-icon",30),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",26)(9,"div",33),t.ɵɵtext(10,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",49),t.ɵɵelement(12,"input",39),t.ɵɵpipe(13,"translate"),t.ɵɵtemplate(14,hK,3,3,"mat-icon",30),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,4,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("key").hasError("required")&&e.get("key").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(13,6,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("value").hasError("required")&&e.get("value").touched)}}function fK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-required"))}function yK(e,n){1&e&&t.ɵɵelement(0,"tb-type-value-panel",54)}function vK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"div",26)(2,"div",27),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",28),t.ɵɵelement(7,"input",50),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,fK,3,3,"mat-icon",30),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",14)(11,"mat-expansion-panel",51)(12,"mat-expansion-panel-header",52)(13,"mat-panel-title")(14,"div",53),t.ɵɵpipe(15,"translate"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(18,yK,1,0,"ng-template",20),t.ɵɵelementEnd()()()),2&e){let e;const n=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,7,"gateway.hints.method-name")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,9,"gateway.method-name")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,11,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.get("method").hasError("required")&&n.get("method").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(15,13,"gateway.hints.arguments")),t.ɵɵadvance(2),t.ɵɵtextInterpolate2(" ",t.ɵɵpipeBind1(17,15,"gateway.arguments"),""," ("+(null==(e=n.get("arguments").value)?null:e.length)+")"," ")}}function xK(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",55),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Key)}}function bK(e,n){if(1&e&&t.ɵɵtemplate(0,uK,38,35,"div",22)(1,gK,15,8,"div",22)(2,vK,19,17,"div",22)(3,xK,1,2,"tb-report-strategy",23),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("ngIf",e.keysType!==e.MappingKeysType.CUSTOM&&e.keysType!==e.MappingKeysType.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.MappingKeysType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.MappingKeysType.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.withReportStrategy&&(e.keysType===e.MappingKeysType.ATTRIBUTES||e.keysType===e.MappingKeysType.TIMESERIES))}}function wK(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",13)(1,"div",14),t.ɵɵelementContainerStart(2,15),t.ɵɵelementStart(3,"mat-expansion-panel",16)(4,"mat-expansion-panel-header",17)(5,"mat-panel-title"),t.ɵɵtemplate(6,J$,4,2,"ng-container",18),t.ɵɵelementStart(7,"div",19),t.ɵɵtext(8),t.ɵɵelementEnd()()(),t.ɵɵtemplate(9,bK,4,4,"ng-template",20),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",21),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.last,a=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",a.keysType!==a.MappingKeysType.RPC_METHODS),t.ɵɵadvance(2),t.ɵɵtextInterpolate(a.valueTitle(e)),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,a.deleteKeyTitle))}}function SK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",11),t.ɵɵtemplate(1,wK,14,7,"div",12),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.keysListFormArray.controls)("ngForTrackBy",e.trackByKey)}}function CK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",56)(1,"span",57),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class _K extends q{constructor(e,t,n){super(n),this.fb=e,this.popover=t,this.store=n,this.valueTypeEnum=en,this.valueTypes=tn,this.valueTypeKeys=Object.values(en),this.rawData=!1,this.withReportStrategy=!0,this.keysDataApplied=new u,this.MappingKeysType=Ui,this.ReportStrategyDefaultValue=on,this.ConnectorType=dt,this.errorText=""}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}trackByKey(e,t){return t}addKey(){let e;e=this.keysType===Ui.RPC_METHODS?this.fb.group({method:["",[me.required]],arguments:[[],[]]}):this.keysType===Ui.CUSTOM?this.fb.group({key:["",[me.required,me.pattern(pn)]],value:["",[me.required,me.pattern(pn)]]}):this.fb.group({key:["",[me.required,me.pattern(pn)]],type:[this.rawData?"raw":this.valueTypeKeys[0]],value:["",[me.required,me.pattern(pn)]],reportStrategy:[{value:null,disabled:this.isReportStrategyDisabled()}]}),this.keysListFormArray.push(e)}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){let e=this.keysListFormArray.value.map((({reportStrategy:e,...t})=>({...t,...e&&{reportStrategy:e}})));if(this.keysType===Ui.CUSTOM){e={};for(let t of this.keysListFormArray.value)e[t.key]=t.value}this.keysDataApplied.emit(e)}prepareKeysFormArray(e){const t=[];return e&&(this.keysType===Ui.CUSTOM&&(e=Object.keys(e).map((t=>({key:t,value:e[t],type:""})))),e.forEach((e=>{let n;if(this.keysType===Ui.RPC_METHODS)n=this.fb.group({method:[e.method,[me.required]],arguments:[[...e.arguments],[]]});else if(this.keysType===Ui.CUSTOM){const{key:t,value:i}=e;n=this.fb.group({key:[t,[me.required,me.pattern(pn)]],value:[i,[me.required,me.pattern(pn)]]})}else{const{key:t,value:i,type:a,reportStrategy:r}=e;n=this.fb.group({key:[t,[me.required,me.pattern(pn)]],type:[a],value:[i,[me.required,me.pattern(pn)]],reportStrategy:[{value:r,disabled:this.isReportStrategyDisabled()}]})}t.push(n)}))),this.fb.array(t)}valueTitle(e){const t=this.keysType===Ui.RPC_METHODS?e.get("method").value:e.get("value").value;return J(t)?"object"==typeof t?JSON.stringify(t):t:""}isReportStrategyDisabled(){return!(this.withReportStrategy&&(this.keysType===Ui.ATTRIBUTES||this.keysType===Ui.TIMESERIES))}static{this.ɵfac=function(e){return new(e||_K)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverComponent),t.ɵɵdirectiveInject(Xe.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:_K,selectors:[["tb-mapping-data-keys-panel"]],inputs:{panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keys:"keys",keysType:"keysType",connectorType:"connectorType",convertorType:"convertorType",sourceType:"sourceType",valueTypeEnum:"valueTypeEnum",valueTypes:"valueTypes",valueTypeKeys:"valueTypeKeys",rawData:"rawData",withReportStrategy:"withReportStrategy"},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:16,consts:[["noKeys",""],["rawOption",""],["rawText",""],[1,"tb-mapping-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex","flex-row","flex-wrap"],[4,"ngIf"],["tbTruncateWithTooltip","",1,"title-container"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],["class","tb-form-panel no-border no-padding",4,"ngIf"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue",4,"ngIf"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style",4,"ngIf"],[1,"tb-form-row"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","type"],[4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs","flex","items-center","justify-between"],["appearance","outline","subscriptSizing","dynamic",1,"no-gap","flex","flex-1"],["matInput","","required","","formControlName","value",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"tb-flex","align-center"],["class","tb-mat-18",3,"svgIcon",4,"ngIf"],[1,"tb-mat-18",3,"svgIcon"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["class","tb-mat-20",3,"svgIcon",4,"ngIf"],[1,"tb-mat-20",3,"svgIcon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","tb-suffix-absolute","flex"],["matInput","","name","value","formControlName","method",3,"placeholder"],[1,"tb-settings"],[1,"flex","flex-wrap"],[1,"title-container",3,"tb-hint-tooltip-icon"],["formControlName","arguments"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"div",5),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,SK,2,2,"div",6),t.ɵɵelementStart(6,"div")(7,"button",7),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(10,CK,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",8)(13,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.applyKeysData())})),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(11);t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,8,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")",""),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.keysListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,n.addKeyTitle)," "),t.ɵɵadvance(6),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,14,"action.apply")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(_K,[se,_,ni,Z$,m$]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] tb-value-input[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}']})}}He([I()],_K.prototype,"rawData",void 0),He([I()],_K.prototype,"withReportStrategy",void 0);const TK=()=>({maxWidth:"970px"}),IK=(e,t)=>[e,t];function EK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.topic-required"))}function MK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.QualityTranslationsMap.get(e))," ")}}function kK(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.ConvertorTypeTranslationsMap.get(e))," ")}}function PK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",41),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("convertorType",e.ConvertorTypeEnum.JSON)("deviceInfoType",e.DeviceInfoType.FULL)}}function DK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",42),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("deviceInfoType",e.DeviceInfoType.FULL)("convertorType",e.ConvertorTypeEnum.BYTES)("sourceTypes",t.ɵɵpureFunction2(3,IK,e.sourceTypesEnum.MSG,e.sourceTypesEnum.CONST))}}function OK(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",37),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function AK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function FK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function RK(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",14)(1,"div",31)(2,"div",32),t.ɵɵtext(3,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",43)(5,"mat-chip-listbox",44),t.ɵɵtemplate(6,AK,2,1,"mat-chip",45),t.ɵɵelementStart(7,"mat-chip",46),t.ɵɵelement(8,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",48,0),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(10),a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES))})),t.ɵɵelementStart(12,"tb-icon",49),t.ɵɵtext(13,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(14,"div",31)(15,"div",32),t.ɵɵtext(16,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"div",43)(18,"mat-chip-listbox",44),t.ɵɵtemplate(19,FK,2,1,"mat-chip",45),t.ɵɵelementStart(20,"mat-chip",46),t.ɵɵelement(21,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"button",48,1),t.ɵɵpipe(24,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(23),a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.TIMESERIES))})),t.ɵɵelementStart(25,"tb-icon",49),t.ɵɵtext(26,"edit"),t.ɵɵelementEnd()()()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",e.converterAttributes),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.converterAttributes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,6,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.converterTelemetry),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.converterTelemetry),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(24,8,"action.edit"))}}function BK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.extension-required"))}function NK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function LK(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",14)(1,"div",21)(2,"div",50),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",23),t.ɵɵelement(7,"input",51),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,BK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",52)(11,"div",35),t.ɵɵtext(12,"gateway.extension-configuration"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",15),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",31)(17,"div",32),t.ɵɵtext(18,"gateway.keys"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",43)(20,"mat-chip-listbox",44),t.ɵɵtemplate(21,NK,2,1,"mat-chip",45),t.ɵɵelementStart(22,"mat-chip",46),t.ɵɵelement(23,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"button",48,2),t.ɵɵpipe(26,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(25),a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.CUSTOM))})),t.ɵɵelementStart(27,"tb-icon",49),t.ɵɵtext(28,"edit"),t.ɵɵelementEnd()()()()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,8,"gateway.extension-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,10,"gateway.extension")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,12,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("converter.custom.extension").hasError("required")&&e.mappingForm.get("converter.custom.extension").touched),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,14,"gateway.extension-configuration-hint")),t.ɵɵadvance(6),t.ɵɵproperty("tbEllipsisChipList",e.customKeys),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.customKeys),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(26,16,"action.edit"))}}function VK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",22),t.ɵɵtext(2,"gateway.topic-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",23),t.ɵɵelement(4,"input",24),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,EK,3,3,"mat-icon",25),t.ɵɵelement(7,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",21)(9,"div",27),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",23)(14,"mat-select",28),t.ɵɵtemplate(15,MK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementContainerStart(16,30),t.ɵɵelementStart(17,"div",31)(18,"div",32),t.ɵɵtext(19,"gateway.payload-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"tb-toggle-select",33),t.ɵɵtemplate(21,kK,3,4,"tb-toggle-option",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"div",34)(23,"div",35),t.ɵɵtext(24,"gateway.data-conversion"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"div",15),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(28,36),t.ɵɵtemplate(29,PK,1,2,"ng-template",17)(30,DK,1,6,"ng-template",17)(31,OK,1,2,"tb-report-strategy",37)(32,RK,27,10,"div",38)(33,LK,29,18,"div",38),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,16,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("topicFilter").hasError("required")&&e.mappingForm.get("topicFilter").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/topic-filter_fn")("tb-help-popup-style",t.ɵɵpureFunction0(24,TK)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,18,"gateway.response-topic-Qos-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,20,"gateway.mqtt-qos")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.qualityTypes),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",e.convertorTypes),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(27,22,e.DataConversionTranslationsMap.get(e.converterType))," "),t.ɵɵadvance(2),t.ɵɵproperty("formGroupName",e.converterType)("ngSwitch",e.converterType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConvertorTypeEnum.JSON),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConvertorTypeEnum.BYTES),t.ɵɵadvance(),t.ɵɵconditional(e.data.withReportStrategy?31:-1),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.converterType===e.ConvertorTypeEnum.BYTES||e.converterType===e.ConvertorTypeEnum.JSON),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.converterType===e.ConvertorTypeEnum.CUSTOM)}}function qK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.RequestTypesTranslationsMap.get(e))," ")}}function GK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.topic-required"))}function zK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",22),t.ɵɵtext(2,"gateway.topic-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",23),t.ɵɵelement(4,"input",57),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,GK,3,3,"mat-icon",25),t.ɵɵelement(7,"div",26),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,5,"gateway.set")),t.ɵɵproperty("formControl",e.mappingForm.get("requestValue").get(e.requestMappingType).get("topicFilter")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue").get(e.requestMappingType).get("topicFilter").hasError("required")&&e.mappingForm.get("requestValue").get(e.requestMappingType).get("topicFilter").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/topic-filter_fn")("tb-help-popup-style",t.ɵɵpureFunction0(7,TK))}}function UK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",58),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("deviceInfoType",e.DeviceInfoType.FULL)}}function jK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",58),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("deviceInfoType",e.DeviceInfoType.PARTIAL)}}function HK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function WK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-info.device-name-expression-required"))}function $K(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function KK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.attribute-name-expression-required"))}function YK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-value-expression-required"))}function XK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-topic-expression-required"))}function ZK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",34)(1,"div",59),t.ɵɵtext(2,"gateway.from-device-request-settings"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",60),t.ɵɵtext(4," gateway.from-device-request-settings-hint "),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",61)(6,"div",62)(7,"div",63),t.ɵɵtext(8,"gateway.device-info.device-name-expression"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",64)(10,"div",65)(11,"mat-form-field",23)(12,"mat-select",66),t.ɵɵtemplate(13,HK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(14,"mat-form-field",23),t.ɵɵelement(15,"input",67),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,WK,3,3,"mat-icon",25),t.ɵɵelement(18,"div",26),t.ɵɵelementEnd()()(),t.ɵɵelementStart(19,"div",21)(20,"div",22),t.ɵɵtext(21,"gateway.attribute-name-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"div",64)(23,"div",65)(24,"mat-form-field",23)(25,"mat-select",68),t.ɵɵtemplate(26,$K,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(27,"mat-form-field",23),t.ɵɵelement(28,"input",69),t.ɵɵpipe(29,"translate"),t.ɵɵtemplate(30,KK,3,3,"mat-icon",25),t.ɵɵelement(31,"div",26),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(32,"div",34)(33,"div",59),t.ɵɵtext(34,"gateway.to-device-response-settings"),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"div",60),t.ɵɵtext(36," gateway.to-device-response-settings-hint "),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"div",21)(38,"div",22),t.ɵɵtext(39,"gateway.response-value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(40,"mat-form-field",23),t.ɵɵelement(41,"input",70),t.ɵɵpipe(42,"translate"),t.ɵɵtemplate(43,YK,3,3,"mat-icon",25),t.ɵɵelement(44,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"div",21)(46,"div",22),t.ɵɵtext(47,"gateway.response-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(48,"mat-form-field",23),t.ɵɵelement(49,"input",71),t.ɵɵpipe(50,"translate"),t.ɵɵtemplate(51,XK,3,3,"mat-icon",25),t.ɵɵelement(52,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(53,"div",72)(54,"mat-slide-toggle",73)(55,"mat-label",74),t.ɵɵpipe(56,"translate"),t.ɵɵtext(57),t.ɵɵpipe(58,"translate"),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(13),t.ɵɵproperty("ngForOf",e.sourceTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.deviceInfo.deviceNameExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.deviceInfo.deviceNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(32,TK)),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",e.sourceTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(29,22,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.attributeNameExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.attributeNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(33,TK)),t.ɵɵadvance(10),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(42,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.valueExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.valueExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(34,TK)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(50,26,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.topicExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.topicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(35,TK)),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(56,28,"gateway.retain-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(58,30,"gateway.retain")," ")}}function QK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-filter-required"))}function JK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.attribute-filter-required"))}function eY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-value-expression-required"))}function tY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-topic-expression-required"))}function nY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",50),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",23),t.ɵɵelement(6,"input",75),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,QK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",21)(10,"div",50),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-form-field",23),t.ɵɵelement(15,"input",76),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,JK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",21)(19,"div",22),t.ɵɵtext(20,"gateway.response-value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",23),t.ɵɵelement(22,"input",70),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,eY,3,3,"mat-icon",25),t.ɵɵelement(25,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"div",21)(27,"div",22),t.ɵɵtext(28,"gateway.response-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-form-field",23),t.ɵɵelement(30,"input",71),t.ɵɵpipe(31,"translate"),t.ɵɵtemplate(32,tY,3,3,"mat-icon",25),t.ɵɵelement(33,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"div",72)(35,"mat-slide-toggle",73)(36,"mat-label",74),t.ɵɵpipe(37,"translate"),t.ɵɵtext(38),t.ɵɵpipe(39,"translate"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,18,"gateway.device-name-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,20,"gateway.device-name-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,22,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.deviceNameFilter").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.deviceNameFilter").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,24,"gateway.attribute-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,26,"gateway.attribute-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,28,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.attributeFilter").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.attributeFilter").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,30,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.valueExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.valueExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(38,TK)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(31,32,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.topicExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.topicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(39,TK)),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(37,34,"gateway.retain-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(39,36,"gateway.retain")," ")}}function iY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-filter-required"))}function aY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-filter-required"))}function rY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.request-topic-expression-required"))}function oY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-expression-required"))}function sY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-topic-expression-required"))}function lY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(4);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.QualityTranslationsMap.get(e))," ")}}function pY(e,n){if(1&e&&t.ɵɵelement(0,"tb-error-icon",84),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("tooltipText",e.responseTimeoutErrorTooltip)}}function cY(e,n){1&e&&(t.ɵɵelementStart(0,"span",85),t.ɵɵtext(1,"gateway.suffix.ms"),t.ɵɵelementEnd())}function dY(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",21)(2,"div",22),t.ɵɵtext(3,"gateway.response-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",23),t.ɵɵelement(5,"input",81),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,sY,3,3,"mat-icon",25),t.ɵɵelement(8,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",21)(10,"div",27),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-form-field",23)(15,"mat-select",82),t.ɵɵtemplate(16,lY,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div",21)(18,"div",22),t.ɵɵtext(19,"gateway.response-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"mat-form-field",23),t.ɵɵelement(21,"input",83),t.ɵɵpipe(22,"translate"),t.ɵɵtemplate(23,pY,1,1,"tb-error-icon",84)(24,cY,2,0,"span",85),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.responseTopicExpression").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.responseTopicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(17,TK)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,11,"gateway.response-topic-Qos-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,13,"gateway.response-topic-Qos")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.qualityTypes),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(22,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((e.mappingForm.get("requestValue.serverSideRpc.responseTimeout").hasError("required")||e.mappingForm.get("requestValue.serverSideRpc.responseTimeout").hasError("min"))&&e.mappingForm.get("requestValue.serverSideRpc.responseTimeout").touched?23:24)}}function uY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",77)(1,"tb-toggle-select",33)(2,"tb-toggle-option",40),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-option",40),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",21)(9,"div",50),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",23),t.ɵɵelement(14,"input",75),t.ɵɵpipe(15,"translate"),t.ɵɵtemplate(16,iY,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",21)(18,"div",50),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",23),t.ɵɵelement(23,"input",78),t.ɵɵpipe(24,"translate"),t.ɵɵtemplate(25,aY,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"div",21)(27,"div",22),t.ɵɵtext(28,"gateway.request-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-form-field",23),t.ɵɵelement(30,"input",79),t.ɵɵpipe(31,"translate"),t.ɵɵtemplate(32,rY,3,3,"mat-icon",25),t.ɵɵelement(33,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"div",21)(35,"div",22),t.ɵɵtext(36,"gateway.value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"mat-form-field",23),t.ɵɵelement(38,"input",70),t.ɵɵpipe(39,"translate"),t.ɵɵtemplate(40,oY,3,3,"mat-icon",25),t.ɵɵelement(41,"div",26),t.ɵɵelementEnd()(),t.ɵɵtemplate(42,dY,25,18,"ng-container",80)),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("value",e.ServerSideRPCType.TWO_WAY),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,21,"gateway.with-response")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",e.ServerSideRPCType.ONE_WAY),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,23,"gateway.without-response")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,25,"gateway.device-name-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,27,"gateway.device-name-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,29,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.deviceNameFilter").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.deviceNameFilter").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(19,31,"gateway.method-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(21,33,"gateway.method-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(24,35,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.methodFilter").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.methodFilter").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(31,37,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.requestTopicExpression").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.requestTopicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(41,TK)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(39,39,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.valueExpression").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.valueExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(42,TK)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.type").value===e.ServerSideRPCType.TWO_WAY)}}function mY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",32),t.ɵɵtext(2,"gateway.request-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",23)(4,"mat-select",53),t.ɵɵtemplate(5,qK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementContainerStart(6,54)(7,55),t.ɵɵtemplate(8,zK,8,8,"div",56)(9,UK,1,1,"ng-template",17)(10,jK,1,1,"ng-template",17)(11,ZK,59,36,"ng-template",17)(12,nY,40,40,"ng-template",17)(13,uY,43,43,"ng-template",17),t.ɵɵelementContainerEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.requestTypes),t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e.mappingForm.get("requestValue").get(e.requestMappingType))("ngSwitch",e.requestMappingType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.requestMappingType===e.RequestTypeEnum.ATTRIBUTE_REQUEST||e.requestMappingType===e.RequestTypeEnum.CONNECT_REQUEST||e.requestMappingType===e.RequestTypeEnum.DISCONNECT_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.CONNECT_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.DISCONNECT_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.ATTRIBUTE_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.ATTRIBUTE_UPDATE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.SERVER_SIDE_RPC)}}function hY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function gY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-node-required"))}function fY(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",37),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function yY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function vY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function xY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function bY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function wY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",86)(1,"div",87)(2,"div",88),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"div",89)(7,"mat-form-field",90)(8,"mat-select",91),t.ɵɵtemplate(9,hY,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"mat-form-field",90),t.ɵɵelement(11,"input",92),t.ɵɵpipe(12,"translate"),t.ɵɵtemplate(13,gY,3,3,"mat-icon",25),t.ɵɵelement(14,"div",26),t.ɵɵpipe(15,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()(),t.ɵɵelement(16,"tb-device-info-table",93),t.ɵɵtemplate(17,fY,1,2,"tb-report-strategy",37),t.ɵɵelementStart(18,"div",31)(19,"div",32),t.ɵɵtext(20,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",43)(22,"mat-chip-listbox",44),t.ɵɵtemplate(23,yY,2,1,"mat-chip",45),t.ɵɵelementStart(24,"mat-chip",46),t.ɵɵelement(25,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"button",48,3),t.ɵɵpipe(28,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(27),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES))})),t.ɵɵelementStart(29,"tb-icon",49),t.ɵɵtext(30,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(31,"div",31)(32,"div",32),t.ɵɵtext(33,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"div",43)(35,"mat-chip-listbox",44),t.ɵɵtemplate(36,vY,2,1,"mat-chip",45),t.ɵɵelementStart(37,"mat-chip",46),t.ɵɵelement(38,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"button",48,4),t.ɵɵpipe(41,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(40),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.TIMESERIES))})),t.ɵɵelementStart(42,"tb-icon",49),t.ɵɵtext(43,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(44,"div",31)(45,"div",32),t.ɵɵtext(46,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(47,"div",43)(48,"mat-chip-listbox",44),t.ɵɵtemplate(49,xY,2,1,"mat-chip",45),t.ɵɵelementStart(50,"mat-chip",46),t.ɵɵelement(51,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(52,"button",48,5),t.ɵɵpipe(54,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(53),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES_UPDATES))})),t.ɵɵelementStart(55,"tb-icon",49),t.ɵɵtext(56,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(57,"div",31)(58,"div",32),t.ɵɵtext(59,"gateway.rpc-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(60,"div",43)(61,"mat-chip-listbox",44),t.ɵɵtemplate(62,bY,2,1,"mat-chip",45),t.ɵɵelementStart(63,"mat-chip",46),t.ɵɵelement(64,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(65,"button",48,6),t.ɵɵpipe(67,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(66),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.RPC_METHODS))})),t.ɵɵelementStart(68,"tb-icon",49),t.ɵɵtext(69,"edit"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,23,"gateway.device-node-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,25,"gateway.device-node")," "),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",t.ɵɵpureFunction2(41,IK,e.OPCUaSourceTypesEnum.PATH,e.OPCUaSourceTypesEnum.IDENTIFIER)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(12,27,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("deviceNodePattern").hasError("required")&&e.mappingForm.get("deviceNodePattern").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind3(15,29,e.ConnectorType.OPCUA,"device-node",e.mappingForm.get("deviceNodeSource").value))("tb-help-popup-style",t.ɵɵpureFunction0(44,TK)),t.ɵɵadvance(2),t.ɵɵproperty("connectorType",e.ConnectorType.OPCUA)("sourceTypes",e.OPCUaSourceTypes)("deviceInfoType",e.DeviceInfoType.FULL),t.ɵɵadvance(),t.ɵɵconditional(e.data.withReportStrategy?17:-1),t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",e.opcAttributes),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcAttributes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(28,33,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.opcTelemetry),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcTelemetry),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(41,35,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.opcAttributesUpdates),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcAttributesUpdates),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(54,37,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.opcRpcMethods),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcRpcMethods),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(67,39,"action.edit"))}}class SY extends A{constructor(e,t,n,i,a,r,o,s,l){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.translate=l,this.MappingType=Ni,this.qualityTypes=Ki,this.QualityTranslationsMap=li,this.convertorTypes=Object.values(ri),this.ConvertorTypeEnum=ri,this.ConvertorTypeTranslationsMap=pi,this.sourceTypes=Object.values(oi),this.OPCUaSourceTypes=Object.values(Fi),this.OPCUaSourceTypesEnum=Fi,this.sourceTypesEnum=oi,this.SourceTypeTranslationsMap=zi,this.requestTypes=Object.values(ci),this.RequestTypeEnum=ci,this.RequestTypesTranslationsMap=di,this.DeviceInfoType=Sa,this.ServerSideRPCType=Qi,this.MappingKeysType=Ui,this.MappingHintTranslationsMap=Zi,this.MappingTypeTranslationsMap=Li,this.DataConversionTranslationsMap=ui,this.HelpLinkByMappingTypeMap=Xi,this.ConnectorType=dt,this.ReportStrategyDefaultValue=on,this.keysPopupClosed=!0,this.destroy$=new we,this.createMappingForm()}get converterAttributes(){if(this.converterType)return this.mappingForm.get("converter").get(this.converterType).value.attributes.map((e=>e.key))}get converterTelemetry(){if(this.converterType)return this.mappingForm.get("converter").get(this.converterType).value.timeseries.map((e=>e.key))}get opcAttributes(){return this.mappingForm.get("attributes").value?.map((e=>e.key))||[]}get opcTelemetry(){return this.mappingForm.get("timeseries").value?.map((e=>e.key))||[]}get opcRpcMethods(){return this.mappingForm.get("rpc_methods").value?.map((e=>e.method))||[]}get opcAttributesUpdates(){return this.mappingForm.get("attributes_updates")?.value?.map((e=>e.key))||[]}get converterType(){return this.mappingForm.get("converter")?.get("type").value}get customKeys(){return Object.keys(this.mappingForm.get("converter").get("custom").value.extensionConfig)}get requestMappingType(){return this.mappingForm.get("requestType").value}get responseTimeoutErrorTooltip(){const e=this.mappingForm.get("requestValue.serverSideRpc.responseTimeout");return e.hasError("required")?this.translate.instant("gateway.response-timeout-required"):e.hasError("min")?this.translate.instant("gateway.response-timeout-limits-error",{min:1}):""}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}createMappingForm(){switch(this.data.mappingType){case Ni.DATA:this.mappingForm=this.fb.group({}),this.createDataMappingForm();break;case Ni.REQUESTS:this.mappingForm=this.fb.group({}),this.createRequestMappingForm();break;case Ni.OPCUA:this.createOPCUAMappingForm()}}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){this.mappingForm.valid&&this.dialogRef.close(this.prepareMappingData())}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))this.popoverService.hidePopover(i);else{const e=(this.data.mappingType!==Ni.OPCUA?this.mappingForm.get("converter").get(this.converterType):this.mappingForm).get(n),t={keys:e.value,keysType:n,rawData:this.mappingForm.get("converter.type")?.value===ri.BYTES,panelTitle:ji.get(n),addKeyTitle:Hi.get(n),deleteKeyTitle:Wi.get(n),noKeysText:$i.get(n),withReportStrategy:this.data.withReportStrategy,connectorType:this.data.mappingType===Ni.OPCUA?dt.OPCUA:dt.MQTT,convertorType:this.converterType};this.data.mappingType===Ni.OPCUA&&(t.valueTypeKeys=Object.values(Fi),t.valueTypeEnum=Fi,t.valueTypes=zi,t.sourceType=this.mappingForm.get("deviceNodeSource").value),this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,_K,"leftBottom",!1,null,t,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(Me(this.destroy$)).subscribe((t=>{this.popoverComponent.hide(),e.patchValue(t),e.markAsDirty()})),this.popoverComponent.tbHideStart.pipe(Me(this.destroy$)).subscribe((()=>{this.keysPopupClosed=!0}))}}prepareMappingData(){const e=this.mappingForm.value;switch(Y(e),this.data.mappingType){case Ni.DATA:const{converter:t,topicFilter:n,subscriptionQos:i}=e,{reportStrategy:a,...r}=t[t.type];return{topicFilter:n,subscriptionQos:i,...a?{reportStrategy:a}:{},converter:{type:t.type,...r}};case Ni.REQUESTS:return{requestType:e.requestType,requestValue:e.requestValue[e.requestType]};default:return e}}getFormValueData(){if(this.data.value&&Object.keys(this.data.value).length)switch(this.data.mappingType){case Ni.DATA:const{converter:e,topicFilter:t,subscriptionQos:n,reportStrategy:i}=this.data.value;return{topicFilter:t,subscriptionQos:n,converter:{type:e.type,[e.type]:{...e,...i?{reportStrategy:i}:{}}}};case Ni.REQUESTS:return{requestType:this.data.value.requestType,requestValue:{[this.data.value.requestType]:this.data.value.requestValue}};default:return this.data.value}}createDataMappingForm(){this.mappingForm.addControl("topicFilter",this.fb.control("",[me.required,me.pattern(pn)])),this.mappingForm.addControl("subscriptionQos",this.fb.control(0)),this.mappingForm.addControl("converter",this.fb.group({type:[ri.JSON,[]],json:this.fb.group({deviceInfo:[{},[]],attributes:[[],[]],timeseries:[[],[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),bytes:this.fb.group({deviceInfo:[{},[]],attributes:[[],[]],timeseries:[[],[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),custom:this.fb.group({extension:["",[me.required,me.pattern(pn)]],extensionConfig:[{},[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]})})),this.mappingForm.patchValue(this.getFormValueData()),this.mappingForm.get("converter.type").valueChanges.pipe(qe(this.mappingForm.get("converter.type").value),Me(this.destroy$)).subscribe((e=>{const t=this.mappingForm.get("converter");t.get("json").disable({emitEvent:!1}),t.get("bytes").disable({emitEvent:!1}),t.get("custom").disable({emitEvent:!1}),t.get(e).enable({emitEvent:!1})}))}createRequestMappingForm(){this.mappingForm.addControl("requestType",this.fb.control(ci.CONNECT_REQUEST,[])),this.mappingForm.addControl("requestValue",this.fb.group({connectRequests:this.fb.group({topicFilter:["",[me.required,me.pattern(pn)]],deviceInfo:[{},[]]}),disconnectRequests:this.fb.group({topicFilter:["",[me.required,me.pattern(pn)]],deviceInfo:[{},[]]}),attributeRequests:this.fb.group({topicFilter:["",[me.required,me.pattern(pn)]],deviceInfo:this.fb.group({deviceNameExpressionSource:[oi.MSG,[]],deviceNameExpression:["",[me.required]]}),attributeNameExpressionSource:[oi.MSG,[]],attributeNameExpression:["",[me.required,me.pattern(pn)]],topicExpression:["",[me.required,me.pattern(pn)]],valueExpression:["",[me.required,me.pattern(pn)]],retain:[!1,[]]}),attributeUpdates:this.fb.group({deviceNameFilter:["",[me.required,me.pattern(pn)]],attributeFilter:["",[me.required,me.pattern(pn)]],topicExpression:["",[me.required,me.pattern(pn)]],valueExpression:["",[me.required,me.pattern(pn)]],retain:[!0,[]]}),serverSideRpc:this.fb.group({type:[Qi.TWO_WAY,[]],deviceNameFilter:["",[me.required,me.pattern(pn)]],methodFilter:["",[me.required,me.pattern(pn)]],requestTopicExpression:["",[me.required,me.pattern(pn)]],responseTopicExpression:["",[me.required,me.pattern(pn)]],valueExpression:["",[me.required,me.pattern(pn)]],responseTopicQoS:[0,[]],responseTimeout:[1e4,[me.required,me.min(1)]]}),reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]})),this.mappingForm.get("requestType").valueChanges.pipe(qe(this.mappingForm.get("requestType").value),Me(this.destroy$)).subscribe((e=>{const t=this.mappingForm.get("requestValue");t.get("connectRequests").disable({emitEvent:!1}),t.get("disconnectRequests").disable({emitEvent:!1}),t.get("attributeRequests").disable({emitEvent:!1}),t.get("attributeUpdates").disable({emitEvent:!1}),t.get("serverSideRpc").disable({emitEvent:!1}),t.get(e).enable()})),this.mappingForm.get("requestValue.serverSideRpc.type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{const t=this.mappingForm.get("requestValue.serverSideRpc");e===Qi.ONE_WAY?(t.get("responseTopicExpression").disable({emitEvent:!1}),t.get("responseTopicQoS").disable({emitEvent:!1}),t.get("responseTimeout").disable({emitEvent:!1})):(t.get("responseTopicExpression").enable({emitEvent:!1}),t.get("responseTopicQoS").enable({emitEvent:!1}),t.get("responseTimeout").enable({emitEvent:!1}))})),this.mappingForm.patchValue(this.getFormValueData())}createOPCUAMappingForm(){this.mappingForm=this.fb.group({deviceNodeSource:[Fi.PATH,[]],deviceNodePattern:["",[me.required]],deviceInfo:[{},[]],attributes:[[],[]],timeseries:[[],[]],rpc_methods:[[],[]],attributes_updates:[[],[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),this.mappingForm.patchValue(this.getFormValueData())}static{this.ɵfac=function(e){return new(e||SY)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(rt.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(le.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:SY,selectors:[["tb-mapping-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:26,vars:19,consts:[["attributesButton",""],["telemetryButton",""],["keysButton",""],["opcAttributesButton",""],["opcTelemetryButton",""],["attributesUpdatesButton",""],["rpcMethodsButton",""],[1,"key-mapping",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-hint","tb-primary-fill"],[3,"ngSwitch"],[3,"ngSwitchCase"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex"],["matInput","","name","value","formControlName","topicFilter",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","subscriptionQos"],[3,"value",4,"ngFor","ngForOf"],["formGroupName","converter"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[3,"formGroupName","ngSwitch"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],["class","tb-form-panel no-border no-padding",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["formControlName","deviceInfo","required","true",3,"convertorType","deviceInfoType"],["formControlName","deviceInfo","required","true",3,"deviceInfoType","convertorType","sourceTypes"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[4,"ngFor","ngForOf"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","name","value","formControlName","extension",3,"placeholder"],[1,"tb-form-row","space-between","same-padding","tb-flex","column"],["formControlName","requestType"],["formGroupName","requestValue"],[3,"formGroup","ngSwitch"],["class","tb-form-row column-xs",4,"ngIf"],["matInput","","name","value",3,"formControl","placeholder"],["formControlName","deviceInfo","required","true",3,"deviceInfoType"],["translate","",1,"tb-form-panel-title","tb-required"],["translate","",1,"tb-form-hint","tb-primary-fill"],["formGroupName","deviceInfo",1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-flex","no-flex","align-center"],["translate","",1,"tb-required"],[1,"flex","flex-1","gap-2"],[1,"flex","w-1/4"],["formControlName","deviceNameExpressionSource"],["matInput","","name","value","formControlName","deviceNameExpression",3,"placeholder"],["formControlName","attributeNameExpressionSource"],["matInput","","name","value","formControlName","attributeNameExpression",3,"placeholder"],["matInput","","name","value","formControlName","valueExpression",3,"placeholder"],["matInput","","name","value","formControlName","topicExpression",3,"placeholder"],[1,"tb-form-row"],["formControlName","retain",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["matInput","","name","value","formControlName","deviceNameFilter",3,"placeholder"],["matInput","","name","value","formControlName","attributeFilter",3,"placeholder"],[1,"tb-flex","row","center","align-center","no-gap","fill-width"],["matInput","","name","value","formControlName","methodFilter",3,"placeholder"],["matInput","","name","value","formControlName","requestTopicExpression",3,"placeholder"],[4,"ngIf"],["matInput","","name","value","formControlName","responseTopicExpression",3,"placeholder"],["formControlName","responseTopicQoS"],["matInput","","name","value","type","number","min","1","formControlName","responseTimeout",3,"placeholder"],["matSuffix","",3,"tooltipText"],["translate","","matSuffix","",1,"block","pr-2"],[1,"device-node-row","tb-form-row","column-xs","pr-4"],["translate","",1,"tb-flex","no-flex","align-center","w-1/5"],[1,"tb-required",3,"tb-hint-tooltip-icon"],[1,"flex","w-1/5"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","deviceNodeSource"],["matInput","","name","value","formControlName","deviceNodePattern",3,"placeholder"],["formControlName","deviceInfo","required","true",3,"connectorType","sourceTypes","deviceInfoType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",7)(1,"mat-toolbar",8)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",9)(6,"div",10),t.ɵɵelementStart(7,"button",11),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",12),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",13)(11,"div",14)(12,"div",15),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(15,16),t.ɵɵtemplate(16,VK,34,25,"ng-template",17)(17,mY,14,9,"ng-template",17)(18,wY,70,45,"ng-template",17),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"div",18)(20,"button",19),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"button",20),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,11,n.MappingTypeTranslationsMap.get(null==n.data?null:n.data.mappingType))),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.HelpLinkByMappingTypeMap.get(n.data.mappingType)),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,13,n.MappingHintTranslationsMap.get(null==n.data?null:n.data.mappingType))," "),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",n.data.mappingType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingType.DATA),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingType.REQUESTS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingType.OPCUA),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(22,15,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.mappingForm.invalid||!n.mappingForm.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,17,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(SY,[se,_,m$,$n,A$,ai,ni]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:grid;height:100%}[_nghost-%COMP%] .key-mapping[_ngcontent-%COMP%]{max-width:900px;display:flex;flex-direction:column}[_nghost-%COMP%] .key-mapping[_ngcontent-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{min-height:64px}[_nghost-%COMP%] .key-mapping[_ngcontent-%COMP%] tb-toggle-select[_ngcontent-%COMP%]{padding:4px 0}[_nghost-%COMP%] .mat-mdc-dialog-content[_ngcontent-%COMP%]{height:670px}[_nghost-%COMP%] .ellipsis-chips-container[_ngcontent-%COMP%]{max-width:70%}[_nghost-%COMP%] .key-mapping .mat-mdc-chip-listbox .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center;flex-wrap:nowrap}[_nghost-%COMP%] .tb-form-row .fixed-title-width{min-width:40px;width:35%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-form-row .mat-mdc-form-field{width:0}[_nghost-%COMP%] .device-node-row.tb-form-row{gap:12px}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}']})}}e("MappingDialogComponent",SY);const CY=["searchInput"],_Y=()=>({minWidth:"96px",textAlign:"center"});function TY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",24)(2,"span",25),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",9),t.ɵɵelementStart(6,"button",11),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageMapping(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",11),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,e.mappingTypeTranslationsMap.get(e.mappingType))),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search"))}}function IY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-header-cell",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext();t.ɵɵclassProp("request-column",n.mappingType===n.mappingTypeEnum.REQUESTS),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,3,e.title)," ")}}function EY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext().$implicit,a=t.ɵɵnextContext();t.ɵɵclassProp("request-column",a.mappingType===a.mappingTypeEnum.REQUESTS),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e[i.def]," ")}}function MY(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,26),t.ɵɵtemplate(1,IY,3,5,"mat-header-cell",27)(2,EY,2,3,"mat-cell",28),t.ɵɵelementContainerEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("matColumnDef",e.def)}}function kY(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",31)}function PY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",11),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageMapping(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",11),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteMapping(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function DY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,PY,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",32),t.ɵɵelementContainer(4,33),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",34)(6,"button",35),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",36),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",37,2),t.ɵɵelementContainer(11,33),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,_Y)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function OY(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",38)}function AY(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class FY{set mappingType(e){this.mappingTypeValue!==e&&(this.mappingTypeValue=e)}get mappingType(){return this.mappingTypeValue}constructor(e,t,n,i){this.translate=e,this.dialog=t,this.dialogService=n,this.fb=i,this.required=!1,this.withReportStrategy=!0,this.mappingTypeTranslationsMap=Li,this.mappingTypeEnum=Ni,this.displayedColumns=[],this.mappingColumns=[],this.textSearchMode=!1,this.hidePageSize=!1,this.activeValue=!1,this.dirtyValue=!1,this.textSearch=this.fb.control("",{nonNullable:!0}),this.onChange=()=>{},this.onTouched=()=>{},this.destroy$=new we,this.mappingFormGroup=this.fb.array([]),this.dirtyValue=!this.activeValue,this.dataSource=new RY}ngOnInit(){this.setMappingColumns(),this.displayedColumns.push(...this.mappingColumns.map((e=>e.def)),"actions"),this.mappingFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.updateTableData(e),this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(De(150),Pe(((e,t)=>(e??"")===t.trim())),Me(this.destroy$)).subscribe((e=>{const t=e.trim();this.updateTableData(this.mappingFormGroup.value,t.trim())}))}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){this.mappingFormGroup.clear(),this.pushDataAsFormArrays(e)}validate(){return!this.required||this.mappingFormGroup.controls.length?null:{mappingFormGroup:{valid:!1}}}enterFilterMode(){this.textSearchMode=!0,setTimeout((()=>{this.searchInputField.nativeElement.focus(),this.searchInputField.nativeElement.setSelectionRange(0,0)}),10)}exitFilterMode(){this.updateTableData(this.mappingFormGroup.value),this.textSearchMode=!1,this.textSearch.reset()}manageMapping(e,t){e&&e.stopPropagation();const n=J(t)?this.mappingFormGroup.at(t).value:{};this.dialog.open(SY,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{mappingType:this.mappingType,value:n,buttonTitle:ne(t)?"action.add":"action.apply",withReportStrategy:this.withReportStrategy}}).afterClosed().pipe(Ne(1),Me(this.destroy$)).subscribe((e=>{e&&(J(t)?this.mappingFormGroup.at(t).patchValue(e):this.pushDataAsFormArrays([e]),this.mappingFormGroup.markAsDirty())}))}updateTableData(e,t){let n=e.map((e=>this.getMappingValue(e)));t&&(n=n.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(n)}deleteMapping(e,t){e&&e.stopPropagation();const n=this.mappingFormGroup.controls[t].value,i=n.deviceInfo?.deviceNameExpression??n.topicFilter??this.getRequestDetails(n);this.dialogService.confirm(this.translate.instant("gateway.delete-mapping-title",{name:i}),this.translate.instant("gateway.delete-mapping-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).subscribe((e=>{e&&(this.mappingFormGroup.removeAt(t),this.mappingFormGroup.markAsDirty())}))}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.mappingFormGroup.push(this.fb.control(e))))}getMappingValue(e){switch(this.mappingType){case Ni.DATA:const t=pi.get(e.converter?.type);return{topicFilter:e.topicFilter,QoS:e.subscriptionQos,converter:t?this.translate.instant(t):""};case Ni.REQUESTS:return{requestType:e.requestType,type:this.translate.instant(di.get(e.requestType)),details:this.getRequestDetails(e)};case Ni.OPCUA:const n=e.deviceInfo?.deviceNameExpression,i=e.deviceInfo?.deviceProfileExpression,{deviceNodePattern:a}=e;return{deviceNodePattern:a,deviceNamePattern:n,deviceProfileExpression:i};default:return{}}}getRequestDetails(e){let t;return t=e.requestType===ci.ATTRIBUTE_UPDATE?e.requestValue.attributeFilter:e.requestType===ci.SERVER_SIDE_RPC?e.requestValue.methodFilter:e.requestValue.topicFilter,t}setMappingColumns(){switch(this.mappingType){case Ni.DATA:this.mappingColumns.push({def:"topicFilter",title:"gateway.topic-filter"},{def:"QoS",title:"gateway.mqtt-qos"},{def:"converter",title:"gateway.payload-type"});break;case Ni.REQUESTS:this.mappingColumns.push({def:"type",title:"gateway.type"},{def:"details",title:"gateway.details"});break;case Ni.OPCUA:this.mappingColumns.push({def:"deviceNodePattern",title:"gateway.device-node"},{def:"deviceNamePattern",title:"gateway.device-name"},{def:"deviceProfileExpression",title:"gateway.device-profile"})}}static{this.ɵfac=function(e){return new(e||FY)(t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(K.DialogService),t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:FY,selectors:[["tb-mapping-table"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(CY,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{required:"required",withReportStrategy:"withReportStrategy",mappingType:"mappingType"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>FY)),multi:!0},{provide:he,useExisting:c((()=>FY)),multi:!0}]),t.ɵɵStandaloneFeature],decls:40,vars:33,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-mapping-table","tb-absolute-fill"],[1,"tb-mapping-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef",4,"ngFor","ngForOf"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-mapping-table-title"],[3,"matColumnDef"],["class","table-value-column",3,"request-column",4,"matHeaderCellDef"],["tbTruncateWithTooltip","","class","table-value-column",3,"request-column",4,"matCellDef"],[1,"table-value-column"],["tbTruncateWithTooltip","",1,"table-value-column"],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,TY,14,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"div",7)(7,"button",8),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",9)(12,"mat-label"),t.ɵɵtext(13," "),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10,0),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵpipe(18,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(19,"mat-icon"),t.ɵɵtext(20,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(21,"div",12)(22,"table",13),t.ɵɵtemplate(23,MY,3,1,"ng-container",14),t.ɵɵelementContainerStart(24,15),t.ɵɵtemplate(25,kY,1,0,"mat-header-cell",16)(26,DY,12,6,"mat-cell",17),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(27,OY,1,0,"mat-header-row",18)(28,AY,1,0,"mat-row",19),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"section",20),t.ɵɵpipe(30,"async"),t.ɵɵelementStart(31,"button",21),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageMapping(i))})),t.ɵɵelementStart(32,"mat-icon",22),t.ɵɵtext(33,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"span"),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(37,"span",23),t.ɵɵpipe(38,"async"),t.ɵɵtext(39," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(4,19,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,21,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,23,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,25,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.mappingColumns),t.ɵɵadvance(4),t.ɵɵproperty("matHeaderRowDef",n.displayedColumns)("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",n.displayedColumns),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(30,27,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,29,"gateway.add-mapping")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(38,31,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(FY,[se,_,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%]{width:100%;height:100%;background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content.tb-outlined-border[_ngcontent-%COMP%]{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;border:solid 1px #e0e0e0;border-radius:4px}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .tb-mapping-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:21%}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column.request-column[_ngcontent-%COMP%]{width:35%}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .ellipsis[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-mapping-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}e("MappingTableComponent",FY),He([I()],FY.prototype,"required",void 0),He([I()],FY.prototype,"withReportStrategy",void 0);let RY=class extends G{constructor(){super()}};function BY(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",7),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SecurityTypeTranslationsMap.get(e))," ")}}function NY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",18),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.username-required"))}function LY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",8)(1,"div",9),t.ɵɵtext(2,"gateway.username"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",10)(4,"mat-form-field",11),t.ɵɵelement(5,"input",12),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,NY,3,3,"mat-icon",13),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",8)(9,"div",14),t.ɵɵtext(10,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",10)(12,"mat-form-field",11),t.ɵɵelement(13,"input",15),t.ɵɵpipe(14,"translate"),t.ɵɵelementStart(15,"div",16),t.ɵɵelement(16,"tb-toggle-password",17),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,3,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("username").hasError("required")&&e.securityFormGroup.get("username").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,5,"gateway.set"))}}function VY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",7),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function qY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",18),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.username-required"))}function GY(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",8)(2,"div",14),t.ɵɵtext(3,"gateway.mode"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",10)(5,"mat-form-field",11)(6,"mat-select",25),t.ɵɵtemplate(7,VY,2,2,"mat-option",4),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(8,"div",8)(9,"div",14),t.ɵɵtext(10,"gateway.username"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",10)(12,"mat-form-field",11),t.ɵɵelement(13,"input",12),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,qY,3,3,"mat-icon",13),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",8)(17,"div",14),t.ɵɵtext(18,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",10)(20,"mat-form-field",11),t.ɵɵelement(21,"input",15),t.ɵɵpipe(22,"translate"),t.ɵɵelementStart(23,"div",16),t.ɵɵelement(24,"tb-toggle-password",17),t.ɵɵelementEnd()()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",e.modeTypes),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,4,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("username").hasError("required")&&e.securityFormGroup.get("username").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(22,6,"gateway.set"))}}function zY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",8)(4,"div",20),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",10)(8,"mat-form-field",11),t.ɵɵelement(9,"input",21),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",8)(12,"div",20),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"div",10)(16,"mat-form-field",11),t.ɵɵelement(17,"input",22),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(19,"div",8)(20,"div",20),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"div",10)(24,"mat-form-field",11),t.ɵɵelement(25,"input",23),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(27,GY,25,8,"ng-container",24)),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,8,"gateway.path-hint")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,10,"gateway.CA-certificate-path")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(10,12,"gateway.set")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,14,"gateway.private-key-path")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(18,16,"gateway.set")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,18,"gateway.client-cert-path")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(26,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mode()===e.SecurityMode.extendedCertificates)}}e("MappingDatasource",RY);class UY{constructor(e,t){this.fb=e,this.cdr=t,this.title="gateway.security",this.mode=l(Ji.certificates),this.BrokerSecurityType=Ri,this.SecurityMode=Ji,this.securityTypes=Object.values(Ri),this.modeTypes=Object.values(Bi),this.SecurityTypeTranslationsMap=Gi,this.destroy$=new we,o((()=>{this.mode()===Ji.basic&&(this.securityTypes=this.securityTypes.filter((e=>e!==Ri.CERTIFICATES)))}))}ngOnInit(){this.securityFormGroup=this.fb.group({type:[Ri.ANONYMOUS,[]],username:["",[me.required,me.pattern(pn)]],password:["",[me.pattern(pn)]],pathToCACert:["",[me.pattern(pn)]],pathToPrivateKey:["",[me.pattern(pn)]],pathToClientCert:["",[me.pattern(pn)]]}),this.mode()===Ji.extendedCertificates&&this.securityFormGroup.addControl("mode",this.fb.control(Bi.NONE,[])),this.securityFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{Y(e),this.onChange(e),this.onTouched()})),this.securityFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.updateValidators(e)))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){if(e)e.type||(e.type=Ri.ANONYMOUS),this.updateValidators(e.type),this.securityFormGroup.reset(e,{emitEvent:!1});else{const e={type:Ri.ANONYMOUS};this.securityFormGroup.reset(e,{emitEvent:!1})}this.cdr.markForCheck()}validate(){return this.securityFormGroup.get("type").value!==Ri.BASIC||this.securityFormGroup.valid?null:{securityForm:{valid:!1}}}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}updateValidators(e){if(e)if(this.securityFormGroup.get("username").disable({emitEvent:!1}),this.securityFormGroup.get("password").disable({emitEvent:!1}),this.securityFormGroup.get("pathToCACert").disable({emitEvent:!1}),this.securityFormGroup.get("pathToPrivateKey").disable({emitEvent:!1}),this.securityFormGroup.get("pathToClientCert").disable({emitEvent:!1}),this.securityFormGroup.get("mode")?.disable({emitEvent:!1}),e===Ri.BASIC)this.securityFormGroup.get("username").enable({emitEvent:!1}),this.securityFormGroup.get("password").enable({emitEvent:!1});else if(e===Ri.CERTIFICATES&&(this.securityFormGroup.get("pathToCACert").enable({emitEvent:!1}),this.securityFormGroup.get("pathToPrivateKey").enable({emitEvent:!1}),this.securityFormGroup.get("pathToClientCert").enable({emitEvent:!1}),this.mode()===Ji.extendedCertificates)){const e=this.securityFormGroup.get("mode");e&&!e.value&&e.setValue(Bi.NONE,{emitEvent:!1}),e?.enable({emitEvent:!1}),this.securityFormGroup.get("username").enable({emitEvent:!1}),this.securityFormGroup.get("password").enable({emitEvent:!1})}}static{this.ɵfac=function(e){return new(e||UY)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:UY,selectors:[["tb-security-config"]],inputs:{title:"title",mode:[1,"mode"]},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>UY)),multi:!0},{provide:he,useExisting:c((()=>UY)),multi:!0}]),t.ɵɵStandaloneFeature],decls:10,vars:8,consts:[[1,"tb-form-row","space-between","same-padding","tb-flex","column",3,"formGroup"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],[1,"fixed-title-width","tb-required"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[3,"ngSwitch"],[3,"ngSwitchCase"],[3,"value"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","username",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width"],["matInput","","type","password","name","value","formControlName","password",3,"placeholder"],["matSuffix","",1,"tb-flex","no-gap","align-center","fill-height"],[1,"tb-flex","align-center","fill-height"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[1,"tb-form-hint","tb-primary-fill"],["tbTruncateWithTooltip","",1,"fixed-title-width"],["matInput","","name","value","formControlName","pathToCACert",3,"placeholder"],["matInput","","name","value","formControlName","pathToPrivateKey",3,"placeholder"],["matInput","","name","value","formControlName","pathToClientCert",3,"placeholder"],[4,"ngIf"],["formControlName","mode"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",3),t.ɵɵtemplate(6,BY,3,4,"tb-toggle-option",4),t.ɵɵelementEnd()(),t.ɵɵelementContainerStart(7,5),t.ɵɵtemplate(8,LY,17,7,"ng-template",6)(9,zY,28,22,"ng-template",6),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.securityFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,6,n.title)),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.securityTypes),t.ɵɵadvance(),t.ɵɵproperty("ngSwitch",n.securityFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.BrokerSecurityType.BASIC),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.BrokerSecurityType.CERTIFICATES))},dependencies:t.ɵɵgetComponentDepsFactory(UY,[se,_,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}'],changeDetection:d.OnPush})}}e("SecurityConfigComponent",UY);const jY=()=>({min:1e3}),HY=()=>({min:50}),WY=()=>({min:100});function $Y(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.server-url-required"))}function KY(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",9),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.timeout-error",t.ɵɵpureFunction0(4,jY)))}function YY(e,n){1&e&&(t.ɵɵelementStart(0,"span",10),t.ɵɵtext(1,"gateway.suffix.ms"),t.ɵɵelementEnd())}function XY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name)}}function ZY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind2(1,1,"gateway.scan-period-error",t.ɵɵpureFunction0(4,jY)))}function QY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind2(1,1,"gateway.poll-period-error",t.ɵɵpureFunction0(4,HY)))}function JY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"div",6),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"div",7),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"mat-form-field",3),t.ɵɵelement(7,"input",23),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,QY,3,5,"mat-icon",5),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,4,"gateway.hints.poll-period")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,6,"gateway.poll-period")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.serverConfigFormGroup.get("pollPeriodInMillis").hasError("required")||e.serverConfigFormGroup.get("pollPeriodInMillis").hasError("min"))&&e.serverConfigFormGroup.get("pollPeriodInMillis").touched)}}function eX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind2(1,1,"gateway.sub-check-period-error",t.ɵɵpureFunction0(4,WY)))}class tX{constructor(e){this.fb=e,this.hideNewFields=!1,this.securityPolicyTypes=qi,this.SecurityMode=Ji,this.destroy$=new we,this.serverConfigFormGroup=this.fb.group({url:["",[me.required,me.pattern(pn)]],timeoutInMillis:[1e3,[me.required,me.min(1e3)]],scanPeriodInMillis:[z,[me.required,me.min(1e3)]],pollPeriodInMillis:[5e3,[me.required,me.min(50)]],enableSubscriptions:[!0,[]],subCheckPeriodInMillis:[100,[me.required,me.min(100)]],showMap:[!1,[]],security:[Vi.BASIC128,[]],identity:[]}),this.serverConfigFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}ngAfterViewInit(){this.hideNewFields&&this.serverConfigFormGroup.get("pollPeriodInMillis").disable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.serverConfigFormGroup.valid?null:{serverConfigFormGroup:{valid:!1}}}writeValue(e){const{timeoutInMillis:t=1e3,scanPeriodInMillis:n=z,pollPeriodInMillis:i=5e3,enableSubscriptions:a=!0,subCheckPeriodInMillis:r=100,showMap:o=!1,security:s=Vi.BASIC128,identity:l={}}=e;this.serverConfigFormGroup.reset({...e,timeoutInMillis:t,scanPeriodInMillis:n,pollPeriodInMillis:i,enableSubscriptions:a,subCheckPeriodInMillis:r,showMap:o,security:s,identity:l},{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||tX)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:tX,selectors:[["tb-opc-server-config"]],inputs:{hideNewFields:"hideNewFields"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>tX)),multi:!0},{provide:he,useExisting:c((()=>tX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:63,vars:56,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],["tbTruncateWithTooltip","","translate","",1,"fixed-title-width"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","url",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],[1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip",""],["matInput","","type","number","min","1000","name","value","formControlName","timeoutInMillis",3,"placeholder"],["matSuffix","",3,"tooltipText"],["translate","","matSuffix","",1,"block","pr-2"],["formControlName","security"],[3,"value",4,"ngFor","ngForOf"],["matInput","","type","number","min","1000","name","value","formControlName","scanPeriodInMillis",3,"placeholder"],["class","tb-form-row column-xs",4,"ngIf"],["matInput","","type","number","min","100","name","value","formControlName","subCheckPeriodInMillis",3,"placeholder"],[1,"tb-form-row"],["formControlName","enableSubscriptions",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","showMap",1,"mat-slide"],["formControlName","identity",3,"mode"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["matInput","","type","number","min","50","name","value","formControlName","pollPeriodInMillis",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.server-url"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",3),t.ɵɵelement(5,"input",4),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,$Y,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",1)(9,"div",6),t.ɵɵpipe(10,"translate"),t.ɵɵelementStart(11,"div",7),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-form-field",3),t.ɵɵelement(15,"input",8),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,KY,2,5,"tb-error-icon",9)(18,YY,2,0,"span",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"div",1)(20,"div",6),t.ɵɵpipe(21,"translate"),t.ɵɵelementStart(22,"div",7),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(25,"mat-form-field",3)(26,"mat-select",11),t.ɵɵtemplate(27,XY,2,2,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(28,"div",1)(29,"div",6),t.ɵɵpipe(30,"translate"),t.ɵɵelementStart(31,"div",7),t.ɵɵtext(32),t.ɵɵpipe(33,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"mat-form-field",3),t.ɵɵelement(35,"input",13),t.ɵɵpipe(36,"translate"),t.ɵɵtemplate(37,ZY,3,5,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵtemplate(38,JY,10,10,"div",14),t.ɵɵelementStart(39,"div",1)(40,"div",6),t.ɵɵpipe(41,"translate"),t.ɵɵelementStart(42,"div",7),t.ɵɵtext(43),t.ɵɵpipe(44,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"mat-form-field",3),t.ɵɵelement(46,"input",15),t.ɵɵpipe(47,"translate"),t.ɵɵtemplate(48,eX,3,5,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(49,"div",16)(50,"mat-slide-toggle",17)(51,"mat-label",18),t.ɵɵpipe(52,"translate"),t.ɵɵelementStart(53,"div",7),t.ɵɵtext(54),t.ɵɵpipe(55,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(56,"div",16)(57,"mat-slide-toggle",19)(58,"mat-label",18),t.ɵɵpipe(59,"translate"),t.ɵɵtext(60),t.ɵɵpipe(61,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelement(62,"tb-security-config",20),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.serverConfigFormGroup),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.serverConfigFormGroup.get("url").hasError("required")&&n.serverConfigFormGroup.get("url").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,26,"gateway.hints.opc-timeout")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,28,"gateway.timeout")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,30,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((n.serverConfigFormGroup.get("timeoutInMillis").hasError("required")||n.serverConfigFormGroup.get("timeoutInMillis").hasError("min"))&&n.serverConfigFormGroup.get("timeoutInMillis").touched?17:18),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,32,"gateway.hints.security-policy")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(24,34,"gateway.security-policy")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",n.securityPolicyTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(30,36,"gateway.hints.scan-period")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(33,38,"gateway.scan-period")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,40,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.serverConfigFormGroup.get("scanPeriodInMillis").hasError("required")||n.serverConfigFormGroup.get("scanPeriodInMillis").hasError("min"))&&n.serverConfigFormGroup.get("scanPeriodInMillis").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.hideNewFields),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(41,42,"gateway.hints.sub-check-period")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(44,44,"gateway.sub-check-period")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(47,46,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.serverConfigFormGroup.get("subCheckPeriodInMillis").hasError("required")||n.serverConfigFormGroup.get("subCheckPeriodInMillis").hasError("min"))&&n.serverConfigFormGroup.get("subCheckPeriodInMillis").touched),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(52,48,"gateway.hints.enable-subscription")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(55,50,"gateway.enable-subscription")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(59,52,"gateway.hints.show-map")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(61,54,"gateway.show-map")," "),t.ɵɵadvance(2),t.ɵɵproperty("mode",n.SecurityMode.extendedCertificates))},dependencies:t.ɵɵgetComponentDepsFactory(tX,[se,_,UY,Wn,ai]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}'],changeDetection:d.OnPush})}}e("OpcServerConfigComponent",tX),He([I()],tX.prototype,"hideNewFields",void 0);class nX extends Ua{constructor(){super(...arguments),this.withReportStrategy=!0,this.mappingTypes=Ni,this.isLegacy=!0}initBasicFormGroup(){return this.fb.group({mapping:[],server:[]})}mapConfigToFormValue(e){let t;return t=e?.server&&"mapping"in e?.server?Za.mapMappingToUpgradedVersion(e.server.mapping):e&&"mapping"in e?Za.mapMappingToCurrentVersion(e.mapping):[],{server:e.server?Za.mapServerToUpgradedVersion(e.server):{},mapping:t}}getMappedValue(e){return{server:Za.mapServerToDowngradedVersion(e)}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(nX)))(n||nX)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:nX,selectors:[["tb-opc-ua-legacy-basic-config"]],inputs:{withReportStrategy:"withReportStrategy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>nX)),multi:!0},{provide:he,useExisting:c((()=>nX)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server",3,"hideNewFields"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"required","withReportStrategy","mappingType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-opc-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,11,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,13,"gateway.server"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("hideNewFields",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,15,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("required",!0)("withReportStrategy",n.withReportStrategy)("mappingType",n.mappingTypes.OPCUA))},dependencies:t.ɵɵgetComponentDepsFactory(nX,[se,_,FY,tX]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("OpcUaLegacyBasicConfigComponent",nX),He([I()],nX.prototype,"withReportStrategy",void 0);class iX extends Ua{constructor(){super(...arguments),this.withReportStrategy=!0,this.mappingTypes=Ni,this.isLegacy=!1}initBasicFormGroup(){return this.fb.group({mapping:[],server:[]})}mapConfigToFormValue(e){return{server:e.server??{},mapping:e.mapping??[]}}getMappedValue(e){return{server:e.server,mapping:e.mapping}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(iX)))(n||iX)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:iX,selectors:[["tb-opc-ua-basic-config"]],inputs:{withReportStrategy:"withReportStrategy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>iX)),multi:!0},{provide:he,useExisting:c((()=>iX)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server",3,"hideNewFields"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"required","withReportStrategy","mappingType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-opc-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,11,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,13,"gateway.server"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("hideNewFields",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,15,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("required",!0)("withReportStrategy",n.withReportStrategy)("mappingType",n.mappingTypes.OPCUA))},dependencies:t.ɵɵgetComponentDepsFactory(iX,[se,_,UY,FY,tX]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("OpcUaBasicConfigComponent",iX),He([I()],iX.prototype,"withReportStrategy",void 0);class aX extends Ua{constructor(){super(...arguments),this.withReportStrategy=!0,this.MappingType=Ni}initBasicFormGroup(){return this.fb.group({mapping:[],requestsMapping:[],broker:[],workers:[]})}getRequestDataArray(e){const t=[];return ie(e)&&Object.keys(e).forEach((n=>{for(const i of e[n])t.push({requestType:n,requestValue:i})})),t}getRequestDataObject(e){return e.reduce(((e,{requestType:t,requestValue:n})=>(e[t].push(n),e)),{connectRequests:[],disconnectRequests:[],attributeRequests:[],attributeUpdates:[],serverSideRpc:[]})}getBrokerMappedValue(e,t){return{...e,maxNumberOfWorkers:t.maxNumberOfWorkers??100,maxMessageNumberPerWorker:t.maxMessageNumberPerWorker??10}}writeValue(e){this.basicFormGroup.setValue(this.mapConfigToFormValue(e),{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(aX)))(n||aX)}})()}static{this.ɵdir=t.ɵɵdefineDirective({type:aX,inputs:{withReportStrategy:"withReportStrategy"},features:[t.ɵɵInheritDefinitionFeature]})}}function rX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",8),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.max-number-of-workers-required"))}function oX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",8),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.max-messages-queue-for-worker-required"))}e("MqttBasicConfigDirective",aX),He([I()],aX.prototype,"withReportStrategy",void 0);class sX{constructor(e){this.fb=e,this.destroy$=new we,this.workersConfigFormGroup=this.fb.group({maxNumberOfWorkers:[100,[me.required,me.min(1)]],maxMessageNumberPerWorker:[10,[me.required,me.min(1)]]}),this.workersConfigFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){const{maxNumberOfWorkers:t,maxMessageNumberPerWorker:n}=e;this.workersConfigFormGroup.reset({maxNumberOfWorkers:t||100,maxMessageNumberPerWorker:n||10},{emitEvent:!1})}validate(){return this.workersConfigFormGroup.valid?null:{workersConfigFormGroup:{valid:!1}}}static{this.ɵfac=function(e){return new(e||sX)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:sX,selectors:[["tb-workers-config-control"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>sX)),multi:!0},{provide:he,useExisting:c((()=>sX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:21,vars:21,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",2,"width","50%",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip",""],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","type","number","min","1","formControlName","maxNumberOfWorkers",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","name","value","type","number","min","1","formControlName","maxMessageNumberPerWorker",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",4),t.ɵɵelement(8,"input",5),t.ɵɵpipe(9,"translate"),t.ɵɵtemplate(10,rX,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",1)(12,"div",2),t.ɵɵpipe(13,"translate"),t.ɵɵelementStart(14,"div",3),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"mat-form-field",4),t.ɵɵelement(18,"input",7),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,oX,3,3,"mat-icon",6),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.workersConfigFormGroup),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,9,"gateway.max-number-of-workers-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,11,"gateway.max-number-of-workers")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(9,13,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.workersConfigFormGroup.get("maxNumberOfWorkers").hasError("min")||n.workersConfigFormGroup.get("maxNumberOfWorkers").hasError("required")&&n.workersConfigFormGroup.get("maxNumberOfWorkers").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(13,15,"gateway.max-messages-queue-for-worker-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,17,"gateway.max-messages-queue-for-worker")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,19,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.workersConfigFormGroup.get("maxMessageNumberPerWorker").hasError("min")||n.workersConfigFormGroup.get("maxMessageNumberPerWorker").hasError("required")&&n.workersConfigFormGroup.get("maxMessageNumberPerWorker").touched))},dependencies:t.ɵɵgetComponentDepsFactory(sX,[se,_,Wn]),encapsulation:2,changeDetection:d.OnPush})}}function lX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",13),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function pX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",13),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.brokerConfigFormGroup.get("port")))}}function cX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",14),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name)}}function dX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",15),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("clientId"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.generate-client-id"))}e("WorkersConfigControlComponent",sX);class uX{constructor(e,t){this.fb=e,this.cdr=t,this.mqttVersions=si,this.portLimits=Oi,this.destroy$=new we,this.brokerConfigFormGroup=this.fb.group({host:["",[me.required,me.pattern(pn)]],port:[null,[me.required,me.min(Oi.MIN),me.max(Oi.MAX)]],version:[5,[]],clientId:["tb_gw_"+ae(5),[me.pattern(pn)]],security:[]}),this.brokerConfigFormGroup.valueChanges.subscribe((e=>{this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}generate(e){this.brokerConfigFormGroup.get(e)?.patchValue("tb_gw_"+ae(5))}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){const{version:t=5,clientId:n=`tb_gw_${ae(5)}`,security:i={}}=e;this.brokerConfigFormGroup.reset({...e,version:t,clientId:n,security:i},{emitEvent:!1}),this.cdr.markForCheck()}validate(){return this.brokerConfigFormGroup.valid?null:{brokerConfigFormGroup:{valid:!1}}}static{this.ɵfac=function(e){return new(e||uX)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:uX,selectors:[["tb-broker-config-control"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>uX)),multi:!0},{provide:he,useExisting:c((()=>uX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:29,vars:16,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["translate","",1,"fixed-title-width"],["formControlName","version"],[3,"value",4,"ngFor","ngForOf"],["matInput","","name","value","formControlName","clientId",3,"placeholder"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"matTooltip","click",4,"ngIf"],["formControlName","security"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"click","matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",3),t.ɵɵelement(5,"input",4),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,lX,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",1)(9,"div",2),t.ɵɵtext(10,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",3),t.ɵɵelement(12,"input",6),t.ɵɵpipe(13,"translate"),t.ɵɵtemplate(14,pX,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"div",1)(16,"div",7),t.ɵɵtext(17,"gateway.mqtt-version"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"mat-form-field",3)(19,"mat-select",8),t.ɵɵtemplate(20,cX,2,2,"mat-option",9),t.ɵɵelementEnd()()(),t.ɵɵelementStart(21,"div",1)(22,"div",7),t.ɵɵtext(23,"gateway.client-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(24,"mat-form-field",3),t.ɵɵelement(25,"input",10),t.ɵɵpipe(26,"translate"),t.ɵɵtemplate(27,dX,4,3,"button",11),t.ɵɵelementEnd()(),t.ɵɵelement(28,"tb-security-config",12),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.brokerConfigFormGroup),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,10,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.brokerConfigFormGroup.get("host").hasError("required")&&n.brokerConfigFormGroup.get("host").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(13,12,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.brokerConfigFormGroup.get("port").hasError("required")||n.brokerConfigFormGroup.get("port").hasError("min")||n.brokerConfigFormGroup.get("port").hasError("max"))&&n.brokerConfigFormGroup.get("port").touched),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.mqttVersions),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(26,14,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!n.brokerConfigFormGroup.get("clientId").value))},dependencies:t.ɵɵgetComponentDepsFactory(uX,[se,_,UY,u$]),encapsulation:2,changeDetection:d.OnPush})}}e("BrokerConfigControlComponent",uX);class mX extends aX{mapConfigToFormValue(e){const{broker:t,mapping:n=[],requestsMapping:i}=e;return{workers:t&&(t.maxNumberOfWorkers||t.maxMessageNumberPerWorker)?{maxNumberOfWorkers:t.maxNumberOfWorkers,maxMessageNumberPerWorker:t.maxMessageNumberPerWorker}:{},mapping:n??[],broker:t??{},requestsMapping:this.getRequestDataArray(i)}}getMappedValue(e){const{broker:t,workers:n,mapping:i,requestsMapping:a}=e||{};return{broker:this.getBrokerMappedValue(t,n),mapping:i,requestsMapping:a?.length?this.getRequestDataObject(a):{}}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(mX)))(n||mX)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:mX,selectors:[["tb-mqtt-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>mX)),multi:!0},{provide:he,useExisting:c((()=>mX)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:24,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","broker"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"withReportStrategy","required","mappingType"],["formControlName","requestsMapping",3,"withReportStrategy","mappingType"],[1,"tb-form-panel","no-border","no-padding"],["formControlName","workers"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-broker-config-control",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-tab",1),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"div",4),t.ɵɵelement(14,"tb-mapping-table",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-tab",1),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"div",7),t.ɵɵelement(18,"tb-workers-config-control",8),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,14,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,16,"gateway.broker.connection"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,18,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("required",!0)("mappingType",n.MappingType.DATA),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,20,"gateway.requests-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("mappingType",n.MappingType.REQUESTS),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(16,22,"gateway.workers-settings")))},dependencies:t.ɵɵgetComponentDepsFactory(mX,[se,_,sX,uX,FY]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("MqttBasicConfigComponent",mX);class hX extends aX{mapConfigToFormValue(e){const{broker:t,mapping:n=[],connectRequests:i=[],disconnectRequests:a=[],attributeRequests:r=[],attributeUpdates:o=[],serverSideRpc:s=[]}=e,l=Na.mapRequestsToUpgradedVersion({connectRequests:i,disconnectRequests:a,attributeRequests:r,attributeUpdates:o,serverSideRpc:s});return{workers:t&&(t.maxNumberOfWorkers||t.maxMessageNumberPerWorker)?{maxNumberOfWorkers:t.maxNumberOfWorkers,maxMessageNumberPerWorker:t.maxMessageNumberPerWorker}:{},mapping:Na.mapMappingToUpgradedVersion(n)||[],broker:t||{},requestsMapping:this.getRequestDataArray(l)}}getMappedValue(e){const{broker:t,workers:n,mapping:i,requestsMapping:a}=e||{},r=a?.length?this.getRequestDataObject(a):{};return{broker:this.getBrokerMappedValue(t,n),mapping:Na.mapMappingToDowngradedVersion(i),...Na.mapRequestsToDowngradedVersion(r)}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(hX)))(n||hX)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:hX,selectors:[["tb-mqtt-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>hX)),multi:!0},{provide:he,useExisting:c((()=>hX)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:24,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","broker"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"withReportStrategy","required","mappingType"],["formControlName","requestsMapping",3,"withReportStrategy","mappingType"],[1,"tb-form-panel","no-border","no-padding"],["formControlName","workers"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-broker-config-control",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-tab",1),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"div",4),t.ɵɵelement(14,"tb-mapping-table",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-tab",1),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"div",7),t.ɵɵelement(18,"tb-workers-config-control",8),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,14,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,16,"gateway.broker.connection"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,18,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("required",!0)("mappingType",n.MappingType.DATA),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,20,"gateway.requests-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("mappingType",n.MappingType.REQUESTS),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(16,22,"gateway.workers-settings")))},dependencies:t.ɵɵgetComponentDepsFactory(hX,[se,_,sX,uX,FY]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("MqttLegacyBasicConfigComponent",hX);class gX extends A{constructor(e,t,n,i,a){super(t,n,a),this.fb=e,this.store=t,this.router=n,this.data=i,this.dialogRef=a,this.portLimits=Oi,this.modbusProtocolTypes=Object.values(ea),this.modbusMethodTypes=Object.values(ta),this.modbusSerialMethodTypes=Object.values(na),this.modbusParities=Object.values(ia),this.modbusByteSizes=la,this.modbusBaudrates=sa,this.modbusOrderType=Object.values(aa),this.ModbusProtocolType=ea,this.ModbusParityLabelsMap=ga,this.ModbusProtocolLabelsMap=ha,this.ModbusMethodLabelsMap=ma,this.ReportStrategyDefaultValue=on,this.modbusHelpLink=O+"/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters",this.serialSpecificControlKeys=["serialPort","baudrate","stopbits","bytesize","parity","strict"],this.tcpUdpSpecificControlKeys=["port","security","host"],this.destroy$=new we,this.showSecurityControl=this.fb.control(!1),this.initializeSlaveFormGroup(),this.updateSlaveFormGroup(),this.updateControlsEnabling(this.data.value.type),this.observeTypeChange(),this.observeShowSecurity(),this.showSecurityControl.patchValue(!!this.data.value.security&&!W(this.data.value.security,{}))}get protocolType(){return this.slaveConfigFormGroup.get("type").value}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}cancel(){this.dialogRef.close(null)}add(){this.slaveConfigFormGroup.valid&&this.dialogRef.close(this.getSlaveResultData())}initializeSlaveFormGroup(){this.slaveConfigFormGroup=this.fb.group({type:[ea.TCP],host:["",[me.required,me.pattern(pn)]],port:[null,[me.required,me.min(Oi.MIN),me.max(Oi.MAX)]],serialPort:["",[me.required,me.pattern(pn)]],method:[ta.SOCKET,[me.required]],baudrate:[this.modbusBaudrates[0]],stopbits:[1],bytesize:[la[0]],parity:[ia.None],strict:[!0],unitId:[null,[me.required]],deviceName:["",[me.required,me.pattern(pn)]],deviceType:["",[me.required,me.pattern(pn)]],timeout:[35],byteOrder:[aa.BIG],wordOrder:[aa.BIG],retries:[pa],retryOnEmpty:[!0],retryOnInvalid:[!0],pollPeriod:[1e3,[me.required]],connectAttemptTimeMs:[500,[me.required]],connectAttemptCount:[5,[me.required]],waitAfterFailedAttemptsMs:[3e4,[me.required]],values:[{}],security:[{}]}),this.addFieldsToFormGroup()}updateSlaveFormGroup(){this.slaveConfigFormGroup.patchValue({...this.data.value,port:this.data.value.type===ea.Serial?null:this.data.value.port,serialPort:this.data.value.type===ea.Serial?this.data.value.port:"",retries:!1===this.data.value.retries?0:Z(this.data.value.retries)?this.data.value.retries:pa,values:{attributes:this.data.value.attributes??[],timeseries:this.data.value.timeseries??[],attributeUpdates:this.data.value.attributeUpdates??[],rpc:this.data.value.rpc??[]}})}observeTypeChange(){this.slaveConfigFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.updateControlsEnabling(e),this.updateMethodType(e)}))}updateMethodType(e){this.slaveConfigFormGroup.get("method").value!==ta.RTU&&this.slaveConfigFormGroup.get("method").patchValue(e===ea.Serial?na.ASCII:ta.SOCKET,{emitEvent:!1})}updateControlsEnabling(e){const[t,n]=e===ea.Serial?[this.serialSpecificControlKeys,this.tcpUdpSpecificControlKeys]:[this.tcpUdpSpecificControlKeys,this.serialSpecificControlKeys];t.forEach((e=>this.slaveConfigFormGroup.get(e)?.enable({emitEvent:!1}))),n.forEach((e=>this.slaveConfigFormGroup.get(e)?.disable({emitEvent:!1}))),this.updateSecurityEnabling(this.showSecurityControl.value)}observeShowSecurity(){this.showSecurityControl.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.updateSecurityEnabling(e)))}updateSecurityEnabling(e){e&&this.protocolType!==ea.Serial?this.slaveConfigFormGroup.get("security").enable({emitEvent:!1}):this.slaveConfigFormGroup.get("security").disable({emitEvent:!1})}static{this.ɵfac=function(e){return new(e||gX)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef))}}static{this.ɵdir=t.ɵɵdefineDirective({type:gX,features:[t.ɵɵInheritDefinitionFeature]})}}e("ModbusSlaveDialogAbstract",gX);const fX=()=>({maxWidth:"970px"});function yX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate3(" ",e.get("tag").value,"",": ","",e.get("value").value," ")}}function vX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",22)(1,"div",23),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"span",24),t.ɵɵtext(5),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"div",25),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"span",26),t.ɵɵtext(10),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",25),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementStart(14,"span",26),t.ɵɵtext(15),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(3,6,n.keysType===n.ModbusValueKey.RPC_REQUESTS?"gateway.method":"gateway.key"),": "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(e.get("tag").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(8,8,"gateway.address"),": "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(e.get("address").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(13,10,"gateway.type"),": "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(e.get("type").value)}}function xX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",46),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.keysType===e.ModbusValueKey.RPC_REQUESTS?"gateway.method-required":"gateway.key-required"))}}function bX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",47),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function wX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",47),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(5);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.ModbusFunctionCodeTranslationsMap.get(e))," ")}}function SX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",31)(1,"div",36),t.ɵɵtext(2,"gateway.function-code"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",33)(4,"mat-select",48),t.ɵɵtemplate(5,wX,3,4,"mat-option",38),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.functionCodesMap.get(e.get("id").value)||n.defaultFunctionCodes)}}function CX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",46),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.objects-count-required"))}function _X(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",46),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.modbus.max-bit"))}function TX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",31)(1,"div",49),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.bit"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",50),t.ɵɵelement(5,"input",51),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,_X,3,3,"mat-icon",35),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(3).$implicit;t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.bit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("bit").hasError("max")&&e.get("bit").touched)}}function IX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",47),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(6);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.BitTargetTypeTranslationMap.get(e)))}}function EX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",31)(1,"div",49),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.bit-target-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",52)(5,"mat-form-field",53)(6,"mat-select",54),t.ɵɵtemplate(7,IX,3,4,"mat-option",38),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(5);t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,2,"gateway.hints.modbus.bit-target-type")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",e.bitTargetTypes)}}function MX(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,TX,8,7,"div",39)(2,EX,8,4,"div",39),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("objectsCount").value>1),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.hideNewFields)}}function kX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",46),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-required"))}function PX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",47),t.ɵɵelement(1,"mat-icon",63),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(5);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("svgIcon",i.ModifierTypesMap.get(e).icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,i.ModifierTypesMap.get(e).name))}}function DX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",46),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.modifier-invalid"))}function OX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",55)(1,"mat-expansion-panel",16)(2,"mat-expansion-panel-header",17)(3,"mat-panel-title")(4,"mat-slide-toggle",56),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label",57),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(9,"div",52)(10,"div",58)(11,"div",36),t.ɵɵtext(12,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",53)(14,"mat-select",59)(15,"mat-select-trigger")(16,"div",60),t.ɵɵelement(17,"mat-icon",61),t.ɵɵelementStart(18,"span"),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(21,PX,5,5,"mat-option",38),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(22,"div",31)(23,"div",36),t.ɵɵtext(24,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",50),t.ɵɵelement(26,"input",62),t.ɵɵpipe(27,"translate"),t.ɵɵtemplate(28,DX,3,3,"mat-icon",35),t.ɵɵelementEnd()()()()}if(2&e){let e,n;const i=t.ɵɵnextContext(2).$implicit,a=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("expanded",a.enableModifiersControlMap.get(i.get("id").value).value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",a.enableModifiersControlMap.get(i.get("id").value)),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,9,"gateway.hints.modbus.modifier")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,11,"gateway.modifier")," "),t.ɵɵadvance(10),t.ɵɵproperty("svgIcon",null==(e=a.ModifierTypesMap.get(i.get("modifierType").value))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,13,null==(n=a.ModifierTypesMap.get(i.get("modifierType").value))?null:n.name)),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",a.modifierTypes),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(27,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",i.get("modifierValue").hasError("pattern")&&i.get("modifierValue").touched)}}function AX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",46),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function FX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",31)(1,"div",64),t.ɵɵtext(2,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",33),t.ɵɵelement(4,"input",65),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,AX,3,3,"mat-icon",35),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,2,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("value").hasError("required")&&e.get("value").touched)}}function RX(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",66),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Key)("isExpansionMode",!0)}}function BX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",27),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelement(3,"div",28),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",29)(5,"div",30),t.ɵɵtext(6,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",31)(8,"div",32),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",33),t.ɵɵelement(13,"input",34),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,xX,3,3,"mat-icon",35),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",29)(17,"div",30),t.ɵɵtext(18,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",31)(20,"div",36),t.ɵɵtext(21," gateway.type "),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",33)(23,"mat-select",37),t.ɵɵtemplate(24,bX,2,2,"mat-option",38),t.ɵɵelementEnd()()(),t.ɵɵtemplate(25,SX,6,1,"div",39),t.ɵɵelementStart(26,"div",31)(27,"div",40),t.ɵɵpipe(28,"translate"),t.ɵɵtext(29,"gateway.objects-count"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",33),t.ɵɵelement(31,"input",41),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,CX,3,3,"mat-icon",35),t.ɵɵelementEnd()(),t.ɵɵtemplate(34,MX,3,2,"ng-container",42),t.ɵɵelementStart(35,"div",31)(36,"div",40),t.ɵɵpipe(37,"translate"),t.ɵɵtext(38,"gateway.address"),t.ɵɵelementEnd(),t.ɵɵelementStart(39,"mat-form-field",33),t.ɵɵelement(40,"input",43),t.ɵɵpipe(41,"translate"),t.ɵɵtemplate(42,kX,3,3,"mat-icon",35),t.ɵɵelementEnd()(),t.ɵɵtemplate(43,OX,29,17,"div",44)(44,FX,7,4,"div",39)(45,RX,1,2,"tb-report-strategy",45),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,20,"gateway.hints.modbus.data-keys")," "),t.ɵɵadvance(2),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/modbus-functions-data-types_fn")("tb-help-popup-style",t.ɵɵpureFunction0(36,fX)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(9,22,n.keysType===n.ModbusValueKey.RPC_REQUESTS?"gateway.hints.modbus.method":"gateway.hints.modbus.key")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,24,n.keysType===n.ModbusValueKey.RPC_REQUESTS?"gateway.method-name":"gateway.key")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,26,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("tag").hasError("required")&&e.get("tag").touched),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",n.modbusDataTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.withFunctionCode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(28,28,"gateway.hints.modbus.objects-count")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,30,"gateway.set")),t.ɵɵproperty("readonly",!n.ModbusEditableDataTypes.includes(e.get("type").value)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("objectsCount").hasError("required")&&e.get("objectsCount").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("type").value===n.ModbusDataType.BITS),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(37,32,"gateway.hints.modbus.address")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(41,34,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("address").hasError("required")&&e.get("address").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.showModifiersMap.get(e.get("id").value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isMaster),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.withReportStrategy)}}function NX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",13)(1,"div",14),t.ɵɵelementContainerStart(2,15),t.ɵɵelementStart(3,"mat-expansion-panel",16)(4,"mat-expansion-panel-header",17)(5,"mat-panel-title"),t.ɵɵtemplate(6,yX,2,3,"div",18)(7,vX,16,12,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()(),t.ɵɵtemplate(9,BX,46,37,"ng-template",19),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",20),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.last,a=t.ɵɵreference(8),r=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",r.isMaster)("ngIfElse",a),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,r.deleteKeyTitle))}}function LX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",11),t.ɵɵtemplate(1,NX,14,7,"div",12),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.keysListFormArray.controls)("ngForTrackBy",e.trackByControlId)}}function VX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",67)(1,"span",68),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class qX{constructor(e,t){this.fb=e,this.popover=t,this.isMaster=!1,this.hideNewFields=!1,this.keysDataApplied=new u,this.withFunctionCode=!0,this.withReportStrategy=!0,this.enableModifiersControlMap=new Map,this.showModifiersMap=new Map,this.functionCodesMap=new Map,this.defaultFunctionCodes=[],this.modbusDataTypes=Object.values(Zt),this.modifierTypes=Object.values(ba),this.bitTargetTypes=Object.values(da),this.BitTargetTypeTranslationMap=ua,this.ModbusEditableDataTypes=Qt,this.ModbusFunctionCodeTranslationsMap=nn,this.ModifierTypesMap=wa,this.ReportStrategyDefaultValue=on,this.ModbusDataType=Zt,this.ModbusValueKey=oa,this.destroy$=new we,this.defaultReadFunctionCodes=[3,4],this.bitsReadFunctionCodes=[1,2],this.defaultWriteFunctionCodes=[6,16],this.bitsWriteFunctionCodes=[5,15]}ngOnInit(){this.withFunctionCode=!this.isMaster||this.keysType!==oa.ATTRIBUTES&&this.keysType!==oa.TIMESERIES,this.withReportStrategy=!(this.isMaster||this.keysType!==oa.ATTRIBUTES&&this.keysType!==oa.TIMESERIES||this.hideNewFields),this.keysListFormArray=this.prepareKeysFormArray(this.values),this.defaultFunctionCodes=this.getDefaultFunctionCodes()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}trackByControlId(e,t){return t.value.id}addKey(){const e=ae(5),t=this.fb.group({tag:["",[me.required,me.pattern(pn)]],value:[{value:"",disabled:!this.isMaster},[me.required,me.pattern(pn)]],type:[Zt.BYTES,[me.required]],address:[null,[me.required]],objectsCount:[1,[me.required]],functionCode:[{value:this.getDefaultFunctionCodes()[0],disabled:!this.withFunctionCode},[me.required]],reportStrategy:[{value:null,disabled:!this.withReportStrategy}],modifierType:[{value:ba.MULTIPLIER,disabled:!0}],modifierValue:[{value:1,disabled:!0},[me.pattern(dn)]],bit:[{value:null,disabled:!0}],bitTargetType:[{value:da.IntegerType,disabled:!0}],id:[{value:e,disabled:!0}]});this.showModifiersMap.set(e,!1),this.enableModifiersControlMap.set(e,this.fb.control(!1)),this.observeKeyDataType(t),this.observeObjectsCount(t),this.observeEnableModifier(t),this.keysListFormArray.push(t)}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover.hide()}applyKeysData(){this.keysDataApplied.emit(this.getFormValue())}getFormValue(){return this.mapKeysWithModifier(this.withReportStrategy?this.cleanUpEmptyStrategies(this.keysListFormArray.value):this.keysListFormArray.value)}cleanUpEmptyStrategies(e){return e.map((e=>{const{reportStrategy:t,...n}=e;return t?e:n}))}mapKeysWithModifier(e){return e.map(((e,t)=>{if(this.showModifiersMap.get(this.keysListFormArray.controls[t].get("id").value)){const{modifierType:t,modifierValue:n,...i}=e;return t?{...i,[t]:n}:i}return e}))}prepareKeysFormArray(e){const t=[];return e&&e.forEach((e=>{const n=this.createDataKeyFormGroup(e);this.observeKeyDataType(n),this.observeObjectsCount(n),this.observeEnableModifier(n),this.functionCodesMap.set(n.get("id").value,this.getFunctionCodes(e.type)),t.push(n)})),this.fb.array(t)}createDataKeyFormGroup(e){const{tag:t,value:n,type:i,address:a,objectsCount:r,functionCode:o,multiplier:s,divider:l,reportStrategy:p,bit:c,bitTargetType:d}=e,u=ae(5),m=this.shouldShowModifier(i);return this.showModifiersMap.set(u,m),this.enableModifiersControlMap.set(u,this.fb.control((s||l)&&m)),this.fb.group({tag:[t,[me.required,me.pattern(pn)]],value:[{value:n,disabled:!this.isMaster},[me.required,me.pattern(pn)]],type:[i,[me.required]],address:[a,[me.required]],objectsCount:[r,[me.required]],functionCode:[{value:o,disabled:!this.withFunctionCode},[me.required]],modifierType:[{value:l?ba.DIVIDER:ba.MULTIPLIER,disabled:!this.enableModifiersControlMap.get(u).value}],bit:[{value:c,disabled:i!==Zt.BITS||r<2},[me.max(r-1)]],bitTargetType:[{value:d??da.IntegerType,disabled:i!==Zt.BITS||this.hideNewFields}],modifierValue:[{value:s??l??1,disabled:!this.enableModifiersControlMap.get(u).value},[me.pattern(dn)]],id:[{value:u,disabled:!0}],reportStrategy:[{value:p,disabled:!this.withReportStrategy}]})}shouldShowModifier(e){return!(this.isMaster||this.keysType!==oa.ATTRIBUTES&&this.keysType!==oa.TIMESERIES||this.ModbusEditableDataTypes.includes(e))}observeKeyDataType(e){e.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((t=>{this.ModbusEditableDataTypes.includes(t)||e.get("objectsCount").patchValue(Jt[t],{emitEvent:!1}),this.toggleBitsFields(e);const n=this.shouldShowModifier(t);this.showModifiersMap.set(e.get("id").value,n),this.updateFunctionCodes(e,t)}))}observeObjectsCount(e){e.get("objectsCount").valueChanges.pipe(Oe((()=>e.get("type").value===Zt.BITS)),Me(this.destroy$)).subscribe((()=>this.toggleBitsFields(e)))}toggleBitsFields(e){const{objectsCount:t,type:n,bit:i,bitTargetType:a}=e.controls,r=n.value===Zt.BITS,o=t.value>1;r&&o?(i.enable({emitEvent:!1}),i.setValidators(me.max(t.value-1))):i.disable({emitEvent:!1}),i.updateValueAndValidity({emitEvent:!1}),a[r?"enable":"disable"]({emitEvent:!1})}observeEnableModifier(e){this.enableModifiersControlMap.get(e.get("id").value).valueChanges.pipe(Me(this.destroy$)).subscribe((t=>this.toggleModifierControls(e,t)))}toggleModifierControls(e,t){const n=e.get("modifierType"),i=e.get("modifierValue");n[t?"enable":"disable"]({emitEvent:!1}),i[t?"enable":"disable"]({emitEvent:!1}),n.markAsDirty(),i.markAsDirty()}updateFunctionCodes(e,t){const n=this.getFunctionCodes(t);this.functionCodesMap.set(e.get("id").value,n),n.includes(e.get("functionCode").value)||e.get("functionCode").patchValue(n[0],{emitEvent:!1})}getFunctionCodes(e){const t=[...e===Zt.BITS?this.bitsWriteFunctionCodes:[],...this.defaultWriteFunctionCodes];if(this.keysType===oa.ATTRIBUTES_UPDATES)return t.sort(((e,t)=>e-t));const n=[...this.defaultReadFunctionCodes];return e===Zt.BITS&&n.push(...this.bitsReadFunctionCodes),this.keysType===oa.RPC_REQUESTS&&n.push(...t),n.sort(((e,t)=>e-t))}getDefaultFunctionCodes(){return this.keysType===oa.ATTRIBUTES_UPDATES?this.defaultWriteFunctionCodes:this.keysType===oa.RPC_REQUESTS?[...this.defaultReadFunctionCodes,...this.defaultWriteFunctionCodes]:this.defaultReadFunctionCodes}static{this.ɵfac=function(e){return new(e||qX)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverComponent))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:qX,selectors:[["tb-modbus-data-keys-panel"]],inputs:{isMaster:"isMaster",hideNewFields:"hideNewFields",panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keysType:"keysType",values:"values"},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:19,vars:16,consts:[["noKeys",""],["tagName",""],[1,"tb-modbus-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],[1,"flex","justify-between","pt-4"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],["class","title-container","tbTruncateWithTooltip","",4,"ngIf","ngIfElse"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],["tbTruncateWithTooltip","",1,"title-container"],[1,"tb-flex"],[1,"title-container","tb-flex"],["tbTruncateWithTooltip","",1,"key-label"],[1,"title-container"],[1,"key-label"],[1,"tb-form-hint","tb-primary-fill","tb-flex","align-center"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","tag",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","1","max","50000","name","value","formControlName","objectsCount",3,"placeholder","readonly"],[4,"ngIf"],["matInput","","type","number","min","0","max","65535","name","value","formControlName","address",3,"placeholder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],["class","stroked tb-form-panel","formControlName","reportStrategy",3,"defaultValue","isExpansionMode",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["formControlName","functionCode"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","tb-suffix-absolute","flex"],["matInput","","formControlName","bit","step","1","type","number","min","0",3,"placeholder"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","bitTargetType"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"mat-slide",3,"click","formControl"],[3,"tb-hint-tooltip-icon"],[1,"tb-form-row","column-xs","w-full"],["formControlName","modifierType"],[1,"tb-flex","align-center"],[1,"tb-mat-18",3,"svgIcon"],["matInput","","required","","formControlName","modifierValue","step","0.1","type","number",3,"placeholder"],[1,"tb-mat-20",3,"svgIcon"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","value",3,"placeholder"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"defaultValue","isExpansionMode"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"div",3)(2,"div",4),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,LX,2,2,"div",5),t.ɵɵelementEnd(),t.ɵɵtemplate(6,VX,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"div",6)(9,"button",7),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"div",8)(13,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.applyKeysData())})),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(7);t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,8,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")",""),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.keysListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,10,n.addKeyTitle)," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,14,"action.apply")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(qX,[se,_,ni,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{width:180px}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .key-label[_ngcontent-%COMP%]{font-weight:400}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:calc(100vh - 112px);max-height:500px;overflow:auto}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}']})}}e("ModbusDataKeysPanelComponent",qX),He([I()],qX.prototype,"isMaster",void 0),He([I()],qX.prototype,"hideNewFields",void 0);const GX=()=>({$implicit:null}),zX=e=>({$implicit:e});function UX(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",7),t.ɵɵelementContainer(2,8),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(),n=t.ɵɵreference(4);t.ɵɵadvance(),t.ɵɵproperty("formGroup",e.valuesFormGroup),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",n)("ngTemplateOutletContext",t.ɵɵpureFunction0(3,GX))}}function jX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-tab",11),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"div",7),t.ɵɵelementContainer(3,8),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2),a=t.ɵɵreference(4);t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(1,4,i.ModbusValuesTranslationsMap.get(e))),t.ɵɵadvance(2),t.ɵɵproperty("formGroup",i.valuesFormGroup.get(e)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",a)("ngTemplateOutletContext",t.ɵɵpureFunction1(6,zX,e))}}function HX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-tab-group",9),t.ɵɵtemplate(1,jX,4,8,"mat-tab",10),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("formGroup",e.valuesFormGroup),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.modbusRegisterTypes)}}function WX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function $X(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function KX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function YX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function XX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",12)(1,"div",13),t.ɵɵtext(2,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",14)(4,"mat-chip-listbox",15),t.ɵɵtemplate(5,WX,2,1,"mat-chip",16),t.ɵɵelementStart(6,"mat-chip",17),t.ɵɵelement(7,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"button",19,2),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(9),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.ATTRIBUTES,i))})),t.ɵɵelementStart(11,"tb-icon",20),t.ɵɵtext(12,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(13,"div",12)(14,"div",13),t.ɵɵtext(15,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",14)(17,"mat-chip-listbox",15),t.ɵɵtemplate(18,$X,2,1,"mat-chip",16),t.ɵɵelementStart(19,"mat-chip",17),t.ɵɵelement(20,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(21,"button",19,3),t.ɵɵpipe(23,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(22),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.TIMESERIES,i))})),t.ɵɵelementStart(24,"tb-icon",20),t.ɵɵtext(25,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(26,"div",12)(27,"div",13),t.ɵɵtext(28,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"div",14)(30,"mat-chip-listbox",15),t.ɵɵtemplate(31,KX,2,1,"mat-chip",16),t.ɵɵelementStart(32,"mat-chip",17),t.ɵɵelement(33,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"button",19,4),t.ɵɵpipe(36,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(35),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.ATTRIBUTES_UPDATES,i))})),t.ɵɵelementStart(37,"tb-icon",20),t.ɵɵtext(38,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(39,"div",12)(40,"div",13),t.ɵɵtext(41,"gateway.rpc-requests"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"div",14)(43,"mat-chip-listbox",15),t.ɵɵtemplate(44,YX,2,1,"mat-chip",16),t.ɵɵelementStart(45,"mat-chip",17),t.ɵɵelement(46,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(47,"button",19,5),t.ɵɵpipe(49,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(48),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.RPC_REQUESTS,i))})),t.ɵɵelementStart(50,"tb-icon",20),t.ɵɵtext(51,"edit"),t.ɵɵelementEnd()()()()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,16,"action.edit")),t.ɵɵproperty("disabled",i.disabled),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.TIMESERIES,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.TIMESERIES,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(23,18,"action.edit")),t.ɵɵproperty("disabled",i.disabled),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES_UPDATES,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES_UPDATES,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(36,20,"action.edit")),t.ɵɵproperty("disabled",i.disabled),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.RPC_REQUESTS,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.RPC_REQUESTS,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(49,22,"action.edit")),t.ɵɵproperty("disabled",i.disabled)}}class ZX{constructor(e,t,n,i,a,r){this.fb=e,this.popoverService=t,this.renderer=n,this.viewContainerRef=i,this.cdr=a,this.destroyRef=r,this.singleMode=!1,this.hideNewFields=!1,this.disabled=!1,this.modbusRegisterTypes=Object.values(ra),this.modbusValueKeys=Object.values(oa),this.ModbusValuesTranslationsMap=ca,this.ModbusValueKey=oa}ngOnInit(){this.initializeValuesFormGroup(),this.observeValuesChanges()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){if(this.singleMode)this.valuesFormGroup.setValue(this.getSingleRegisterState(e),{emitEvent:!1});else{const{holding_registers:t,coils_initializer:n,input_registers:i,discrete_inputs:a}=e;this.valuesFormGroup.setValue({holding_registers:this.getSingleRegisterState(t),coils_initializer:this.getSingleRegisterState(n),input_registers:this.getSingleRegisterState(i),discrete_inputs:this.getSingleRegisterState(a)},{emitEvent:!1})}this.cdr.markForCheck()}validate(){return this.valuesFormGroup.valid?null:{valuesFormGroup:{valid:!1}}}setDisabledState(e){this.disabled=e,this.cdr.markForCheck()}getValueGroup(e,t){return t?this.valuesFormGroup.get(t).get(e):this.valuesFormGroup.get(e)}manageKeys(e,t,n,i){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const a=t._elementRef.nativeElement;if(this.popoverService.hasPopover(a))return void this.popoverService.hidePopover(a);const r=this.getValueGroup(n,i),o={values:r.value,isMaster:!this.singleMode,keysType:n,panelTitle:fa.get(n),addKeyTitle:ya.get(n),deleteKeyTitle:va.get(n),noKeysText:xa.get(n),hideNewFields:this.hideNewFields};this.popoverComponent=this.popoverService.displayPopover(a,this.renderer,this.viewContainerRef,qX,"leftTop",!1,null,o,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(In(this.destroyRef)).subscribe((e=>{this.popoverComponent.hide(),r.patchValue(e),r.markAsDirty(),this.cdr.markForCheck()}))}initializeValuesFormGroup(){const e=()=>this.fb.group(this.modbusValueKeys.reduce(((e,t)=>(e[t]=this.fb.control([[],[]]),e)),{}));this.singleMode?this.valuesFormGroup=e():this.valuesFormGroup=this.fb.group(this.modbusRegisterTypes.reduce(((t,n)=>(t[n]=e(),t)),{}))}observeValuesChanges(){this.valuesFormGroup.valueChanges.pipe(In(this.destroyRef)).subscribe((e=>{this.onChange(e),this.onTouched()}))}getSingleRegisterState(e){return{attributes:e?.attributes??[],timeseries:e?.timeseries??[],attributeUpdates:e?.attributeUpdates??[],rpc:e?.rpc??[]}}static{this.ɵfac=function(e){return new(e||ZX)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(rt.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ZX,selectors:[["tb-modbus-values"]],inputs:{singleMode:"singleMode",hideNewFields:"hideNewFields"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>ZX)),multi:!0},{provide:he,useExisting:c((()=>ZX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:5,vars:2,consts:[["multipleView",""],["singleView",""],["attributesButton",""],["telemetryButton",""],["attributesUpdatesButton",""],["rpcRequestsButton",""],[4,"ngIf","ngIfElse"],[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[3,"ngTemplateOutlet","ngTemplateOutletContext"],[3,"formGroup"],[3,"label",4,"ngFor","ngForOf"],[3,"label"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[4,"ngFor","ngForOf"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","disabled","matTooltip"],["matButtonIcon",""]],template:function(e,n){if(1&e&&t.ɵɵtemplate(0,UX,3,4,"ng-container",6)(1,HX,2,2,"ng-template",null,0,t.ɵɵtemplateRefExtractor)(3,XX,52,24,"ng-template",null,1,t.ɵɵtemplateRefExtractor),2&e){const e=t.ɵɵreference(2);t.ɵɵproperty("ngIf",n.singleMode)("ngIfElse",e)}},dependencies:t.ɵɵgetComponentDepsFactory(ZX,[se,_,$n]),styles:['@charset "UTF-8";[_nghost-%COMP%] .ellipsis-chips-container{max-width:70%}[_nghost-%COMP%] .mat-mdc-tab-body-wrapper{min-height:320px} .mdc-evolution-chip-set__chips{align-items:center}'],changeDetection:d.OnPush})}}function QX(e,n){1&e&&(t.ɵɵelementStart(0,"div",2)(1,"div",10),t.ɵɵtext(2,"gateway.server-hostname"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",5)(4,"mat-form-field",6),t.ɵɵelement(5,"input",16),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,1,"gateway.set")))}function JX(e,n){1&e&&(t.ɵɵelementStart(0,"div",17)(1,"mat-slide-toggle",18)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.request-client-certificate")," "))}e("ModbusValuesComponent",ZX),He([I()],ZX.prototype,"singleMode",void 0),He([I()],ZX.prototype,"hideNewFields",void 0);class eZ{constructor(e,t){this.fb=e,this.cdr=t,this.isMaster=!1,this.disabled=!1,this.destroy$=new we,this.securityConfigFormGroup=this.fb.group({certfile:["",[me.required,me.pattern(pn)]],keyfile:["",[me.required,me.pattern(pn)]],password:["",[me.required,me.pattern(pn)]],server_hostname:["",[me.pattern(pn)]],reqclicert:[{value:!1,disabled:!0}]}),this.observeValueChanges()}ngOnChanges(){this.updateMasterEnabling()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){this.disabled=e,this.disabled?this.securityConfigFormGroup.disable({emitEvent:!1}):this.securityConfigFormGroup.enable({emitEvent:!1}),this.updateMasterEnabling(),this.cdr.markForCheck()}validate(){return this.securityConfigFormGroup.valid?null:{securityConfigFormGroup:{valid:!1}}}writeValue(e){const{certfile:t,password:n,keyfile:i,server_hostname:a}=e,r={certfile:t??"",password:n??"",keyfile:i??"",server_hostname:a??"",reqclicert:!!e.reqclicert};this.securityConfigFormGroup.reset(r,{emitEvent:!1})}updateMasterEnabling(){this.isMaster?(this.disabled||this.securityConfigFormGroup.get("reqclicert").enable({emitEvent:!1}),this.securityConfigFormGroup.get("server_hostname").disable({emitEvent:!1})):(this.disabled||this.securityConfigFormGroup.get("server_hostname").enable({emitEvent:!1}),this.securityConfigFormGroup.get("reqclicert").disable({emitEvent:!1}))}observeValueChanges(){this.securityConfigFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}static{this.ɵfac=function(e){return new(e||eZ)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:eZ,selectors:[["tb-modbus-security-config"]],inputs:{isMaster:"isMaster"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>eZ)),multi:!0},{provide:he,useExisting:c((()=>eZ)),multi:!0}]),t.ɵɵNgOnChangesFeature,t.ɵɵStandaloneFeature],decls:33,vars:21,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-hint","tb-primary-fill"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["tbTruncateWithTooltip","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","required","","formControlName","certfile",3,"placeholder"],[1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["matInput","","name","value","required","","formControlName","keyfile",3,"placeholder"],["translate","",1,"fixed-title-width"],["matInput","","type","password","required","","name","value","formControlName","password",3,"placeholder"],["matSuffix","",1,"tb-flex","no-gap","align-center","fill-height"],[1,"tb-flex","align-center","fill-height"],["class","tb-form-row space-between tb-flex fill-width",4,"ngIf"],["class","tb-form-row",4,"ngIf"],["matInput","","name","value","formControlName","server_hostname",3,"placeholder"],[1,"tb-form-row"],["formControlName","reqclicert",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",2)(5,"div",3),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"span",4),t.ɵɵtext(8,"gateway.client-cert-path"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",5)(10,"mat-form-field",6),t.ɵɵelement(11,"input",7),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(13,"div",2)(14,"div",8),t.ɵɵpipe(15,"translate"),t.ɵɵelementStart(16,"span",4),t.ɵɵtext(17,"gateway.private-key-path"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",5)(19,"mat-form-field",6),t.ɵɵelement(20,"input",9),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"div",2)(23,"div",10),t.ɵɵtext(24,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"div",5)(26,"mat-form-field",6),t.ɵɵelement(27,"input",11),t.ɵɵpipe(28,"translate"),t.ɵɵelementStart(29,"div",12),t.ɵɵelement(30,"tb-toggle-password",13),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(31,QX,7,3,"div",14)(32,JX,5,3,"div",15),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.securityConfigFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,9,"gateway.hints.path-in-os")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,11,"gateway.hints.ca-cert")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(12,13,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(15,15,"gateway.private-key-path")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,17,"gateway.set")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,19,"gateway.set")),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",!n.isMaster),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isMaster))},dependencies:t.ɵɵgetComponentDepsFactory(eZ,[se,_,Wn]),encapsulation:2,changeDetection:d.OnPush})}}function tZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusProtocolLabelsMap.get(e))}}function nZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function iZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",53),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,nZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("host").hasError("required")&&e.slaveConfigFormGroup.get("host").touched)}}function aZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.slaveConfigFormGroup.get("port")))}}function rZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",55),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,aZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.hints.modbus.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",e.portLimits.MIN),t.ɵɵpropertyInterpolate("max",e.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.slaveConfigFormGroup.get("port").hasError("required")||e.slaveConfigFormGroup.get("port").hasError("min")||e.slaveConfigFormGroup.get("port").hasError("max"))&&e.slaveConfigFormGroup.get("port").touched)}}function oZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function sZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",56),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,oZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.serial-port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("serialPort").hasError("required")&&e.slaveConfigFormGroup.get("serialPort").touched)}}function lZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusMethodLabelsMap.get(e))}}function pZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function cZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function dZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusParityLabelsMap.get(e))}}function uZ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",17)(2,"div",18),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4,"gateway.baudrate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",19)(6,"mat-select",57),t.ɵɵtemplate(7,pZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",17)(9,"div",18),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"gateway.bytesize"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",19)(13,"mat-select",58),t.ɵɵtemplate(14,cZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",17)(16,"div",18),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"gateway.stopbits"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"mat-form-field",19),t.ɵɵelement(20,"input",59),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"div",17)(23,"div",18),t.ɵɵpipe(24,"translate"),t.ɵɵtext(25,"gateway.parity"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",19)(27,"mat-select",60),t.ɵɵtemplate(28,dZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(29,"div",36)(30,"mat-slide-toggle",61)(31,"mat-label",39),t.ɵɵpipe(32,"translate"),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.modbus.baudrate")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusBaudrates),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,12,"gateway.hints.modbus.bytesize")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusByteSizes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(17,14,"gateway.hints.modbus.stopbits")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,16,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(24,18,"gateway.hints.modbus.parity")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusParities),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(32,20,"gateway.hints.modbus.strict")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(34,22,"gateway.strict")," ")}}function mZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.unit-id-required"))}function hZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function gZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function fZ(e,n){1&e&&(t.ɵɵelementStart(0,"div",36)(1,"mat-slide-toggle",62)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.send-data-on-change")," "))}function yZ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",63),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Device)("isExpansionMode",!0)}}function vZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function xZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function bZ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",64)(1,"mat-expansion-panel",65)(2,"mat-expansion-panel-header",66)(3,"mat-panel-title")(4,"mat-slide-toggle",67),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(8,"tb-modbus-security-config",68),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("expanded",e.showSecurityControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showSecurityControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,3,"gateway.tls-connection")," ")}}e("ModbusSecurityConfigComponent",eZ),He([I()],eZ.prototype,"isMaster",void 0);class wZ extends gX{constructor(e,t,n,i,a){super(e,t,n,i,a),this.fb=e,this.store=t,this.router=n,this.data=i,this.dialogRef=a}getSlaveResultData(){const{values:e,type:t,serialPort:n,...i}=this.slaveConfigFormGroup.value,a={...i,type:t,...e};return t===ea.Serial&&(a.port=n),a.reportStrategy||delete a.reportStrategy,Y(a),a}addFieldsToFormGroup(){this.slaveConfigFormGroup.addControl("reportStrategy",this.fb.control(null))}static{this.ɵfac=function(e){return new(e||wZ)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:wZ,selectors:[["tb-modbus-slave-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:142,vars:97,consts:[["serialPort",""],["reportStrategy",""],[1,"slaves-config-container"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"tb-form-panel",3,"formGroup"],[1,"stroked","tb-form-panel"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","method"],[4,"ngIf"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","unitId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],["class","tb-form-row",4,"ngIf","ngIfElse"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["formControlName","byteOrder"],["formControlName","wordOrder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],[1,"tb-form-row"],["matInput","","type","number","min","0","name","retries","formControlName","retries",3,"placeholder"],["formControlName","retryOnEmpty",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","retryOnInvalid",1,"mat-slide"],[1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","100","name","value","formControlName","pollPeriod",3,"placeholder"],["translate","",1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","500","name","value","formControlName","connectAttemptTimeMs",3,"placeholder"],["matInput","","type","number","min","1","name","value","formControlName","connectAttemptCount",3,"placeholder"],["matInput","","type","number","min","30000","name","value","formControlName","waitAfterFailedAttemptsMs",3,"placeholder"],["formControlName","values",3,"singleMode","hideNewFields"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matInput","","name","value","formControlName","serialPort",3,"placeholder"],["formControlName","baudrate"],["formControlName","bytesize"],["matInput","","type","number","min","0","name","value","formControlName","stopbits",3,"placeholder"],["formControlName","parity"],["formControlName","strict",1,"mat-slide"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"defaultValue","isExpansionMode"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide","justify-start",3,"click","formControl"],["formControlName","security",1,"security-config"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"mat-toolbar",3)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",4)(6,"div",5),t.ɵɵelementStart(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",7),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",8)(11,"div",9)(12,"div",10)(13,"div",11)(14,"div",12),t.ɵɵtext(15,"gateway.server-connection"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"tb-toggle-select",13),t.ɵɵtemplate(17,tZ,2,2,"tb-toggle-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",10),t.ɵɵtemplate(19,iZ,8,7,"div",15)(20,rZ,8,9,"div",16)(21,sZ,8,7,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(23,"div",17)(24,"div",18),t.ɵɵpipe(25,"translate"),t.ɵɵtext(26," gateway.method "),t.ɵɵelementEnd(),t.ɵɵelementStart(27,"mat-form-field",19)(28,"mat-select",20),t.ɵɵtemplate(29,lZ,2,2,"mat-option",14),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(30,uZ,35,24,"ng-container",21),t.ɵɵelementStart(31,"div",17)(32,"div",22),t.ɵɵpipe(33,"translate"),t.ɵɵtext(34,"gateway.unit-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"mat-form-field",19),t.ɵɵelement(36,"input",23),t.ɵɵpipe(37,"translate"),t.ɵɵtemplate(38,mZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"div",17)(40,"div",25),t.ɵɵtext(41,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"mat-form-field",19),t.ɵɵelement(43,"input",26),t.ɵɵpipe(44,"translate"),t.ɵɵtemplate(45,hZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"div",17)(47,"div",25),t.ɵɵtext(48,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(49,"mat-form-field",19),t.ɵɵelement(50,"input",27),t.ɵɵpipe(51,"translate"),t.ɵɵtemplate(52,gZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵtemplate(53,fZ,5,3,"div",28)(54,yZ,1,2,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(56,"div",29)(57,"mat-expansion-panel",30)(58,"mat-expansion-panel-header")(59,"mat-panel-title")(60,"div",31),t.ɵɵtext(61,"gateway.advanced-connection-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(62,"div",10)(63,"div",17)(64,"div",18),t.ɵɵpipe(65,"translate"),t.ɵɵtext(66,"gateway.connection-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(67,"mat-form-field",19),t.ɵɵelement(68,"input",32),t.ɵɵpipe(69,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(70,"div",17)(71,"div",18),t.ɵɵpipe(72,"translate"),t.ɵɵtext(73,"gateway.byte-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(74,"mat-form-field",19)(75,"mat-select",33),t.ɵɵtemplate(76,vZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(77,"div",17)(78,"div",18),t.ɵɵpipe(79,"translate"),t.ɵɵtext(80,"gateway.word-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(81,"mat-form-field",19)(82,"mat-select",34),t.ɵɵtemplate(83,xZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵtemplate(84,bZ,9,5,"div",35),t.ɵɵelementStart(85,"div",36)(86,"div",18),t.ɵɵpipe(87,"translate"),t.ɵɵtext(88,"gateway.retries"),t.ɵɵelementEnd(),t.ɵɵelementStart(89,"mat-form-field",19),t.ɵɵelement(90,"input",37),t.ɵɵpipe(91,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(92,"div",36)(93,"mat-slide-toggle",38)(94,"mat-label",39),t.ɵɵpipe(95,"translate"),t.ɵɵtext(96),t.ɵɵpipe(97,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(98,"div",36)(99,"mat-slide-toggle",40)(100,"mat-label",39),t.ɵɵpipe(101,"translate"),t.ɵɵtext(102),t.ɵɵpipe(103,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(104,"div",17)(105,"div",41),t.ɵɵpipe(106,"translate"),t.ɵɵelementStart(107,"span",42),t.ɵɵtext(108," gateway.poll-period "),t.ɵɵelementEnd()(),t.ɵɵelementStart(109,"mat-form-field",19),t.ɵɵelement(110,"input",43),t.ɵɵpipe(111,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(112,"div",17)(113,"div",44),t.ɵɵpipe(114,"translate"),t.ɵɵtext(115,"gateway.connect-attempt-time"),t.ɵɵelementEnd(),t.ɵɵelementStart(116,"mat-form-field",19),t.ɵɵelement(117,"input",45),t.ɵɵpipe(118,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(119,"div",17)(120,"div",44),t.ɵɵpipe(121,"translate"),t.ɵɵtext(122,"gateway.connect-attempt-count"),t.ɵɵelementEnd(),t.ɵɵelementStart(123,"mat-form-field",19),t.ɵɵelement(124,"input",46),t.ɵɵpipe(125,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(126,"div",17)(127,"div",44),t.ɵɵpipe(128,"translate"),t.ɵɵtext(129,"gateway.wait-after-failed-attempts"),t.ɵɵelementEnd(),t.ɵɵelementStart(130,"mat-form-field",19),t.ɵɵelement(131,"input",47),t.ɵɵpipe(132,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(133,"div",29),t.ɵɵelement(134,"tb-modbus-values",48),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(135,"div",49)(136,"button",50),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(137),t.ɵɵpipe(138,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(139,"button",51),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(140),t.ɵɵpipe(141,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(22),i=t.ɵɵreference(55);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,45,"gateway.server-slave")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.modbusHelpLink),t.ɵɵadvance(4),t.ɵɵproperty("formGroup",n.slaveConfigFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.modbusProtocolTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(25,47,"gateway.hints.modbus.framer-type")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.protocolType===n.ModbusProtocolType.Serial?n.modbusSerialMethodTypes:n.modbusMethodTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType===n.ModbusProtocolType.Serial),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(33,49,"gateway.hints.modbus.unit-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(37,51,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("unitId").hasError("required")&&n.slaveConfigFormGroup.get("unitId").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(44,53,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceName").hasError("required")&&n.slaveConfigFormGroup.get("deviceName").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(51,55,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceType").hasError("required")&&n.slaveConfigFormGroup.get("deviceType").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.data.hideNewFields)("ngIfElse",i),t.ɵɵadvance(11),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(65,57,"gateway.hints.modbus.connection-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(69,59,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(72,61,"gateway.hints.modbus.byte-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(79,63,"gateway.hints.modbus.word-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(87,65,"gateway.hints.modbus.retries")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(91,67,"gateway.set")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(95,69,"gateway.hints.modbus.retries-on-empty")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(97,71,"gateway.retries-on-empty")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(101,73,"gateway.hints.modbus.retries-on-invalid")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(103,75,"gateway.retries-on-invalid")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(106,77,"gateway.hints.modbus.poll-period")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(111,79,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(114,81,"gateway.hints.modbus.connect-attempt-time")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(118,83,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(121,85,"gateway.hints.modbus.connect-attempt-count")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(125,87,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(128,89,"gateway.hints.modbus.wait-after-failed-attempts")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(132,91,"gateway.set")),t.ɵɵadvance(3),t.ɵɵproperty("singleMode",!0)("hideNewFields",n.data.hideNewFields),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(138,93,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.slaveConfigFormGroup.invalid||!n.slaveConfigFormGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(141,95,n.data.buttonTitle)," ")}},dependencies:t.ɵɵgetComponentDepsFactory(wZ,[se,_,ZX,eZ,u$,ni,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .slaves-config-container[_ngcontent-%COMP%]{width:80vw;max-width:900px}[_nghost-%COMP%] .slave-name-label[_ngcontent-%COMP%]{margin-right:16px;color:#000000de}[_nghost-%COMP%] .fixed-title-width-260[_ngcontent-%COMP%]{min-width:260px}[_nghost-%COMP%] .security-config .fixed-title-width{min-width:230px}'],changeDetection:d.OnPush})}}function SZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusProtocolLabelsMap.get(e))}}function CZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function _Z(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",53),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,CZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("host").hasError("required")&&e.slaveConfigFormGroup.get("host").touched)}}function TZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.slaveConfigFormGroup.get("port")))}}function IZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",55),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,TZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.hints.modbus.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",e.portLimits.MIN),t.ɵɵpropertyInterpolate("max",e.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.slaveConfigFormGroup.get("port").hasError("required")||e.slaveConfigFormGroup.get("port").hasError("min")||e.slaveConfigFormGroup.get("port").hasError("max"))&&e.slaveConfigFormGroup.get("port").touched)}}function EZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function MZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",56),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,EZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.serial-port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("serialPort").hasError("required")&&e.slaveConfigFormGroup.get("serialPort").touched)}}function kZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusMethodLabelsMap.get(e))}}function PZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function DZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function OZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusParityLabelsMap.get(e))}}function AZ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",17)(2,"div",18),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4,"gateway.baudrate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",19)(6,"mat-select",57),t.ɵɵtemplate(7,PZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",17)(9,"div",18),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"gateway.bytesize"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",19)(13,"mat-select",58),t.ɵɵtemplate(14,DZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",17)(16,"div",18),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"gateway.stopbits"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"mat-form-field",19),t.ɵɵelement(20,"input",59),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"div",17)(23,"div",18),t.ɵɵpipe(24,"translate"),t.ɵɵtext(25,"gateway.parity"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",19)(27,"mat-select",60),t.ɵɵtemplate(28,OZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(29,"div",36)(30,"mat-slide-toggle",61)(31,"mat-label",39),t.ɵɵpipe(32,"translate"),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.modbus.baudrate")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusBaudrates),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,12,"gateway.hints.modbus.bytesize")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusByteSizes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(17,14,"gateway.hints.modbus.stopbits")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,16,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(24,18,"gateway.hints.modbus.parity")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusParities),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(32,20,"gateway.hints.modbus.strict")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(34,22,"gateway.strict")," ")}}function FZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.unit-id-required"))}function RZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function BZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function NZ(e,n){1&e&&(t.ɵɵelementStart(0,"div",36)(1,"mat-slide-toggle",62)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.send-data-on-change")," "))}function LZ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",63),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Device)("isExpansionMode",!0)}}function VZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function qZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function GZ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",64)(1,"mat-expansion-panel",65)(2,"mat-expansion-panel-header",66)(3,"mat-panel-title")(4,"mat-slide-toggle",67),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(8,"tb-modbus-security-config",68),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("expanded",e.showSecurityControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showSecurityControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,3,"gateway.tls-connection")," ")}}e("ModbusSlaveDialogComponent",wZ);class zZ extends gX{constructor(e,t,n,i,a){super(e,t,n,i,a),this.fb=e,this.store=t,this.router=n,this.data=i,this.dialogRef=a}getSlaveResultData(){const{values:e,type:t,serialPort:n,...i}=this.slaveConfigFormGroup.value,a={...i,type:t,...e};return t===ea.Serial&&(a.port=n),Y(a),a}addFieldsToFormGroup(){this.slaveConfigFormGroup.addControl("sendDataOnlyOnChange",this.fb.control(!1))}static{this.ɵfac=function(e){return new(e||zZ)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:zZ,selectors:[["tb-modbus-legacy-slave-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:142,vars:97,consts:[["serialPort",""],["reportStrategy",""],[1,"slaves-config-container"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"tb-form-panel",3,"formGroup"],[1,"stroked","tb-form-panel"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","method"],[4,"ngIf"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","unitId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],["class","tb-form-row",4,"ngIf","ngIfElse"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["formControlName","byteOrder"],["formControlName","wordOrder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],[1,"tb-form-row"],["matInput","","type","number","min","0","name","retries","formControlName","retries",3,"placeholder"],["formControlName","retryOnEmpty",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","retryOnInvalid",1,"mat-slide"],[1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","100","name","value","formControlName","pollPeriod",3,"placeholder"],["translate","",1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","500","name","value","formControlName","connectAttemptTimeMs",3,"placeholder"],["matInput","","type","number","min","1","name","value","formControlName","connectAttemptCount",3,"placeholder"],["matInput","","type","number","min","30000","name","value","formControlName","waitAfterFailedAttemptsMs",3,"placeholder"],["formControlName","values",3,"singleMode","hideNewFields"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matInput","","name","value","formControlName","serialPort",3,"placeholder"],["formControlName","baudrate"],["formControlName","bytesize"],["matInput","","type","number","min","0","name","value","formControlName","stopbits",3,"placeholder"],["formControlName","parity"],["formControlName","strict",1,"mat-slide"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"defaultValue","isExpansionMode"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide","justify-start",3,"click","formControl"],["formControlName","security",1,"security-config"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"mat-toolbar",3)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",4)(6,"div",5),t.ɵɵelementStart(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",7),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",8)(11,"div",9)(12,"div",10)(13,"div",11)(14,"div",12),t.ɵɵtext(15,"gateway.server-connection"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"tb-toggle-select",13),t.ɵɵtemplate(17,SZ,2,2,"tb-toggle-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",10),t.ɵɵtemplate(19,_Z,8,7,"div",15)(20,IZ,8,9,"div",16)(21,MZ,8,7,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(23,"div",17)(24,"div",18),t.ɵɵpipe(25,"translate"),t.ɵɵtext(26," gateway.method "),t.ɵɵelementEnd(),t.ɵɵelementStart(27,"mat-form-field",19)(28,"mat-select",20),t.ɵɵtemplate(29,kZ,2,2,"mat-option",14),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(30,AZ,35,24,"ng-container",21),t.ɵɵelementStart(31,"div",17)(32,"div",22),t.ɵɵpipe(33,"translate"),t.ɵɵtext(34,"gateway.unit-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"mat-form-field",19),t.ɵɵelement(36,"input",23),t.ɵɵpipe(37,"translate"),t.ɵɵtemplate(38,FZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"div",17)(40,"div",25),t.ɵɵtext(41,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"mat-form-field",19),t.ɵɵelement(43,"input",26),t.ɵɵpipe(44,"translate"),t.ɵɵtemplate(45,RZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"div",17)(47,"div",25),t.ɵɵtext(48,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(49,"mat-form-field",19),t.ɵɵelement(50,"input",27),t.ɵɵpipe(51,"translate"),t.ɵɵtemplate(52,BZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵtemplate(53,NZ,5,3,"div",28)(54,LZ,1,2,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(56,"div",29)(57,"mat-expansion-panel",30)(58,"mat-expansion-panel-header")(59,"mat-panel-title")(60,"div",31),t.ɵɵtext(61,"gateway.advanced-connection-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(62,"div",10)(63,"div",17)(64,"div",18),t.ɵɵpipe(65,"translate"),t.ɵɵtext(66,"gateway.connection-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(67,"mat-form-field",19),t.ɵɵelement(68,"input",32),t.ɵɵpipe(69,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(70,"div",17)(71,"div",18),t.ɵɵpipe(72,"translate"),t.ɵɵtext(73,"gateway.byte-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(74,"mat-form-field",19)(75,"mat-select",33),t.ɵɵtemplate(76,VZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(77,"div",17)(78,"div",18),t.ɵɵpipe(79,"translate"),t.ɵɵtext(80,"gateway.word-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(81,"mat-form-field",19)(82,"mat-select",34),t.ɵɵtemplate(83,qZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵtemplate(84,GZ,9,5,"div",35),t.ɵɵelementStart(85,"div",36)(86,"div",18),t.ɵɵpipe(87,"translate"),t.ɵɵtext(88,"gateway.retries"),t.ɵɵelementEnd(),t.ɵɵelementStart(89,"mat-form-field",19),t.ɵɵelement(90,"input",37),t.ɵɵpipe(91,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(92,"div",36)(93,"mat-slide-toggle",38)(94,"mat-label",39),t.ɵɵpipe(95,"translate"),t.ɵɵtext(96),t.ɵɵpipe(97,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(98,"div",36)(99,"mat-slide-toggle",40)(100,"mat-label",39),t.ɵɵpipe(101,"translate"),t.ɵɵtext(102),t.ɵɵpipe(103,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(104,"div",17)(105,"div",41),t.ɵɵpipe(106,"translate"),t.ɵɵelementStart(107,"span",42),t.ɵɵtext(108," gateway.poll-period "),t.ɵɵelementEnd()(),t.ɵɵelementStart(109,"mat-form-field",19),t.ɵɵelement(110,"input",43),t.ɵɵpipe(111,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(112,"div",17)(113,"div",44),t.ɵɵpipe(114,"translate"),t.ɵɵtext(115,"gateway.connect-attempt-time"),t.ɵɵelementEnd(),t.ɵɵelementStart(116,"mat-form-field",19),t.ɵɵelement(117,"input",45),t.ɵɵpipe(118,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(119,"div",17)(120,"div",44),t.ɵɵpipe(121,"translate"),t.ɵɵtext(122,"gateway.connect-attempt-count"),t.ɵɵelementEnd(),t.ɵɵelementStart(123,"mat-form-field",19),t.ɵɵelement(124,"input",46),t.ɵɵpipe(125,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(126,"div",17)(127,"div",44),t.ɵɵpipe(128,"translate"),t.ɵɵtext(129,"gateway.wait-after-failed-attempts"),t.ɵɵelementEnd(),t.ɵɵelementStart(130,"mat-form-field",19),t.ɵɵelement(131,"input",47),t.ɵɵpipe(132,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(133,"div",29),t.ɵɵelement(134,"tb-modbus-values",48),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(135,"div",49)(136,"button",50),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(137),t.ɵɵpipe(138,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(139,"button",51),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(140),t.ɵɵpipe(141,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(22),i=t.ɵɵreference(55);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,45,"gateway.server-slave")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.modbusHelpLink),t.ɵɵadvance(4),t.ɵɵproperty("formGroup",n.slaveConfigFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.modbusProtocolTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(25,47,"gateway.hints.modbus.framer-type")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.protocolType===n.ModbusProtocolType.Serial?n.modbusSerialMethodTypes:n.modbusMethodTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType===n.ModbusProtocolType.Serial),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(33,49,"gateway.hints.modbus.unit-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(37,51,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("unitId").hasError("required")&&n.slaveConfigFormGroup.get("unitId").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(44,53,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceName").hasError("required")&&n.slaveConfigFormGroup.get("deviceName").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(51,55,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceType").hasError("required")&&n.slaveConfigFormGroup.get("deviceType").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.data.hideNewFields)("ngIfElse",i),t.ɵɵadvance(11),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(65,57,"gateway.hints.modbus.connection-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(69,59,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(72,61,"gateway.hints.modbus.byte-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(79,63,"gateway.hints.modbus.word-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(87,65,"gateway.hints.modbus.retries")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(91,67,"gateway.set")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(95,69,"gateway.hints.modbus.retries-on-empty")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(97,71,"gateway.retries-on-empty")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(101,73,"gateway.hints.modbus.retries-on-invalid")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(103,75,"gateway.retries-on-invalid")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(106,77,"gateway.hints.modbus.poll-period")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(111,79,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(114,81,"gateway.hints.modbus.connect-attempt-time")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(118,83,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(121,85,"gateway.hints.modbus.connect-attempt-count")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(125,87,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(128,89,"gateway.hints.modbus.wait-after-failed-attempts")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(132,91,"gateway.set")),t.ɵɵadvance(3),t.ɵɵproperty("singleMode",!0)("hideNewFields",n.data.hideNewFields),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(138,93,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.slaveConfigFormGroup.invalid||!n.slaveConfigFormGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(141,95,n.data.buttonTitle)," ")}},dependencies:t.ɵɵgetComponentDepsFactory(zZ,[se,_,ZX,eZ,u$,ni]),styles:['@charset "UTF-8";[_nghost-%COMP%] .slaves-config-container[_ngcontent-%COMP%]{width:80vw;max-width:900px}[_nghost-%COMP%] .slave-name-label[_ngcontent-%COMP%]{margin-right:16px;color:#000000de}[_nghost-%COMP%] .fixed-title-width-260[_ngcontent-%COMP%]{min-width:260px}[_nghost-%COMP%] .security-config .fixed-title-width{min-width:230px}'],changeDetection:d.OnPush})}}function UZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusProtocolLabelsMap.get(e))}}function jZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function HZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",14),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",39),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,jZ,3,3,"mat-icon",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("host").hasError("required")&&e.slaveConfigFormGroup.get("host").touched)}}function WZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.slaveConfigFormGroup.get("port")))}}function $Z(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",14),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",41),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,WZ,3,3,"mat-icon",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.hints.modbus.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",e.portLimits.MIN),t.ɵɵpropertyInterpolate("max",e.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.slaveConfigFormGroup.get("port").hasError("required")||e.slaveConfigFormGroup.get("port").hasError("min")||e.slaveConfigFormGroup.get("port").hasError("max"))&&e.slaveConfigFormGroup.get("port").touched)}}function KZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function YZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",14),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",42),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,KZ,3,3,"mat-icon",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.serial-port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("port").hasError("required")&&e.slaveConfigFormGroup.get("port").touched)}}function XZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusMethodLabelsMap.get(e))}}function ZZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.unit-id-required"))}function QZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function JZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function eQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function tQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",11),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.baudrate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12)(5,"mat-select",43),t.ɵɵtemplate(6,eQ,2,2,"mat-option",6),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,2,"gateway.hints.modbus.baudrate")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusBaudrates)}}function nQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function iQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function aQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",44)(1,"mat-expansion-panel",45)(2,"mat-expansion-panel-header",46)(3,"mat-panel-title")(4,"mat-slide-toggle",47),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(8,"tb-modbus-security-config",48),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("expanded",e.showSecurityControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showSecurityControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,3,"gateway.tls-connection")," ")}}e("ModbusLegacySlaveDialogComponent",zZ);class rQ{constructor(e){this.fb=e,this.ModbusProtocolLabelsMap=ha,this.ModbusMethodLabelsMap=ma,this.portLimits=Oi,this.modbusProtocolTypes=Object.values(ea),this.modbusMethodTypes=Object.values(ta),this.modbusSerialMethodTypes=Object.values(na),this.modbusOrderType=Object.values(aa),this.ModbusProtocolType=ea,this.modbusBaudrates=sa,this.isSlaveEnabled=!1,this.serialSpecificControlKeys=["serialPort","baudrate"],this.tcpUdpSpecificControlKeys=["port","security","host"],this.destroy$=new we,this.showSecurityControl=this.fb.control(!1),this.slaveConfigFormGroup=this.fb.group({type:[ea.TCP],host:["",[me.required,me.pattern(pn)]],port:[null,[me.required,me.min(Oi.MIN),me.max(Oi.MAX)]],serialPort:["",[me.required,me.pattern(pn)]],method:[ta.SOCKET],unitId:[null,[me.required]],baudrate:[this.modbusBaudrates[0]],deviceName:["",[me.required,me.pattern(pn)]],deviceType:["",[me.required,me.pattern(pn)]],pollPeriod:[1e3,[me.required]],sendDataToThingsBoard:[!1],byteOrder:[aa.BIG],wordOrder:[aa.BIG],security:[],identity:this.fb.group({vendorName:["",[me.pattern(pn)]],productCode:["",[me.pattern(pn)]],vendorUrl:["",[me.pattern(pn)]],productName:["",[me.pattern(pn)]],modelName:["",[me.pattern(pn)]]}),values:[]}),this.observeValueChanges(),this.observeTypeChange(),this.observeShowSecurity()}get protocolType(){return this.slaveConfigFormGroup.get("type").value}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.slaveConfigFormGroup.valid?null:{slaveConfigFormGroup:{valid:!1}}}writeValue(e){this.showSecurityControl.patchValue(!!e.security&&!W(e.security,{})),this.updateSlaveConfig(e)}setDisabledState(e){this.isSlaveEnabled=!e,this.updateFormEnableState()}observeValueChanges(){this.slaveConfigFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{e.type===ea.Serial&&(e.port=e.serialPort,delete e.serialPort),this.onChange(e),this.onTouched()}))}observeTypeChange(){this.slaveConfigFormGroup.get("type").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.updateFormEnableState(),this.updateMethodType(e)}))}updateMethodType(e){this.slaveConfigFormGroup.get("method").value!==ta.RTU&&this.slaveConfigFormGroup.get("method").patchValue(e===ea.Serial?na.ASCII:ta.SOCKET,{emitEvent:!1})}updateFormEnableState(){this.isSlaveEnabled?(this.slaveConfigFormGroup.enable({emitEvent:!1}),this.showSecurityControl.enable({emitEvent:!1})):(this.slaveConfigFormGroup.disable({emitEvent:!1}),this.showSecurityControl.disable({emitEvent:!1})),this.updateEnablingByProtocol(),this.updateSecurityEnable(this.showSecurityControl.value)}observeShowSecurity(){this.showSecurityControl.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>this.updateSecurityEnable(e)))}updateSecurityEnable(e){e&&this.isSlaveEnabled&&this.protocolType!==ea.Serial?this.slaveConfigFormGroup.get("security").enable({emitEvent:!1}):this.slaveConfigFormGroup.get("security").disable({emitEvent:!1})}updateEnablingByProtocol(){const e=this.protocolType===ea.Serial,t=e?this.serialSpecificControlKeys:this.tcpUdpSpecificControlKeys,n=e?this.tcpUdpSpecificControlKeys:this.serialSpecificControlKeys;this.isSlaveEnabled&&t.forEach((e=>this.slaveConfigFormGroup.get(e)?.enable({emitEvent:!1}))),n.forEach((e=>this.slaveConfigFormGroup.get(e)?.disable({emitEvent:!1})))}updateSlaveConfig(e){const{vendorName:t="",productCode:n="",vendorUrl:i="",productName:a="",modelName:r=""}=e.identity??{},o={vendorName:t,productCode:n,vendorUrl:i,productName:a,modelName:r},{type:s=ea.TCP,method:l=ta.RTU,unitId:p=0,deviceName:c="",deviceType:d="",pollPeriod:u=1e3,sendDataToThingsBoard:m=!1,byteOrder:h=aa.BIG,wordOrder:g=aa.BIG,security:f={},values:y={},baudrate:v=this.modbusBaudrates[0],host:x="",port:b=null}=e,w={type:s,method:l,unitId:p,deviceName:c,deviceType:d,pollPeriod:u,sendDataToThingsBoard:!!m,byteOrder:h,wordOrder:g,security:f,identity:o,values:y,baudrate:v,host:s===ea.Serial?"":x,port:s===ea.Serial?null:b,serialPort:s===ea.Serial?b:""};this.slaveConfigFormGroup.setValue(w,{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||rQ)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:rQ,selectors:[["tb-modbus-slave-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>rQ)),multi:!0},{provide:he,useExisting:c((()=>rQ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:112,vars:59,consts:[["serialPort",""],[1,"slave-container",3,"formGroup"],[1,"slave-content","tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-panel","no-border","no-padding","padding-top"],["class","tb-form-row column-xs",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","method"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","unitId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","100","name","value","formControlName","pollPeriod",3,"placeholder"],[1,"tb-form-row"],["formControlName","sendDataToThingsBoard",1,"mat-slide"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],["formControlName","byteOrder"],["formControlName","wordOrder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],[3,"formGroup"],["matInput","","name","value","formControlName","vendorName",3,"placeholder"],["matInput","","name","value","formControlName","productCode",3,"placeholder"],["matInput","","name","value","formControlName","vendorUrl",3,"placeholder"],["matInput","","name","value","formControlName","productName",3,"placeholder"],["matInput","","name","value","formControlName","modelName",3,"placeholder"],["formControlName","values"],[3,"value"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matInput","","name","value","formControlName","serialPort",3,"placeholder"],["formControlName","baudrate"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide",3,"click","formControl"],["formControlName","security"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"div",2)(2,"div",3)(3,"div",4),t.ɵɵtext(4,"gateway.server-slave-config"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",5),t.ɵɵtemplate(6,UZ,2,2,"tb-toggle-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",7),t.ɵɵtemplate(8,HZ,8,7,"div",8)(9,$Z,8,9,"div",9)(10,YZ,8,7,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",10)(13,"div",11),t.ɵɵpipe(14,"translate"),t.ɵɵtext(15," gateway.method "),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-form-field",12)(17,"mat-select",13),t.ɵɵtemplate(18,XZ,2,2,"mat-option",6),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(19,"div",10)(20,"div",14),t.ɵɵpipe(21,"translate"),t.ɵɵtext(22,"gateway.unit-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",12),t.ɵɵelement(24,"input",15),t.ɵɵpipe(25,"translate"),t.ɵɵtemplate(26,ZZ,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(27,"div",10)(28,"div",17),t.ɵɵtext(29,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",12),t.ɵɵelement(31,"input",18),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,QZ,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"div",10)(35,"div",17),t.ɵɵtext(36,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"mat-form-field",12),t.ɵɵelement(38,"input",19),t.ɵɵpipe(39,"translate"),t.ɵɵtemplate(40,JZ,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(41,"div",10)(42,"div",20),t.ɵɵpipe(43,"translate"),t.ɵɵelementStart(44,"span",21),t.ɵɵtext(45," gateway.poll-period "),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"mat-form-field",12),t.ɵɵelement(47,"input",22),t.ɵɵpipe(48,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(49,tQ,7,4,"div",8),t.ɵɵelementStart(50,"div",23)(51,"mat-slide-toggle",24)(52,"mat-label"),t.ɵɵtext(53),t.ɵɵpipe(54,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(55,"div",25)(56,"mat-expansion-panel",26)(57,"mat-expansion-panel-header")(58,"mat-panel-title")(59,"div",27),t.ɵɵtext(60,"gateway.advanced-connection-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(61,"div",7)(62,"div",10)(63,"div",11),t.ɵɵpipe(64,"translate"),t.ɵɵtext(65,"gateway.byte-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(66,"mat-form-field",12)(67,"mat-select",28),t.ɵɵtemplate(68,nQ,2,2,"mat-option",6),t.ɵɵelementEnd()()(),t.ɵɵelementStart(69,"div",10)(70,"div",11),t.ɵɵpipe(71,"translate"),t.ɵɵtext(72,"gateway.word-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(73,"mat-form-field",12)(74,"mat-select",29),t.ɵɵtemplate(75,iQ,2,2,"mat-option",6),t.ɵɵelementEnd()()(),t.ɵɵtemplate(76,aQ,9,5,"div",30),t.ɵɵelementContainerStart(77,31),t.ɵɵelementStart(78,"div",10)(79,"div",4),t.ɵɵtext(80,"gateway.vendor-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(81,"mat-form-field",12),t.ɵɵelement(82,"input",32),t.ɵɵpipe(83,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(84,"div",10)(85,"div",4),t.ɵɵtext(86,"gateway.product-code"),t.ɵɵelementEnd(),t.ɵɵelementStart(87,"mat-form-field",12),t.ɵɵelement(88,"input",33),t.ɵɵpipe(89,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(90,"div",10)(91,"div",4),t.ɵɵtext(92,"gateway.vendor-url"),t.ɵɵelementEnd(),t.ɵɵelementStart(93,"mat-form-field",12),t.ɵɵelement(94,"input",34),t.ɵɵpipe(95,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(96,"div",10)(97,"div",4),t.ɵɵtext(98,"gateway.product-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(99,"mat-form-field",12),t.ɵɵelement(100,"input",35),t.ɵɵpipe(101,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(102,"div",10)(103,"div",4),t.ɵɵtext(104,"gateway.model-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(105,"mat-form-field",12),t.ɵɵelement(106,"input",36),t.ɵɵpipe(107,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()(),t.ɵɵelementStart(108,"div",25)(109,"div",27),t.ɵɵtext(110,"gateway.values"),t.ɵɵelementEnd(),t.ɵɵelement(111,"tb-modbus-values",37),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵreference(11);t.ɵɵproperty("formGroup",n.slaveConfigFormGroup),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.modbusProtocolTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(14,29,"gateway.hints.modbus.framer-type")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.protocolType===n.ModbusProtocolType.Serial?n.modbusSerialMethodTypes:n.modbusMethodTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,31,"gateway.hints.modbus.unit-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(25,33,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("unitId").hasError("required")&&n.slaveConfigFormGroup.get("unitId").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,35,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceName").hasError("required")&&n.slaveConfigFormGroup.get("deviceName").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(39,37,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceType").hasError("required")&&n.slaveConfigFormGroup.get("deviceType").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(43,39,"gateway.hints.modbus.poll-period")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(48,41,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType===n.ModbusProtocolType.Serial),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(54,43,"gateway.send-data-to-platform")," "),t.ɵɵadvance(10),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(64,45,"gateway.hints.modbus.byte-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(71,47,"gateway.hints.modbus.word-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.slaveConfigFormGroup.get("identity")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(83,49,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(89,51,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(95,53,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(101,55,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(107,57,"gateway.set"))}},dependencies:t.ɵɵgetComponentDepsFactory(rQ,[se,_,ZX,eZ,u$,Wn]),encapsulation:2,changeDetection:d.OnPush})}}e("ModbusSlaveConfigComponent",rQ);const oQ=["searchInput"],sQ=()=>["deviceName","info","unitId","type","actions"],lQ=()=>({minWidth:"96px",textAlign:"center"});function pQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",9)(1,"div",28)(2,"span",29),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",11),t.ɵɵelementStart(6,"button",13),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageSlave(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",13),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.servers-slaves")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search")))}function cQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.device-name")))}function dQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.deviceName)}}function uQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.info")," "))}function mQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){let e;const i=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(null!==(e=i.host)&&void 0!==e?e:i.port)}}function hQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.unit-id")," "))}function gQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.unitId)}}function fQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30)(1,"div"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.type")))}function yQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.ModbusProtocolLabelsMap.get(e.type)," ")}}function vQ(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",32)}function xQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageSlave(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",13),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteSlave(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function bQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,xQ,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",33),t.ɵɵelementContainer(4,34),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",35)(6,"button",36),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",37),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",38,2),t.ɵɵelementContainer(11,34),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,lQ)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function wQ(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",39)}function SQ(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class CQ{constructor(e,t,n,i,a){this.translate=e,this.dialog=t,this.dialogService=n,this.fb=i,this.cdr=a,this.isLegacy=!1,this.textSearchMode=!1,this.textSearch=this.fb.control("",{nonNullable:!0}),this.ModbusProtocolLabelsMap=ha,this.onChange=()=>{},this.onTouched=()=>{},this.destroy$=new we,this.masterFormGroup=this.fb.group({slaves:this.fb.array([])}),this.dataSource=new _Q}get slaves(){return this.masterFormGroup.get("slaves")}ngOnInit(){this.masterFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.updateTableData(e.slaves),this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(De(150),Pe(((e,t)=>(e??"")===t.trim())),Me(this.destroy$)).subscribe((e=>this.updateTableData(this.slaves.value,e.trim())))}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){this.slaves.clear(),this.pushDataAsFormArrays(e.slaves)}enterFilterMode(){this.textSearchMode=!0,this.cdr.detectChanges();const e=this.searchInputField.nativeElement;e.focus(),e.setSelectionRange(0,0)}exitFilterMode(){this.updateTableData(this.slaves.value),this.textSearchMode=!1,this.textSearch.reset()}manageSlave(e,t){e&&e.stopPropagation();const n=J(t),i=n?this.slaves.at(t).value:{};this.getSlaveDialog(i,n?"action.apply":"action.add").afterClosed().pipe(Ne(1),Me(this.destroy$)).subscribe((e=>{e&&(n?this.slaves.at(t).patchValue(e):this.slaves.push(this.fb.control(e)),this.masterFormGroup.markAsDirty())}))}getSlaveDialog(e,t){return this.isLegacy?this.dialog.open(zZ,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,hideNewFields:!0,buttonTitle:t}}):this.dialog.open(wZ,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,hideNewFields:!1}})}deleteSlave(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-slave-title",{name:this.slaves.controls[t].value.deviceName}),this.translate.instant("gateway.delete-slave-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(Ne(1),Me(this.destroy$)).subscribe((e=>{e&&(this.slaves.removeAt(t),this.masterFormGroup.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.slaves.push(this.fb.control(e))))}static{this.ɵfac=function(e){return new(e||CQ)(t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(K.DialogService),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:CQ,selectors:[["tb-modbus-master-table"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(oQ,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{isLegacy:"isLegacy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>CQ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:55,vars:41,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-master-table","tb-absolute-fill"],[1,"tb-form-panel","no-border","no-padding","padding-top","hint-container"],["tbTruncateWithTooltip","",1,"tb-form-hint","tb-primary-fill","tb-flex"],[1,"tb-master-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","table-value-column",4,"matHeaderCellDef"],["class","table-value-column",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-master-table-title"],[1,"table-value-column"],["tbTruncateWithTooltip",""],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"div",5),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"div",6)(6,"mat-toolbar",7),t.ɵɵtemplate(7,pQ,14,9,"div",8),t.ɵɵpipe(8,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-toolbar",7)(10,"div",9)(11,"button",10),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"mat-icon"),t.ɵɵtext(14,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-form-field",11)(16,"mat-label"),t.ɵɵtext(17," "),t.ɵɵelementEnd(),t.ɵɵelement(18,"input",12,0),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"button",13),t.ɵɵpipe(22,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(23,"mat-icon"),t.ɵɵtext(24,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(25,"div",14)(26,"table",15),t.ɵɵelementContainerStart(27,16),t.ɵɵtemplate(28,cQ,4,3,"mat-header-cell",17)(29,dQ,3,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(30,16),t.ɵɵtemplate(31,uQ,3,3,"mat-header-cell",17)(32,mQ,3,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(33,16),t.ɵɵtemplate(34,hQ,3,3,"mat-header-cell",17)(35,gQ,3,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(36,16),t.ɵɵtemplate(37,fQ,4,3,"mat-header-cell",17)(38,yQ,2,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(39,19),t.ɵɵtemplate(40,vQ,1,0,"mat-header-cell",20)(41,bQ,12,6,"mat-cell",21),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(42,wQ,1,0,"mat-header-row",22)(43,SQ,1,0,"mat-row",23),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"section",24),t.ɵɵpipe(45,"async"),t.ɵɵelementStart(46,"button",25),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageSlave(i))})),t.ɵɵelementStart(47,"mat-icon",26),t.ɵɵtext(48,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(49,"span"),t.ɵɵtext(50),t.ɵɵpipe(51,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(52,"span",27),t.ɵɵpipe(53,"async"),t.ɵɵtext(54," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,23,"gateway.hints.modbus-master")),t.ɵɵadvance(3),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(8,25,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(12,27,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(20,29,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(22,31,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","deviceName"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","info"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","unitId"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","type"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(39,sQ))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(40,sQ)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(45,33,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(51,35,"gateway.add-slave")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(53,37,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(CQ,[se,_,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%]{width:100%;height:calc(100% - 60px);background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .tb-master-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:15%}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-master-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{z-index:1000}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}e("ModbusMasterTableComponent",CQ),He([ot()],CQ.prototype,"isLegacy",void 0);class _Q extends G{constructor(){super()}}e("SlavesDatasource",_Q);class TQ extends Ua{constructor(){super(),this.enableSlaveControl=new fe(!1),this.enableSlaveControl.valueChanges.pipe(In()).subscribe((e=>{this.updateSlaveEnabling(e),this.basicFormGroup.get("slave").updateValueAndValidity({emitEvent:!!this.onChange})}))}writeValue(e){super.writeValue(e),this.onEnableSlaveControl(e)}validate(){const{master:e,slave:t}=this.basicFormGroup.value,n=!e?.slaves?.length&&(W(t,{})||!t);return!this.basicFormGroup.valid||n?{basicFormGroup:{valid:!1}}:null}initBasicFormGroup(){return this.fb.group({master:[],slave:[]})}updateSlaveEnabling(e){e?this.basicFormGroup.get("slave").enable({emitEvent:!1}):this.basicFormGroup.get("slave").disable({emitEvent:!1})}onEnableSlaveControl(e){this.enableSlaveControl.setValue(!!e.slave&&!W(e.slave,{}))}static{this.ɵfac=function(e){return new(e||TQ)}}static{this.ɵdir=t.ɵɵdefineDirective({type:TQ,features:[t.ɵɵInheritDefinitionFeature]})}}e("ModbusBasicConfigDirective",TQ);class IQ extends TQ{constructor(){super(...arguments),this.isLegacy=!1}mapConfigToFormValue({master:e,slave:t}){return{master:e?.slaves?e:{slaves:[]},slave:t??{}}}getMappedValue(e){return{master:e.master,slave:e.slave}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(IQ)))(n||IQ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:IQ,selectors:[["tb-modbus-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>IQ)),multi:!0},{provide:he,useExisting:c((()=>IQ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:19,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","master",3,"isLegacy"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-form-hint","tb-primary-fill","tb-flex","center"],[1,"tb-form-row"],[1,"mat-slide",3,"formControl"],["formControlName","slave"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-modbus-master-table",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4)(10,"div",5),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",6)(14,"mat-slide-toggle",7)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(18,"tb-modbus-slave-config",8),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(5,11,"gateway.master-connections")),t.ɵɵadvance(2),t.ɵɵproperty("isLegacy",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(8,13,"gateway.server-config")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,15,"gateway.hints.modbus-server")),t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.enableSlaveControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,17,"gateway.enable")," "))},dependencies:t.ɵɵgetComponentDepsFactory(IQ,[se,_,rQ,CQ,$n]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}'],changeDetection:d.OnPush})}}e("ModbusBasicConfigComponent",IQ);class EQ extends TQ{constructor(){super(...arguments),this.isLegacy=!0}mapConfigToFormValue(e){return{master:e.master?.slaves?e.master:{slaves:[]},slave:e.slave?Xa.mapSlaveToUpgradedVersion(e.slave):{}}}getMappedValue(e){return{master:e.master,slave:e.slave?Xa.mapSlaveToDowngradedVersion(e.slave):{}}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(EQ)))(n||EQ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:EQ,selectors:[["tb-modbus-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>EQ)),multi:!0},{provide:he,useExisting:c((()=>EQ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:19,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","master",3,"isLegacy"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-form-hint","tb-primary-fill","tb-flex","center"],[1,"tb-form-row"],[1,"mat-slide",3,"formControl"],["formControlName","slave"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-modbus-master-table",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4)(10,"div",5),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",6)(14,"mat-slide-toggle",7)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(18,"tb-modbus-slave-config",8),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(5,11,"gateway.master-connections")),t.ɵɵadvance(2),t.ɵɵproperty("isLegacy",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(8,13,"gateway.server-config")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,15,"gateway.hints.modbus-server")),t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.enableSlaveControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,17,"gateway.enable")," "))},dependencies:t.ɵɵgetComponentDepsFactory(EQ,[se,_,rQ,CQ,$n]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}'],changeDetection:d.OnPush})}}e("ModbusLegacyBasicConfigComponent",EQ);const MQ=()=>({maxWidth:"970px"});function kQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",20),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.get("key").value," ")}}function PQ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate(e.get("methodRPC").value)}}function DQ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate(e.get("attributeOnThingsBoard").value)}}function OQ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate2(" ",e.get("requestExpression").value+" - ","",e.get("attributeNameExpression").value," ")}}function AQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21),t.ɵɵtemplate(1,PQ,2,1,"ng-container",22)(2,DQ,2,1,"ng-container",22)(3,OQ,2,2,"ng-container",22),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("ngSwitch",e.keysType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.SocketValueKey.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.SocketValueKey.ATTRIBUTES_UPDATES),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.SocketValueKey.ATTRIBUTES_REQUESTS)}}function FQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function RQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function BQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function NQ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",42),2&e){const e=t.ɵɵnextContext(5);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Key)}}function LQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",24)(2,"div",25),t.ɵɵtext(3,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",26)(5,"div",27),t.ɵɵpipe(6,"translate"),t.ɵɵpipe(7,"translate"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"div",28)(11,"mat-form-field",29),t.ɵɵelement(12,"input",30),t.ɵɵpipe(13,"translate"),t.ɵɵtemplate(14,FQ,3,3,"mat-icon",31),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(15,"div",24)(16,"div",25),t.ɵɵtext(17,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"div",26)(19,"div",32)(20,"span",33),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(23,"div",34),t.ɵɵelementEnd(),t.ɵɵelementStart(24,"label",35),t.ɵɵtext(25,"from"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",36),t.ɵɵelement(27,"input",37),t.ɵɵpipe(28,"translate"),t.ɵɵtemplate(29,RQ,3,3,"mat-icon",31),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"label",38),t.ɵɵtext(31,"to"),t.ɵɵelementEnd(),t.ɵɵelementStart(32,"mat-form-field",36),t.ɵɵelement(33,"input",39),t.ɵɵpipe(34,"translate"),t.ɵɵtemplate(35,BQ,3,3,"mat-icon",31),t.ɵɵelementEnd()(),t.ɵɵtemplate(36,NQ,1,2,"tb-report-strategy",40),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",n.keysType===n.SocketValueKey.ATTRIBUTES?t.ɵɵpipeBind1(6,12,"gateway.hints.socket.key-attribute"):t.ɵɵpipeBind1(7,14,"gateway.hints.socket.key-telemetry")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,16,"gateway.key")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(13,18,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("key").hasError("required")&&e.get("key").touched),t.ɵɵadvance(7),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,20,"gateway.byte")),t.ɵɵadvance(2),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/byte_fn")("tb-help-popup-style",t.ɵɵpureFunction0(26,MQ)),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,22,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("byteFrom").hasError("required")&&e.get("byteFrom").touched),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(34,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("byteTo").hasError("required")&&e.get("byteTo").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.withReportStrategy&&(n.keysType===n.SocketValueKey.ATTRIBUTES||n.keysType===n.SocketValueKey.TIMESERIES))}}function VQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-required"))}function qQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}function GQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",26)(2,"div",43),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",28)(6,"mat-form-field",29),t.ɵɵelement(7,"input",44),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,VQ,3,3,"mat-icon",31),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",26)(11,"div",45),t.ɵɵpipe(12,"translate"),t.ɵɵtext(13," gateway.encoding "),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",28)(15,"mat-form-field",29)(16,"mat-select",46),t.ɵɵtemplate(17,qQ,2,2,"mat-option",47),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(18,"div",48)(19,"mat-slide-toggle",49),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(20,"mat-label",50),t.ɵɵpipe(21,"translate"),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,7,"gateway.method-name")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("methodRPC").hasError("required")&&e.get("methodRPC").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(12,11,"gateway.hints.encoding")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,13,"gateway.hints.socket.with-response")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(23,15,"gateway.rpc.withResponse")," ")}}function zQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e)," ")}}function UQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e)," ")}}function jQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.request-expression-required"))}function HQ(e,n){1&e&&t.ɵɵelement(0,"div",34),2&e&&t.ɵɵproperty("tb-help-popup","widget/lib/gateway/request-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(2,MQ))}function WQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e)," ")}}function $Q(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.attribute-name-expression-required"))}function KQ(e,n){1&e&&t.ɵɵelement(0,"div",34),2&e&&t.ɵɵproperty("tb-help-popup","widget/lib/gateway/attribute-name-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(2,MQ))}function YQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",26)(2,"div",45),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4," gateway.type "),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",28)(6,"mat-form-field",29)(7,"mat-select",52),t.ɵɵtemplate(8,zQ,3,4,"mat-option",47),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(9,"div",26)(10,"div",43),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",53)(14,"mat-form-field",29)(15,"mat-select",54),t.ɵɵtemplate(16,UQ,3,4,"mat-option",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"mat-form-field",29),t.ɵɵelement(18,"input",55),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,jQ,3,3,"mat-icon",31)(21,HQ,1,3,"div",56),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"div",26)(23,"div",43),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"div",53)(27,"mat-form-field",29)(28,"mat-select",57),t.ɵɵtemplate(29,WQ,3,4,"mat-option",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"mat-form-field",29),t.ɵɵelement(31,"input",58),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,$Q,3,3,"mat-icon",31)(34,KQ,1,3,"div",56),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,12,"gateway.hints.socket.attribute-requests-type")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.requestsType),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,14,"gateway.request-expression")," "),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.expressionType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,16,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("requestExpression").hasError("required")&&e.get("requestExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("requestExpressionSource").value===n.ExpressionType.Expression),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,18,"gateway.attribute-name-expression")," "),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.expressionType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("attributeNameExpression").hasError("required")&&e.get("attributeNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("attributeNameExpressionSource").value===n.ExpressionType.Expression)}}function XQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}function ZQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.socket.attribute-on-platform-required"))}function QQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",26)(2,"div",45),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4," gateway.encoding "),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",28)(6,"mat-form-field",29)(7,"mat-select",46),t.ɵɵtemplate(8,XQ,2,2,"mat-option",47),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(9,"div",26)(10,"div",43),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",28)(14,"mat-form-field",29),t.ɵɵelement(15,"input",59),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,ZQ,3,3,"mat-icon",31),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,5,"gateway.hints.encoding")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,7,"gateway.attribute-on-platform")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("attributeOnThingsBoard").hasError("required")&&e.get("attributeOnThingsBoard").touched)}}function JQ(e,n){if(1&e&&t.ɵɵtemplate(0,LQ,37,27,"div",23)(1,GQ,24,17,"div",23)(2,YQ,35,22,"div",23)(3,QQ,18,11,"div",23),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.TIMESERIES||e.keysType===e.SocketValueKey.ATTRIBUTES),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.ATTRIBUTES_REQUESTS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.ATTRIBUTES_UPDATES)}}function eJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",12)(1,"div",13),t.ɵɵelementContainerStart(2,14),t.ɵɵelementStart(3,"mat-expansion-panel",15)(4,"mat-expansion-panel-header",16)(5,"mat-panel-title"),t.ɵɵtemplate(6,kQ,2,1,"div",17)(7,AQ,4,4,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()(),t.ɵɵtemplate(9,JQ,4,4,"ng-template",18),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",19),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.last,a=t.ɵɵreference(8),r=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",r.keysType===r.SocketValueKey.TIMESERIES||r.keysType===r.SocketValueKey.ATTRIBUTES)("ngIfElse",a),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,r.deleteKeyTitle))}}function tJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10),t.ɵɵtemplate(1,eJ,14,7,"div",11),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.keysListFormArray.controls)("ngForTrackBy",e.trackByKey)}}function nJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",60)(1,"span",61),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class iJ extends q{constructor(e,t,n){super(n),this.fb=e,this.popover=t,this.store=n,this.withReportStrategy=!0,this.keysDataApplied=new u,this.SocketValueKey=hi,this.socketEncoding=Object.values(ht),this.requestsType=Object.values(fi),this.expressionType=Object.values(yi),this.ExpressionType=yi,this.ReportStrategyDefaultValue=on}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}trackByKey(e,t){return t}addKey(){let e;e=this.keysType===hi.RPC_METHODS?this.fb.group({methodRPC:["",[me.required]],encoding:[ht.UTF16,[me.required]],withResponse:[!0]}):this.keysType===hi.ATTRIBUTES_UPDATES?this.fb.group({encoding:[ht.UTF16,[me.required]],attributeOnThingsBoard:["",[me.required]]}):this.keysType===hi.ATTRIBUTES_REQUESTS?this.fb.group({type:[fi.Shared],requestExpressionSource:[yi.Constant],attributeNameExpressionSource:[yi.Constant],requestExpression:["",[me.required]],attributeNameExpression:["",[me.required]]}):this.fb.group({key:["",[me.required,me.pattern(pn)]],byteFrom:[0,[me.required]],byteTo:[0,[me.required]],reportStrategy:[{value:null,disabled:this.isReportStrategyDisabled()}]}),this.keysListFormArray.push(e)}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){let e=this.keysListFormArray.value.map((({reportStrategy:e,...t})=>({...t,...e&&{reportStrategy:e}})));this.keysDataApplied.emit(e)}prepareKeysFormArray(e){const t=[];return e?.forEach((e=>{let n;if(this.keysType===hi.RPC_METHODS){const t=e;n=this.fb.group({methodRPC:[t.methodRPC,[me.required]],encoding:[t.encoding,[me.required]],withResponse:[t.withResponse]})}else if(this.keysType===hi.ATTRIBUTES_REQUESTS){const t=e;n=this.fb.group({type:[t.type??fi.Shared],requestExpressionSource:[t.requestExpressionSource??yi.Constant],attributeNameExpressionSource:[t.attributeNameExpressionSource??yi.Constant],requestExpression:[t.requestExpression,[me.required]],attributeNameExpression:[t.attributeNameExpression,[me.required]]})}else if(this.keysType===hi.ATTRIBUTES_UPDATES)n=this.fb.group({encoding:[e.encoding??ht.UTF16],attributeOnThingsBoard:[e.attributeOnThingsBoard,[me.required]]});else{const{key:t,byteFrom:i,byteTo:a,reportStrategy:r}=e;n=this.fb.group({key:[t,[me.required,me.pattern(pn)]],byteFrom:[i??0,[me.required]],byteTo:[a??0,[me.required]],reportStrategy:[{value:r,disabled:this.isReportStrategyDisabled()}]})}t.push(n)})),this.fb.array(t)}isReportStrategyDisabled(){return!(this.withReportStrategy&&(this.keysType===hi.ATTRIBUTES||this.keysType===hi.TIMESERIES))}static{this.ɵfac=function(e){return new(e||iJ)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverComponent),t.ɵɵdirectiveInject(Xe.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:iJ,selectors:[["tb-device-data-keys-panel"]],inputs:{panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keys:"keys",keysType:"keysType",withReportStrategy:"withReportStrategy"},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:16,consts:[["noKeys",""],["valueTitle",""],[1,"tb-device-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],["class","title-container",4,"ngIf","ngIfElse"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"title-container"],[1,"title-container",3,"ngSwitch"],[4,"ngSwitchCase"],["class","tb-form-panel no-border no-padding",4,"ngIf"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],[1,"fixed-title-width","tb-flex","align-center"],[1,"tb-required"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["for","byteFrom",1,"tb-small-label"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","flex-1"],["matInput","","required","","formControlName","byteFrom","type","number","id","byteFrom",3,"placeholder"],["for","byteTo",1,"tb-small-label"],["matInput","","required","","formControlName","byteTo","type","number","id","byteTo",3,"placeholder"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue"],[1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","methodRPC",3,"placeholder"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","encoding"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-row"],["formControlName","withResponse",1,"mat-slide",3,"click"],[3,"tb-hint-tooltip-icon"],[3,"value"],["formControlName","type"],[1,"tb-flex"],["formControlName","requestExpressionSource"],["matInput","","name","value","formControlName","requestExpression",3,"placeholder"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style",4,"ngIf"],["formControlName","attributeNameExpressionSource"],["matInput","","name","value","formControlName","attributeNameExpression",3,"placeholder"],["matInput","","name","value","formControlName","attributeOnThingsBoard",3,"placeholder"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"div",3)(2,"div",4),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,tJ,2,2,"div",5),t.ɵɵelementStart(6,"div")(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(10,nJ,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",7)(13,"button",8),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.applyKeysData())})),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(11);t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,8,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")"," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.keysListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,n.addKeyTitle)," "),t.ɵɵadvance(6),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,14,"action.apply")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(iJ,[se,_,ni]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-small-label[_ngcontent-%COMP%]{font-size:16px;padding-right:0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}'],changeDetection:d.OnPush})}}e("DeviceDataKeysPanelComponent",iJ),He([I()],iJ.prototype,"withReportStrategy",void 0);const aJ=()=>({maxWidth:"970px"});function rJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-filter-required"))}function oJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function sJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function lJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",41),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}function pJ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",27),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function cJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function dJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function uJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.type," ")}}function mJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.encoding," ")}}function hJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.methodRPC," ")}}class gJ extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.destroyRef=l,this.cdr=p,this.deviceFormGroup=this.fb.group({address:["",[me.required,me.pattern(pn)]],deviceName:["",[me.required,me.pattern(pn)]],deviceType:["",[me.required,me.pattern(pn)]],encoding:[ht.UTF8],telemetry:[[]],attributes:[[]],attributeRequests:[[]],attributeUpdates:[[]],serverSideRpc:[[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),this.keysPopupClosed=!0,this.SocketValueKey=hi,this.socketDeviceHelpLink=O+"/docs/iot-gateway/config/socket/#device-subsection",this.socketEncoding=Object.values(ht),this.ReportStrategyDefaultValue=on,this.deviceFormGroup.patchValue(this.data.value,{emitEvent:!1})}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.deviceFormGroup.valid){const e=this.deviceFormGroup.value;Y(e),this.dialogRef.close(e)}}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))return void this.popoverService.hidePopover(i);const a=this.deviceFormGroup.get(n),r={keys:a.value,keysType:n,panelTitle:gi.get(n),addKeyTitle:vi.get(n),deleteKeyTitle:xi.get(n),noKeysText:bi.get(n),withReportStrategy:this.data.withReportStrategy};this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,iJ,"leftBottom",!1,null,r,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(In(this.destroyRef)).subscribe((e=>{this.popoverComponent.hide(),a.patchValue(e),a.markAsDirty(),this.cdr.markForCheck()})),this.popoverComponent.tbHideStart.pipe(In(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}static{this.ɵfac=function(e){return new(e||gJ)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(rt.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:gJ,selectors:[["tb-device-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:119,vars:57,consts:[["socketTelemetryButton",""],["attributesButton",""],["attributeRequestsButton",""],["attributesUpdatesButton",""],["rpcMethodsButton",""],[1,"dialog-mapping",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width"],["translate","",1,"tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","address",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","encoding"],[3,"value",4,"ngFor","ngForOf"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[4,"ngFor","ngForOf"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",5)(1,"mat-toolbar",6)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",7)(6,"div",8),t.ɵɵelementStart(7,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",10),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",11)(11,"div",12)(12,"div",13)(13,"div",14)(14,"div",15),t.ɵɵtext(15," gateway.address-filter "),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"div",16)(17,"mat-form-field",17),t.ɵɵelement(18,"input",18),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,rJ,3,3,"mat-icon",19),t.ɵɵelement(21,"div",20),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"div",13)(23,"div",21),t.ɵɵtext(24,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"div",16)(26,"mat-form-field",17),t.ɵɵelement(27,"input",22),t.ɵɵpipe(28,"translate"),t.ɵɵtemplate(29,oJ,3,3,"mat-icon",19),t.ɵɵelementEnd()()(),t.ɵɵelementStart(30,"div",13)(31,"div",21),t.ɵɵtext(32,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"div",16)(34,"mat-form-field",17),t.ɵɵelement(35,"input",23),t.ɵɵpipe(36,"translate"),t.ɵɵtemplate(37,sJ,3,3,"mat-icon",19),t.ɵɵelementEnd()()(),t.ɵɵelementStart(38,"div",13)(39,"div",24),t.ɵɵpipe(40,"translate"),t.ɵɵtext(41," gateway.encoding "),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"div",16)(43,"mat-form-field",17)(44,"mat-select",25),t.ɵɵtemplate(45,lJ,2,2,"mat-option",26),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(46,pJ,1,2,"tb-report-strategy",27),t.ɵɵelementStart(47,"div",28)(48,"div",29),t.ɵɵtext(49,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(50,"div",30)(51,"mat-chip-listbox",31),t.ɵɵtemplate(52,cJ,2,1,"mat-chip",32),t.ɵɵelementStart(53,"mat-chip",33),t.ɵɵelement(54,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(55,"button",35,0),t.ɵɵpipe(57,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(56);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.TIMESERIES))})),t.ɵɵelementStart(58,"tb-icon",36),t.ɵɵtext(59,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(60,"div",28)(61,"div",29),t.ɵɵtext(62,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(63,"div",30)(64,"mat-chip-listbox",31),t.ɵɵtemplate(65,dJ,2,1,"mat-chip",32),t.ɵɵelementStart(66,"mat-chip",33),t.ɵɵelement(67,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(68,"button",35,1),t.ɵɵpipe(70,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(69);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.ATTRIBUTES))})),t.ɵɵelementStart(71,"tb-icon",36),t.ɵɵtext(72,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(73,"div",28)(74,"div",29),t.ɵɵtext(75,"gateway.attribute-requests"),t.ɵɵelementEnd(),t.ɵɵelementStart(76,"div",30)(77,"mat-chip-listbox",31),t.ɵɵtemplate(78,uJ,2,1,"mat-chip",32),t.ɵɵelementStart(79,"mat-chip",33),t.ɵɵelement(80,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(81,"button",35,2),t.ɵɵpipe(83,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(82);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.ATTRIBUTES_REQUESTS))})),t.ɵɵelementStart(84,"tb-icon",36),t.ɵɵtext(85,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(86,"div",28)(87,"div",29),t.ɵɵtext(88,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(89,"div",30)(90,"mat-chip-listbox",31),t.ɵɵtemplate(91,mJ,2,1,"mat-chip",32),t.ɵɵelementStart(92,"mat-chip",33),t.ɵɵelement(93,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(94,"button",35,3),t.ɵɵpipe(96,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(95);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.ATTRIBUTES_UPDATES))})),t.ɵɵelementStart(97,"tb-icon",36),t.ɵɵtext(98,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(99,"div",28)(100,"div",29),t.ɵɵtext(101,"gateway.rpc-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(102,"div",30)(103,"mat-chip-listbox",31),t.ɵɵtemplate(104,hJ,2,1,"mat-chip",32),t.ɵɵelementStart(105,"mat-chip",33),t.ɵɵelement(106,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(107,"button",35,4),t.ɵɵpipe(109,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(108);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.RPC_METHODS))})),t.ɵɵelementStart(110,"tb-icon",36),t.ɵɵtext(111,"edit"),t.ɵɵelementEnd()()()()()(),t.ɵɵelementStart(112,"div",37)(113,"button",38),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(114),t.ɵɵpipe(115,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(116,"button",39),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(117),t.ɵɵpipe(118,"translate"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵproperty("formGroup",n.deviceFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,32,"gateway.device")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.socketDeviceHelpLink),t.ɵɵadvance(12),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,34,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceFormGroup.get("address").hasError("required")&&n.deviceFormGroup.get("address").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/address-filter_fn")("tb-help-popup-style",t.ɵɵpureFunction0(56,aJ)),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,36,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceFormGroup.get("deviceName").hasError("required")&&n.deviceFormGroup.get("deviceName").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,38,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceFormGroup.get("deviceType").hasError("required")&&n.deviceFormGroup.get("deviceType").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(40,40,"gateway.hints.encoding")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(),t.ɵɵconditional(n.data.withReportStrategy?46:-1),t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("telemetry").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("telemetry").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(57,42,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(70,44,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributeRequests").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("attributeRequests").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(83,46,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(96,48,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(109,50,"action.edit")),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(115,52,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.deviceFormGroup.invalid||!n.deviceFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(118,54,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(gJ,[se,_,$n,ni]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:grid;height:100%}[_nghost-%COMP%] .tb-form-panel[_ngcontent-%COMP%]{width:77vw;max-width:800px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%]{max-width:900px;display:flex;flex-direction:column}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{min-height:64px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] tb-toggle-select[_ngcontent-%COMP%]{padding:4px 0}[_nghost-%COMP%] .mat-mdc-dialog-content[_ngcontent-%COMP%]{height:670px}[_nghost-%COMP%] .ellipsis-chips-container[_ngcontent-%COMP%]{max-width:70%}[_nghost-%COMP%] .dialog-mapping .mat-mdc-chip-listbox .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center}[_nghost-%COMP%] .tb-form-row .fixed-title-width{min-width:40px;width:35%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center}[_nghost-%COMP%] .tb-form-row .mat-mdc-form-field{width:0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}[_nghost-%COMP%] .device-config{gap:12px;padding-left:10px;padding-right:10px}[_nghost-%COMP%] .device-node-pattern-field{flex-basis:3%}'],changeDetection:d.OnPush})}}e("DeviceDialogComponent",gJ);const fJ=["searchInput"],yJ=()=>["address","deviceName","actions"],vJ=()=>({minWidth:"96px",textAlign:"center"});function xJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",26)(2,"span",27),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",9),t.ɵɵelementStart(6,"button",11),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageDevices(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",11),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.devices")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search")))}function bJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.address-filter")," "))}function wJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.address)}}function SJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.device-name")))}function CJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.deviceName)}}function _J(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",30)}function TJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",11),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageDevices(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",11),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteDevice(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function IJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,TJ,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",31),t.ɵɵelementContainer(4,32),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",33)(6,"button",34),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",35),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",36,2),t.ɵɵelementContainer(11,32),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,vJ)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function EJ(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",37)}function MJ(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class kJ{constructor(e,t,n,i,a){this.translate=e,this.dialog=t,this.dialogService=n,this.fb=i,this.cdr=a,this.withReportStrategy=!0,this.textSearchMode=!1,this.textSearch=this.fb.control("",{nonNullable:!0}),this.onChange=()=>{},this.destroy$=new we,this.devicesFormGroup=this.fb.array([]),this.dataSource=new PJ}ngOnInit(){this.devicesFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.updateTableData(e),this.onChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(De(150),Pe(((e,t)=>(e??"")===t.trim())),Me(this.destroy$)).subscribe((e=>this.updateTableData(this.devicesFormGroup.value,e.trim())))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){this.devicesFormGroup.clear(),this.pushDataAsFormArrays(e)}enterFilterMode(){this.textSearchMode=!0,this.cdr.detectChanges();const e=this.searchInputField.nativeElement;e.focus(),e.setSelectionRange(0,0)}exitFilterMode(){this.updateTableData(this.devicesFormGroup.value),this.textSearchMode=!1,this.textSearch.reset()}manageDevices(e,t){e&&e.stopPropagation();const n=J(t),i=n?this.devicesFormGroup.at(t).value:{};this.getDeviceDialog(i,n?"action.apply":"action.add").afterClosed().pipe(Ne(1),Me(this.destroy$)).subscribe((e=>{e&&(n?this.devicesFormGroup.at(t).patchValue(e):this.devicesFormGroup.push(this.fb.control(e)),this.devicesFormGroup.markAsDirty())}))}validate(){return this.devicesFormGroup.controls.length?null:{devicesFormGroup:{valid:!1}}}getDeviceDialog(e,t){return this.dialog.open(gJ,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,withReportStrategy:this.withReportStrategy}})}deleteDevice(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-device-title",{name:this.devicesFormGroup.controls[t].value.deviceName}),this.translate.instant("gateway.delete-device-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(Ne(1),Me(this.destroy$)).subscribe((e=>{e&&(this.devicesFormGroup.removeAt(t),this.devicesFormGroup.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.devicesFormGroup.push(this.fb.control(e))))}static{this.ɵfac=function(e){return new(e||kJ)(t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(K.DialogService),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:kJ,selectors:[["tb-devices-config-table"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(fJ,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{withReportStrategy:"withReportStrategy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>kJ)),multi:!0},{provide:he,useExisting:c((()=>kJ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:45,vars:36,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-device-table","tb-absolute-fill"],[1,"tb-device-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","table-value-column",4,"matHeaderCellDef"],["class","table-value-column",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-device-table-title"],[1,"table-value-column"],["tbTruncateWithTooltip",""],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,xJ,14,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"div",7)(7,"button",8),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",9)(12,"mat-label"),t.ɵɵtext(13," "),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10,0),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵpipe(18,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(19,"mat-icon"),t.ɵɵtext(20,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(21,"div",12)(22,"table",13),t.ɵɵelementContainerStart(23,14),t.ɵɵtemplate(24,bJ,3,3,"mat-header-cell",15)(25,wJ,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(26,14),t.ɵɵtemplate(27,SJ,4,3,"mat-header-cell",15)(28,CJ,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(29,17),t.ɵɵtemplate(30,_J,1,0,"mat-header-cell",18)(31,IJ,12,6,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(32,EJ,1,0,"mat-header-row",20)(33,MJ,1,0,"mat-row",21),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"section",22),t.ɵɵpipe(35,"async"),t.ɵɵelementStart(36,"button",23),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageDevices(i))})),t.ɵɵelementStart(37,"mat-icon",24),t.ɵɵtext(38,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(39,"span"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(42,"span",25),t.ɵɵpipe(43,"async"),t.ɵɵtext(44," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(4,20,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,22,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,24,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,26,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","address"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","deviceName"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(34,yJ))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(35,yJ)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(35,28,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,30,"gateway.add-device")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(43,32,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(kJ,[se,_,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%]{width:100%;height:100%;background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:35%}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}e("DevicesConfigTableComponent",kJ),He([I()],kJ.prototype,"withReportStrategy",void 0);let PJ=class extends G{constructor(){super()}};function DJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",14),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function OJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",15),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-required"))}function AJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",15),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.socketConfigFormGroup.get("port")))}}function FJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",15),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.socketConfigFormGroup.get("bufferSize").hasError("min")?"gateway.buffer-size-range":"gateway.buffer-size-required"))}}e("DevicesDatasource",PJ);class RJ{constructor(e){this.fb=e,this.portLimits=Oi,this.socketTypes=Object.values(mi),this.onChange=e=>{},this.destroy$=new we,this.socketConfigFormGroup=this.fb.group({address:["",[me.required,me.pattern(pn)]],type:[mi.TCP],port:[5e4,[me.required,me.min(Oi.MIN),me.max(Oi.MAX)]],bufferSize:[1024,[me.required,me.min(1),me.pattern(pn)]]}),this.socketConfigFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){const{address:t="",type:n=mi.TCP,port:i=5e4,bufferSize:a=1024}=e??{};this.socketConfigFormGroup.reset({address:t,type:n,port:i,bufferSize:a})}validate(){return this.socketConfigFormGroup.valid?null:{socketConfigFormGroup:{valid:!1}}}static{this.ɵfac=function(e){return new(e||RJ)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:RJ,selectors:[["tb-socket-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>RJ)),multi:!0},{provide:he,useExisting:c((()=>RJ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:34,vars:25,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width"],["tbTruncateWithTooltip",""],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","address",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","name","value","min","1","formControlName","bufferSize",3,"placeholder"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2)(3,"div",3),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"tb-toggle-select",4),t.ɵɵtemplate(7,DJ,2,2,"tb-toggle-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",1)(9,"div",6),t.ɵɵtext(10,"gateway.address"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",7)(12,"mat-form-field",8),t.ɵɵelement(13,"input",9),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,OJ,3,3,"mat-icon",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",1)(17,"div",6),t.ɵɵtext(18,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",7)(20,"mat-form-field",8),t.ɵɵelement(21,"input",11),t.ɵɵpipe(22,"translate"),t.ɵɵtemplate(23,AJ,3,3,"mat-icon",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(24,"div",1)(25,"div",12),t.ɵɵpipe(26,"translate"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"div",7)(30,"mat-form-field",8),t.ɵɵelement(31,"input",13),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,FJ,3,3,"mat-icon",10),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.socketConfigFormGroup),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,13,"gateway.connection-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.socketTypes),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.socketConfigFormGroup.get("address").hasError("required")&&n.socketConfigFormGroup.get("address").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(22,17,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.socketConfigFormGroup.get("port").hasError("required")||n.socketConfigFormGroup.get("port").hasError("min")||n.socketConfigFormGroup.get("port").hasError("max"))&&n.socketConfigFormGroup.get("port").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(26,19,"gateway.hints.buffer-size")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(28,21,"gateway.buffer-size")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,23,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.socketConfigFormGroup.get("bufferSize").hasError("required")||n.socketConfigFormGroup.get("bufferSize").hasError("min")&&n.socketConfigFormGroup.get("bufferSize").touched))},dependencies:t.ɵɵgetComponentDepsFactory(RJ,[se,_,u$,Wn]),encapsulation:2,changeDetection:d.OnPush})}}e("SocketConfigComponent",RJ);class BJ extends Ua{constructor(){super(...arguments),this.isLegacy=!1}getMappedValue(e){return e}initBasicFormGroup(){return this.fb.group({socket:[],devices:[]})}mapConfigToFormValue(e){return{socket:e.socket??{},devices:e.devices??[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(BJ)))(n||BJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:BJ,selectors:[["tb-socket-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>BJ)),multi:!0},{provide:he,useExisting:c((()=>BJ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:10,vars:14,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","socket"],["formControlName","devices",3,"withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-socket-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-devices-config-table",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,8,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,10,"gateway.socket"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,12,"gateway.devices"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("withReportStrategy",!n.isLegacy))},dependencies:t.ɵɵgetComponentDepsFactory(BJ,[se,_,RJ,kJ]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("SocketBasicConfigComponent",BJ);class NJ extends Ua{constructor(){super(...arguments),this.isLegacy=!0}getMappedValue(e){return Qa.mapSocketToDowngradedVersion(e)}initBasicFormGroup(){return this.fb.group({socket:[],devices:[]})}mapConfigToFormValue(e){return{socket:Qa.mapSocketToUpgradedVersion(e),devices:e?.devices?Qa.mapDevicesToUpgradedVersion(e.devices):[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(NJ)))(n||NJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:NJ,selectors:[["tb-socket-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>NJ)),multi:!0},{provide:he,useExisting:c((()=>NJ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:10,vars:14,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","socket"],["formControlName","devices",3,"withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-socket-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-devices-config-table",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,8,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,10,"gateway.socket"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,12,"gateway.devices"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("withReportStrategy",!n.isLegacy))},dependencies:t.ɵɵgetComponentDepsFactory(NJ,[se,_,RJ,kJ]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}function LJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.name-required"))}function VJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function qJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.applicationConfigFormGroup.get("port")))}}function GJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.object-id-required"))}function zJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.vendor-id-required"))}function UJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SegmentationTypeTranslationsMap.get(e))," ")}}e("SocketLegacyBasicConfigComponent",NJ);class jJ extends za{constructor(){super(...arguments),this.segmentationTypes=Object.values(Ca),this.SegmentationTypeTranslationsMap=_a,this.portLimits=Oi}get applicationConfigFormGroup(){return this.formGroup}initFormGroup(){return this.fb.group({objectName:["",[me.required,me.pattern(pn)]],host:["",[me.required,me.pattern(pn)]],port:[null,[me.required,me.min(Oi.MIN),me.max(Oi.MAX)]],mask:[""],objectIdentifier:[null,[me.required]],vendorIdentifier:[null,[me.required]],maxApduLengthAccepted:[],segmentationSupported:[Ca.BOTH],networkNumber:[],deviceDiscoveryTimeoutInSec:[]})}mapOnChangeValue(e){return Y(e),e}onWriteValue(e){const{maxApduLengthAccepted:t=1476,segmentationSupported:n=Ca.BOTH,networkNumber:i=3,deviceDiscoveryTimeoutInSec:a=5,...r}=e;this.formGroup.reset({...r,maxApduLengthAccepted:t,segmentationSupported:n,networkNumber:i,deviceDiscoveryTimeoutInSec:a},{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(jJ)))(n||jJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:jJ,selectors:[["tb-bacnet-application-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>jJ)),multi:!0},{provide:he,useExisting:c((()=>jJ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:83,vars:53,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","autocomplete","off","name","value","formControlName","objectName",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["translate","",1,"fixed-title-width"],["matInput","","name","value","formControlName","mask",3,"placeholder"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","objectIdentifier",3,"placeholder"],["matInput","","type","number","min","0","name","value","formControlName","vendorIdentifier",3,"placeholder"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-panel","no-border","no-padding","padding-top"],["matInput","","type","number","min","0","name","value","formControlName","maxApduLengthAccepted",3,"placeholder"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","segmentationSupported"],[3,"value"],["matInput","","type","number","min","0","name","value","formControlName","networkNumber",3,"placeholder"],["matInput","","type","number","min","0","name","value","formControlName","deviceDiscoveryTimeoutInSec",3,"placeholder"],["translate","","matSuffix","",1,"block","pr-2"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.bacnet.object-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4),t.ɵɵelement(6,"input",5),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,LJ,3,3,"mat-icon",6),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",1)(10,"div",2),t.ɵɵtext(11,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",4),t.ɵɵelement(13,"input",7),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,VJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"div",1)(17,"div",2),t.ɵɵtext(18,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"mat-form-field",4),t.ɵɵelement(20,"input",8),t.ɵɵpipe(21,"translate"),t.ɵɵtemplate(22,qJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"div",1)(24,"div",9),t.ɵɵtext(25,"gateway.network-mask"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",4),t.ɵɵelement(27,"input",10),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(29,"div",1)(30,"div",11),t.ɵɵpipe(31,"translate"),t.ɵɵtext(32,"gateway.object-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"mat-form-field",4),t.ɵɵelement(34,"input",12),t.ɵɵpipe(35,"translate"),t.ɵɵtemplate(36,GJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(37,"div",1)(38,"div",11),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40,"gateway.vendor-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"mat-form-field",4),t.ɵɵelement(42,"input",13),t.ɵɵpipe(43,"translate"),t.ɵɵtemplate(44,zJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"div",14)(46,"mat-expansion-panel",15)(47,"mat-expansion-panel-header")(48,"mat-panel-title")(49,"div",16),t.ɵɵtext(50,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(51,"div",17)(52,"div",1)(53,"div",11),t.ɵɵpipe(54,"translate"),t.ɵɵtext(55,"gateway.bacnet.apdu-length"),t.ɵɵelementEnd(),t.ɵɵelementStart(56,"mat-form-field",4),t.ɵɵelement(57,"input",18),t.ɵɵpipe(58,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(59,"div",1)(60,"div",19),t.ɵɵpipe(61,"translate"),t.ɵɵtext(62,"gateway.bacnet.segmentation.label"),t.ɵɵelementEnd(),t.ɵɵelementStart(63,"mat-form-field",4)(64,"mat-select",20),t.ɵɵrepeaterCreate(65,UJ,3,4,"mat-option",21,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()(),t.ɵɵelementStart(67,"div",1)(68,"div",19),t.ɵɵpipe(69,"translate"),t.ɵɵtext(70,"gateway.bacnet.network-number"),t.ɵɵelementEnd(),t.ɵɵelementStart(71,"mat-form-field",4),t.ɵɵelement(72,"input",22),t.ɵɵpipe(73,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(74,"div",1)(75,"div",19),t.ɵɵpipe(76,"translate"),t.ɵɵtext(77,"gateway.bacnet.device-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(78,"mat-form-field",4),t.ɵɵelement(79,"input",23),t.ɵɵpipe(80,"translate"),t.ɵɵelementStart(81,"span",24),t.ɵɵtext(82,"gateway.suffix.s"),t.ɵɵelementEnd()()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.applicationConfigFormGroup),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,23,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("objectName").hasError("required")&&n.applicationConfigFormGroup.get("objectName").touched?8:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,25,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("host").hasError("required")&&n.applicationConfigFormGroup.get("host").touched?15:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,27,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((n.applicationConfigFormGroup.get("port").hasError("required")||n.applicationConfigFormGroup.get("port").hasError("min")||n.applicationConfigFormGroup.get("port").hasError("max"))&&n.applicationConfigFormGroup.get("port").touched?22:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,29,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(31,31,"gateway.hints.bacnet.object-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(35,33,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("objectIdentifier").hasError("required")&&n.applicationConfigFormGroup.get("objectIdentifier").touched?36:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(39,35,"gateway.hints.bacnet.vendor-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(43,37,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("vendorIdentifier").hasError("required")&&n.applicationConfigFormGroup.get("vendorIdentifier").touched?44:-1),t.ɵɵadvance(9),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(54,39,"gateway.hints.bacnet.apdu-length")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(58,41,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(61,43,"gateway.hints.bacnet.segmentation")),t.ɵɵadvance(5),t.ɵɵrepeater(n.segmentationTypes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(69,45,"gateway.hints.bacnet.network-number")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(73,47,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(76,49,"gateway.hints.bacnet.device-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(80,51,"gateway.set")))},dependencies:t.ɵɵgetComponentDepsFactory(jJ,[se,_,u$]),encapsulation:2,changeDetection:d.OnPush})}}function HJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",11),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function WJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",14),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",15)(6,"mat-form-field",6),t.ɵɵelement(7,"input",16),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,HJ,3,3,"mat-icon",11),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",e.keyType===e.BacnetDeviceKeysType.TIMESERIES?t.ɵɵpipeBind1(1,4,"gateway.hints.socket.key-telemetry"):t.ɵɵpipeBind1(2,6,"gateway.hints.socket.key-attribute")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,8,"gateway.key")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,10,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.formGroup.get("key").hasError("required")&&e.formGroup.get("key").touched?9:-1)}}function $J(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",11),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-required"))}function KJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",14),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",15)(5,"mat-form-field",6),t.ɵɵelement(6,"input",17),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,$J,3,3,"mat-icon",11),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,4,"gateway.hints.method")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,6,"gateway.method")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.formGroup.get("method").hasError("required")&&e.formGroup.get("method").touched?8:-1)}}function YJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.BacnetRequestTypeTranslationsMap.get(e))," ")}}function XJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",18)(2,"div",14),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-toggle-select",19),t.ɵɵrepeaterCreate(7,YJ,3,4,"tb-toggle-option",8,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.hints.bacnet.request-type")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,4,"gateway.bacnet.request-type.label")),t.ɵɵadvance(3),t.ɵɵrepeater(e.requestTypes)}}function ZJ(e,n){1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",4),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.request-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",6),t.ɵɵelement(5,"input",20),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,2,"gateway.hints.bacnet.request-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,4,"gateway.set")))}function QJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.BacnetKeyObjectTypeTranslationsMap.get(e))," ")}}function JJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",11),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.object-id-required"))}function e1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.BacnetPropertyIdTranslationsMap.get(e))," ")}}function t1(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",13),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Key)}}class n1 extends za{constructor(){super(...arguments),this.withReportStrategy=!0,this.propertyIds=Aa.get(Pa.analogOutput),this.objectTypes=Object.values(Pa),this.requestTypes=Object.values(Ra),this.ReportStrategyDefaultValue=on,this.BacnetDeviceKeysType=Ta,this.BacnetKeyObjectTypeTranslationsMap=Da,this.BacnetPropertyIdTranslationsMap=Fa,this.BacnetRequestTypeTranslationsMap=Ba}ngOnInit(){this.formGroup=this.initKeyFormGroup(),this.observeValueChanges(),this.observeObjectType()}isReportStrategyDisabled(){return!(this.withReportStrategy&&(this.keyType===Ta.ATTRIBUTES||this.keyType===Ta.TIMESERIES))}initKeyFormGroup(){return this.fb.group({key:[{value:"",disabled:this.keyType===Ta.RPC_METHODS},[me.required,me.pattern(pn)]],method:[{value:"",disabled:this.keyType!==Ta.RPC_METHODS},[me.required,me.pattern(pn)]],objectType:[Pa.analogOutput],objectId:[0,[me.required]],propertyId:[Oa.presentValue],requestTimeout:[{value:0,disabled:this.keyType!==Ta.RPC_METHODS}],requestType:[{value:Ra.Write,disabled:this.keyType!==Ta.RPC_METHODS}],reportStrategy:[{value:null,disabled:this.isReportStrategyDisabled()}]})}observeObjectType(){this.formGroup.get("objectType").valueChanges.pipe(In(this.destroyRef)).subscribe((e=>{this.propertyIds=Aa.get(e),this.propertyIds.includes(this.formGroup.get("propertyId").value)||this.formGroup.get("propertyId").patchValue(this.propertyIds[0],{emitEvent:!1})}))}initFormGroup(){return this.fb.group({})}mapOnChangeValue({reportStrategy:e,...t}){return e?{...t,reportStrategy:e}:t}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(n1)))(n||n1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:n1,selectors:[["tb-bacnet-device-data-key"]],inputs:{keyType:"keyType",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>n1)),multi:!0},{provide:he,useExisting:c((()=>n1)),multi:!0}]),t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:35,vars:15,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap","raw-value-option"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","objectType"],[3,"value"],[1,"tb-form-table-row-cell","tb-flex","no-gap"],["matInput","","type","number","min","0","name","value","formControlName","objectId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["formControlName","propertyId"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matInput","","name","value","formControlName","method",3,"placeholder"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["formControlName","requestType","appearance","fill"],["matInput","","type","number","min","0","name","value","formControlName","requestTimeout",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtemplate(5,WJ,10,12)(6,KJ,9,10),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",1)(8,"div",2),t.ɵɵtext(9,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵtemplate(10,XJ,9,6,"div",3)(11,ZJ,7,6,"div",3),t.ɵɵelementStart(12,"div",3)(13,"div",4),t.ɵɵpipe(14,"translate"),t.ɵɵtext(15,"gateway.object-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",5)(17,"mat-form-field",6)(18,"mat-select",7),t.ɵɵrepeaterCreate(19,QJ,3,4,"mat-option",8,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()()(),t.ɵɵelementStart(21,"div",9)(22,"mat-form-field",6),t.ɵɵelement(23,"input",10),t.ɵɵpipe(24,"translate"),t.ɵɵtemplate(25,JJ,3,3,"mat-icon",11),t.ɵɵelementEnd()()(),t.ɵɵelementStart(26,"div",3)(27,"div",4),t.ɵɵpipe(28,"translate"),t.ɵɵtext(29,"gateway.property-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",6)(31,"mat-select",12),t.ɵɵrepeaterCreate(32,e1,3,4,"mat-option",8,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()()(),t.ɵɵtemplate(34,t1,1,2,"tb-report-strategy",13),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.formGroup),t.ɵɵadvance(5),t.ɵɵconditional(n.keyType!==n.BacnetDeviceKeysType.RPC_METHODS?5:6),t.ɵɵadvance(5),t.ɵɵconditional(n.keyType===n.BacnetDeviceKeysType.RPC_METHODS?10:-1),t.ɵɵadvance(),t.ɵɵconditional(n.keyType===n.BacnetDeviceKeysType.RPC_METHODS?11:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(14,9,"gateway.hints.bacnet.key-object-id")),t.ɵɵadvance(6),t.ɵɵrepeater(n.objectTypes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(24,11,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.formGroup.get("objectId").hasError("required")&&n.formGroup.get("objectId").touched?25:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(28,13,"gateway.hints.bacnet.property-id")),t.ɵɵadvance(5),t.ɵɵrepeater(n.propertyIds),t.ɵɵadvance(2),t.ɵɵconditional(n.isReportStrategyDisabled()?-1:34))},dependencies:t.ɵɵgetComponentDepsFactory(n1,[se,_,ni]),encapsulation:2,changeDetection:d.OnPush})}}function i1(e,n){if(1&e&&t.ɵɵelement(0,"tb-bacnet-device-data-key",17),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("formControl",e)("keyType",n.keysType)("withReportStrategy",n.withReportStrategy)}}function a1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵelementContainerStart(2,11),t.ɵɵelementStart(3,"mat-expansion-panel",12)(4,"mat-expansion-panel-header",13)(5,"mat-panel-title")(6,"div",14),t.ɵɵtext(7),t.ɵɵelementEnd()()(),t.ɵɵtemplate(8,i1,1,3,"ng-template",15),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",16),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"delete"),t.ɵɵelementEnd()()()}if(2&e){let e;const i=n.$implicit,a=n.$index,r=n.$count,o=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",i),t.ɵɵadvance(),t.ɵɵproperty("expanded",a===r-1),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",null!==(e=null==i.value?null:i.value.key)&&void 0!==e?e:null==i.value?null:i.value.method," "),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(10,4,o.deleteKeyTitle))}}function r1(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3),t.ɵɵrepeaterCreate(1,a1,13,6,"div",9,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵrepeater(e.keysListFormArray.controls)}}function o1(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"span",18),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class s1 extends q{constructor(e,t,n){super(n),this.fb=e,this.popover=t,this.store=n,this.withReportStrategy=!0,this.keysDataApplied=p(),this.ReportStrategyDefaultValue=on}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}addKey(){this.keysListFormArray.push(this.fb.control({}))}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){this.keysDataApplied.emit(this.keysListFormArray.value)}prepareKeysFormArray(e){const t=[];return e?.forEach((e=>{t.push(this.fb.control(e))})),this.fb.array(t)}static{this.ɵfac=function(e){return new(e||s1)(t.ɵɵdirectiveInject(de.UntypedFormBuilder),t.ɵɵdirectiveInject(at.TbPopoverComponent),t.ɵɵdirectiveInject(Xe.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:s1,selectors:[["tb-bacnet-device-data-keys-panel"]],inputs:{panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keys:"keys",keysType:"keysType",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:18,vars:15,consts:[[1,"tb-device-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],[1,"tb-form-panel","no-border","no-padding","key-panel"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"title-container"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[3,"formControl","keyType","withReportStrategy"],["translate","",1,"tb-prompt"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,r1,3,0,"div",3)(6,o1,3,1,"div",4),t.ɵɵelementStart(7,"div")(8,"button",5),t.ɵɵlistener("click",(function(){return n.addKey()})),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",6)(12,"button",7),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"button",8),t.ɵɵlistener("click",(function(){return n.applyKeysData()})),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,7,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")"," "),t.ɵɵadvance(2),t.ɵɵconditional(n.keysListFormArray.controls.length?5:6),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(10,9,n.addKeyTitle)," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,11,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,13,"action.apply")," "))},dependencies:t.ɵɵgetComponentDepsFactory(s1,[se,_,n1]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-small-label[_ngcontent-%COMP%]{font-size:16px;padding-right:0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}'],changeDetection:d.OnPush})}}function l1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function p1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function c1(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",12)(1,"mat-expansion-panel",35)(2,"mat-expansion-panel-header")(3,"mat-panel-title")(4,"div",36),t.ɵɵtext(5,"gateway.advanced-configuration-settings"),t.ɵɵelementEnd()()(),t.ɵɵelement(6,"tb-string-items-list",37),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()),2&e){let e;const n=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,2,"gateway.bacnet.alt-responses-address")),t.ɵɵpropertyInterpolate("placeholder",null!=(e=n.deviceFormGroup.get("altResponsesAddresses").value)&&e.length?"":t.ɵɵpipeBind1(8,4,"gateway.address"))}}function d1(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",21),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function u1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.poll-period-required"))}function m1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function h1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function g1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function f1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.method," ")}}class y1 extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.cdr=l,this.destroyRef=p,this.deviceFormGroup=this.fb.group({host:["",[me.required,me.pattern(pn)]],port:["",[me.required]],networkMask:[""],deviceInfo:[],altResponsesAddresses:[{value:[],disabled:this.data.hideNewFields}],pollPeriod:[1e4,[me.required,me.min(0)]],timeseries:[[]],attributes:[[]],attributeUpdates:[[]],serverSideRpc:[[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),this.keysPopupClosed=!0,this.BacnetDeviceKeysType=Ta,this.DeviceInfoType=Sa,this.deviceHelpLink=O+"/docs/iot-gateway/config/bacnet/#device-object-settings",this.sourceTypes=Object.values(yi),this.ConnectorType=dt,this.ReportStrategyDefaultValue=on,this.deviceFormGroup.patchValue(this.data.value,{emitEvent:!1})}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.deviceFormGroup.valid){const{altResponsesAddresses:e,reportStrategy:t,...n}=this.deviceFormGroup.value;this.dialogRef.close({altResponsesAddresses:e??[],...t?{reportStrategy:t}:{},...n})}}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))return void this.popoverService.hidePopover(i);const a=this.deviceFormGroup.get(n),r={keys:a.value,keysType:n,panelTitle:Ia.get(n),addKeyTitle:Ea.get(n),deleteKeyTitle:Ma.get(n),noKeysText:ka.get(n),withReportStrategy:this.data.withReportStrategy};this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,s1,"leftBottom",!1,null,r,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.subscribe((e=>{this.popoverComponent.hide(),a.patchValue(e),a.markAsDirty(),this.cdr.markForCheck()})),this.popoverComponent.tbHideStart.pipe(In(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}static{this.ɵfac=function(e){return new(e||y1)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(rt.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:y1,selectors:[["tb-bacnet-device-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:106,vars:48,consts:[["attributesButton",""],["socketTelemetryButton",""],["attributesUpdatesButton",""],["rpcMethodsButton",""],[1,"dialog-mapping",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","port",3,"placeholder"],["translate","",1,"fixed-title-width"],["matInput","","name","value","formControlName","networkMask",3,"placeholder"],["formControlName","deviceInfo","required","true",3,"deviceInfoType","sourceTypes","connectorType"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],[1,"fixed-title-width","tb-required"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","0","name","value","formControlName","pollPeriod",3,"placeholder"],[1,"tb-form-row","space-between","tb-flex"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[1,"tb-settings","chips-panel","w-full"],["translate","",1,"tb-form-panel-title"],["editable","","floatLabel","always","formControlName","altResponsesAddresses",1,"chips-list",3,"label","placeholder"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",4)(1,"mat-toolbar",5)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",6)(6,"div",7),t.ɵɵelementStart(7,"button",8),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",9),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",10)(11,"div",11)(12,"div",12)(13,"div",13),t.ɵɵtext(14,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-form-field",14),t.ɵɵelement(16,"input",15),t.ɵɵpipe(17,"translate"),t.ɵɵtemplate(18,l1,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"div",12)(20,"div",13),t.ɵɵtext(21,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",14),t.ɵɵelement(23,"input",17),t.ɵɵpipe(24,"translate"),t.ɵɵtemplate(25,p1,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"div",12)(27,"div",18),t.ɵɵtext(28,"gateway.network-mask"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-form-field",14),t.ɵɵelement(30,"input",19),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(32,c1,9,6,"div",12),t.ɵɵelement(33,"tb-device-info-table",20),t.ɵɵtemplate(34,d1,1,2,"tb-report-strategy",21),t.ɵɵelementStart(35,"div",12)(36,"div",22)(37,"span",23),t.ɵɵtext(38,"gateway.poll-period"),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"mat-form-field",14),t.ɵɵelement(40,"input",24),t.ɵɵpipe(41,"translate"),t.ɵɵtemplate(42,u1,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(43,"div",25)(44,"div",18),t.ɵɵtext(45,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(46,"div",26)(47,"mat-chip-listbox",27),t.ɵɵrepeaterCreate(48,m1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(50,"mat-chip",28),t.ɵɵelement(51,"label",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(52,"button",30,0),t.ɵɵpipe(54,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(53);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.ATTRIBUTES))})),t.ɵɵelementStart(55,"tb-icon",31),t.ɵɵtext(56,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(57,"div",25)(58,"div",18),t.ɵɵtext(59,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(60,"div",26)(61,"mat-chip-listbox",27),t.ɵɵrepeaterCreate(62,h1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(64,"mat-chip",28),t.ɵɵelement(65,"label",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(66,"button",30,1),t.ɵɵpipe(68,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(67);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.TIMESERIES))})),t.ɵɵelementStart(69,"tb-icon",31),t.ɵɵtext(70,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(71,"div",25)(72,"div",18),t.ɵɵtext(73,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(74,"div",26)(75,"mat-chip-listbox",27),t.ɵɵrepeaterCreate(76,g1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(78,"mat-chip",28),t.ɵɵelement(79,"label",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(80,"button",30,2),t.ɵɵpipe(82,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(81);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.ATTRIBUTES_UPDATES))})),t.ɵɵelementStart(83,"tb-icon",31),t.ɵɵtext(84,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(85,"div",25)(86,"div",18),t.ɵɵtext(87,"gateway.rpc-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(88,"div",26)(89,"mat-chip-listbox",27),t.ɵɵrepeaterCreate(90,f1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(92,"mat-chip",28),t.ɵɵelement(93,"label",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(94,"button",30,3),t.ɵɵpipe(96,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(95);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.RPC_METHODS))})),t.ɵɵelementStart(97,"tb-icon",31),t.ɵɵtext(98,"edit"),t.ɵɵelementEnd()()()()()(),t.ɵɵelementStart(99,"div",32)(100,"button",33),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(101),t.ɵɵpipe(102,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(103,"button",34),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(104),t.ɵɵpipe(105,"translate"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵproperty("formGroup",n.deviceFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,26,"gateway.device")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.deviceHelpLink),t.ɵɵadvance(10),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(17,28,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.deviceFormGroup.get("host").hasError("required")&&n.deviceFormGroup.get("host").touched?18:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(24,30,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.deviceFormGroup.get("port").hasError("required")&&n.deviceFormGroup.get("port").touched?25:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(31,32,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.data.hideNewFields?-1:32),t.ɵɵadvance(),t.ɵɵproperty("deviceInfoType",n.DeviceInfoType.FULL)("sourceTypes",n.sourceTypes)("connectorType",n.ConnectorType.BACNET),t.ɵɵadvance(),t.ɵɵconditional(n.data.withReportStrategy?34:-1),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(41,34,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.deviceFormGroup.get("pollPeriod").hasError("required")&&n.deviceFormGroup.get("pollPeriod").touched?42:-1),t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(54,36,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("timeseries").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("timeseries").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(68,38,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(82,40,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(96,42,"action.edit")),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(102,44,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.deviceFormGroup.invalid||!n.deviceFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(105,46,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(y1,[se,_,$n,Wn,A$,ni]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:grid;height:100%}[_nghost-%COMP%] .tb-form-panel[_ngcontent-%COMP%]{width:77vw;max-width:800px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%]{max-width:900px;display:flex;flex-direction:column}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{min-height:64px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] tb-toggle-select[_ngcontent-%COMP%]{padding:4px 0}[_nghost-%COMP%] .mat-mdc-dialog-content[_ngcontent-%COMP%]{height:670px}[_nghost-%COMP%] .ellipsis-chips-container[_ngcontent-%COMP%]{max-width:70%}[_nghost-%COMP%] .chips-panel[_ngcontent-%COMP%]{padding:6px 6px 6px 0}[_nghost-%COMP%] .dialog-mapping .mat-mdc-chip-listbox .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center}[_nghost-%COMP%] .tb-form-row .fixed-title-width{min-width:40px;width:35%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center}[_nghost-%COMP%] .tb-form-row .mat-mdc-form-field{width:0}[_nghost-%COMP%] .tb-form-row .chips-list .mat-mdc-form-field{width:100%}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}[_nghost-%COMP%] .device-config{gap:12px;padding-left:10px;padding-right:10px}[_nghost-%COMP%] .device-node-pattern-field{flex-basis:3%}'],changeDetection:d.OnPush})}}const v1=()=>["deviceName","host","port","actions"],x1=()=>({minWidth:"96px",textAlign:"center"});function b1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",26)(2,"span",27),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",9),t.ɵɵelementStart(6,"button",11),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageDevices(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",11),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.devices")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search")))}function w1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.device-name")))}function S1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(null==e.deviceInfo?null:e.deviceInfo.deviceNameExpression)}}function C1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.host")," "))}function _1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.host)}}function T1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.port")," "))}function I1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.port)}}function E1(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",30)}function M1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",11),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageDevices(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",11),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteDevice(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function k1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,M1,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",31),t.ɵɵelementContainer(4,32),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",33)(6,"button",34),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",35),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",36,2),t.ɵɵelementContainer(11,32),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,x1)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function P1(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",37)}function D1(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class O1 extends $a{constructor(){super(...arguments),this.hideNewFields=!1}getDatasource(){return new A1}manageDevices(e,t){e&&e.stopPropagation();const n=J(t),i=n?this.entityFormArray.at(t).value:{};this.getDeviceDialog(i,n?"action.apply":"action.add").afterClosed().pipe(Ne(1),In(this.destroyRef)).subscribe((e=>{e&&(n?this.entityFormArray.at(t).patchValue(e):this.entityFormArray.push(this.fb.control(e)),this.entityFormArray.markAsDirty())}))}deleteDevice(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-device-title",{name:this.entityFormArray.controls[t].value.deviceInfo?.deviceNameExpression}),this.translate.instant("gateway.delete-device-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(Ne(1),In(this.destroyRef)).subscribe((e=>{e&&(this.entityFormArray.removeAt(t),this.entityFormArray.markAsDirty())}))}getDeviceDialog(e,t){return this.dialog.open(y1,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,withReportStrategy:this.withReportStrategy,hideNewFields:this.hideNewFields}})}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())||e.deviceNameExpression?.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(O1)))(n||O1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:O1,selectors:[["tb-bacnet-devices-config-table"]],inputs:{hideNewFields:[2,"hideNewFields","hideNewFields",m]},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>O1)),multi:!0},{provide:he,useExisting:c((()=>O1)),multi:!0}]),t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:48,vars:37,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-device-table","tb-absolute-fill"],[1,"tb-device-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","table-value-column",4,"matHeaderCellDef"],["class","table-value-column",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-device-table-title"],[1,"table-value-column"],["tbTruncateWithTooltip",""],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,b1,14,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"div",7)(7,"button",8),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",9)(12,"mat-label"),t.ɵɵtext(13," "),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10,0),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵpipe(18,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(19,"mat-icon"),t.ɵɵtext(20,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(21,"div",12)(22,"table",13),t.ɵɵelementContainerStart(23,14),t.ɵɵtemplate(24,w1,4,3,"mat-header-cell",15)(25,S1,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(26,14),t.ɵɵtemplate(27,C1,3,3,"mat-header-cell",15)(28,_1,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(29,14),t.ɵɵtemplate(30,T1,3,3,"mat-header-cell",15)(31,I1,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(32,17),t.ɵɵtemplate(33,E1,1,0,"mat-header-cell",18)(34,k1,12,6,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(35,P1,1,0,"mat-header-row",20)(36,D1,1,0,"mat-row",21),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"section",22),t.ɵɵpipe(38,"async"),t.ɵɵelementStart(39,"button",23),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageDevices(i))})),t.ɵɵelementStart(40,"mat-icon",24),t.ɵɵtext(41,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"span"),t.ɵɵtext(43),t.ɵɵpipe(44,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(45,"span",25),t.ɵɵpipe(46,"async"),t.ɵɵtext(47," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(4,21,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,23,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,25,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,27,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","deviceName"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","host"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","port"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(35,v1))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(36,v1)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(38,29,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(44,31,"gateway.add-device")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(46,33,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(O1,[se,_,Wn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%]{width:100%;height:100%;background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:21%}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}class A1 extends G{constructor(){super()}}class F1 extends Ua{initBasicFormGroup(){return this.fb.group({application:[],devices:[]})}mapConfigToFormValue(e){return{application:e.application??{},devices:e.devices??[]}}getMappedValue(e){return{application:e.application,devices:e.devices??[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(F1)))(n||F1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:F1,selectors:[["tb-bacnet-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>F1)),multi:!0},{provide:he,useExisting:c((()=>F1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:15,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","application"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","devices",3,"hideNewFields","withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-bacnet-application-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-bacnet-devices-config-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.application"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.devices"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("hideNewFields",n.isLegacy)("withReportStrategy",n.withReportStrategy))},dependencies:t.ɵɵgetComponentDepsFactory(F1,[se,_,jJ,O1]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}class R1 extends Ua{constructor(){super(...arguments),this.isLegacy=!0}initBasicFormGroup(){return this.fb.group({application:[],devices:[]})}mapConfigToFormValue(e){return{application:e.general?Ja.mapApplicationToUpgradedVersion(e.general):{},devices:e.devices?.length?Ja.mapDevicesToUpgradedVersion(e.devices):[]}}getMappedValue(e){return{general:e.application?Ja.mapApplicationToDowngradedVersion(e.application):{},devices:e.devices?.length?Ja.mapDevicesToDowngradedVersion(e.devices):[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(R1)))(n||R1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:R1,selectors:[["tb-bacnet-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>R1)),multi:!0},{provide:he,useExisting:c((()=>R1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:15,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","application"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","devices",3,"hideNewFields","withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-bacnet-application-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-bacnet-devices-config-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.application"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.devices"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("hideNewFields",n.isLegacy)("withReportStrategy",n.withReportStrategy))},dependencies:t.ɵɵgetComponentDepsFactory(R1,[se,_,jJ,O1]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}function B1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",5),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function N1(e,n){if(1&e&&(t.ɵɵelement(0,"tb-error-icon",8),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵpipe(2,"translate")),2&e){t.ɵɵnextContext();const e=t.ɵɵreadContextLet(16);t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(2,3,t.ɵɵpipeBind1(1,1,e)))}}class L1 extends za{constructor(){super(...arguments),this.portLimits=Oi}get serverConfigFormGroup(){return this.formGroup}initFormGroup(){return this.fb.group({host:["",[me.required,me.pattern(pn)]],port:[null,[me.required,me.min(Oi.MIN),me.max(Oi.MAX)]],SSL:[!1],security:this.fb.group({cert:[""],key:[""]})})}mapOnChangeValue(e){return Y(e),e}onWriteValue(e){this.formGroup.reset(e,{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(L1)))(n||L1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:L1,selectors:[["tb-rest-server-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>L1)),multi:!0},{provide:he,useExisting:c((()=>L1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:46,vars:43,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[1,"tb-form-row","column-xs"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matSuffix","",3,"tooltipText"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["formControlName","SSL",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],[3,"formGroup"],["tbTruncateWithTooltip","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","cert",3,"placeholder"],["matInput","","name","value","formControlName","key",3,"placeholder"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",3),t.ɵɵelement(6,"input",4),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,B1,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",6)(10,"div",2),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",3),t.ɵɵelement(14,"input",7),t.ɵɵpipe(15,"translate"),t.ɵɵdeclareLet(16),t.ɵɵtemplate(17,N1,3,5,"tb-error-icon",8),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",9)(19,"div",10),t.ɵɵtext(20,"gateway.security"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",11)(22,"mat-slide-toggle",12)(23,"mat-label",13),t.ɵɵpipe(24,"translate"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerStart(27,14),t.ɵɵelementStart(28,"div",11)(29,"div",15),t.ɵɵpipe(30,"translate"),t.ɵɵtext(31),t.ɵɵpipe(32,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"div",16)(34,"mat-form-field",3),t.ɵɵelement(35,"input",17),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(37,"div",11)(38,"div",15),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"div",16)(43,"mat-form-field",3),t.ɵɵelement(44,"input",18),t.ɵɵpipe(45,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()),2&e){t.ɵɵproperty("formGroup",n.serverConfigFormGroup),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,18,"gateway.hints.rest.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.serverConfigFormGroup.get("host").hasError("required")&&n.serverConfigFormGroup.get("host").touched?8:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,22,"gateway.hints.rest.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,24,"gateway.set")),t.ɵɵadvance(2);const e=t.ɵɵstoreLet(n.serverConfigFormGroup.get("port"));t.ɵɵadvance(),t.ɵɵconditional((e.hasError("required")||e.hasError("min")||e.hasError("max"))&&e.touched?17:-1),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(24,27,"gateway.hints.rest.ssl-verify")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(26,29,"gateway.rest.ssl-verify")," "),t.ɵɵadvance(2),t.ɵɵproperty("formGroup",n.serverConfigFormGroup.get("security")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(30,31,"gateway.hints.rest.cert")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(32,33,"gateway.certificate")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,35,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(39,37,"gateway.hints.rest.key")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,39,"gateway.key")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(45,41,"gateway.set"))}},dependencies:t.ɵɵgetComponentDepsFactory(L1,[se,_,u$,Wn,ai]),encapsulation:2,changeDetection:d.OnPush})}}const V1=()=>({min:1});function q1(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",4),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.ResponseTypeTranslationsMap.get(e)))}}function G1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",4),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function z1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",4),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function U1(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,7),t.ɵɵelementStart(1,"div",8)(2,"div",9),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",10)(6,"mat-form-field",11)(7,"mat-select",12),t.ɵɵrepeaterCreate(8,G1,2,2,"mat-option",4,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(10,"div",8)(11,"div",9),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",10)(15,"mat-form-field",11)(16,"mat-select",13),t.ɵɵrepeaterCreate(17,z1,2,2,"mat-option",4,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("formGroup",e.responseConfigFormGroup.get(e.ResponseType.CONST)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rest.on-success")),t.ɵɵadvance(5),t.ɵɵrepeater(e.responseStatuses),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,5,"gateway.rest.on-error")),t.ɵɵadvance(5),t.ɵɵrepeater(e.responseStatuses)}}function j1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.response-timeout-required"))}function H1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,V1)))}function W1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function $1(e,n){1&e&&(t.ɵɵelementStart(0,"span",18),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function K1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.response-attribute-required"))}function Y1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.response-attribute-pattern"))}function X1(e,n){if(1&e&&(t.ɵɵdeclareLet(0),t.ɵɵelementContainerStart(1,7),t.ɵɵelementStart(2,"div",8)(3,"mat-slide-toggle",14)(4,"mat-label"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(7,"div",8)(8,"div",15),t.ɵɵtext(9,"gateway.timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-form-field",11),t.ɵɵelement(11,"input",16),t.ɵɵpipe(12,"translate"),t.ɵɵdeclareLet(13),t.ɵɵtemplate(14,j1,2,3,"tb-error-icon",17)(15,H1,2,5,"tb-error-icon",17)(16,W1,2,3,"tb-error-icon",17)(17,$1,2,0,"span",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",8)(19,"div",15),t.ɵɵtext(20,"gateway.rest.response-attribute"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",11),t.ɵɵelement(22,"input",19),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,K1,2,3,"tb-error-icon",17)(25,Y1,2,3,"tb-error-icon",17),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(),n=e.responseConfigFormGroup.get(e.ResponseType.ADVANCED);t.ɵɵadvance(),t.ɵɵproperty("formGroup",n),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,6,"gateway.rest.response-expected")," "),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(12,8,"gateway.set"));const i=n.get("timeout").touched;t.ɵɵadvance(3),t.ɵɵconditional(n.get("timeout").hasError("required")&&i?14:n.get("timeout").hasError("min")&&i?15:n.get("timeout").hasError("pattern")&&i?16:17),t.ɵɵadvance(8),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,10,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.get("responseAttribute").hasError("required")&&n.get("responseAttribute").touched?24:n.get("responseAttribute").hasError("pattern")&&n.get("responseAttribute").touched?25:-1)}}class Z1 extends za{get responseConfigFormGroup(){return this.formGroup}constructor(){super(),this.ResponseTypeTranslationsMap=Ti,this.ResponseType=Ci,this.responseTypes=Object.values(Ci),this.responseStatuses=Object.values(_i),this.observeIsExpected()}initFormGroup(){return this.fb.group({type:[Ci.DEFAULT],[Ci.CONST]:this.fb.group({successResponse:[_i.OK],unsuccessfulResponse:[_i.ERROR]}),[Ci.ADVANCED]:this.fb.group({responseExpected:[!0],timeout:[null,[me.required,me.min(.001),me.pattern(ln)]],responseAttribute:["",[me.required,me.pattern(pn)]]})})}mapOnChangeValue({type:e,...t}){return{type:e,...t[e]??{}}}onWriteValue(e){const{type:t=Ci.DEFAULT,...n}=e??{};this.toggleIsExpected(n.responseExpected,t),this.responseConfigFormGroup.patchValue({type:t,[t]:n},{emitEvent:!1})}toggleIsExpected(e,t){const n=e&&t===Ci.ADVANCED;this.responseConfigFormGroup.get(Ci.ADVANCED).get("timeout")[n?"enable":"disable"]({emitEvent:!1}),this.responseConfigFormGroup.get(Ci.ADVANCED).get("responseAttribute")[n?"enable":"disable"]({emitEvent:!1})}observeIsExpected(){Ee(this.responseConfigFormGroup.get("type").valueChanges,this.responseConfigFormGroup.get(Ci.ADVANCED).get("responseExpected").valueChanges).pipe(In()).subscribe((()=>this.toggleIsExpected(this.responseConfigFormGroup.get(Ci.ADVANCED).get("responseExpected").value,this.responseConfigFormGroup.get("type").value)))}static{this.ɵfac=function(e){return new(e||Z1)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Z1,selectors:[["tb-rest-response-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>Z1)),multi:!0},{provide:he,useExisting:c((()=>Z1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:7,consts:[[1,"tb-form-row","space-between","same-padding","tb-flex","column","size-full",3,"formGroup"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],[1,"fixed-title-width","tb-required"],["formControlName","type","appearance","fill"],[3,"value"],[3,"ngSwitch"],[3,"ngSwitchCase"],[3,"formGroup"],[1,"tb-form-row"],[1,"fixed-title-width"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","successResponse"],["formControlName","unsuccessfulResponse"],["formControlName","responseExpected",1,"mat-slide"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["matSuffix","",3,"tooltipText"],["translate","","matSuffix","",1,"block","pr-2"],["matInput","","type","text","name","value","formControlName","responseAttribute",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",3),t.ɵɵrepeaterCreate(6,q1,3,4,"tb-toggle-option",4,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()(),t.ɵɵelementContainerStart(8,5),t.ɵɵtemplate(9,U1,19,7,"ng-template",6)(10,X1,26,12,"ng-template",6),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.responseConfigFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,5,"gateway.response")),t.ɵɵadvance(3),t.ɵɵrepeater(n.responseTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",n.responseConfigFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.ResponseType.CONST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.ResponseType.ADVANCED))},dependencies:t.ɵɵgetComponentDepsFactory(Z1,[se,_,ai]),encapsulation:2,changeDetection:d.OnPush})}}function Q1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",15),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.endpoint-required"))}function J1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",17),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function e0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",15),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.http-methods-required"))}function t0(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",17),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.RestConvertorTypeTranslationsMap.get(e)))}}function n0(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",27),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function i0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",40),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function a0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",40),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function r0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",28)(1,"div",20)(2,"div",21),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",33)(6,"mat-chip-listbox",34),t.ɵɵrepeaterCreate(7,i0,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(9,"mat-chip",35),t.ɵɵelement(10,"label",36),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"button",37,0),t.ɵɵpipe(13,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(12),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES))})),t.ɵɵelementStart(14,"tb-icon",38),t.ɵɵtext(15,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(16,"div",20)(17,"div",39),t.ɵɵtext(18,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",33)(20,"mat-chip-listbox",34),t.ɵɵrepeaterCreate(21,a0,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(23,"mat-chip",35),t.ɵɵelement(24,"label",36),t.ɵɵelementEnd()(),t.ɵɵelementStart(25,"button",37,1),t.ɵɵpipe(27,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(26),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.TIMESERIES))})),t.ɵɵelementStart(28,"tb-icon",38),t.ɵɵtext(29,"edit"),t.ɵɵelementEnd()()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,5,"gateway.attributes")),t.ɵɵadvance(3),t.ɵɵproperty("tbEllipsisChipList",e.converterAttributes),t.ɵɵadvance(),t.ɵɵrepeater(e.converterAttributes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(13,7,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.converterTelemetry),t.ɵɵadvance(),t.ɵɵrepeater(e.converterTelemetry),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(27,9,"action.edit"))}}function o0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",15),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.extension-required"))}function s0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",40),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function l0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",28)(1,"div",11)(2,"div",12),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",41),t.ɵɵelement(7,"input",42),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,o0,2,3,"tb-error-icon",15),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",43)(11,"div",24),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",25),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"div",20)(18,"div",21),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",44)(22,"mat-chip-listbox",34),t.ɵɵtemplate(23,s0,3,1,"mat-chip",45),t.ɵɵelementStart(24,"mat-chip",35),t.ɵɵelement(25,"label",36),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"button",37,2),t.ɵɵpipe(28,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(27),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.CUSTOM))})),t.ɵɵelementStart(29,"tb-icon",38),t.ɵɵtext(30,"edit"),t.ɵɵelementEnd()()()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.rest.extension")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,12,"gateway.extension")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,14,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("converter.extension").hasError("required")&&e.mappingFormGroup.get("converter.extension").touched?9:-1),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,16,"gateway.extension-configuration")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,18,"gateway.extension-configuration-hint")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,20,"gateway.keys")),t.ɵɵadvance(3),t.ɵɵproperty("tbEllipsisChipList",e.customKeys),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.customKeys),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(28,22,"action.edit"))}}class p0 extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.destroyRef=l,this.cd=p,this.mappingFormGroup=this.fb.group({endpoint:["",[me.required,me.pattern(pn)]],HTTPMethods:[[gt.POST],[me.required]],security:[{type:Ri.ANONYMOUS}],converter:this.fb.group({type:[wi.JSON,[]],deviceInfo:[],reportStrategy:[],attributes:[[]],timeseries:[[]],extension:["",[me.required,me.pattern(pn)]],extensionConfig:[]}),response:[]}),this.keysPopupClosed=!0,this.helpLink=`${O}/docs/iot-gateway/config/rest/#mapping-section`,this.httpMethods=Object.values(gt),this.converterTypes=Object.values(wi),this.restSourceTypes=Object.values(Si),this.SecurityMode=Ji,this.MappingKeysType=Ui,this.DeviceInfoType=Sa,this.RestConvertorTypeTranslationsMap=Pi,this.RestDataConversionTranslationsMap=Di,this.RestConverterType=wi,this.ConnectorType=dt,this.ReportStrategyDefaultValue=on,this.setInitialFormValue(),this.observeConverterTypeChange(),this.toggleConverterFieldsByType(this.converterType)}get converterType(){return this.mappingFormGroup.get("converter")?.get("type").value}get converterAttributes(){if(this.converterType)return this.mappingFormGroup.get("converter").value.attributes.map((e=>e.key))}get converterTelemetry(){if(this.converterType)return this.mappingFormGroup.get("converter").value.timeseries.map((e=>e.key))}get customKeys(){return Object.keys(this.mappingFormGroup.get("converter").value.extensionConfig??{})}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.mappingFormGroup.valid){const{converter:e,...t}=this.mappingFormGroup.value,{reportStrategy:n,...i}=e,a={...t,reportStrategy:n,converter:i};Y(a),this.dialogRef.close(a)}}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))this.popoverService.hidePopover(i);else{const e=this.mappingFormGroup.get("converter").get(n),t={keys:e.value,keysType:n,panelTitle:ji.get(n),addKeyTitle:Hi.get(n),deleteKeyTitle:Wi.get(n),noKeysText:$i.get(n),withReportStrategy:this.data.withReportStrategy,convertorType:this.converterType,connectorType:dt.REST};this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,_K,"leftBottom",!1,null,t,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(In(this.destroyRef)).subscribe((t=>{this.popoverComponent.hide(),e.patchValue(t),e.markAsDirty(),this.cd.markForCheck()})),this.popoverComponent.tbHideStart.pipe(In(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}}setInitialFormValue(){const{converter:e,reportStrategy:t,...n}=this.data.value;this.mappingFormGroup.patchValue({...n,converter:{...e,...t?{reportStrategy:t}:{}}},{emitEvent:!1})}observeConverterTypeChange(){this.mappingFormGroup.get("converter").get("type").valueChanges.pipe(In(this.destroyRef)).subscribe((e=>this.toggleConverterFieldsByType(e)))}toggleConverterFieldsByType(e){const t=e===wi.JSON;this.mappingFormGroup.get("converter").get("attributes")[t?"enable":"disable"]({emitEvent:!1}),this.mappingFormGroup.get("converter").get("timeseries")[t?"enable":"disable"]({emitEvent:!1}),this.mappingFormGroup.get("converter").get("extension")[t?"disable":"enable"]({emitEvent:!1}),this.mappingFormGroup.get("converter").get("extensionConfig")[t?"disable":"enable"]({emitEvent:!1})}static{this.ɵfac=function(e){return new(e||p0)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(rt.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:p0,selectors:[["tb-rest-mapping-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:59,vars:45,consts:[["attributesButton",""],["telemetryButton",""],["keysButton",""],[1,"h-full","w-[77vw]","max-w-3xl",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","endpoint",3,"placeholder"],["matSuffix","",3,"tooltipText"],["formControlName","HTTPMethods","multiple",""],[3,"value"],["formControlName","security",3,"mode"],["formGroupName","converter"],[1,"tb-form-row","space-between","tb-flex"],[1,"fixed-title-width"],["formControlName","type","appearance","fill"],[1,"tb-form-panel","stroked"],[1,"tb-form-panel-title"],[1,"tb-form-hint","tb-primary-fill"],["formControlName","deviceInfo","required","true",1,"device-info",3,"sourceTypes","convertorType","deviceInfoType","connectorType"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],[1,"tb-form-panel","no-border","no-padding","w-full"],["formControlName","response"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[1,"tb-flex","max-w-70%"],[1,"tb-flex","gw-chip-list",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["translate","",1,"fixed-title-width"],["tbTruncateWithTooltip",""],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex"],["matInput","","name","value","formControlName","extension",3,"placeholder"],[1,"tb-form-row","space-between","same-padding","tb-flex","column"],[1,"tb-flex","ellipsis-chips-container"],[4,"ngFor","ngForOf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",3)(1,"mat-toolbar",4)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",5)(6,"div",6),t.ɵɵelementStart(7,"button",7),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",8),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",9)(11,"div",10)(12,"div",11)(13,"div",12),t.ɵɵpipe(14,"translate"),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-form-field",13),t.ɵɵelement(18,"input",14),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,Q1,2,3,"tb-error-icon",15),t.ɵɵelementEnd()(),t.ɵɵelementStart(21,"div",11)(22,"div",12),t.ɵɵpipe(23,"translate"),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",13)(27,"mat-select",16),t.ɵɵrepeaterCreate(28,J1,2,2,"mat-option",17,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd(),t.ɵɵtemplate(30,e0,2,3,"tb-error-icon",15),t.ɵɵelementEnd()(),t.ɵɵelement(31,"tb-security-config",18),t.ɵɵelementContainerStart(32,19),t.ɵɵelementStart(33,"div",20)(34,"div",21),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"tb-toggle-select",22),t.ɵɵrepeaterCreate(38,t0,3,4,"tb-toggle-option",17,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()(),t.ɵɵelementStart(40,"div",23)(41,"div",24),t.ɵɵtext(42),t.ɵɵpipe(43,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"div",25),t.ɵɵtext(45),t.ɵɵpipe(46,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(47,"tb-device-info-table",26),t.ɵɵtemplate(48,n0,1,2,"tb-report-strategy",27)(49,r0,30,11,"div",28)(50,l0,31,24,"div",28),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelement(51,"tb-rest-response-config",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(52,"div",30)(53,"button",31),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(54),t.ɵɵpipe(55,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(56,"button",32),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(57),t.ɵɵpipe(58,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,23,"gateway.data-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.helpLink),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(14,25,"gateway.hints.rest.endpoint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,27,"gateway.rest.endpoint")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,29,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.mappingFormGroup.get("endpoint").hasError("required")&&n.mappingFormGroup.get("endpoint").touched?20:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(23,31,"gateway.hints.rest.http-methods")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(25,33,"gateway.rest.http-methods")),t.ɵɵadvance(4),t.ɵɵrepeater(n.httpMethods),t.ɵɵadvance(2),t.ɵɵconditional(n.mappingFormGroup.get("HTTPMethods").hasError("required")&&n.mappingFormGroup.get("HTTPMethods").touched?30:-1),t.ɵɵadvance(),t.ɵɵproperty("mode",n.SecurityMode.basic),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,35,"gateway.payload-type")),t.ɵɵadvance(3),t.ɵɵrepeater(n.converterTypes),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(43,37,"gateway.data-conversion")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(46,39,n.RestDataConversionTranslationsMap.get(n.converterType))," "),t.ɵɵadvance(2),t.ɵɵproperty("sourceTypes",n.restSourceTypes)("convertorType",n.RestConverterType.JSON)("deviceInfoType",n.DeviceInfoType.FULL)("connectorType",n.ConnectorType.REST),t.ɵɵadvance(),t.ɵɵconditional(n.data.withReportStrategy?48:-1),t.ɵɵadvance(),t.ɵɵconditional(n.converterType===n.RestConverterType.JSON?49:50),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(55,41,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.mappingFormGroup.invalid||!n.mappingFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(58,43,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(p0,[se,_,$n,A$,UY,Z1,ai,ni]),styles:['@charset "UTF-8";[_nghost-%COMP%] .device-info .fixed-title-width{min-width:0}'],changeDetection:d.OnPush})}}const c0=()=>["endpoint","httpMethods","securityType","actions"];function d0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",6)(1,"div",29),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"span",8),t.ɵɵelementStart(5,"button",10),t.ɵɵpipe(6,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageMapping(n))})),t.ɵɵelementStart(7,"mat-icon"),t.ɵɵtext(8,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",10),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"gateway.data-mapping")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,7,"action.search")))}function u0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rest.endpoint")))}function m0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",32)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.endpoint)}}function h0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",33),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rest.http-methods")," "))}function g0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",38),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function f0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",34)(1,"mat-chip-listbox",35),t.ɵɵrepeaterCreate(2,g0,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(4,"mat-chip",36),t.ɵɵelement(5,"label",37),t.ɵɵelementEnd()()()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵproperty("tbEllipsisChipList",e.HTTPMethods),t.ɵɵadvance(),t.ɵɵrepeater(e.HTTPMethods)}}function y0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.security-type")," "))}function v0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",39)(1,"div",31),t.ɵɵtext(2),t.ɵɵpipe(3,"titlecase"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,null==e.security?null:e.security.type))}}function x0(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",40)}function b0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",10),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageMapping(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",10),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteMapping(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function w0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,b0,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",41),t.ɵɵelementContainer(4,42),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",43)(6,"button",44),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",45),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",46,2),t.ɵɵelementContainer(11,42),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(4),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function S0(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",47)}function C0(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class _0 extends $a{getDatasource(){return new T0}manageMapping(e,t){e&&e.stopPropagation();const n=J(t),i=n?this.entityFormArray.at(t).value:{};this.getMappingDialog(i,n?"action.apply":"action.add").afterClosed().pipe(In(this.destroyRef)).subscribe((e=>{e&&(n?this.entityFormArray.at(t).patchValue(e):this.entityFormArray.push(this.fb.control(e)),this.entityFormArray.markAsDirty())}))}deleteMapping(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-mapping-title",{name:this.entityFormArray.at(t).value.endpoint}),this.translate.instant("gateway.delete-mapping-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(In(this.destroyRef)).subscribe((e=>{e&&(this.entityFormArray.removeAt(t),this.entityFormArray.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())||e.type?.toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}getMappingDialog(e,t){return this.dialog.open(p0,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,withReportStrategy:this.withReportStrategy}})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(_0)))(n||_0)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:_0,selectors:[["tb-rest-mapping-table"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>_0)),multi:!0},{provide:he,useExisting:c((()=>_0)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:47,vars:37,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-absolute-fill","size-full"],[1,"flex","size-full","flex-col"],[1,"gw-table-toolbar","mat-mdc-table-toolbar"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"gw-connector-table"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","!w-1/5",4,"matHeaderCellDef"],["class","w-1/5",4,"matCellDef"],["class","w-1/3 !pl-0",4,"matHeaderCellDef"],["class","w-1/3",4,"matCellDef"],["class","w-1/6",4,"matHeaderCellDef"],["class","w-1/6",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"!w-1/5"],["tbTruncateWithTooltip",""],[1,"w-1/5"],[1,"w-1/3","!pl-0"],[1,"w-1/3"],[1,"tb-flex",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text","http-method-label","font-medium"],["tbTruncateWithTooltip","",1,"http-method-label","font-medium"],[1,"w-1/6"],[1,"w-12"],[1,"lt-lg:!hidden","flex","min-w-24","flex-1","flex-row","items-stretch","justify-end","text-center"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,d0,13,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"button",7),t.ɵɵpipe(7,"translate"),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",8)(11,"mat-label"),t.ɵɵtext(12," "),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",9,0),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵpipe(17,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(18,"mat-icon"),t.ɵɵtext(19,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",11)(21,"table",12),t.ɵɵelementContainerStart(22,13),t.ɵɵtemplate(23,u0,4,3,"mat-header-cell",14)(24,m0,3,1,"mat-cell",15),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(25,13),t.ɵɵtemplate(26,h0,3,3,"mat-header-cell",16)(27,f0,6,1,"mat-cell",17),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(28,13),t.ɵɵtemplate(29,y0,3,3,"mat-header-cell",18)(30,v0,4,3,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(31,20),t.ɵɵtemplate(32,x0,1,0,"mat-header-cell",21)(33,w0,12,3,"mat-cell",22),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(34,S0,1,0,"mat-header-row",23)(35,C0,1,0,"mat-row",24),t.ɵɵelementEnd(),t.ɵɵelementStart(36,"section",25),t.ɵɵpipe(37,"async"),t.ɵɵelementStart(38,"button",26),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageMapping(i))})),t.ɵɵelementStart(39,"mat-icon",27),t.ɵɵtext(40,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"span"),t.ɵɵtext(42),t.ɵɵpipe(43,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(44,"span",28),t.ɵɵpipe(45,"async"),t.ɵɵtext(46," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵconditional(!1===t.ɵɵpipeBind1(4,21,n.dataSource.isEmpty())?3:-1),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,23,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,25,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,27,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","endpoint"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","httpMethods"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","securityType"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(35,c0))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(36,c0)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(37,29,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(43,31,"gateway.add-mapping")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(45,33,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(_0,[se,_,Wn,$n]),styles:[".http-method-label[_ngcontent-%COMP%]{font-size:12px}"],changeDetection:d.OnPush})}}let T0=class extends G{constructor(){super()}};function I0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" : ",e.value.value," ")}}function E0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function M0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function k0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",16)(1,"div",17),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",18)(5,"mat-form-field",19),t.ɵɵelement(6,"input",20),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,E0,3,3,"mat-icon",21),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",16)(10,"div",22),t.ɵɵtext(11,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",19),t.ɵɵelement(13,"input",23),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,M0,3,3,"mat-icon",21),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,5,"gateway.key")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.get("key").hasError("required")&&e.get("key").touched?8:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.get("value").hasError("required")&&e.get("value").touched?15:-1)}}function P0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵelementContainerStart(2,11),t.ɵɵelementStart(3,"mat-expansion-panel",12)(4,"mat-expansion-panel-header")(5,"mat-panel-title")(6,"div",13),t.ɵɵtext(7),t.ɵɵelementEnd(),t.ɵɵtemplate(8,I0,2,1,"div",13),t.ɵɵelementEnd()(),t.ɵɵtemplate(9,k0,16,11,"ng-template",14),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",15),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.$index,a=n.$count;t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i===a-1),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",null==e.value?null:e.value.key," "),t.ɵɵadvance(),t.ɵɵconditional(null!=e.value&&e.value.value?8:-1),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,"gateway.rest.delete-http-header"))}}function D0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3),t.ɵɵrepeaterCreate(1,P0,14,7,"div",9,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵrepeater(e.keysListFormArray.controls)}}function O0(e,n){1&e&&(t.ɵɵelementStart(0,"div",4)(1,"span",24),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rest.no-http-headers")))}class A0 extends q{constructor(e,t){super(t),this.fb=e,this.store=t,this.withReportStrategy=!0,this.keysDataApplied=p(),this.ReportStrategyDefaultValue=on}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}addKey(){this.keysListFormArray.push(this.fb.group({key:["",[me.required,me.pattern(pn)]],value:["",[me.required,me.pattern(pn)]]}))}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){this.keysDataApplied.emit(this.keysListFormArray.value.reduce(((e,{key:t,value:n})=>({...e,[t]:n})),{}))}prepareKeysFormArray(e){const t=[];return Object.keys(e)?.forEach((n=>{t.push(this.fb.group({key:[n,[me.required,me.pattern(pn)]],value:[e[n],[me.required,me.pattern(pn)]]}))})),this.fb.array(t)}static{this.ɵfac=function(e){return new(e||A0)(t.ɵɵdirectiveInject(de.UntypedFormBuilder),t.ɵɵdirectiveInject(Xe.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:A0,selectors:[["tb-rest-http-headers-panel"]],inputs:{keys:"keys",popover:"popover",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:18,vars:15,consts:[[1,"w-[77vw]","max-w-2xl"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],[1,"tb-form-panel","no-border","no-padding","h-[500px]"],[1,"tb-flex","no-flex","center","align-center","h-[500px]"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],["tbTruncateWithTooltip","",1,"max-w-[11vw]"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",1,"gw-delete-icon-button",3,"click","matTooltip"],[1,"tb-form-row"],[1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","type","text","name","value","formControlName","value",3,"placeholder"],[1,"tb-prompt"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,D0,3,0,"div",3)(6,O0,4,3,"div",4),t.ɵɵelementStart(7,"div")(8,"button",5),t.ɵɵlistener("click",(function(){return n.addKey()})),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",6)(12,"button",7),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"button",8),t.ɵɵlistener("click",(function(){return n.applyKeysData()})),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,7,"gateway.rest.http-headers"),""," ("+n.keysListFormArray.controls.length+")"," "),t.ɵɵadvance(2),t.ɵɵconditional(n.keysListFormArray.controls.length?5:6),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(10,9,"gateway.rest.add-http-header")," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,11,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,13,"action.apply")," "))},dependencies:t.ɵɵgetComponentDepsFactory(A0,[se,_]),encapsulation:2,changeDetection:d.OnPush})}}const F0=()=>({min:1}),R0=()=>({maxWidth:"970px"});function B0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.RestRequestTypesTranslationsMap.get(e)))}}function N0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.endpoint-required"))}function L0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.endpoint-pattern"))}function V0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function q0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.http-methods-required"))}function G0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,e))}}function z0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.rest.endpoint"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",21),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,N0,2,3,"tb-error-icon",22)(8,L0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",9)(10,"div",19),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"gateway.rest.http-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",20)(14,"mat-select",23),t.ɵɵrepeaterCreate(15,V0,2,2,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd(),t.ɵɵtemplate(17,q0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",9)(19,"div",24),t.ɵɵpipe(20,"translate"),t.ɵɵtext(21,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",20)(23,"mat-select",25),t.ɵɵrepeaterCreate(24,G0,3,4,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,6,"gateway.hints.rest.endpoint")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("endpoint").hasError("required")&&e.mappingFormGroup.get("endpoint").touched?7:e.mappingFormGroup.get("endpoint").hasError("pattern")&&e.mappingFormGroup.get("endpoint").touched?8:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,10,"gateway.hints.rest.http-methods")),t.ɵɵadvance(5),t.ɵɵrepeater(e.httpMethods),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("HTTPMethods").hasError("required")&&e.mappingFormGroup.get("HTTPMethods").touched?17:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(20,12,"gateway.hints.rest.scope-type")),t.ɵɵadvance(5),t.ɵɵrepeater(e.requestsScopeType)}}function U0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-required"))}function j0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-pattern"))}function H0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.device-name-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",26),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,U0,2,3,"tb-error-icon",22)(8,j0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.device-name-filter-hint")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("deviceNameFilter").hasError("required")&&e.mappingFormGroup.get("deviceNameFilter").touched?7:e.mappingFormGroup.get("deviceNameFilter").hasError("pattern")&&e.mappingFormGroup.get("deviceNameFilter").touched?8:-1)}}function W0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.method-filter-required"))}function $0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.method-filter-pattern"))}function K0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.method-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",27),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,W0,2,3,"tb-error-icon",22)(8,$0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.rest.method-filter")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("methodFilter").hasError("required")&&e.mappingFormGroup.get("methodFilter").touched?7:e.mappingFormGroup.get("methodFilter").hasError("pattern")&&e.mappingFormGroup.get("methodFilter").touched?8:-1)}}function Y0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-filter-required"))}function X0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-filter-pattern"))}function Z0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.attribute-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",28),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,Y0,2,3,"tb-error-icon",22)(8,X0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.rest.attribute-filter")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("attributeFilter").hasError("required")&&e.mappingFormGroup.get("attributeFilter").touched?7:e.mappingFormGroup.get("attributeFilter").hasError("pattern")&&e.mappingFormGroup.get("attributeFilter").touched?8:-1)}}function Q0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.request-url-expression-required"))}function J0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.request-url-expression-pattern"))}function e2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function t2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.value-expression-required"))}function n2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.value-expression-pattern"))}function i2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",29)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.rest.request-url-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",30),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,Q0,2,3,"tb-error-icon",22)(8,J0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",9)(10,"div",19),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"gateway.rest.http-method"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",20)(14,"mat-select",31),t.ɵɵrepeaterCreate(15,e2,2,2,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div",9)(18,"div",19),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"gateway.value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",20),t.ɵɵelement(22,"input",32),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,t2,2,3,"tb-error-icon",22)(25,n2,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,7,"gateway.hints.rest.attribute-url-expression")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("requestUrlExpression").hasError("required")&&e.mappingFormGroup.get("requestUrlExpression").touched?7:e.mappingFormGroup.get("requestUrlExpression").hasError("pattern")&&e.mappingFormGroup.get("requestUrlExpression").touched?8:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,11,"gateway.hints.rest.http-method")),t.ɵɵadvance(5),t.ɵɵrepeater(e.httpMethods),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(19,13,"gateway.hints.rest.value-expression")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("valueExpression").hasError("required")&&e.mappingFormGroup.get("valueExpression").touched?24:e.mappingFormGroup.get("valueExpression").hasError("pattern")&&e.mappingFormGroup.get("valueExpression").touched?25:-1)}}function a2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,F0)))}function r2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function o2(e,n){1&e&&(t.ɵɵelementStart(0,"span",34),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function s2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵtext(2,"gateway.response-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",20),t.ɵɵelement(4,"input",33),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,a2,2,5,"tb-error-icon",22)(7,r2,2,3,"tb-error-icon",22)(8,o2,2,0,"span",34),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,2,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("responseTimeout").hasError("min")&&e.mappingFormGroup.get("responseTimeout").touched?6:e.mappingFormGroup.get("responseTimeout").hasError("pattern")&&e.mappingFormGroup.get("responseTimeout").touched?7:8)}}function l2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-required"))}function p2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-pattern"))}function c2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-name-expression-required"))}function d2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-name-expression-pattern"))}function u2(e,n){1&e&&t.ɵɵelement(0,"div",38),2&e&&t.ɵɵproperty("tb-help-popup","widget/lib/gateway/rest-json_fn")("tb-help-popup-style",t.ɵɵpureFunction0(2,R0))}function m2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.device-name-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",35),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,l2,2,3,"tb-error-icon",22)(8,p2,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",9)(10,"div",36),t.ɵɵtext(11,"gateway.attribute-name-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",20),t.ɵɵelement(13,"input",37),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,c2,2,3,"tb-error-icon",22)(16,d2,2,3,"tb-error-icon",22)(17,u2,1,3,"div",38),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.device-name-filter-hint")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("deviceNameExpression").hasError("required")&&e.mappingFormGroup.get("deviceNameExpression").touched?7:e.mappingFormGroup.get("deviceNameExpression").hasError("pattern")&&e.mappingFormGroup.get("deviceNameExpression").touched?8:-1),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("attributeNameExpression").hasError("required")&&e.mappingFormGroup.get("attributeNameExpression").touched?15:e.mappingFormGroup.get("attributeNameExpression").hasError("pattern")&&e.mappingFormGroup.get("attributeNameExpression").touched?16:17)}}function h2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,F0)))}function g2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function f2(e,n){1&e&&(t.ɵɵelementStart(0,"span",34),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function y2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵtext(2,"gateway.timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",20),t.ɵɵelement(4,"input",39),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,h2,2,5,"tb-error-icon",22)(7,g2,2,3,"tb-error-icon",22)(8,f2,2,0,"span",34),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,2,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("timeout").hasError("min")&&e.mappingFormGroup.get("timeout").touched?6:e.mappingFormGroup.get("timeout").hasError("pattern")&&e.mappingFormGroup.get("timeout").touched?7:8)}}function v2(e,n){1&e&&(t.ɵɵelementStart(0,"div",14)(1,"mat-slide-toggle",40)(2,"mat-label",41),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.hints.rest.update-ssl")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.rest.ssl-verify")," "))}function x2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",53),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.value)}}function b2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,F0)))}function w2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function S2(e,n){1&e&&(t.ɵɵelementStart(0,"span",34),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function C2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.tries-min"))}function _2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.tries-pattern"))}function T2(e,n){1&e&&(t.ɵɵelementStart(0,"div",14)(1,"mat-slide-toggle",54)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.rest.allow-redirects")," "))}function I2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",8)(1,"mat-expansion-panel",42)(2,"mat-expansion-panel-header")(3,"mat-panel-title",43)(4,"div",44),t.ɵɵtext(5,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(6,"div",45)(7,"div",10),t.ɵɵtext(8,"gateway.rest.http-headers"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"div",46)(10,"mat-chip-listbox",47),t.ɵɵpipe(11,"keyvalue"),t.ɵɵrepeaterCreate(12,x2,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵpipe(14,"keyvalue"),t.ɵɵelementStart(15,"mat-chip",48),t.ɵɵelement(16,"label",49),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"button",50,0),t.ɵɵpipe(19,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(18),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i))})),t.ɵɵelementStart(20,"tb-icon",51),t.ɵɵtext(21,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(22,"div",9)(23,"div",10),t.ɵɵtext(24,"gateway.timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",20),t.ɵɵelement(26,"input",39),t.ɵɵpipe(27,"translate"),t.ɵɵtemplate(28,b2,2,5,"tb-error-icon",22)(29,w2,2,3,"tb-error-icon",22)(30,S2,2,0,"span",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(31,"div",9)(32,"div",10),t.ɵɵtext(33,"gateway.rest.tries"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"mat-form-field",20),t.ɵɵelement(35,"input",52),t.ɵɵpipe(36,"translate"),t.ɵɵtemplate(37,C2,2,3,"tb-error-icon",22)(38,_2,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵtemplate(39,T2,5,3,"div",14),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(10),t.ɵɵproperty("tbEllipsisChipList",t.ɵɵpipeBind1(11,7,e.mappingFormGroup.get("httpHeaders").value)),t.ɵɵadvance(2),t.ɵɵrepeater(t.ɵɵpipeBind1(14,9,e.mappingFormGroup.get("httpHeaders").value)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,11,"action.edit")),t.ɵɵadvance(9),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(27,13,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("timeout").hasError("min")&&e.mappingFormGroup.get("timeout").touched?28:e.mappingFormGroup.get("timeout").hasError("pattern")&&e.mappingFormGroup.get("timeout").touched?29:30),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("tries").hasError("min")&&e.mappingFormGroup.get("tries").touched?37:e.mappingFormGroup.get("tries").hasError("pattern")&&e.mappingFormGroup.get("tries").touched?38:-1),t.ɵɵadvance(2),t.ɵɵconditional(e.requestType===e.RestRequestType.ATTRIBUTE_UPDATE?39:-1)}}class E2 extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.cdr=l,this.destroyRef=p,this.requestTypes=Object.values(Ii),this.requestsScopeType=Object.values(Mi),this.httpMethods=Object.values(gt),this.helpLink=`${O}/docs/iot-gateway/config/rest/#attribute-request-section`,this.RestRequestTypesTranslationsMap=Ei,this.SecurityMode=Ji,this.RestRequestType=Ii,this.mappingFormGroup=this.fb.group({requestType:[Ii.ATTRIBUTE_REQUEST],endpoint:["",[me.required,me.pattern(pn)]],HTTPMethods:[[gt.POST],[me.required]],HTTPMethod:[gt.POST,[me.required]],type:[Mi.Shared],security:[{type:Ri.ANONYMOUS}],timeout:[null,[me.min(.001),me.pattern(ln)]],deviceNameExpression:["",[me.required,me.pattern(pn)]],attributeNameExpression:["",[me.required,me.pattern(pn)]],SSLVerify:[!1],deviceNameFilter:["",[me.required,me.pattern(pn)]],methodFilter:["",[me.required,me.pattern(pn)]],attributeFilter:["",[me.required,me.pattern(pn)]],requestUrlExpression:[this.data.defaultRequestUrl,[me.required,me.pattern(pn)]],valueExpression:["",[me.required,me.pattern(pn)]],responseTimeout:[null,[me.min(.001),me.pattern(ln)]],tries:[null,[me.min(1),me.pattern(ln)]],allowRedirects:[!1],httpHeaders:[{}]}),this.keysPopupClosed=!0,this.mappingFormGroup.patchValue(this.data.value,{emitEvent:!1}),this.observeRequestTypeChange(),this.toggleFieldsByRequestType(this.mappingFormGroup.get("requestType").value)}get requestType(){return this.mappingFormGroup.get("requestType").value}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.mappingFormGroup.valid){const{requestType:e,...t}=this.mappingFormGroup.value;Y(t),this.dialogRef.close({requestType:e,requestValue:t})}}manageKeys(e,t){e.stopPropagation();const n=t._elementRef.nativeElement;if(this.popoverService.hasPopover(n))return void this.popoverService.hidePopover(n);this.popoverService.hasPopover(n)&&this.popoverService.hidePopover(n),this.keysPopupClosed=!1;const i=this.popoverService.displayPopover(n,this.renderer,this.viewContainerRef,A0,"leftBottom",!1,null,{keys:this.mappingFormGroup.get("httpHeaders").value,withReportStrategy:this.data.withReportStrategy},{},{},{},!0);i.tbComponentRef.instance.popover=i,i.tbComponentRef.instance.keysDataApplied.subscribe((e=>{i.hide(),this.mappingFormGroup.get("httpHeaders").patchValue(e),this.mappingFormGroup.get("httpHeaders").markAsDirty(),this.cdr.markForCheck()})),i.tbHideStart.pipe(In(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}observeRequestTypeChange(){this.mappingFormGroup.get("requestType").valueChanges.pipe(Pe(),In(this.destroyRef)).subscribe((e=>this.toggleFieldsByRequestType(e)))}toggleFieldsByRequestType(e){this.mappingFormGroup.disable({emitEvent:!1}),ki.get("all").forEach((e=>this.mappingFormGroup.get(e).enable({emitEvent:!1}))),ki.get(e).forEach((e=>this.mappingFormGroup.get(e).enable({emitEvent:!1})))}static{this.ɵfac=function(e){return new(e||E2)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(Ze.Router),t.ɵɵdirectiveInject(Ue),t.ɵɵdirectiveInject(je.MatDialogRef),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(rt.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:E2,selectors:[["tb-rest-requests-mapping-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:37,vars:23,consts:[["button",""],[1,"h-full","w-[77vw]","max-w-3xl",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row"],["translate","",1,"fixed-title-width"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex"],["formControlName","requestType"],[3,"value"],[1,"tb-form-row","tb-flex","fill-width"],["formControlName","security",3,"mode"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","type","text","name","value","formControlName","endpoint",3,"placeholder"],["matSuffix","",3,"tooltipText"],["formControlName","HTTPMethods","multiple",""],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","type"],["matInput","","type","text","name","value","formControlName","deviceNameFilter",3,"placeholder"],["matInput","","type","text","name","value","formControlName","methodFilter",3,"placeholder"],["matInput","","type","text","name","value","formControlName","attributeFilter",3,"placeholder"],[1,"tb-form-row","request-url-row"],["matInput","","type","text","name","value","formControlName","requestUrlExpression",3,"placeholder"],["formControlName","HTTPMethod"],["matInput","","type","text","name","value","formControlName","valueExpression",3,"placeholder"],["matInput","","type","number","min","0","name","value","formControlName","responseTimeout",3,"placeholder"],["translate","","matSuffix","",1,"block","pr-2"],["matInput","","type","text","name","value","formControlName","deviceNameExpression",3,"placeholder"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","type","text","name","value","formControlName","attributeNameExpression",3,"placeholder"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["formControlName","SSLVerify",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],[1,"tb-settings"],[1,"justify-end"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","space-between","tb-flex"],[1,"tb-flex","gw-chip-list"],[1,"tb-flex",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["matInput","","type","number","min","0","name","value","formControlName","tries",3,"placeholder"],["tbTruncateWithTooltip",""],["formControlName","allowRedirects",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",1)(1,"mat-toolbar",2)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",3)(6,"div",4),t.ɵɵelementStart(7,"button",5),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",6),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",7)(11,"div",8)(12,"div",9)(13,"div",10),t.ɵɵtext(14,"gateway.request-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-form-field",11)(16,"mat-select",12),t.ɵɵrepeaterCreate(17,B0,3,4,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()(),t.ɵɵtemplate(19,z0,26,14)(20,H0,9,7,"div",9)(21,K0,9,7,"div",9)(22,Z0,9,7,"div",9)(23,i2,26,17)(24,s2,9,4,"div",9)(25,m2,18,11)(26,y2,9,4,"div",9)(27,v2,6,6,"div",14),t.ɵɵelement(28,"tb-security-config",15),t.ɵɵtemplate(29,I2,40,17,"div",8),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"div",16)(31,"button",17),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(32),t.ɵɵpipe(33,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"button",18),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,17,"gateway.requests-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.helpLink),t.ɵɵadvance(11),t.ɵɵrepeater(n.requestTypes),t.ɵɵadvance(2),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_REQUEST?19:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType!==n.RestRequestType.ATTRIBUTE_REQUEST?20:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.SERVER_SIDE_RPC?21:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_UPDATE?22:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType!==n.RestRequestType.ATTRIBUTE_REQUEST?23:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.SERVER_SIDE_RPC?24:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_REQUEST?25:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_REQUEST?26:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_UPDATE?27:-1),t.ɵɵadvance(),t.ɵɵproperty("mode",n.SecurityMode.basic),t.ɵɵadvance(),t.ɵɵconditional(n.requestType!==n.RestRequestType.ATTRIBUTE_REQUEST?29:-1),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(33,19,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.mappingFormGroup.invalid||!n.mappingFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(36,21,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(E2,[se,_,$n,UY,ai]),styles:["[_nghost-%COMP%] .tb-form-row.request-url-row[_ngcontent-%COMP%]{gap:6px}"],changeDetection:d.OnPush})}}const M2=()=>["type","details","actions"],k2=()=>({});function P2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",6)(1,"div",26),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"span",8),t.ɵɵelementStart(5,"button",10),t.ɵɵpipe(6,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageMapping(n))})),t.ɵɵelementStart(7,"mat-icon"),t.ɵɵtext(8,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",10),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"gateway.requests-mapping")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,7,"action.search")))}function D2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",27)(1,"div",28),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.type")))}function O2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",29)(1,"div",28),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,i.RestRequestTypesTranslationsMap.get(e.requestType)))}}function A2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.details")," "))}function F2(e,n){if(1&e&&t.ɵɵtext(0),2&e){let e;const n=t.ɵɵnextContext().$implicit,i=t.ɵɵnextContext();t.ɵɵtextInterpolate1(" ",i.Object.values(null!==(e=n.requestValue.httpHeaders)&&void 0!==e?e:t.ɵɵpureFunction0(1,k2)).join(", ")," ")}}function R2(e,n){if(1&e&&t.ɵɵtext(0),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵtextInterpolate1(" ",e.requestValue.methodFilter," ")}}function B2(e,n){if(1&e&&t.ɵɵtext(0),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵtextInterpolate1(" ",e.requestValue.endpoint," ")}}function N2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",29)(1,"div",28),t.ɵɵtemplate(2,F2,1,2)(3,R2,1,1)(4,B2,1,1),t.ɵɵelementEnd()()),2&e){let e;const i=n.$implicit;t.ɵɵadvance(2),t.ɵɵconditional("attributeUpdates"===(e=i.requestType)?2:"serverSideRpc"===e?3:4)}}function L2(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",31)}function V2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",10),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageMapping(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",10),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteMapping(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function q2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,V2,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",32),t.ɵɵelementContainer(4,33),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",34)(6,"button",35),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",36),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",37,2),t.ɵɵelementContainer(11,33),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(4),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function G2(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",38)}function z2(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class U2 extends $a{constructor(){super(...arguments),this.Object=Object,this.RestRequestTypesTranslationsMap=Ei}getDatasource(){return new j2}manageMapping(e,t){e&&e.stopPropagation();const n=J(t),{requestType:i=Ii.ATTRIBUTE_REQUEST,requestValue:a={}}=n?this.entityFormArray.at(t).value:{};this.getMappingDialog({requestType:i,...a},n?"action.apply":"action.add").afterClosed().pipe(In(this.destroyRef)).subscribe((e=>{e&&(n?this.entityFormArray.at(t).patchValue(e):this.entityFormArray.push(this.fb.control(e)),this.entityFormArray.markAsDirty())}))}deleteMapping(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-mapping-title",{name:this.getRequestDetails(this.entityFormArray.at(t).value)}),this.translate.instant("gateway.delete-mapping-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(In(this.destroyRef)).subscribe((e=>{e&&(this.entityFormArray.removeAt(t),this.entityFormArray.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>{const n=this.getRequestDetails(e);return Object.values(e).some((e=>Q(e)&&this.translate.instant(Ei.get(e)).toLowerCase().includes(t.toLowerCase())||n?.toLowerCase().includes(t.toLowerCase())))}))),this.dataSource.loadData(e)}getRequestDetails(e){let t;switch(e.requestType){case"attributeUpdates":t=Object.values(e.requestValue.httpHeaders).join(", ");break;case"serverSideRpc":t=e.requestValue.methodFilter;break;default:t=e.requestValue.endpoint}return t}validate(){return null}getMappingDialog(e,t){return this.dialog.open(E2,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,defaultRequestUrl:this.defaultRequestUrl,withReportStrategy:this.withReportStrategy}})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(U2)))(n||U2)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:U2,selectors:[["tb-rest-request-mapping-table"]],inputs:{defaultRequestUrl:"defaultRequestUrl"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>U2)),multi:!0},{provide:he,useExisting:c((()=>U2)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:44,vars:36,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-absolute-fill","size-full"],[1,"flex","size-full","flex-col"],[1,"gw-table-toolbar","mat-mdc-table-toolbar"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"gw-connector-table"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","!w-1/3",4,"matHeaderCellDef"],["class","w-1/3",4,"matCellDef"],["class","w-1/3 !pl-0",4,"matHeaderCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"!w-1/3"],["tbTruncateWithTooltip",""],[1,"w-1/3"],[1,"w-1/3","!pl-0"],[1,"w-12"],[1,"lt-lg:!hidden","flex","min-w-24","flex-1","flex-row","items-stretch","justify-end","text-center"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,P2,13,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"button",7),t.ɵɵpipe(7,"translate"),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",8)(11,"mat-label"),t.ɵɵtext(12," "),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",9,0),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵpipe(17,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(18,"mat-icon"),t.ɵɵtext(19,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",11)(21,"table",12),t.ɵɵelementContainerStart(22,13),t.ɵɵtemplate(23,D2,4,3,"mat-header-cell",14)(24,O2,4,3,"mat-cell",15),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(25,13),t.ɵɵtemplate(26,A2,3,3,"mat-header-cell",16)(27,N2,5,1,"mat-cell",15),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(28,17),t.ɵɵtemplate(29,L2,1,0,"mat-header-cell",18)(30,q2,12,3,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(31,G2,1,0,"mat-header-row",20)(32,z2,1,0,"mat-row",21),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"section",22),t.ɵɵpipe(34,"async"),t.ɵɵelementStart(35,"button",23),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageMapping(i))})),t.ɵɵelementStart(36,"mat-icon",24),t.ɵɵtext(37,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"span"),t.ɵɵtext(39),t.ɵɵpipe(40,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(41,"span",25),t.ɵɵpipe(42,"async"),t.ɵɵtext(43," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵconditional(!1===t.ɵɵpipeBind1(4,20,n.dataSource.isEmpty())?3:-1),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,22,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,24,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,26,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","type"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","details"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(34,M2))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(35,M2)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(34,28,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(40,30,"gateway.add-mapping")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(42,32,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(U2,[se,_,Wn]),encapsulation:2,changeDetection:d.OnPush})}}class j2 extends G{constructor(){super()}}class H2 extends Ua{initBasicFormGroup(){return this.fb.group({server:[],mapping:[],requestsMapping:[]})}getRequestDataArray(e){const t=[];return ie(e)&&Object.keys(e).forEach((n=>{for(const i of e[n])t.push({requestType:n,requestValue:i})})),t}getRequestDataObject(e){return e.reduce(((e,{requestType:t,requestValue:n})=>(e[t]?.push(n),e)),{attributeRequests:[],attributeUpdates:[],serverSideRpc:[]})}updateDefaultUrl({host:e,port:t,SSL:n}){this.defaultRequestUrl=e&&t?`${n?"https":"http"}//${e}:${t}/`:document.location.origin}writeValue(e){this.basicFormGroup.setValue(this.mapConfigToFormValue(e),{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(H2)))(n||H2)}})()}static{this.ɵdir=t.ɵɵdefineDirective({type:H2,features:[t.ɵɵInheritDefinitionFeature]})}}class W2 extends H2{mapConfigToFormValue(e){return{server:e.server??{},mapping:e.mapping??[],requestsMapping:this.getRequestDataArray(e.requestsMapping)}}getMappedValue(e){return this.updateDefaultUrl(e?.server??{}),{server:e.server??{},mapping:e.mapping??[],requestsMapping:this.getRequestDataObject(e.requestsMapping??[])}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(W2)))(n||W2)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:W2,selectors:[["tb-rest-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>W2)),multi:!0},{provide:he,useExisting:c((()=>W2)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:13,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server"],["formControlName","mapping"],["formControlName","requestsMapping",3,"defaultRequestUrl"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-rest-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-rest-mapping-table",4),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-tab",1),t.ɵɵpipe(11,"translate"),t.ɵɵelement(12,"tb-rest-request-mapping-table",5),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.server"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(11,15,"gateway.requests-mapping")),t.ɵɵadvance(2),t.ɵɵproperty("defaultRequestUrl",n.defaultRequestUrl))},dependencies:t.ɵɵgetComponentDepsFactory(W2,[se,_,L1,_0,U2]),encapsulation:2,changeDetection:d.OnPush})}}class $2 extends H2{mapConfigToFormValue({attributeRequests:e=[],attributeUpdates:t=[],serverSideRpc:n=[],mapping:i=[],...a}){return{server:{...a??{}},mapping:er.mapMappingToUpgradedVersion(i),requestsMapping:this.getRequestDataArray({attributeRequests:e,attributeUpdates:t,serverSideRpc:n})}}getMappedValue({requestsMapping:e,mapping:t=[],server:n={}}){this.updateDefaultUrl(n);const{attributeRequests:i=[],attributeUpdates:a=[],serverSideRpc:r=[]}=this.getRequestDataObject(e);return{...n,mapping:er.mapMappingToDowngradedVersion(t),attributeRequests:i,attributeUpdates:a,serverSideRpc:r}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory($2)))(n||$2)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:$2,selectors:[["tb-rest-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>$2)),multi:!0},{provide:he,useExisting:c((()=>$2)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:13,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server"],["formControlName","mapping"],["formControlName","requestsMapping",3,"defaultRequestUrl"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-rest-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-rest-mapping-table",4),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-tab",1),t.ɵɵpipe(11,"translate"),t.ɵɵelement(12,"tb-rest-request-mapping-table",5),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.server"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(11,15,"gateway.requests-mapping")),t.ɵɵadvance(2),t.ɵɵproperty("defaultRequestUrl",n.defaultRequestUrl))},dependencies:t.ɵɵgetComponentDepsFactory($2,[se,_,L1,_0,U2]),encapsulation:2,changeDetection:d.OnPush})}}const K2=(e,t)=>({hasErrors:e,noErrors:t}),Y2=()=>({minWidth:"144px",maxWidth:"144px",textAlign:"center"}),X2=()=>({minWidth:"144px",maxWidth:"144px",width:"144px",textAlign:"center"}),Z2=e=>({"tb-current-entity":e});function Q2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",32),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"async"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onAddConnector(n))})),t.ɵɵelementStart(3,"mat-icon"),t.ɵɵtext(4,"add"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.add")),t.ɵɵproperty("disabled",t.ɵɵpipeBind1(2,4,e.isLoading$))}}function J2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",33)(1,"button",34),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onAddConnector(n))})),t.ɵɵelementStart(2,"mat-icon",35),t.ɵɵtext(3,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"span"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,1,"gateway.add-connector")))}function e3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",36),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-enabled")," "))}function t3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell")(1,"mat-slide-toggle",37),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return n.stopPropagation(),t.ɵɵresetView(a.onEnableConnector(i))})),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("checked",i.activeConnectors.includes(e.key))}}function n3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",38),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-name"),""))}function i3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function a3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-type")," "))}function r3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",40),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.returnType(e)," ")}}function o3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.configuration")," "))}function s3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",40)(1,"div",41),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassMap(i.isConnectorSynced(e)?"status-sync":"status-unsync"),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.isConnectorSynced(e)?"sync":"out of sync"," ")}}function l3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-status")," "))}function p3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell",40)(1,"span",42),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorLogs(i,n))})),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassMap(t.ɵɵpureFunction2(3,K2,+i.getErrorsCount(e)>0,0==+i.getErrorsCount(e)||""===i.getErrorsCount(e))),t.ɵɵpropertyInterpolate("matTooltip","Errors: "+i.getErrorsCount(e))}}function c3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell"),t.ɵɵelement(1,"div",43),t.ɵɵelementStart(2,"div",44),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,Y2)),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.connectors-table-actions")))}function d3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell")(1,"div",45)(2,"button",46),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorRpc(i,n))})),t.ɵɵelementStart(4,"mat-icon"),t.ɵɵtext(5,"private_connectivity"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"button",47),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorLogs(i,n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"list"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",47),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteConnector(i,n))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(14,"div",48)(15,"button",49),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(16,"mat-icon",50),t.ɵɵtext(17,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"mat-menu",51,1)(20,"button",46),t.ɵɵpipe(21,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorRpc(i,n))})),t.ɵɵelementStart(22,"mat-icon"),t.ɵɵtext(23,"private_connectivity"),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"button",47),t.ɵɵpipe(25,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorLogs(i,n))})),t.ɵɵelementStart(26,"mat-icon"),t.ɵɵtext(27,"list"),t.ɵɵelementEnd()(),t.ɵɵelementStart(28,"button",47),t.ɵɵpipe(29,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteConnector(i,n))})),t.ɵɵelementStart(30,"mat-icon"),t.ɵɵtext(31,"delete"),t.ɵɵelementEnd()()()()()}if(2&e){const e=n.$implicit,i=t.ɵɵreference(19);t.ɵɵadvance(),t.ɵɵstyleMap(t.ɵɵpureFunction0(23,X2)),t.ɵɵadvance(),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(3,11,"gateway.rpc.rpc"))("disabled",!e.value.configurationJson.id),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(7,13,"gateway.logs.logs")),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,15,"gateway.delete-connector")),t.ɵɵadvance(5),t.ɵɵproperty("matMenuTriggerFor",i),t.ɵɵadvance(5),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(21,17,"gateway.rpc.rpc"))("disabled",!e.value.configurationJson.id),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(25,19,"gateway.logs.logs")),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(29,21,"gateway.delete-connector"))}}function u3(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",52)}function m3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-row",53),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.selectConnector(n,i))})),t.ɵɵelementEnd()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵclassMap(t.ɵɵpureFunction1(2,Z2,i.isSameConnector(e)))}}function h3(e,n){if(1&e&&(t.ɵɵelementStart(0,"span",54),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1("v",e.connectorForm.get("configVersion").value,"")}}function g3(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-select",55)(1,"tb-toggle-option",56),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"tb-toggle-option",56),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("value",e.ConnectorConfigurationModes.BASIC),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,4,"gateway.basic")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",e.ConnectorConfigurationModes.ADVANCED),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,6,"gateway.advanced")," ")}}function f3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-mqtt-basic-config",64),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function y3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-mqtt-legacy-basic-config",64),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function v3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,f3,2,4,"tb-mqtt-basic-config",63),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,y3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.MQTT))("ngIfElse",e)}}function x3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-opc-ua-basic-config",64),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function b3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-opc-ua-legacy-basic-config",64),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function w3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,x3,2,4,"tb-opc-ua-basic-config",63),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,b3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.OPCUA))("ngIfElse",e)}}function S3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-modbus-basic-config",66),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function C3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-modbus-legacy-basic-config",66),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function _3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,S3,1,1,"tb-modbus-basic-config",65),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,C3,1,1,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.MODBUS))("ngIfElse",e)}}function T3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-socket-basic-config",66),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function I3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-socket-legacy-basic-config",66),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function E3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,T3,1,1,"tb-socket-basic-config",65),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,I3,1,1,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.SOCKET))("ngIfElse",e)}}function M3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-bacnet-basic-config",64),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function k3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-bacnet-legacy-basic-config",64),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function P3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,M3,2,4,"tb-bacnet-basic-config",63),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,k3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.BACNET))("ngIfElse",e)}}function D3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-rest-basic-config",68),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function O3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-rest-legacy-basic-config",68),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function A3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,D3,2,4,"tb-rest-basic-config",67),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,O3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.REST))("ngIfElse",e)}}function F3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0)(1,61),t.ɵɵtemplate(2,v3,5,5,"ng-container",62)(3,w3,5,5,"ng-container",62)(4,_3,5,5,"ng-container",62)(5,E3,5,5,"ng-container",62)(6,P3,5,5,"ng-container",62)(7,A3,5,5,"ng-container",62),t.ɵɵelementContainerEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("ngSwitch",e.initialConnector.type),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MQTT),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.OPCUA),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MODBUS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.SOCKET),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.BACNET),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.REST)}}function R3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-tab-group")(1,"mat-tab",69),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,70),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",69),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-json-object-edit",71),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()),2&e){t.ɵɵnextContext(2);const e=t.ɵɵreference(41);t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,6,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,8,"gateway.configuration"),"*"),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,10,"gateway.configuration")),t.ɵɵproperty("fillHeight",!0)}}function B3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",57),t.ɵɵtemplate(1,F3,8,7,"ng-container",58)(2,R3,8,12,"ng-template",null,2,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(4,"div",59)(5,"button",60),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.onSaveConnector())})),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()}if(2&e){let e;const n=t.ɵɵreference(3),i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngIf",(null==(e=i.connectorForm.get("mode"))?null:e.value)===i.ConnectorConfigurationModes.BASIC)("ngIfElse",n),t.ɵɵadvance(4),t.ɵɵproperty("disabled",!i.connectorForm.dirty||i.connectorForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,4,"action.save")," ")}}function N3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",88),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.connectorForm.get("name").hasError("duplicateName")?"gateway.connector-duplicate-name":"gateway.name-required"))}}function L3(e,n){1&e&&(t.ɵɵelementStart(0,"div",73)(1,"div",84),t.ɵɵtext(2,"gateway.connectors-table-class"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",75)(4,"mat-form-field",76),t.ɵɵelement(5,"input",89),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,1,"gateway.set")))}function V3(e,n){1&e&&(t.ɵɵelementStart(0,"div",73)(1,"div",84),t.ɵɵtext(2,"gateway.connectors-table-key"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",75)(4,"mat-form-field",76),t.ɵɵelement(5,"input",90),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,1,"gateway.set")))}function q3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",56),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function G3(e,n){1&e&&(t.ɵɵelementStart(0,"div",73)(1,"mat-slide-toggle",91)(2,"mat-label",92),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.send-change-data-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.send-change-data")," "))}function z3(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",93),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Connector)}}function U3(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",72)(1,"div",73)(2,"div",74),t.ɵɵtext(3,"gateway.name"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",75)(5,"mat-form-field",76),t.ɵɵelement(6,"input",77),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,N3,3,3,"mat-icon",78),t.ɵɵelementEnd()()(),t.ɵɵtemplate(9,L3,7,3,"div",79)(10,V3,7,3,"div",79),t.ɵɵelementStart(11,"div",80)(12,"div",81),t.ɵɵtext(13,"gateway.logs-configuration"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",82)(15,"mat-slide-toggle",83)(16,"mat-label"),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(19,"div",73)(20,"div",84),t.ɵɵtext(21,"gateway.remote-logging-level"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"div",75)(23,"mat-form-field",76)(24,"mat-select",85),t.ɵɵtemplate(25,q3,2,2,"mat-option",86),t.ɵɵelementEnd()()()()(),t.ɵɵtemplate(26,G3,6,6,"div",79),t.ɵɵpipe(27,"withReportStrategy"),t.ɵɵtemplate(28,z3,1,2,"tb-report-strategy",87),t.ɵɵpipe(29,"withReportStrategy"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("formGroup",e.connectorForm),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.connectorForm.get("name").hasError("required")&&e.connectorForm.get("name").touched||e.connectorForm.get("name").hasError("duplicateName")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.connectorForm.get("type").value===e.ConnectorType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.connectorForm.get("type").value===e.ConnectorType.GRPC),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,11,"gateway.enable-remote-logging")," "),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",e.gatewayLogLevel),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.connectorForm.get("type").value===e.ConnectorType.MQTT&&!t.ɵɵpipeBind2(27,13,e.connectorForm.get("configVersion").value,e.ConnectorType.MQTT)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(29,16,e.connectorForm.get("configVersion").value,e.connectorForm.get("type").value))}}class j3{isErrorState(e){return e&&e.invalid}}e("ForceErrorStateMatcher",j3);class H3 extends q{constructor(e,t,n,i,a,r,o,s,l,p,c){super(e),this.store=e,this.fb=t,this.translate=n,this.attributeService=i,this.dialogService=a,this.dialog=r,this.telemetryWsService=o,this.zone=s,this.utils=l,this.withReportStrategy=p,this.cd=c,this.ConnectorType=dt,this.allowBasicConfig=new Set([dt.MQTT,dt.OPCUA,dt.MODBUS,dt.SOCKET,dt.BACNET,dt.REST]),this.gatewayLogLevel=Object.values(pt),this.displayedColumns=["enabled","key","type","syncStatus","errors","actions"],this.GatewayConnectorTypesTranslatesMap=ut,this.ConnectorConfigurationModes=an,this.ReportStrategyDefaultValue=on,this.basicConfigInitSubject=new we,this.activeData=[],this.inactiveData=[],this.sharedAttributeData=[],this.subscriptionOptions={callbacks:{onDataUpdated:()=>this.ctx.ngZone.run((()=>{this.onErrorsUpdated()})),onDataUpdateError:(e,t)=>this.ctx.ngZone.run((()=>{this.onDataUpdateError(t)}))}},this.destroy$=new we,this.attributeUpdateSubject=new we,this.initDataSources(),this.initConnectorForm(),this.observeAttributeChange()}ngAfterViewInit(){this.dataSource.sort=this.sort,this.dataSource.sortingDataAccessor=this.getSortingDataAccessor(),this.ctx.$scope.gatewayConnectors=this,this.loadConnectors(),this.loadGatewayState(),this.observeModeChange()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}onSaveConnector(){this.saveConnector(this.getUpdatedConnectorData(this.connectorForm.value),!1)}saveConnector(e,t=!0){const n=t||this.activeConnectors.includes(this.initialConnector.name)?D.SHARED_SCOPE:D.SERVER_SCOPE;Ie(this.getEntityAttributeTasks(e,n)).pipe(Ne(1)).subscribe((n=>{this.showToast(t?this.translate.instant("gateway.connector-created"):this.translate.instant("gateway.connector-updated")),this.initialConnector=e,this.updateData(!0),this.connectorForm.markAsPristine()}))}getEntityAttributeTasks(e,t){const n=[],i=[{key:e.name,value:e}],a=[],r=!this.activeConnectors.includes(e.name)&&t===D.SHARED_SCOPE||!this.inactiveConnectors.includes(e.name)&&t===D.SERVER_SCOPE,o=this.initialConnector&&this.initialConnector.name!==e.name;return o&&(a.push({key:this.initialConnector.name}),this.removeConnectorFromList(this.initialConnector.name,!0),this.removeConnectorFromList(this.initialConnector.name,!1)),r&&(t===D.SHARED_SCOPE?this.activeConnectors.push(e.name):this.inactiveConnectors.push(e.name)),(o||r)&&n.push(this.getSaveEntityAttributesTask(t)),n.push(this.attributeService.saveEntityAttributes(this.device,t,i)),a.length&&n.push(this.attributeService.deleteEntityAttributes(this.device,t,a)),n}getSaveEntityAttributesTask(e){const t=e===D.SHARED_SCOPE?"active_connectors":"inactive_connectors",n=e===D.SHARED_SCOPE?this.activeConnectors:this.inactiveConnectors;return this.attributeService.saveEntityAttributes(this.device,e,[{key:t,value:n}])}removeConnectorFromList(e,t){const n=t?this.activeConnectors:this.inactiveConnectors,i=n.indexOf(e);-1!==i&&n.splice(i,1)}getUpdatedConnectorData(e){const t={...e};return t.configuration=`${re(t.name)}.json`,delete t.basicConfig,t.type!==dt.GRPC&&delete t.key,t.type!==dt.CUSTOM&&delete t.class,this.allowBasicConfig.has(t.type)||delete t.mode,this.withReportStrategy.transform(t.configVersion,t.type)&&(t.configurationJson.reportStrategy=t.reportStrategy,ne(t.reportStrategy)&&delete t.reportStrategy,ne(t.configurationJson.reportStrategy)&&delete t.configurationJson.reportStrategy),this.gatewayVersion&&!t.configVersion&&(t.configVersion=this.gatewayVersion),t.ts=Date.now(),t}updateData(e=!1){this.pageLink.sortOrder.property=this.sort.active,this.pageLink.sortOrder.direction=w[this.sort.direction.toUpperCase()],this.attributeDataSource.loadAttributes(this.device,D.CLIENT_SCOPE,this.pageLink,e).subscribe((e=>{this.activeData=e.data.filter((e=>this.activeConnectors.includes(e.key))),this.combineData(),this.generateSubscription(),this.setClientData(e)})),this.inactiveConnectorsDataSource.loadAttributes(this.device,D.SHARED_SCOPE,this.pageLink,e).subscribe((e=>{this.sharedAttributeData=e.data.filter((e=>this.activeConnectors.includes(e.key))),this.combineData()})),this.serverDataSource.loadAttributes(this.device,D.SERVER_SCOPE,this.pageLink,e).subscribe((e=>{this.inactiveData=e.data.filter((e=>this.inactiveConnectors.includes(e.key))),this.combineData()}))}isConnectorSynced(e){const t=e.value;if(!t.ts||e.skipSync||!this.isGatewayActive)return!1;if(-1===this.activeData.findIndex((e=>("string"==typeof e.value?JSON.parse(e.value):e.value).name===t.name)))return!1;return-1!==this.sharedAttributeData.findIndex((e=>{const n=e.value,i=n.name===t.name,a=W(n.configurationJson,{})&&i,r=this.hasSameConfig(n.configurationJson,t.configurationJson),o=n.ts&&n.ts<=t.ts;return i&&o&&(r||a)}))}hasSameConfig(e,t){const{name:n,id:i,enableRemoteLogging:a,logLevel:r,reportStrategy:o,configVersion:s,...l}=e,{name:p,id:c,enableRemoteLogging:d,logLevel:u,reportStrategy:m,configVersion:h,...g}=t;return W(l,g)}combineData(){const e=[...this.activeData,...this.inactiveData,...this.sharedAttributeData].reduce(((e,t)=>{const n=e.findIndex((e=>e.key===t.key));return-1===n?e.push(t):t.lastUpdateTs>e[n].lastUpdateTs&&!this.isConnectorSynced(e[n])&&(e[n]={...t,skipSync:!0}),e}),[]);this.dataSource.data=e.map((e=>({...e,value:"string"==typeof e.value?JSON.parse(e.value):e.value})))}clearOutConnectorForm(){this.initialConnector=null,this.connectorForm.setValue({mode:an.BASIC,name:"",type:dt.MQTT,sendDataOnlyOnChange:!1,enableRemoteLogging:!1,logLevel:pt.INFO,key:"auto",class:"",configuration:"",configurationJson:{},basicConfig:{},configVersion:"",reportStrategy:[{value:{},disabled:!0}]},{emitEvent:!1}),this.connectorForm.markAsPristine()}selectConnector(e,t){e&&e.stopPropagation();const n=t.value;n?.name!==this.initialConnector?.name&&this.confirmConnectorChange().subscribe((e=>{e&&this.setFormValue(n)}))}isSameConnector(e){if(!this.initialConnector)return!1;const t=e.value;return this.initialConnector.name===t.name}showToast(e){this.store.dispatch({type:"[Notification] Show",notification:{message:e,type:"success",duration:1e3,verticalPosition:"top",horizontalPosition:"left",target:"dashboardRoot",forceDismiss:!0}})}returnType(e){const t=e.value;return this.GatewayConnectorTypesTranslatesMap.get(t.type)}deleteConnector(e,t){t?.stopPropagation();const n=`Delete connector "${e.key}"?`;this.dialogService.confirm(n,"All connector data will be deleted.","Cancel","Delete").pipe(Ne(1),Ve((t=>{if(!t)return;const n=[],i=this.activeConnectors.includes(e.value?.name)?D.SHARED_SCOPE:D.SERVER_SCOPE;return n.push(this.attributeService.deleteEntityAttributes(this.device,i,[e])),this.removeConnectorFromList(e.key,!0),this.removeConnectorFromList(e.key,!1),n.push(this.getSaveEntityAttributesTask(i)),Ie(n)}))).subscribe((()=>{this.initialConnector&&this.initialConnector.name!==e.key||(this.clearOutConnectorForm(),this.cd.detectChanges()),this.updateData(!0)}))}connectorLogs(e,t){t&&t.stopPropagation();const n=te(this.ctx.stateController.getStateParams());n.connector_logs=e,n.targetEntityParamName="connector_logs",this.ctx.stateController.openState("connector_logs",n)}connectorRpc(e,t){t&&t.stopPropagation();const n=te(this.ctx.stateController.getStateParams());n.connector_rpc=e,n.targetEntityParamName="connector_rpc",this.ctx.stateController.openState("connector_rpc",n)}onEnableConnector(e){e.value.ts=(new Date).getTime(),this.updateActiveConnectorKeys(e.key),this.attributeUpdateSubject.next(e)}getErrorsCount(e){const t=e.key,n=this.subscription&&this.subscription.data.find((e=>e&&e.dataKey.name===`${t}_ERRORS_COUNT`));return n&&this.activeConnectors.includes(t)?n.data[0][1]||0:"Inactive"}onAddConnector(e){e?.stopPropagation(),this.confirmConnectorChange().pipe(Ne(1),Oe(Boolean),Ve((()=>this.openAddConnectorDialog())),Oe(Boolean)).subscribe((e=>this.addConnector(e)))}addConnector(e){e.configurationJson||(e.configurationJson={}),this.gatewayVersion&&!e.configVersion&&(e.configVersion=this.gatewayVersion),e.basicConfig=e.configurationJson,this.initialConnector=e;const t=this.connectorForm.get("type").value;this.setInitialConnectorValues(e),this.saveConnector(this.getUpdatedConnectorData(e)),t!==e.type&&this.allowBasicConfig.has(e.type)?this.basicConfigInitSubject.pipe(Ne(1)).subscribe((()=>{this.patchConnectorBasicConfig(e.basicConfig)})):this.patchConnectorBasicConfig(e.basicConfig)}setInitialConnectorValues(e){const{basicConfig:t,mode:n,enableRemoteLogging:i,...a}=e;this.toggleReportStrategy(e),this.connectorForm.get("mode").setValue(this.allowBasicConfig.has(e.type)?e.mode??an.BASIC:null,{emitEvent:!1}),this.connectorForm.get("enableRemoteLogging").setValue(i,{emitEvent:!1}),this.connectorForm.patchValue(a,{emitEvent:!1})}openAddConnectorDialog(){return this.ctx.ngZone.run((()=>this.dialog.open(w$,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{dataSourceData:this.dataSource.data,gatewayVersion:this.gatewayVersion}}).afterClosed()))}uniqNameRequired(){return e=>{const t=e.value?.trim().toLowerCase(),n=this.dataSource.data.some((e=>e.value.name.toLowerCase()===t)),i=this.initialConnector?.name.toLowerCase()===t;return n&&!i?{duplicateName:{valid:!1}}:null}}initDataSources(){const e={property:"key",direction:w.ASC};this.pageLink=new S(1e3,0,null,e),this.attributeDataSource=new Kn(this.attributeService,this.telemetryWsService,this.zone,this.translate),this.inactiveConnectorsDataSource=new Kn(this.attributeService,this.telemetryWsService,this.zone,this.translate),this.serverDataSource=new Kn(this.attributeService,this.telemetryWsService,this.zone,this.translate),this.dataSource=new x([])}initConnectorForm(){this.connectorForm=this.fb.group({mode:[an.BASIC],name:["",[me.required,this.uniqNameRequired(),me.pattern(pn)]],type:["",[me.required]],enableRemoteLogging:[!1],logLevel:["",[me.required]],sendDataOnlyOnChange:[!1],key:["auto"],class:[""],configuration:[""],configurationJson:[{},[me.required]],basicConfig:[{}],configVersion:[""],reportStrategy:[{value:{},disabled:!0}]})}getSortingDataAccessor(){return(e,t)=>{switch(t){case"syncStatus":return this.isConnectorSynced(e)?1:0;case"enabled":return this.activeConnectors.includes(e.key)?1:0;case"errors":const n=this.getErrorsCount(e);return"string"==typeof n?this.sort.direction.toUpperCase()===w.DESC?-1:1/0:n;default:return e[t]||e.value[t]}}}loadConnectors(){this.device&&this.device.id!==B&&Ie([this.attributeService.getEntityAttributes(this.device,D.SHARED_SCOPE,["active_connectors"]),this.attributeService.getEntityAttributes(this.device,D.SERVER_SCOPE,["inactive_connectors"]),this.attributeService.getEntityAttributes(this.device,D.CLIENT_SCOPE,["Version"])]).pipe(Me(this.destroy$)).subscribe((e=>{this.activeConnectors=this.parseConnectors(e[0]),this.inactiveConnectors=this.parseConnectors(e[1]),this.gatewayVersion=e[2][0]?.value,this.updateData(!0)}))}loadGatewayState(){this.attributeService.getEntityAttributes(this.device,D.SERVER_SCOPE).pipe(Me(this.destroy$)).subscribe((e=>{const t=e.find((e=>"active"===e.key)).value,n=e.find((e=>"lastDisconnectTime"===e.key))?.value,i=e.find((e=>"lastConnectTime"===e.key))?.value;this.isGatewayActive=this.getGatewayStatus(t,i,n)}))}parseConnectors(e){const t=e?.[0]?.value||[];return Q(t)?JSON.parse(t):t}observeModeChange(){this.connectorForm.get("mode").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{e===an.BASIC&&this.patchConnectorBasicConfig(this.connectorForm.get("configurationJson").value)}))}observeAttributeChange(){this.attributeUpdateSubject.pipe(De(300),Ae((e=>this.executeAttributeUpdates(e))),Me(this.destroy$)).subscribe()}updateActiveConnectorKeys(e){if(this.activeConnectors.includes(e)){const t=this.activeConnectors.indexOf(e);-1!==t&&this.activeConnectors.splice(t,1),this.inactiveConnectors.push(e)}else{const t=this.inactiveConnectors.indexOf(e);-1!==t&&this.inactiveConnectors.splice(t,1),this.activeConnectors.push(e)}}executeAttributeUpdates(e){Ie(this.getAttributeExecutionTasks(e)).pipe(Ne(1),Ae((()=>this.updateData(!0))),Me(this.destroy$)).subscribe()}getAttributeExecutionTasks(e){const t=this.activeConnectors.includes(e.key),n=t?D.SERVER_SCOPE:D.SHARED_SCOPE,i=t?D.SHARED_SCOPE:D.SERVER_SCOPE;return[this.attributeService.saveEntityAttributes(this.device,D.SHARED_SCOPE,[{key:"active_connectors",value:this.activeConnectors}]),this.attributeService.saveEntityAttributes(this.device,D.SERVER_SCOPE,[{key:"inactive_connectors",value:this.inactiveConnectors}]),this.attributeService.deleteEntityAttributes(this.device,n,[e]),this.attributeService.saveEntityAttributes(this.device,i,[e])]}onDataUpdateError(e){const t=this.utils.parseException(e);let n=t.name;t.message&&(n+=": "+t.message),console.error(n)}onErrorsUpdated(){this.cd.detectChanges()}onDataUpdated(){const e=this.ctx.defaultSubscription.data,t=e.find((e=>"active"===e.dataKey.name)).data[0][1],n=e.find((e=>"lastDisconnectTime"===e.dataKey.name)).data[0][1],i=e.find((e=>"lastConnectTime"===e.dataKey.name)).data[0][1];this.isGatewayActive=this.getGatewayStatus(t,i,n),this.cd.detectChanges()}getGatewayStatus(e,t,n){return!!e&&(!n||t>n)}generateSubscription(){if(this.subscription&&this.subscription.unsubscribe(),this.device){const e=[{type:N.entity,entityType:L.DEVICE,entityId:this.device.id,entityName:"Gateway",timeseries:[]}];this.dataSource.data.forEach((t=>{e[0].timeseries.push({name:`${t.key}_ERRORS_COUNT`,label:`${t.key}_ERRORS_COUNT`})})),this.ctx.subscriptionApi.createSubscriptionFromInfo(R.latest,e,this.subscriptionOptions,!1,!0).subscribe((e=>{this.subscription=e}))}}createBasicConfigWatcher(){this.basicConfigSub&&this.basicConfigSub.unsubscribe(),this.basicConfigSub=this.connectorForm.get("basicConfig").valueChanges.pipe(Oe((()=>!!this.initialConnector)),Me(this.destroy$)).subscribe((e=>{const t=this.connectorForm.get("configurationJson"),n=this.connectorForm.get("type").value,i=this.connectorForm.get("mode").value;if(!W(e,t?.value)&&this.allowBasicConfig.has(n)&&i===an.BASIC){const n={...t.value,...e};this.connectorForm.get("configurationJson").patchValue(n,{emitEvent:!1})}}))}createJsonConfigWatcher(){this.jsonConfigSub&&this.jsonConfigSub.unsubscribe(),this.jsonConfigSub=this.connectorForm.get("configurationJson").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{const t=this.connectorForm.get("basicConfig"),n=this.connectorForm.get("type").value,i=this.connectorForm.get("mode").value;!W(e,t?.value)&&this.allowBasicConfig.has(n)&&i===an.ADVANCED&&this.connectorForm.get("basicConfig").patchValue(e,{emitEvent:!1})}))}confirmConnectorChange(){return this.initialConnector&&this.connectorForm.dirty?this.dialogService.confirm(this.translate.instant("gateway.change-connector-title"),this.translate.instant("gateway.change-connector-text"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0):_e(!0)}setFormValue(e){this.connectorForm.disabled&&this.connectorForm.enable();const t=Ya.getConfig({configuration:"",key:"auto",configurationJson:{},...e},this.gatewayVersion);this.gatewayVersion&&!t.configVersion&&(t.configVersion=this.gatewayVersion),t.basicConfig=t.configurationJson,this.initialConnector=t,this.updateConnector(t)}updateConnector(e){this.jsonConfigSub?.unsubscribe(),this.allowBasicConfig.has(e.type)?this.updateBasicConfigConnector(e):(this.setInitialConnectorValues(e),this.connectorForm.markAsPristine(),this.createJsonConfigWatcher())}updateBasicConfigConnector(e){this.basicConfigSub?.unsubscribe();const t=this.connectorForm.get("type").value;this.setInitialConnectorValues(e),t!==e.type&&this.allowBasicConfig.has(e.type)&&e.mode!==an.ADVANCED?this.basicConfigInitSubject.asObservable().pipe(Ne(1)).subscribe((()=>{this.patchConnectorBasicConfig(e.basicConfig)})):this.patchConnectorBasicConfig(e.basicConfig)}patchConnectorBasicConfig(e){this.connectorForm.get("basicConfig").patchValue(e,{emitEvent:!1}),this.connectorForm.markAsPristine(),this.createBasicConfigWatcher(),this.createJsonConfigWatcher()}toggleReportStrategy(e){const t=this.connectorForm.get("reportStrategy"),n=this.connectorForm.get("sendDataOnlyOnChange");this.connectorForm.get("reportStrategy").reset(e.reportStrategy,{emitEvent:!1}),this.withReportStrategy.transform(e.configVersion,e.type)?(t.enable({emitEvent:!1}),n.disable({emitEvent:!1})):(t.disable({emitEvent:!1}),e.type===dt.MQTT&&n.enable({emitEvent:!1}))}setClientData(e){if(this.initialConnector){const t=e.data.find((e=>e.key===this.initialConnector.name));t&&(t.value="string"==typeof t.value?JSON.parse(t.value):t.value,this.isConnectorSynced(t)&&t.value.configurationJson&&this.setFormValue({...t.value,mode:this.connectorForm.get("mode").value??t.value.mode}))}}static{this.ɵfac=function(e){return new(e||H3)(t.ɵɵdirectiveInject(Xe.Store),t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(le.TranslateService),t.ɵɵdirectiveInject(K.AttributeService),t.ɵɵdirectiveInject(K.DialogService),t.ɵɵdirectiveInject(je.MatDialog),t.ɵɵdirectiveInject(K.TelemetryWebsocketService),t.ɵɵdirectiveInject(t.NgZone),t.ɵɵdirectiveInject(K.UtilsService),t.ɵɵdirectiveInject(tr),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:H3,selectors:[["tb-gateway-connector"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(v,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.sort=e.first)}},inputs:{ctx:"ctx",device:"device"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:st,useClass:j3},tr]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:42,vars:21,consts:[["generalTabContent",""],["cellActionsMenu","matMenu"],["defaultConfig",""],["legacy",""],[1,"connector-container","tb-form-panel","no-border"],[1,"table-section","tb-form-panel","no-padding","section-container","flex"],[1,"mat-mdc-table-toolbar","justify-between"],["mat-icon-button","","matTooltipPosition","above",3,"disabled","matTooltip","click",4,"ngIf"],[1,"table-container"],["class","mat-headline-5 tb-absolute-fill tb-add-new items-center justify-center",4,"ngIf"],["mat-table","","matSort","","matSortDisableClear","",3,"dataSource","matSortActive","matSortDirection"],["matColumnDef","enabled","sticky",""],["style","width: 60px;min-width: 60px;",4,"matHeaderCellDef"],[4,"matCellDef"],["matColumnDef","key"],["mat-sort-header","","style","width: 40%",4,"matHeaderCellDef"],["matColumnDef","type"],["mat-sort-header","","style","width: 30%",4,"matHeaderCellDef"],["style","text-transform: uppercase",4,"matCellDef"],["matColumnDef","syncStatus"],["matColumnDef","errors"],["matColumnDef","actions","stickyEnd",""],[4,"matHeaderCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],["class","mat-row-select",3,"class","click",4,"matRowDef","matRowDefColumns"],[1,"tb-form-panel","section-container","flex",3,"formGroup"],[1,"tb-form-panel-title","tb-flex","no-flex","space-between","align-center"],[1,"tb-form-panel-title"],["class","version-placeholder",4,"ngIf"],["formControlName","mode","appearance","fill",4,"ngIf"],["translate","",1,"no-data-found","items-center","justify-center"],["class","tb-form-panel section-container no-border no-padding tb-flex space-between",4,"ngIf"],["mat-icon-button","","matTooltipPosition","above",3,"click","disabled","matTooltip"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],[2,"width","60px","min-width","60px"],[3,"click","checked"],["mat-sort-header","",2,"width","40%"],["mat-sort-header","",2,"width","30%"],[2,"text-transform","uppercase"],[1,"status"],["matTooltipPosition","above",1,"dot",3,"click","matTooltip"],[1,"gt-md:!hidden",2,"width","48px","min-width","48px","max-width","48px"],[1,"lt-lg:!hidden"],[1,"lt-md:!hidden","flex-row","justify-end"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip","disabled"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"gt-sm:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"],[1,"mat-row-select",3,"click"],[1,"version-placeholder"],["formControlName","mode","appearance","fill"],[3,"value"],[1,"tb-form-panel","section-container","no-border","no-padding","tb-flex","space-between"],[4,"ngIf","ngIfElse"],[1,"flex","justify-end"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[3,"ngSwitch"],[4,"ngSwitchCase"],["formControlName","basicConfig",3,"generalTabContent","withReportStrategy","initialized",4,"ngIf","ngIfElse"],["formControlName","basicConfig",3,"initialized","generalTabContent","withReportStrategy"],["formControlName","basicConfig",3,"generalTabContent","initialized",4,"ngIf","ngIfElse"],["formControlName","basicConfig",3,"initialized","generalTabContent"],["class","h-full","formControlName","basicConfig",3,"generalTabContent","withReportStrategy","initialized",4,"ngIf","ngIfElse"],["formControlName","basicConfig",1,"h-full",3,"initialized","generalTabContent","withReportStrategy"],[3,"label"],[3,"ngTemplateOutlet"],["jsonRequired","","formControlName","configurationJson",1,"configuration-json",3,"fillHeight","label"],[1,"tb-form-panel","no-border","no-padding","padding-top","section-container","flex",3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","autocomplete","off","name","value","formControlName","name",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row"],["formControlName","enableRemoteLogging",1,"mat-slide"],["translate","",1,"fixed-title-width"],["formControlName","logLevel"],[3,"value",4,"ngFor","ngForOf"],["class","stroked tb-form-panel","formControlName","reportStrategy",3,"isExpansionMode","defaultValue",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","class",3,"placeholder"],["matInput","","name","value","formControlName","key",3,"placeholder"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",4)(1,"section",5)(2,"mat-toolbar",6)(3,"h2"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(6,Q2,5,6,"button",7),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",8),t.ɵɵtemplate(8,J2,7,3,"section",9),t.ɵɵelementStart(9,"table",10),t.ɵɵelementContainerStart(10,11),t.ɵɵtemplate(11,e3,3,3,"mat-header-cell",12)(12,t3,2,1,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(13,14),t.ɵɵtemplate(14,n3,3,3,"mat-header-cell",15)(15,i3,2,1,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(16,16),t.ɵɵtemplate(17,a3,3,3,"mat-header-cell",17)(18,r3,2,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(19,19),t.ɵɵtemplate(20,o3,3,3,"mat-header-cell",17)(21,s3,3,3,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(22,20),t.ɵɵtemplate(23,l3,3,3,"mat-header-cell",17)(24,p3,2,6,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(25,21),t.ɵɵtemplate(26,c3,5,6,"mat-header-cell",22)(27,d3,32,24,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(28,u3,1,0,"mat-header-row",23)(29,m3,1,4,"mat-row",24),t.ɵɵelementEnd()()(),t.ɵɵelementStart(30,"section",25)(31,"div",26)(32,"div",27),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵtemplate(35,h3,2,1,"span",28),t.ɵɵelementEnd(),t.ɵɵtemplate(36,g3,7,8,"tb-toggle-select",29),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"span",30),t.ɵɵtext(38," gateway.select-connector "),t.ɵɵelementEnd(),t.ɵɵtemplate(39,B3,8,6,"section",31),t.ɵɵelementEnd()(),t.ɵɵtemplate(40,U3,30,19,"ng-template",null,0,t.ɵɵtemplateRefExtractor)),2&e&&(t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,17,"gateway.connectors")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",null==n.dataSource||null==n.dataSource.data?null:n.dataSource.data.length),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!(null!=n.dataSource&&(null!=n.dataSource.data&&n.dataSource.data.length))),t.ɵɵadvance(),t.ɵɵproperty("dataSource",n.dataSource)("matSortActive",n.pageLink.sortOrder.property)("matSortDirection",n.pageLink.sortDirection()),t.ɵɵadvance(19),t.ɵɵproperty("matHeaderRowDef",n.displayedColumns)("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",n.displayedColumns),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.connectorForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate2(" ",null!=n.initialConnector&&n.initialConnector.type?n.GatewayConnectorTypesTranslatesMap.get(n.initialConnector.type):""," ",t.ɵɵpipeBind1(34,19,"gateway.configuration")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.connectorForm.get("configVersion").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.initialConnector&&n.allowBasicConfig.has(n.initialConnector.type)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.initialConnector),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.initialConnector))},dependencies:t.ɵɵgetComponentDepsFactory(H3,[se,_,tr,d$,mX,hX,iX,nX,IQ,EQ,BJ,NJ,ni,F1,R1,$2,W2]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block;overflow-x:auto;padding:0}[_nghost-%COMP%] .version-placeholder[_ngcontent-%COMP%]{color:gray;font-size:12px}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%]{height:100%;width:100%;flex-direction:row}@media screen and (max-width: 1279px){[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%]{flex-direction:column}}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] > section[_ngcontent-%COMP%]:not(.table-section){max-width:unset}@media screen and (min-width: 1280px){[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] > section[_ngcontent-%COMP%]:not(.table-section){max-width:50%}}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .table-section[_ngcontent-%COMP%]{min-height:35vh;overflow:hidden}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .table-section[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .flex[_ngcontent-%COMP%]{flex:1}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .input-container[_ngcontent-%COMP%]{height:auto}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .section-container[_ngcontent-%COMP%]{background-color:#fff}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{background:transparent;color:#000000de!important}[_nghost-%COMP%] .mat-mdc-slide-toggle[_ngcontent-%COMP%]{margin:0 8px}[_nghost-%COMP%] .status[_ngcontent-%COMP%]{text-align:center;border-radius:16px;font-weight:500;width:fit-content;padding:5px 15px}[_nghost-%COMP%] .status-sync[_ngcontent-%COMP%]{background:#1980380f;color:#198038}[_nghost-%COMP%] .status-unsync[_ngcontent-%COMP%]{background:#cb25300f;color:#cb2530}[_nghost-%COMP%] mat-row[_ngcontent-%COMP%]{cursor:pointer}[_nghost-%COMP%] .dot[_ngcontent-%COMP%]{height:12px;width:12px;background-color:#bbb;border-radius:50%;display:inline-block}[_nghost-%COMP%] .hasErrors[_ngcontent-%COMP%]{background-color:#cb2530}[_nghost-%COMP%] .noErrors[_ngcontent-%COMP%]{background-color:#198038}[_nghost-%COMP%] .connector-container .mat-mdc-tab-group, [_nghost-%COMP%] .connector-container .mat-mdc-tab-body-wrapper{height:100%}[_nghost-%COMP%] .connector-container .mat-mdc-tab-body.mat-mdc-tab-body-active{position:absolute}[_nghost-%COMP%] .connector-container .tb-form-row .fixed-title-width{min-width:120px;width:30%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .connector-container .tb-add-new{display:flex;z-index:999;pointer-events:none;background-color:#fff}[_nghost-%COMP%] .connector-container .tb-add-new button.connector{height:auto;padding-right:12px;font-size:20px;border-style:dashed;border-width:2px;border-radius:8px;display:flex;flex-wrap:wrap;justify-content:center;align-items:center;color:#00000061}@media screen and (min-width: 960px){[_nghost-%COMP%] .configuration-json .ace_tooltip{transform:translate(-250px,-120px)}}']})}}e("GatewayConnectorComponent",H3);class W3{constructor(e){this.deviceService=e}download(e){e&&e.stopPropagation(),this.deviceId&&this.deviceService.downloadGatewayDockerComposeFile(this.deviceId).subscribe((()=>{}))}static{this.ɵfac=function(e){return new(e||W3)(t.ɵɵdirectiveInject(K.DeviceService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:W3,selectors:[["tb-gateway-command"]],inputs:{deviceId:"deviceId"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:32,vars:9,consts:[["mat-dialog-content","",1,"tb-form-panel","no-border",2,"padding","16px 16px 8px"],[1,"tb-no-data-text"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","no-border","no-padding","space-between"],["translate","",1,"tb-no-data-text","tb-commands-hint"],["mat-stroked-button","","color","primary","href","https://docs.docker.com/compose/install/","target","_blank"],["mat-stroked-button","","color","primary",3,"click"],["usePlainMarkdown","","containerClass","start-code","data","\n ```bash\n docker compose up\n {:copy-code}\n ```\n "]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",2)(5,"div",3),t.ɵɵtext(6,"device.connectivity.install-necessary-client-tools"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",4)(8,"div",5),t.ɵɵtext(9,"gateway.install-docker-compose"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"a",6)(11,"mat-icon"),t.ɵɵtext(12,"description"),t.ɵɵelementEnd(),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",2)(16,"div",3),t.ɵɵtext(17,"gateway.download-configuration-file"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"div",4)(19,"div",5),t.ɵɵtext(20,"gateway.download-docker-compose"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"button",7),t.ɵɵlistener("click",(function(e){return n.download(e)})),t.ɵɵelementStart(22,"mat-icon"),t.ɵɵtext(23,"download"),t.ɵɵelementEnd(),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(26,"div",2)(27,"div",3),t.ɵɵtext(28,"gateway.launch-gateway"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"div",5),t.ɵɵtext(30,"gateway.launch-docker-compose"),t.ɵɵelementEnd(),t.ɵɵelement(31,"tb-markdown",8),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"gateway.docker-label")),t.ɵɵadvance(11),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,5,"common.documentation")," "),t.ɵɵadvance(11),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,7,"action.download")," "))},dependencies:t.ɵɵgetComponentDepsFactory(W3,[se,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-commands-hint[_ngcontent-%COMP%]{color:inherit;font-weight:400;flex:1}[_nghost-%COMP%] .tb-markdown-view .start-code .code-wrapper{padding:0}[_nghost-%COMP%] .tb-markdown-view .start-code .code-wrapper pre[class*=language-]{margin:0;background:#f3f6fa;border-color:#305680;padding-right:38px;overflow:scroll;padding-bottom:4px;min-height:42px;scrollbar-width:thin}[_nghost-%COMP%] .tb-markdown-view .start-code .code-wrapper pre[class*=language-]::-webkit-scrollbar{width:4px;height:4px}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn{right:-2px}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn p{color:#305680}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn p, [_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn div{background-color:#f3f6fa}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn div img{display:none}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn div:after{content:"";position:initial;display:block;width:18px;height:18px;background:#305680;mask-image:url(/assets/copy-code-icon.svg);-webkit-mask-image:url(/assets/copy-code-icon.svg);mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat}']})}}var $3,K3,Y3;e("DeviceGatewayCommandComponent",W3),e("GatewayBasicConfigTab",$3),function(e){e[e.general=0]="general",e[e.logs=1]="logs",e[e.storage=2]="storage",e[e.grpc=3]="grpc",e[e.statistics=4]="statistics",e[e.other=5]="other"}($3||e("GatewayBasicConfigTab",$3={})),e("StorageTypes",K3),function(e){e.MEMORY="memory",e.FILE="file",e.SQLITE="sqlite"}(K3||e("StorageTypes",K3={})),e("LocalLogsConfigs",Y3),function(e){e.service="service",e.connector="connector",e.converter="converter",e.tb_connection="tb_connection",e.storage="storage",e.extension="extension"}(Y3||e("LocalLogsConfigs",Y3={}));const X3=e("LocalLogsConfigTranslateMap",new Map([[Y3.service,"Service"],[Y3.connector,"Connector"],[Y3.converter,"Converter"],[Y3.tb_connection,"TB Connection"],[Y3.storage,"Storage"],[Y3.extension,"Extension"]])),Z3=e("StorageTypesTranslationMap",new Map([[K3.MEMORY,"gateway.storage-types.memory-storage"],[K3.FILE,"gateway.storage-types.file-storage"],[K3.SQLITE,"gateway.storage-types.sqlite"]]));var Q3;e("LogSavingPeriod",Q3),function(e){e.days="D",e.hours="H",e.minutes="M",e.seconds="S"}(Q3||e("LogSavingPeriod",Q3={}));const J3=e("LogSavingPeriodTranslations",new Map([[Q3.days,"gateway.logs.days"],[Q3.hours,"gateway.logs.hours"],[Q3.minutes,"gateway.logs.minutes"],[Q3.seconds,"gateway.logs.seconds"]]));var e4;e("SecurityTypes",e4),function(e){e.ACCESS_TOKEN="accessToken",e.USERNAME_PASSWORD="usernamePassword",e.TLS_ACCESS_TOKEN="tlsAccessToken",e.TLS_PRIVATE_KEY="tlsPrivateKey"}(e4||e("SecurityTypes",e4={}));const t4=e("SecurityTypesTranslationsMap",new Map([[e4.ACCESS_TOKEN,"gateway.security-types.access-token"],[e4.USERNAME_PASSWORD,"gateway.security-types.username-password"],[e4.TLS_ACCESS_TOKEN,"gateway.security-types.tls-access-token"]])),n4=e("logsHandlerClass","thingsboard_gateway.tb_utility.tb_rotating_file_handler.TimedRotatingFileHandler"),i4=e("logsLegacyHandlerClass","thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler");function a4(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",10),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.storageTypesTranslationMap.get(e))," ")}}function r4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-read-record-count-required")," "))}function o4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-read-record-count-min")," "))}function s4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-read-record-count-pattern")," "))}function l4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-required")," "))}function p4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-min")," "))}function c4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-pattern")," "))}function d4(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",11)(1,"mat-form-field",12)(2,"mat-label",13),t.ɵɵtext(3,"gateway.storage-read-record-count"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",14),t.ɵɵtemplate(5,r4,3,3,"mat-error",15)(6,o4,3,3,"mat-error",15)(7,s4,3,3,"mat-error",15),t.ɵɵelementStart(8,"mat-icon",16),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",12)(12,"mat-label",13),t.ɵɵtext(13,"gateway.storage-max-records"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",17),t.ɵɵtemplate(15,l4,3,3,"mat-error",15)(16,p4,3,3,"mat-error",15)(17,c4,3,3,"mat-error",15),t.ɵɵelementStart(18,"mat-icon",16),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"info_outlined "),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.storageFormGroup.get("read_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("read_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("read_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,8,"gateway.hints.read-record-count")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,10,"gateway.hints.max-records-count"))}}function u4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-data-folder-path-required")," "))}function m4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-data-folder-path-pattern")," "))}function h4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-files-required")," "))}function g4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-files-min")," "))}function f4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-files-pattern")," "))}function y4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-required")," "))}function v4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-min")," "))}function x4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-pattern")," "))}function b4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-required")," "))}function w4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-min")," "))}function S4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-pattern")," "))}function C4(e,n){if(1&e&&(t.ɵɵelementStart(0,"section")(1,"div",18)(2,"mat-form-field",12)(3,"mat-label",13),t.ɵɵtext(4,"gateway.storage-data-folder-path"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",19),t.ɵɵtemplate(6,u4,3,3,"mat-error",15)(7,m4,3,3,"mat-error",15),t.ɵɵelementStart(8,"mat-icon",20),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",12)(12,"mat-label",13),t.ɵɵtext(13,"gateway.storage-max-files"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",21),t.ɵɵtemplate(15,h4,3,3,"mat-error",15)(16,g4,3,3,"mat-error",15)(17,f4,3,3,"mat-error",15),t.ɵɵelementStart(18,"mat-icon",16),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(21,"div",11)(22,"mat-form-field",12)(23,"mat-label",13),t.ɵɵtext(24,"gateway.storage-max-read-record-count-storage"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",22),t.ɵɵtemplate(26,y4,3,3,"mat-error",15)(27,v4,3,3,"mat-error",15)(28,x4,3,3,"mat-error",15),t.ɵɵelementStart(29,"mat-icon",16),t.ɵɵpipe(30,"translate"),t.ɵɵtext(31,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(32,"mat-form-field",12)(33,"mat-label",13),t.ɵɵtext(34,"gateway.storage-max-records"),t.ɵɵelementEnd(),t.ɵɵelement(35,"input",23),t.ɵɵtemplate(36,b4,3,3,"mat-error",15)(37,w4,3,3,"mat-error",15)(38,S4,3,3,"mat-error",15),t.ɵɵelementStart(39,"mat-icon",16),t.ɵɵpipe(40,"translate"),t.ɵɵtext(41,"info_outlined "),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.storageFormGroup.get("data_folder_path").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("data_folder_path").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,15,"gateway.hints.data-folder")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_file_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_file_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_file_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,17,"gateway.hints.max-file-count")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(30,19,"gateway.hints.max-read-count")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(40,21,"gateway.hints.max-records"))}}function _4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-path-required")," "))}function T4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-path-pattern")," "))}function I4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-check-in-hours-required")," "))}function E4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-check-in-hours-min")," "))}function M4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-check-in-hours-pattern")," "))}function k4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-in-days-required")," "))}function P4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-in-days-min")," "))}function D4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-in-days-pattern")," "))}function O4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-required")," "))}function A4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-min")," "))}function F4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-pattern")," "))}function R4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.size-limit-required")," "))}function B4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.size-limit-min")," "))}function N4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.size-limit-pattern")," "))}function L4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.max-db-amount-required")," "))}function V4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.max-db-amount-min")," "))}function q4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.max-db-amount-pattern")," "))}function G4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.oversize-check-period-required")," "))}function z4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.oversize-check-period-min")," "))}function U4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.oversize-check-period-pattern")," "))}function j4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.writing-batch-size-required")," "))}function H4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.writing-batch-size-min")," "))}function W4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.writing-batch-size-pattern")," "))}function $4(e,n){if(1&e&&(t.ɵɵelementStart(0,"section")(1,"div",24)(2,"mat-form-field",25)(3,"mat-label",13),t.ɵɵtext(4,"gateway.storage-path"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",26),t.ɵɵtemplate(6,_4,3,3,"mat-error",15)(7,T4,3,3,"mat-error",15),t.ɵɵelementStart(8,"mat-icon",16),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",12)(12,"mat-label",13),t.ɵɵtext(13,"gateway.messages-ttl-check-in-hours"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",27),t.ɵɵtemplate(15,I4,3,3,"mat-error",15)(16,E4,3,3,"mat-error",15)(17,M4,3,3,"mat-error",15),t.ɵɵelementStart(18,"mat-icon",16),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(21,"div",11)(22,"mat-form-field",12)(23,"mat-label",13),t.ɵɵtext(24,"gateway.messages-ttl-in-days"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",28),t.ɵɵtemplate(26,k4,3,3,"mat-error",15)(27,P4,3,3,"mat-error",15)(28,D4,3,3,"mat-error",15),t.ɵɵelementStart(29,"mat-icon",29),t.ɵɵpipe(30,"translate"),t.ɵɵtext(31,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(32,"mat-form-field",12)(33,"mat-label",13),t.ɵɵtext(34,"gateway.storage-max-read-record-count"),t.ɵɵelementEnd(),t.ɵɵelement(35,"input",22),t.ɵɵtemplate(36,O4,3,3,"mat-error",15)(37,A4,3,3,"mat-error",15)(38,F4,3,3,"mat-error",15),t.ɵɵelementStart(39,"mat-icon",29),t.ɵɵpipe(40,"translate"),t.ɵɵtext(41,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(42,"div",11)(43,"mat-form-field",12)(44,"mat-label",13),t.ɵɵtext(45,"gateway.size-limit"),t.ɵɵelementEnd(),t.ɵɵelement(46,"input",30),t.ɵɵtemplate(47,R4,3,3,"mat-error",15)(48,B4,3,3,"mat-error",15)(49,N4,3,3,"mat-error",15),t.ɵɵelementStart(50,"mat-icon",29),t.ɵɵpipe(51,"translate"),t.ɵɵtext(52,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(53,"mat-form-field",12)(54,"mat-label",13),t.ɵɵtext(55,"gateway.max-db-amount"),t.ɵɵelementEnd(),t.ɵɵelement(56,"input",31),t.ɵɵtemplate(57,L4,3,3,"mat-error",15)(58,V4,3,3,"mat-error",15)(59,q4,3,3,"mat-error",15),t.ɵɵelementStart(60,"mat-icon",29),t.ɵɵpipe(61,"translate"),t.ɵɵtext(62,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(63,"div",11)(64,"mat-form-field",12)(65,"mat-label",13),t.ɵɵtext(66,"gateway.oversize-check-period"),t.ɵɵelementEnd(),t.ɵɵelement(67,"input",32),t.ɵɵtemplate(68,G4,3,3,"mat-error",15)(69,z4,3,3,"mat-error",15)(70,U4,3,3,"mat-error",15),t.ɵɵelementStart(71,"mat-icon",29),t.ɵɵpipe(72,"translate"),t.ɵɵtext(73,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(74,"mat-form-field",12)(75,"mat-label",13),t.ɵɵtext(76,"gateway.writing-batch-size"),t.ɵɵelementEnd(),t.ɵɵelement(77,"input",33),t.ɵɵtemplate(78,j4,3,3,"mat-error",15)(79,H4,3,3,"mat-error",15)(80,W4,3,3,"mat-error",15),t.ɵɵelementStart(81,"mat-icon",29),t.ɵɵpipe(82,"translate"),t.ɵɵtext(83,"info_outlined "),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.storageFormGroup.get("data_file_path").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("data_file_path").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,31,"gateway.hints.data-folder")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_check_in_hours").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_check_in_hours").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_check_in_hours").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,33,"gateway.hints.ttl-check-hour")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_in_days").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_in_days").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_in_days").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(30,35,"gateway.hints.ttl-messages-day")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(40,37,"gateway.hints.max-read-count")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",e.storageFormGroup.get("size_limit").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("size_limit").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("size_limit").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(51,39,"gateway.size-limit-hint")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_db_amount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_db_amount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_db_amount").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(61,41,"gateway.max-db-amount-hint")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",e.storageFormGroup.get("oversize_check_period").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("oversize_check_period").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("oversize_check_period").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(72,43,"gateway.oversize-check-period-hint")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("writing_batch_size").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("writing_batch_size").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("writing_batch_size").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(82,45,"gateway.writing-batch-size-hint"))}}class K4{constructor(e){this.fb=e,this.initialized=new u,this.StorageTypes=K3,this.storageTypes=Object.values(K3),this.storageTypesTranslationMap=Z3,this.onChange=()=>{},this.numberValidators=[me.min(1),me.pattern(ln)],this.requiredNumberValidators=[me.required,...this.numberValidators],this.requiredDirValidators=[me.required,me.pattern(un)],this.storageConfig={[K3.MEMORY]:{read_records_count:this.requiredNumberValidators,max_records_count:this.requiredNumberValidators},[K3.FILE]:{data_folder_path:this.requiredDirValidators,max_file_count:this.requiredNumberValidators,max_read_records_count:this.requiredNumberValidators,max_records_per_file:this.requiredNumberValidators},[K3.SQLITE]:{data_file_path:this.requiredDirValidators,messages_ttl_check_in_hours:this.requiredNumberValidators,messages_ttl_in_days:this.requiredNumberValidators,max_read_records_count:this.requiredNumberValidators,size_limit:this.numberValidators,max_db_amount:this.numberValidators,oversize_check_period:this.numberValidators,writing_batch_size:this.numberValidators}},this.storageFormGroup=this.initStorageFormGroup(),this.observeStorageTypeChanges(),this.storageFormGroup.valueChanges.pipe(In()).subscribe((e=>{this.onChange(e)}))}ngAfterViewInit(){this.updateValidators(this.storageFormGroup.get("type").value),this.initialized.emit({storage:this.storageFormGroup.value})}writeValue(e){J(e)&&(this.storageFormGroup.patchValue(e,{emitEvent:!1}),this.updateValidators(e.type))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.storageFormGroup.valid?null:{storageFormGroup:{valid:!1}}}initStorageFormGroup(){return this.fb.group({type:[K3.MEMORY,[me.required]],read_records_count:[100],max_records_count:[1e5],data_folder_path:["./data/"],max_file_count:[10],max_read_records_count:[10],max_records_per_file:[1e4],data_file_path:["./data/"],messages_ttl_check_in_hours:[1],messages_ttl_in_days:[7],size_limit:[1024],max_db_amount:[10],oversize_check_period:[1],writing_batch_size:[1e3]})}observeStorageTypeChanges(){this.storageFormGroup.get("type").valueChanges.pipe(In()).subscribe((e=>{this.updateValidators(e)}))}updateValidators(e){const t=this.storageConfig[e];Object.keys(this.storageFormGroup.controls).forEach((e=>{if("type"===e)return;const n=this.storageFormGroup.get(e),i=t[e];i?(n.enable({emitEvent:!1}),n.setValidators(i),n.updateValueAndValidity({emitEvent:!1}),n.markAsTouched({emitEvent:!1})):(n.disable({emitEvent:!1}),n.clearValidators(),n.updateValueAndValidity({emitEvent:!1}),n.markAsUntouched({emitEvent:!1}))}))}static{this.ɵfac=function(e){return new(e||K4)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:K4,selectors:[["tb-gateway-storage-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>K4)),multi:!0},{provide:he,useExisting:c((()=>K4)),multi:!0}]),t.ɵɵStandaloneFeature],decls:15,vars:9,consts:[[1,"mat-content","mat-padding","flex","w-full","flex-col","gap-2",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom","w-full"],["translate","",1,"tb-form-panel-title"],["translate","",1,"tb-form-panel-hint"],["formControlName","type",1,"flex"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-panel-hint"],[3,"ngSwitch"],["class","tb-form-row no-border no-padding tb-standard-fields column-xs",4,"ngSwitchCase"],[4,"ngSwitchCase"],[3,"value"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["translate",""],["type","number","matInput","","formControlName","read_records_count"],[4,"ngIf"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["type","number","matInput","","formControlName","max_records_count"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs","mb-2"],["matInput","","formControlName","data_folder_path"],["aria-hidden","false","aria-label","help-icon","matSuffix","",1,"mat-form-field-infix","pointer-event","suffix-icon",2,"cursor","pointer",3,"matTooltip"],["matInput","","type","number","formControlName","max_file_count"],["matInput","","type","number","formControlName","max_read_records_count"],["matInput","","type","number","formControlName","max_records_per_file"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs","align-start","mb-2"],["appearance","outline","subscriptSizing","dynamic",1,"flex"],["matInput","","formControlName","data_file_path"],["matInput","","type","number","formControlName","messages_ttl_check_in_hours"],["matInput","","type","number","formControlName","messages_ttl_in_days"],["matIconSuffix","",1,"cursor-pointer",3,"matTooltip"],["matInput","","type","number","formControlName","size_limit"],["matInput","","type","number","formControlName","max_db_amount"],["matInput","","type","number","formControlName","oversize_check_period"],["matInput","","type","number","formControlName","writing_batch_size"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.storage"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5,"gateway.hints.storage"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-toggle-select",4),t.ɵɵtemplate(7,a4,3,4,"tb-toggle-option",5),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"div",6),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(11,7),t.ɵɵtemplate(12,d4,21,12,"section",8)(13,C4,42,23,"section",9)(14,$4,84,47,"section",9),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.storageFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.storageTypes),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,7,"gateway.hints."+n.storageFormGroup.get("type").value)),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",n.storageFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.StorageTypes.MEMORY),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.StorageTypes.FILE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.StorageTypes.SQLITE))},dependencies:t.ɵɵgetComponentDepsFactory(K4,[se,_]),encapsulation:2})}}function Y4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-required")," "))}function X4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-min")," "))}function Z4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-max")," "))}function Q4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-pattern")," "))}function J4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-timeout-required")," "))}function e5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-timeout-min")," "))}function t5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-timeout-pattern")," "))}function n5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-required")," "))}function i5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-min")," "))}function a5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-pattern")," "))}function r5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-time-between-pings-required")," "))}function o5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-time-between-pings-min")," "))}function s5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-time-between-pings-pattern")," "))}function l5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-max-pings-without-data-required")," "))}function p5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-max-pings-without-data-min")," "))}function c5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-max-pings-without-data-pattern")," "))}function d5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-ping-interval-without-data-required")," "))}function u5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-ping-interval-without-data-min")," "))}function m5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-ping-interval-without-data-pattern")," "))}class h5{constructor(e){this.fb=e,this.initialized=new u,this.onChange=()=>{},this.grpcFormGroup=this.initGrpcFormGroup(),this.grpcFormGroup.valueChanges.pipe(In()).subscribe((e=>{this.onChange(e)})),this.grpcFormGroup.get("enabled").valueChanges.pipe(In()).subscribe((e=>{this.toggleRpcFields(e)}))}ngAfterViewInit(){this.initialized.emit({grpc:this.grpcFormGroup.value})}writeValue(e){e&&this.toggleRpcFields(e.enabled),this.grpcFormGroup.patchValue(e,{emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.grpcFormGroup.valid?null:{grpcFormGroup:{valid:!1}}}toggleRpcFields(e){const t=this.grpcFormGroup;e?(t.get("serverPort").enable({emitEvent:!1}),t.get("keepAliveTimeMs").enable({emitEvent:!1}),t.get("keepAliveTimeoutMs").enable({emitEvent:!1}),t.get("keepalivePermitWithoutCalls").enable({emitEvent:!1}),t.get("maxPingsWithoutData").enable({emitEvent:!1}),t.get("minTimeBetweenPingsMs").enable({emitEvent:!1}),t.get("minPingIntervalWithoutDataMs").enable({emitEvent:!1})):(t.get("serverPort").disable({emitEvent:!1}),t.get("keepAliveTimeMs").disable({emitEvent:!1}),t.get("keepAliveTimeoutMs").disable({emitEvent:!1}),t.get("keepalivePermitWithoutCalls").disable({emitEvent:!1}),t.get("maxPingsWithoutData").disable({emitEvent:!1}),t.get("minTimeBetweenPingsMs").disable({emitEvent:!1}),t.get("minPingIntervalWithoutDataMs").disable({emitEvent:!1}))}initGrpcFormGroup(){return this.fb.group({enabled:[!1],serverPort:[9595,[me.required,me.min(1),me.max(65535),me.pattern(ln)]],keepAliveTimeMs:[1e4,[me.required,me.min(1),me.pattern(ln)]],keepAliveTimeoutMs:[5e3,[me.required,me.min(1),me.pattern(ln)]],keepalivePermitWithoutCalls:[!0],maxPingsWithoutData:[0,[me.required,me.min(0),me.pattern(ln)]],minTimeBetweenPingsMs:[1e4,[me.required,me.min(1),me.pattern(ln)]],minPingIntervalWithoutDataMs:[5e3,[me.required,me.min(1),me.pattern(ln)]]})}static{this.ɵfac=function(e){return new(e||h5)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:h5,selectors:[["tb-gateway-grpc-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>h5)),multi:!0},{provide:he,useExisting:c((()=>h5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:75,vars:47,consts:[[1,"mat-content","mat-padding","flex","flex-col","gap-2",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom","w-full"],["color","primary","formControlName","enabled",1,"mat-slide"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["color","primary","formControlName","keepalivePermitWithoutCalls",1,"mat-slide"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["translate",""],["matInput","","formControlName","serverPort","type","number","min","0"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],[4,"ngIf"],["matInput","","formControlName","keepAliveTimeoutMs","type","number","min","0"],["matInput","","formControlName","keepAliveTimeMs","type","number","min","0"],["matInput","","formControlName","minTimeBetweenPingsMs","type","number","min","0"],["matInput","","formControlName","maxPingsWithoutData","type","number","min","0"],["matInput","","formControlName","minPingIntervalWithoutDataMs","type","number","min","0"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"mat-slide-toggle",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",3),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"mat-slide-toggle",4),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"section")(11,"section",5)(12,"mat-form-field",6)(13,"mat-label",7),t.ɵɵtext(14,"gateway.server-port"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",8),t.ɵɵelementStart(16,"mat-icon",9),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(19,Y4,3,3,"mat-error",10)(20,X4,3,3,"mat-error",10)(21,Z4,3,3,"mat-error",10)(22,Q4,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",6)(24,"mat-label",7),t.ɵɵtext(25,"gateway.grpc-keep-alive-timeout"),t.ɵɵelementEnd(),t.ɵɵelement(26,"input",11),t.ɵɵelementStart(27,"mat-icon",9),t.ɵɵpipe(28,"translate"),t.ɵɵtext(29,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(30,J4,3,3,"mat-error",10)(31,e5,3,3,"mat-error",10)(32,t5,3,3,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"section",5)(34,"mat-form-field",6)(35,"mat-label",7),t.ɵɵtext(36,"gateway.grpc-keep-alive"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",12),t.ɵɵelementStart(38,"mat-icon",9),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(41,n5,3,3,"mat-error",10)(42,i5,3,3,"mat-error",10)(43,a5,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-form-field",6)(45,"mat-label",7),t.ɵɵtext(46,"gateway.grpc-min-time-between-pings"),t.ɵɵelementEnd(),t.ɵɵelement(47,"input",13),t.ɵɵelementStart(48,"mat-icon",9),t.ɵɵpipe(49,"translate"),t.ɵɵtext(50,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(51,r5,3,3,"mat-error",10)(52,o5,3,3,"mat-error",10)(53,s5,3,3,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(54,"section",5)(55,"mat-form-field",6)(56,"mat-label",7),t.ɵɵtext(57,"gateway.grpc-max-pings-without-data"),t.ɵɵelementEnd(),t.ɵɵelement(58,"input",14),t.ɵɵelementStart(59,"mat-icon",9),t.ɵɵpipe(60,"translate"),t.ɵɵtext(61,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(62,l5,3,3,"mat-error",10)(63,p5,3,3,"mat-error",10)(64,c5,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(65,"mat-form-field",6)(66,"mat-label",7),t.ɵɵtext(67,"gateway.grpc-min-ping-interval-without-data"),t.ɵɵelementEnd(),t.ɵɵelement(68,"input",15),t.ɵɵelementStart(69,"mat-icon",9),t.ɵɵpipe(70,"translate"),t.ɵɵtext(71,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(72,d5,3,3,"mat-error",10)(73,u5,3,3,"mat-error",10)(74,m5,3,3,"mat-error",10),t.ɵɵelementEnd()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.grpcFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,29,"gateway.grpc")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,31,"gateway.hints.permit-without-calls")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,33,"gateway.permit-without-calls")," "),t.ɵɵadvance(8),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,35,"gateway.hints.server-port")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("max")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(28,37,"gateway.hints.grpc-keep-alive-timeout")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeoutMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeoutMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeoutMs").hasError("pattern")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(39,39,"gateway.hints.grpc-keep-alive")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeMs").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(49,41,"gateway.hints.grpc-min-time-between-pings")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minTimeBetweenPingsMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minTimeBetweenPingsMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minTimeBetweenPingsMs").hasError("pattern")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(60,43,"gateway.hints.grpc-max-pings-without-data")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("maxPingsWithoutData").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("maxPingsWithoutData").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("maxPingsWithoutData").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(70,45,"gateway.hints.grpc-min-ping-interval-without-data")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minPingIntervalWithoutDataMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minPingIntervalWithoutDataMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minPingIntervalWithoutDataMs").hasError("pattern")))},dependencies:t.ɵɵgetComponentDepsFactory(h5,[se,_]),encapsulation:2})}}function g5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.date-format-required")," "))}function f5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.log-format-required")," "))}function y5(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function v5(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",30),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.localLogsConfigTranslateMap.get(e))}}function x5(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function b5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.file-path-required")," "))}function w5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.saving-period-required")," "))}function S5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.saving-period-min")," "))}function C5(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e.value)," ")}}function _5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.backup-count-required")," "))}function T5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.backup-count-min")," "))}class I5{constructor(e){this.fb=e,this.initialized=new u,this.logSavingPeriods=J3,this.localLogsConfigs=Object.keys(Y3),this.localLogsConfigTranslateMap=X3,this.gatewayLogLevel=Object.values(pt),this.remoteLogLevel=Object.values(pt).filter((e=>e!==pt.NONE)),this.onChange=()=>{},this.logsFormGroup=this.initLogsFormGroup(),this.showRemoteLogsControl=this.fb.control(!1),this.logsFormGroup.valueChanges.pipe(In()).subscribe((e=>{this.onChange(e)})),this.logSelector=this.fb.control(Y3.service);for(const e of Object.keys(Y3))this.addLocalLogConfig(e,{});this.showRemoteLogsControl.valueChanges.pipe(In()).subscribe((e=>this.logsFormGroup.get("logLevel")[e?"enable":"disable"]()))}ngAfterViewInit(){this.initialized.emit({logs:this.logsFormGroup.value})}writeValue(e){this.logsFormGroup.patchValue(e,{emitEvent:!1}),this.updateRemoteLogs(e?.logLevel??pt.NONE)}registerOnChange(e){this.onChange=e}registerOnTouched(e){}getLogFormGroup(e){return this.logsFormGroup.get(`local.${e}`)}validate(){return this.logsFormGroup.valid?null:{logsFormGroup:{valid:!1}}}initLogsFormGroup(){return this.fb.group({dateFormat:["%Y-%m-%d %H:%M:%S",[me.required,me.pattern(/^[^\s].*[^\s]$/)]],logFormat:["%(asctime)s.%(msecs)03d - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s",[me.required,me.pattern(/^[^\s].*[^\s]$/)]],type:["remote",[me.required]],logLevel:[{value:pt.INFO,disabled:!0}],local:this.fb.group({})})}addLocalLogConfig(e,t){const n=this.logsFormGroup.get("local"),i=this.fb.group({logLevel:[t.logLevel||pt.INFO,[me.required]],filePath:[t.filePath||"./logs",[me.required]],backupCount:[t.backupCount||7,[me.required,me.min(0)]],savingTime:[t.savingTime||3,[me.required,me.min(0)]],savingPeriod:[t.savingPeriod||Q3.days,[me.required]]});n.addControl(e,i,{emitEvent:!1})}updateRemoteLogs(e){const t=e&&e!==pt.NONE;this.showRemoteLogsControl.patchValue(t,{emitEvent:!1}),this.logsFormGroup.get("logLevel")[t?"enable":"disable"]({emitEvent:!1}),this.logsFormGroup.get("logLevel").patchValue(e===pt.NONE?pt.INFO:e,{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||I5)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:I5,selectors:[["tb-gateway-logs-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>I5)),multi:!0},{provide:he,useExisting:c((()=>I5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:72,vars:33,consts:[[1,"mat-content","mat-padding","flex","flex-col","gap-2",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom"],[1,"flex","flex-col"],["appearance","outline"],["translate",""],["matInput","","formControlName","dateFormat"],[4,"ngIf"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["matInput","","formControlName","logFormat","rows","2"],[1,"tb-form-panel"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide",3,"click","formControl"],[3,"tb-hint-tooltip-icon"],["formControlName","logLevel"],[3,"value",4,"ngFor","ngForOf"],["formGroupName","local",1,"tb-form-panel","no-padding-bottom"],["translate","",1,"tb-form-panel-title"],[1,"toggle-group",3,"formControl"],["class","first-capital",3,"value",4,"ngFor","ngForOf"],[3,"formGroup"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["matInput","","formControlName","filePath"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","saving-period"],["matInput","","formControlName","savingTime","type","number","min","0"],["appearance","outline","hideRequiredMarker","",2,"min-width","110px","width","30%"],["formControlName","savingPeriod"],["matInput","","formControlName","backupCount","type","number","min","0"],[3,"value"],[1,"first-capital",3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2)(3,"mat-form-field",3)(4,"mat-label",4),t.ɵɵtext(5,"gateway.logs.date-format"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",5),t.ɵɵtemplate(7,g5,3,3,"mat-error",6),t.ɵɵelementStart(8,"mat-icon",7),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",3)(12,"mat-label",4),t.ɵɵtext(13,"gateway.logs.log-format"),t.ɵɵelementEnd(),t.ɵɵelement(14,"textarea",8),t.ɵɵtemplate(15,f5,3,3,"mat-error",6),t.ɵɵelementStart(16,"mat-icon",7),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"info_outlined "),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(19,"div",9)(20,"mat-expansion-panel",10)(21,"mat-expansion-panel-header",11)(22,"mat-panel-title")(23,"mat-slide-toggle",12),t.ɵɵlistener("click",(function(e){return e.stopPropagation()})),t.ɵɵelementStart(24,"mat-label")(25,"div",13),t.ɵɵpipe(26,"translate"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(29,"mat-form-field",3)(30,"mat-label",4),t.ɵɵtext(31,"gateway.logs.level"),t.ɵɵelementEnd(),t.ɵɵelementStart(32,"mat-select",14),t.ɵɵtemplate(33,y5,2,2,"mat-option",15),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(34,"div",16)(35,"div",17),t.ɵɵtext(36,"gateway.logs.local"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"tb-toggle-select",18),t.ɵɵtemplate(38,v5,2,2,"tb-toggle-option",19),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(39,20),t.ɵɵelementStart(40,"div",21)(41,"mat-form-field",22)(42,"mat-label",4),t.ɵɵtext(43,"gateway.logs.level"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-select",14),t.ɵɵtemplate(45,x5,2,2,"mat-option",15),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"mat-form-field",22)(47,"mat-label",4),t.ɵɵtext(48,"gateway.logs.file-path"),t.ɵɵelementEnd(),t.ɵɵelement(49,"input",23),t.ɵɵtemplate(50,b5,3,3,"mat-error",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(51,"div",21)(52,"div",24)(53,"mat-form-field",22)(54,"mat-label",4),t.ɵɵtext(55,"gateway.logs.saving-period"),t.ɵɵelementEnd(),t.ɵɵelement(56,"input",25),t.ɵɵtemplate(57,w5,3,3,"mat-error",6)(58,S5,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(59,"mat-form-field",26)(60,"mat-select",27),t.ɵɵtemplate(61,C5,3,4,"mat-option",15),t.ɵɵpipe(62,"keyvalue"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(63,"mat-form-field",22)(64,"mat-label",4),t.ɵɵtext(65,"gateway.logs.backup-count"),t.ɵɵelementEnd(),t.ɵɵelement(66,"input",28),t.ɵɵtemplate(67,_5,3,3,"mat-error",6)(68,T5,3,3,"mat-error",6),t.ɵɵelementStart(69,"mat-icon",7),t.ɵɵpipe(70,"translate"),t.ɵɵtext(71,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.logsFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.logsFormGroup.get("dateFormat").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,21,"gateway.hints.date-form")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.logsFormGroup.get("logFormat").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,23,"gateway.hints.log-format")),t.ɵɵadvance(4),t.ɵɵproperty("expanded",n.showRemoteLogsControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.showRemoteLogsControl),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(26,25,"gateway.hints.remote-log")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,27,"gateway.logs.remote")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.remoteLogLevel),t.ɵɵadvance(4),t.ɵɵproperty("formControl",n.logSelector),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.localLogsConfigs),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.getLogFormGroup(n.logSelector.value)),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.gatewayLogLevel),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".filePath").hasError("required")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".savingTime").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".savingTime").hasError("min")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(62,29,n.logSavingPeriods)),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".backupCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".backupCount").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(70,31,"gateway.hints.backup-count")))},dependencies:t.ɵɵgetComponentDepsFactory(I5,[se,_]),encapsulation:2})}}function E5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"security.clientId-required")," "))}function M5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",6),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"gateway.copy-client-id")),t.ɵɵproperty("copyText",e.usernameFormGroup.get("clientId").value)}}function k5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("clientId"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-client-id"))}function P5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"security.username-required")," "))}function D5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",6),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"gateway.copy-username")),t.ɵɵproperty("copyText",e.usernameFormGroup.get("username").value)}}function O5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("username"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-user-name"))}function A5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",6),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"gateway.copy-password")),t.ɵɵproperty("copyText",e.usernameFormGroup.get("password").value)}}function F5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("password"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-password"))}function R5(e,n){1&e&&(t.ɵɵelement(0,"tb-error",14),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("error",t.ɵɵpipeBind1(1,1,"device.client-id-or-user-name-necessary"))}function B5(e,n){1&e&&(t.ɵɵelement(0,"tb-error",14),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("error",t.ɵɵpipeBind1(1,1,"gateway.hints.username-required-with-password"))}class N5{constructor(e){this.fb=e,this.onChange=()=>{},this.initForm(),this.usernameFormGroup.valueChanges.pipe(In()).subscribe((e=>this.onChange(e)))}writeValue(e){this.usernameFormGroup.patchValue(e,{emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}setDisabledState(e){e?this.usernameFormGroup.disable({emitEvent:!1}):this.usernameFormGroup.enable({emitEvent:!1})}validate(){return this.usernameFormGroup.valid?null:{usernameFormGroup:{valid:!1}}}initForm(){this.usernameFormGroup=this.createSecurityFormGroup()}createSecurityFormGroup(){return this.fb.group({clientId:[null,[me.pattern(/^[^.\s]+$/)]],username:[null,[me.pattern(/^[^.\s]+$/)]],password:[null,[me.pattern(/^[^.\s]+$/)]]},{validators:[this.atLeastOneRequired,this.usernameRequired]})}atLeastOneRequired(e){const t=e.get("clientId").value,n=e.get("username").value;return t||n?null:{atLeastOneRequired:!0}}usernameRequired(e){const t=e.get("username").value,n=e.get("password").value;return!t&&n?{usernameRequired:!0}:null}generate(e){this.usernameFormGroup.get(e).patchValue(ae(20))}static{this.ɵfac=function(e){return new(e||N5)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:N5,selectors:[["tb-gateway-username-configuration"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>N5)),multi:!0},{provide:he,useExisting:c((()=>N5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:33,vars:17,consts:[[3,"formGroup"],[1,"xs:flex-col","no-border","no-padding","tb-standard-fields","flex","gap-2"],["appearance","outline",1,"flex","flex-1"],["translate",""],["matInput","","formControlName","clientId"],[4,"ngIf"],["matSuffix","","miniButton","false","tooltipPosition","above","icon","content_copy",3,"copyText","tooltipText"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"matTooltip"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["matInput","","formControlName","username"],["appearance","outline","subscriptSizing","dynamic",2,"width","100%"],["matInput","","formControlName","password"],["class","block",3,"error",4,"ngIf"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"click","matTooltip"],[1,"block",3,"error"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"mat-form-field",2)(3,"mat-label",3),t.ɵɵtext(4,"security.clientId"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",4),t.ɵɵtemplate(6,E5,3,3,"mat-error",5)(7,M5,2,4,"tb-copy-button",6)(8,k5,4,3,"button",7),t.ɵɵelementStart(9,"mat-icon",8),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",2)(13,"mat-label",3),t.ɵɵtext(14,"security.username"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",9),t.ɵɵtemplate(16,P5,3,3,"mat-error",5)(17,D5,2,4,"tb-copy-button",6)(18,O5,4,3,"button",7),t.ɵɵelementStart(19,"mat-icon",8),t.ɵɵpipe(20,"translate"),t.ɵɵtext(21,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"mat-form-field",10)(23,"mat-label",3),t.ɵɵtext(24,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",11),t.ɵɵtemplate(26,A5,2,4,"tb-copy-button",6)(27,F5,4,3,"button",7),t.ɵɵelementStart(28,"mat-icon",8),t.ɵɵpipe(29,"translate"),t.ɵɵtext(30,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵtemplate(31,R5,2,3,"tb-error",12)(32,B5,2,3,"tb-error",12)),2&e&&(t.ɵɵproperty("formGroup",n.usernameFormGroup),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.usernameFormGroup.get("clientId").hasError("required")),t.ɵɵadvance(),t.ɵɵconditional(n.usernameFormGroup.get("clientId").value?7:8),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,11,"gateway.hints.client-id")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.usernameFormGroup.get("username").hasError("required")),t.ɵɵadvance(),t.ɵɵconditional(n.usernameFormGroup.get("username").value?17:18),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(20,13,"gateway.hints.username")),t.ɵɵadvance(7),t.ɵɵconditional(n.usernameFormGroup.get("password").value?26:27),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(29,15,"gateway.hints.password")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.usernameFormGroup.hasError("atLeastOneRequired")&&n.usernameFormGroup.touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.usernameFormGroup.hasError("usernameRequired")&&n.usernameFormGroup.touched))},dependencies:t.ɵɵgetComponentDepsFactory(N5,[se,_]),encapsulation:2})}}class L5{constructor(e,t){this.deviceService=e,this.destroyRef=t,this.initialCredentialsSubject=new Ce(null)}get initialCredentials(){return this.initialCredentialsSubject.value}get initialCredentials$(){return this.initialCredentialsSubject.asObservable()}updateCredentials(e){let t={};switch(e.type){case e4.USERNAME_PASSWORD:this.shouldUpdateCredentials(e)&&(t=this.generateMqttCredentials(e));break;case e4.ACCESS_TOKEN:case e4.TLS_ACCESS_TOKEN:this.shouldUpdateAccessToken(e)&&(t={credentialsType:U.ACCESS_TOKEN,credentialsId:e.accessToken,credentialsValue:null})}return this.initialCredentialsSubject.next({...this.initialCredentials,...t}),Object.keys(t).length?this.deviceService.saveDeviceCredentials(this.initialCredentials):_e(null)}setInitialCredentials(e){this.deviceService.getDeviceCredentials(e.id).pipe(In(this.destroyRef)).subscribe((e=>{this.initialCredentialsSubject.next({...e,version:null})}))}shouldUpdateSecurityConfig(e){switch(e.type){case e4.USERNAME_PASSWORD:return this.shouldUpdateCredentials(e);case e4.ACCESS_TOKEN:case e4.TLS_ACCESS_TOKEN:return this.shouldUpdateAccessToken(e)}}credentialsToSecurityConfig(e){const t=e.credentialsType===U.MQTT_BASIC?e4.USERNAME_PASSWORD:e4.ACCESS_TOKEN;if(e.credentialsType!==U.MQTT_BASIC)return{type:t,accessToken:e.credentialsId};if(e.credentialsValue){const{clientId:n,userName:i,password:a}=JSON.parse(e.credentialsValue);return{type:t,clientId:n,username:i,password:a}}}shouldUpdateCredentials(e){if(this.initialCredentials.credentialsType!==U.MQTT_BASIC)return!0;const t=JSON.parse(this.initialCredentials.credentialsValue);return!(t.clientId===e.clientId&&t.userName===e.username&&t.password===e.password)}shouldUpdateAccessToken(e){return this.initialCredentials.credentialsType!==U.ACCESS_TOKEN||this.initialCredentials.credentialsId!==e.accessToken}generateMqttCredentials(e){const{clientId:t,username:n,password:i}=e,a={...t&&{clientId:t},...n&&{userName:n},...i&&{password:i}};return{credentialsType:U.MQTT_BASIC,credentialsValue:JSON.stringify(a)}}static{this.ɵfac=function(e){return new(e||L5)(t.ɵɵinject(K.DeviceService),t.ɵɵinject(t.DestroyRef))}}static{this.ɵprov=t.ɵɵdefineInjectable({token:L5,factory:L5.ɵfac})}}function V5(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e.value)," ")}}function q5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"security.access-token-required")," "))}function G5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",13),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext(2);t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"device.copy-access-token")),t.ɵɵproperty("copyText",e.securityFormGroup.get("accessToken").value)}}function z5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",16),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.generateAccessToken())})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-access-token"))}function U5(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",9)(1,"mat-label",10),t.ɵɵtext(2,"security.access-token"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",11),t.ɵɵtemplate(4,q5,3,3,"mat-error",12)(5,G5,2,4,"tb-copy-button",13)(6,z5,4,3,"button",14),t.ɵɵelementStart(7,"mat-icon",15),t.ɵɵpipe(8,"translate"),t.ɵɵtext(9,"info_outlined "),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.securityFormGroup.get("accessToken").hasError("required")),t.ɵɵadvance(),t.ɵɵconditional(e.securityFormGroup.get("accessToken").value?5:6),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,3,"gateway.hints.token"))}}function j5(e,n){1&e&&(t.ɵɵelement(0,"tb-file-input",17),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"translate")),2&e&&(t.ɵɵpropertyInterpolate("hint",t.ɵɵpipeBind1(1,5,"gateway.hints.ca-cert")),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,7,"security.ca-cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(3,9,"gateway.drop-file")),t.ɵɵproperty("allowedExtensions","pem,cert,key")("accept",".pem, application/pem,.cert, application/cert, .key,application/key"))}class H5{constructor(e,t,n){this.fb=e,this.cd=t,this.gatewayCredentialsService=n,this.initialized=new u,this.securityTypes=t4,this.onChange=()=>{},this.securityFormGroup=this.createSecurityFormGroup(),this.setupFormListeners()}ngAfterViewInit(){const{usernamePassword:e,...t}=this.securityFormGroup.value;this.initialized.emit({thingsboard:{security:e?{...t,...e}:t}})}writeValue(e){e?this.updateFormBySecurityConfig(e):this.updateFormBySecurityConfig(this.gatewayCredentialsService.credentialsToSecurityConfig(this.gatewayCredentialsService.initialCredentials))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.securityFormGroup.valid?null:{securityFormGroup:{valid:!1}}}updateFormBySecurityConfig(e){const{clientId:t,username:n,password:i,...a}=e??{};a?.type===e4.USERNAME_PASSWORD?this.securityFormGroup.patchValue({...a,usernamePassword:{clientId:t,username:n,password:i}},{emitEvent:!1}):this.securityFormGroup.patchValue(a,{emitEvent:!1}),this.toggleBySecurityType(this.securityFormGroup.get("type").value)}createSecurityFormGroup(){return this.fb.group({type:[e4.ACCESS_TOKEN,[me.required]],accessToken:[null,[me.required,me.pattern(/^[^.\s]+$/)]],caCert:[null,[me.required]],usernamePassword:[]})}setupFormListeners(){this.securityFormGroup.valueChanges.pipe(In()).subscribe((({usernamePassword:e,...t})=>{this.onChange(e?{...t,...e}:t)})),this.securityFormGroup.get("type").valueChanges.pipe(In()).subscribe((e=>{this.toggleBySecurityType(e)})),this.securityFormGroup.get("caCert").valueChanges.pipe(In()).subscribe((()=>this.cd.detectChanges()))}toggleBySecurityType(e){switch(this.securityFormGroup.disable({emitEvent:!1}),this.securityFormGroup.get("type").enable({emitEvent:!1}),e){case e4.ACCESS_TOKEN:this.securityFormGroup.get("accessToken").enable({emitEvent:!1});break;case e4.TLS_PRIVATE_KEY:this.securityFormGroup.get("caCert").enable({emitEvent:!1});break;case e4.TLS_ACCESS_TOKEN:this.securityFormGroup.get("accessToken").enable({emitEvent:!1}),this.securityFormGroup.get("caCert").enable({emitEvent:!1});break;case e4.USERNAME_PASSWORD:this.securityFormGroup.get("usernamePassword").enable({emitEvent:!1})}}generateAccessToken(){this.securityFormGroup.get("accessToken").patchValue(ae(20))}static{this.ɵfac=function(e){return new(e||H5)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(L5))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:H5,selectors:[["tb-gateway-security-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>H5)),multi:!0},{provide:he,useExisting:c((()=>H5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:10,vars:8,consts:[[1,"tb-form-panel"],["translate","",1,"tb-form-panel-title"],[3,"formGroup"],["formControlName","type",1,"toggle-group","flex"],[3,"value",4,"ngFor","ngForOf"],["appearance","outline",4,"ngIf"],["formControlName","usernamePassword"],["formControlName","caCert",3,"hint","label","allowedExtensions","accept","dropLabel",4,"ngIf"],[3,"value"],["appearance","outline"],["translate",""],["matInput","","formControlName","accessToken"],[4,"ngIf"],["matSuffix","","miniButton","false","tooltipPosition","above","icon","content_copy",3,"copyText","tooltipText"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"matTooltip"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"click","matTooltip"],["formControlName","caCert",3,"hint","label","allowedExtensions","accept","dropLabel"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2,"security.security"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(3,2),t.ɵɵelementStart(4,"tb-toggle-select",3),t.ɵɵtemplate(5,V5,3,4,"tb-toggle-option",4),t.ɵɵpipe(6,"keyvalue"),t.ɵɵelementEnd(),t.ɵɵtemplate(7,U5,10,5,"mat-form-field",5),t.ɵɵelement(8,"tb-gateway-username-configuration",6),t.ɵɵtemplate(9,j5,4,11,"tb-file-input",7),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(3),t.ɵɵproperty("formGroup",n.securityFormGroup),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(6,6,n.securityTypes)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.securityFormGroup.get("type").value.toLowerCase().includes("accesstoken")),t.ɵɵadvance(),t.ɵɵclassProp("hidden","usernamePassword"!==n.securityFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.securityFormGroup.get("type").value.toLowerCase().includes("tls")))},dependencies:t.ɵɵgetComponentDepsFactory(H5,[se,_,N5]),encapsulation:2})}}const W5=["configGroup"];function $5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-host-required")))}function K5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-required")))}function Y5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-min")))}function X5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-max")))}function Z5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-pattern")))}function Q5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-security-configuration",20),t.ɵɵlistener("initialized",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext(2);return t.ɵɵresetView(i.onInitialized(n))})),t.ɵɵelementEnd()}}function J5(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",21),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("isConfig",!0)("defaultValue",e.ReportStrategyDefaultValue.Gateway)}}function e6(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",7)(1,"div",8)(2,"div",9),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"mat-slide-toggle",10),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",9),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-slide-toggle",11),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"div",12)(13,"mat-form-field",13)(14,"mat-label",14),t.ɵɵtext(15,"gateway.thingsboard-host"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",15),t.ɵɵelementStart(17,"mat-icon",16),t.ɵɵpipe(18,"translate"),t.ɵɵtext(19,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(20,$5,3,3,"mat-error"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",13)(22,"mat-label",14),t.ɵɵtext(23,"gateway.thingsboard-port"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",17),t.ɵɵtemplate(25,K5,3,3,"mat-error")(26,Y5,3,3,"mat-error")(27,X5,3,3,"mat-error")(28,Z5,3,3,"mat-error"),t.ɵɵelementStart(29,"mat-icon",16),t.ɵɵpipe(30,"translate"),t.ɵɵtext(31,"info_outlined "),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(32,Q5,1,0,"tb-gateway-security-configuration",18),t.ɵɵpipe(33,"async"),t.ɵɵtemplate(34,J5,1,3,"tb-report-strategy",19),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.remote-configuration")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,12,"gateway.remote-configuration")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(8,14,"gateway.hints.remote-shell")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,16,"gateway.remote-shell")," "),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,18,"gateway.hints.host")),t.ɵɵadvance(3),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.host").hasError("required")?20:-1),t.ɵɵadvance(5),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.port").hasError("required")?25:e.basicFormGroup.get("thingsboard.port").hasError("min")?26:e.basicFormGroup.get("thingsboard.port").hasError("max")?27:e.basicFormGroup.get("thingsboard.port").hasError("pattern")?28:-1),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(30,20,"gateway.hints.port")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",t.ɵɵpipeBind1(33,22,e.initialCredentials$)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.withReportStrategy)}}function t6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-slide-toggle",23)(1,"mat-label",33),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"mat-slide-toggle",34)(6,"mat-label",33),t.ɵɵpipe(7,"translate"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,4,"gateway.hints.enable-general-statistics")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,6,"gateway.statistics.general-statistics")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(7,8,"gateway.hints.enable-custom-statistics")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,"gateway.statistics.custom-statistics")," "))}function n6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-slide-toggle",23),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.statistics")," "))}function i6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.send-period-required")))}function a6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.send-period-min")))}function r6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.send-period-pattern")))}function o6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.custom-send-period-required")))}function s6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.custom-send-period-min")))}function l6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.custom-send-period-pattern")))}function p6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-required")))}function c6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-pattern")," "))}function d6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-already-exists")))}function u6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-required")))}function m6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-min")))}function h6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-pattern")))}function g6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.command-required")))}function f6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.command-pattern")))}function y6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",35)(1,"section",36)(2,"section",37)(3,"mat-form-field",38)(4,"mat-label",14),t.ɵɵtext(5,"gateway.statistics.name"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",39),t.ɵɵtemplate(7,p6,3,3,"mat-error")(8,c6,3,3,"mat-error")(9,d6,3,3,"mat-error"),t.ɵɵelementStart(10,"mat-icon",16),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(13,"mat-form-field",38)(14,"mat-label",14),t.ɵɵtext(15,"gateway.statistics.timeout"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",40),t.ɵɵtemplate(17,u6,3,3,"mat-error")(18,m6,3,3,"mat-error")(19,h6,3,3,"mat-error"),t.ɵɵelementStart(20,"mat-icon",16),t.ɵɵpipe(21,"translate"),t.ɵɵtext(22,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(23,"section")(24,"mat-form-field",41)(25,"mat-label",14),t.ɵɵtext(26,"gateway.statistics.command"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",42),t.ɵɵtemplate(28,g6,3,3,"mat-error")(29,f6,3,3,"mat-error"),t.ɵɵelementStart(30,"mat-icon",16),t.ɵɵpipe(31,"translate"),t.ɵɵtext(32,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(33,"section")(34,"mat-expansion-panel",43)(35,"mat-expansion-panel-header")(36,"mat-panel-title")(37,"div",28),t.ɵɵtext(38,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(39,"mat-form-field",38)(40,"mat-label",14),t.ɵɵtext(41,"gateway.statistics.install-cmd"),t.ɵɵelementEnd(),t.ɵɵelement(42,"input",44),t.ɵɵelementStart(43,"mat-icon",45),t.ɵɵpipe(44,"translate"),t.ɵɵtext(45,"info_outlined "),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(46,"button",46),t.ɵɵpipe(47,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.removeCommandControl(i,n))})),t.ɵɵelementStart(48,"mat-icon"),t.ɵɵtext(49,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.index,a=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("formGroupName",i),t.ɵɵadvance(6),t.ɵɵconditional(e.get("attributeOnGateway").hasError("required")?7:e.get("attributeOnGateway").hasError("pattern")?8:e.get("attributeOnGateway").hasError("duplicateName")?9:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,10,"gateway.hints.attribute")),t.ɵɵadvance(7),t.ɵɵconditional(e.get("timeout").hasError("required")?17:e.get("timeout").hasError("min")?18:e.get("timeout").hasError("pattern")?19:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(21,12,"gateway.hints.timeout")),t.ɵɵadvance(8),t.ɵɵconditional(e.get("command").hasError("required")?28:e.get("command").hasError("pattern")?29:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(31,14,"gateway.hints.command")),t.ɵɵadvance(13),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(44,16,"gateway.hints.install-cmd")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(47,18,"gateway.statistics.remove")),t.ɵɵproperty("disabled",!a.basicFormGroup.get("thingsboard.remoteConfiguration").value)}}function v6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",22),t.ɵɵtemplate(2,t6,10,12)(3,n6,3,3,"mat-slide-toggle",23),t.ɵɵelementStart(4,"mat-form-field",24)(5,"mat-label",14),t.ɵɵtext(6,"gateway.statistics.send-period"),t.ɵɵelementEnd(),t.ɵɵelement(7,"input",25),t.ɵɵtemplate(8,i6,3,3,"mat-error")(9,a6,3,3,"mat-error")(10,r6,3,3,"mat-error"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",24)(12,"mat-label",14),t.ɵɵtext(13,"gateway.statistics.custom-send-period"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",26),t.ɵɵtemplate(15,o6,3,3,"mat-error")(16,s6,3,3,"mat-error")(17,l6,3,3,"mat-error"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",27)(19,"div",28),t.ɵɵtext(20,"gateway.statistics.commands"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",29),t.ɵɵtext(22,"gateway.hints.commands"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(23,30),t.ɵɵtemplate(24,y6,50,20,"div",31),t.ɵɵelementStart(25,"button",32),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.addCommand())})),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵconditional(e.hasUpdatedStatistics?2:3),t.ɵɵadvance(6),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.statistics.statsSendPeriodInSeconds").hasError("required")?8:e.basicFormGroup.get("thingsboard.statistics.statsSendPeriodInSeconds").hasError("min")?9:e.basicFormGroup.get("thingsboard.statistics.statsSendPeriodInSeconds").hasError("pattern")?10:-1),t.ɵɵadvance(7),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.statistics.customStatsSendPeriodInSeconds").hasError("required")?15:e.basicFormGroup.get("thingsboard.statistics.customStatsSendPeriodInSeconds").hasError("min")?16:e.basicFormGroup.get("thingsboard.statistics.customStatsSendPeriodInSeconds").hasError("pattern")?17:-1),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",e.commandFormArray().controls),t.ɵɵadvance(),t.ɵɵproperty("disabled",!e.basicFormGroup.get("thingsboard.remoteConfiguration").value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(27,6,"gateway.statistics.add")," ")}}function x6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-timeout-seconds-required")))}function b6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-timeout-seconds-min")))}function w6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-timeout-seconds-pattern")))}function S6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-check-period-seconds-required")))}function C6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-check-period-seconds-min")))}function _6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-check-period-seconds-pattern")))}function T6(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",37)(1,"mat-form-field",38)(2,"mat-label",14),t.ɵɵtext(3,"gateway.inactivity-timeout-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",56),t.ɵɵtemplate(5,x6,3,3,"mat-error")(6,b6,3,3,"mat-error")(7,w6,3,3,"mat-error"),t.ɵɵelementStart(8,"mat-icon",16),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",38)(12,"mat-label",14),t.ɵɵtext(13,"gateway.inactivity-check-period-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",57),t.ɵɵtemplate(15,S6,3,3,"mat-error")(16,C6,3,3,"mat-error")(17,_6,3,3,"mat-error"),t.ɵɵelementStart(18,"mat-icon",16),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"info_outlined "),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityTimeoutSeconds").hasError("required")?5:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityTimeoutSeconds").hasError("min")?6:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityTimeoutSeconds").hasError("pattern")?7:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,4,"gateway.hints.inactivity-timeout")),t.ɵɵadvance(7),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds").hasError("required")?15:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds").hasError("min")?16:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds").hasError("pattern")?17:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,6,"gateway.hints.inactivity-period"))}}function I6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.min-pack-send-delay-required")))}function E6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.min-pack-send-delay-min")))}function M6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.min-pack-send-delay-pattern")))}function k6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.mqtt-qos-required")))}function P6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.mqtt-qos-range")))}function D6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.mqtt-qos-range")))}function O6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.check-connectors-configuration-required")))}function A6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.check-connectors-configuration-min")))}function F6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.check-connectors-configuration-pattern")))}function R6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.max-payload-size-bytes-required")))}function B6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.max-payload-size-bytes-min")))}function N6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.max-payload-size-bytes-pattern")))}function L6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.min-pack-size-to-send-required")))}function V6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.min-pack-size-to-send-min")))}function q6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.min-pack-size-to-send-pattern")))}function G6(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",7)(1,"div",47)(2,"div",9),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"mat-slide-toggle",48),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(7,T6,21,8,"section",49),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"div",8)(9,"div",28),t.ɵɵtext(10,"gateway.advanced"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"section",37)(12,"mat-form-field",38)(13,"mat-label",14),t.ɵɵtext(14,"gateway.min-pack-send-delay"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",50),t.ɵɵtemplate(16,I6,3,3,"mat-error")(17,E6,3,3,"mat-error")(18,M6,3,3,"mat-error"),t.ɵɵelementStart(19,"mat-icon",16),t.ɵɵpipe(20,"translate"),t.ɵɵtext(21,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"mat-form-field",38)(23,"mat-label",14),t.ɵɵtext(24,"gateway.mqtt-qos"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-select",51)(26,"mat-option",52),t.ɵɵtext(27,"0"),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-option",52),t.ɵɵtext(29,"1"),t.ɵɵelementEnd()(),t.ɵɵtemplate(30,k6,3,3,"mat-error")(31,P6,3,3,"mat-error")(32,D6,3,3,"mat-error"),t.ɵɵelementStart(33,"mat-icon",16),t.ɵɵpipe(34,"translate"),t.ɵɵtext(35,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(36,"section",37)(37,"mat-form-field",38)(38,"mat-label",14),t.ɵɵtext(39,"gateway.statistics.check-connectors-configuration"),t.ɵɵelementEnd(),t.ɵɵelement(40,"input",53),t.ɵɵtemplate(41,O6,3,3,"mat-error")(42,A6,3,3,"mat-error")(43,F6,3,3,"mat-error"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-form-field",38)(45,"mat-label",14),t.ɵɵtext(46,"gateway.statistics.max-payload-size-bytes"),t.ɵɵelementEnd(),t.ɵɵelement(47,"input",54),t.ɵɵtemplate(48,R6,3,3,"mat-error")(49,B6,3,3,"mat-error")(50,N6,3,3,"mat-error"),t.ɵɵelementStart(51,"mat-icon",16),t.ɵɵpipe(52,"translate"),t.ɵɵtext(53,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(54,"section",37)(55,"mat-form-field",38)(56,"mat-label",14),t.ɵɵtext(57,"gateway.statistics.min-pack-size-to-send"),t.ɵɵelementEnd(),t.ɵɵelement(58,"input",55),t.ɵɵtemplate(59,L6,3,3,"mat-error")(60,V6,3,3,"mat-error")(61,q6,3,3,"mat-error"),t.ɵɵelementStart(62,"mat-icon",16),t.ɵɵpipe(63,"translate"),t.ɵɵtext(64,"info_outlined "),t.ɵɵelementEnd()()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassProp("no-padding-bottom",e.basicFormGroup.get("thingsboard.checkingDeviceActivity.checkDeviceInactivity").value),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,16,"gateway.hints.check-device-activity")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,18,"gateway.checking-device-activity")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.basicFormGroup.get("thingsboard.checkingDeviceActivity.checkDeviceInactivity").value),t.ɵɵadvance(9),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.minPackSendDelayMS").hasError("required")?16:e.basicFormGroup.get("thingsboard.minPackSendDelayMS").hasError("min")?17:e.basicFormGroup.get("thingsboard.minPackSendDelayMS").hasError("pattern")?18:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(20,20,"gateway.hints.minimal-pack-delay")),t.ɵɵadvance(7),t.ɵɵproperty("value",0),t.ɵɵadvance(2),t.ɵɵproperty("value",1),t.ɵɵadvance(2),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.qos").hasError("required")?30:e.basicFormGroup.get("thingsboard.qos").hasError("min")?31:e.basicFormGroup.get("thingsboard.qos").hasError("max")?32:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(34,22,"gateway.hints.qos")),t.ɵɵadvance(8),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.checkConnectorsConfigurationInSeconds").hasError("required")?41:e.basicFormGroup.get("thingsboard.checkConnectorsConfigurationInSeconds").hasError("min")?42:e.basicFormGroup.get("thingsboard.checkConnectorsConfigurationInSeconds").hasError("pattern")?43:-1),t.ɵɵadvance(7),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.maxPayloadSizeBytes").hasError("required")?48:e.basicFormGroup.get("thingsboard.maxPayloadSizeBytes").hasError("min")?49:e.basicFormGroup.get("thingsboard.maxPayloadSizeBytes").hasError("pattern")?50:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(52,24,"gateway.hints.max-payload-size-bytes")),t.ɵɵadvance(8),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.minPackSizeToSend").hasError("required")?59:e.basicFormGroup.get("thingsboard.minPackSizeToSend").hasError("min")?60:e.basicFormGroup.get("thingsboard.minPackSizeToSend").hasError("pattern")?61:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(63,26,"gateway.hints.min-pack-size-to-send"))}}class z6{constructor(e,t,n,i,a){this.fb=e,this.deviceService=t,this.gatewayCredentialsService=n,this.destroyRef=i,this.dialog=a,this.gatewayVersion=ct.Legacy,this.dialogMode=!1,this.withReportStrategy=!1,this.initialized=new u,this.ReportStrategyDefaultValue=on,this.initialCredentials$=this.gatewayCredentialsService.initialCredentials$,this.onChange=()=>{},this.destroy$=new we,this.initBasicFormGroup(),this.observeFormChanges(),this.basicFormGroup.valueChanges.pipe(Me(this.destroy$)).subscribe((()=>{this.onChange(this.basicFormGroup.getRawValue())}))}ngOnChanges(e){e.withReportStrategy&&!e.withReportStrategy.firstChange&&this.withReportStrategy&&this.basicFormGroup.get("thingsboard.reportStrategy").enable({emitEvent:!1}),e.gatewayVersion?.previousValue!==e.gatewayVersion.currentValue&&this.onVersionChange()}ngAfterViewInit(){this.defaultTab&&(this.configGroup.selectedIndex=$3[this.defaultTab])}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){this.basicFormGroup.patchValue(e,{emitEvent:!1});const t=this.basicFormGroup.get("thingsboard.reportStrategy"),n=e?.thingsboard?.reportStrategy;"DISABLED"===n?.type?t.disable({emitEvent:!1}):this.withReportStrategy&&t.enable({emitEvent:!1});const i=e?.thingsboard?.statistics?.commands??[];this.commandFormArray().clear({emitEvent:!1}),i.forEach((e=>this.addCommand(e,!1)))}validate(){return this.basicFormGroup.valid?null:{basicFormGroup:{valid:!1}}}commandFormArray(){return this.basicFormGroup.get("thingsboard.statistics.commands")}removeCommandControl(e,t){""!==t.pointerType&&(this.commandFormArray().removeAt(e),this.basicFormGroup.markAsDirty())}openConfigurationConfirmDialog(){this.deviceService.getDevice(this.device.id).pipe(Me(this.destroy$)).subscribe((e=>{this.dialog.open(fW,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{gatewayName:e.name}}).afterClosed().pipe(Ne(1)).subscribe((e=>{e||this.basicFormGroup.get("thingsboard.remoteConfiguration").setValue(!0,{emitEvent:!1})}))}))}addCommand(e,t=!0){const{attributeOnGateway:n=null,command:i=null,timeout:a=10,installCmd:r=""}=e||{},o=this.fb.group({attributeOnGateway:[n,[me.required,me.pattern(pn),this.uniqNameRequired()]],command:[i,[me.required,me.pattern(/^(?=\S).*\S$/)]],timeout:[a,[me.required,me.min(1),me.pattern(ln),me.pattern(/^[^.\s]+$/)]],installCmd:[r,me.pattern(pn)]});this.commandFormArray().push(o,{emitEvent:t})}uniqNameRequired(){return e=>{const t=e.value?.trim().toLowerCase();return e.dirty&&t&&this.commandFormArray().value.some((e=>e.attributeOnGateway?.toLowerCase()===t))?{duplicateName:{valid:!1}}:null}}onInitialized(e){this.basicFormGroup.patchValue(e,{emitEvent:!1}),this.initialized.emit(this.basicFormGroup.value)}initBasicFormGroup(){this.basicFormGroup=this.fb.group({thingsboard:this.initThingsboardFormGroup(),storage:[],grpc:[],connectors:this.fb.array([]),logs:[]})}initThingsboardFormGroup(){return this.fb.group({host:[window.location.hostname,[me.required,me.pattern(/^[^\s]+$/)]],port:[1883,[me.required,me.min(1),me.max(65535),me.pattern(ln)]],remoteShell:[!1],remoteConfiguration:[!0],checkConnectorsConfigurationInSeconds:[60,[me.required,me.min(1),me.pattern(ln)]],statistics:this.fb.group({enable:[!0],enableCustom:[{value:!1,disabled:!0}],statsSendPeriodInSeconds:[3600,[me.required,me.min(60),me.pattern(ln)]],customStatsSendPeriodInSeconds:[3600,[me.required,me.min(60),me.pattern(ln)]],commands:this.fb.array([])}),maxPayloadSizeBytes:[8196,[me.required,me.min(100),me.pattern(ln)]],minPackSendDelayMS:[50,[me.required,me.min(10),me.pattern(ln)]],minPackSizeToSend:[500,[me.required,me.min(100),me.pattern(ln)]],handleDeviceRenaming:[!0],checkingDeviceActivity:this.initCheckingDeviceActivityFormGroup(),security:[],qos:[1],reportStrategy:[null]})}initCheckingDeviceActivityFormGroup(){return this.fb.group({checkDeviceInactivity:[!1],inactivityTimeoutSeconds:[300,[me.min(1),me.pattern(ln)]],inactivityCheckPeriodSeconds:[10,[me.min(1),me.pattern(ln)]]})}onVersionChange(){if(this.hasUpdatedStatistics=Ya.parseVersion(this.gatewayVersion)>=Ya.parseVersion(ct.v3_7_3),this.hasUpdatedStatistics){const e=this.basicFormGroup.get("thingsboard.statistics.enableCustom");e.enable({emitEvent:!1}),this.basicFormGroup.get("thingsboard.statistics.enable").valueChanges.pipe(In(this.destroyRef)).subscribe((t=>{t||e.patchValue(!1,{emitEvent:!1}),e[t?"enable":"disable"]({emitEvent:!1})}))}}observeFormChanges(){this.observeRemoteConfigurationChanges(),this.observeDeviceActivityChanges()}observeRemoteConfigurationChanges(){this.basicFormGroup.get("thingsboard.remoteConfiguration").valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{e||this.openConfigurationConfirmDialog()}))}observeDeviceActivityChanges(){const e=this.basicFormGroup.get("thingsboard.checkingDeviceActivity");e.get("checkDeviceInactivity").valueChanges.pipe(Me(this.destroy$)).subscribe((t=>{e.updateValueAndValidity();const n=[me.min(1),me.required,me.pattern(ln)];t?(e.get("inactivityTimeoutSeconds").setValidators(n),e.get("inactivityCheckPeriodSeconds").setValidators(n)):(e.get("inactivityTimeoutSeconds").clearValidators(),e.get("inactivityCheckPeriodSeconds").clearValidators()),e.get("inactivityTimeoutSeconds").updateValueAndValidity({emitEvent:!1}),e.get("inactivityCheckPeriodSeconds").updateValueAndValidity({emitEvent:!1})}))}static{this.ɵfac=function(e){return new(e||z6)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(K.DeviceService),t.ɵɵdirectiveInject(L5),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(je.MatDialog))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:z6,selectors:[["tb-gateway-basic-configuration"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(W5,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.configGroup=e.first)}},inputs:{device:"device",defaultTab:"defaultTab",gatewayVersion:"gatewayVersion",dialogMode:"dialogMode",withReportStrategy:"withReportStrategy"},outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>z6)),multi:!0},{provide:he,useExisting:c((()=>z6)),multi:!0}]),t.ɵɵNgOnChangesFeature,t.ɵɵStandaloneFeature],decls:20,vars:21,consts:[["configGroup",""],[1,"tab-group-block",3,"formGroup"],[3,"label"],["matTabContent",""],["formControlName","logs",1,"configuration-block",3,"initialized"],["formControlName","storage",3,"initialized"],["formControlName","grpc",3,"initialized"],["formGroupName","thingsboard",1,"mat-content","mat-padding","configuration-block"],[1,"tb-form-panel","no-padding-bottom"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["color","primary","formControlName","remoteConfiguration",1,"mat-slide"],["color","primary","formControlName","remoteShell",1,"mat-slide"],[1,"no-border","no-padding","tb-standard-fields","xs:flex-col","flex","gap-2"],["appearance","outline",1,"flex","flex-1"],["translate",""],["matInput","","formControlName","host"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["matInput","","formControlName","port","type","number","min","0"],["formControlName","security",3,"initialized",4,"ngIf"],["class","tb-form-panel","formControlName","reportStrategy",3,"isExpansionMode","isConfig","defaultValue",4,"ngIf"],["formControlName","security",3,"initialized"],["formControlName","reportStrategy",1,"tb-form-panel",3,"isExpansionMode","isConfig","defaultValue"],["formGroupName","statistics",1,"tb-form-panel","no-padding-bottom"],["color","primary","formControlName","enable",1,"mat-slide"],["appearance","outline"],["matInput","","formControlName","statsSendPeriodInSeconds","type","number","min","60"],["matInput","","formControlName","customStatsSendPeriodInSeconds","type","number","min","60"],[1,"tb-form-panel"],["translate","",1,"tb-form-panel-title"],["translate","",1,"tb-form-panel-hint"],["formGroupName","statistics"],["formArrayName","commands","class","statistics-container flex flex-row",4,"ngFor","ngForOf"],["mat-stroked-button","","color","primary","type","button",2,"width","fit-content",3,"click","disabled"],[3,"tb-hint-tooltip-icon"],["color","primary","formControlName","enableCustom",1,"mat-slide"],["formArrayName","commands",1,"statistics-container","flex","flex-row"],[1,"tb-form-panel","stroked","no-padding-bottom","no-gap","command-container",3,"formGroupName"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["matInput","","formControlName","attributeOnGateway"],["matInput","","formControlName","timeout","type","number","min","0"],["appearance","outline",1,"mat-block"],["matInput","","formControlName","command"],[1,"tb-settings","pb-4"],["matInput","","formControlName","installCmd"],["matIconSuffix","",1,"cursor-pointer",3,"matTooltip"],["mat-icon-button","","matTooltipPosition","above",1,"tb-box-button",3,"click","disabled","matTooltip"],["formGroupName","checkingDeviceActivity",1,"tb-form-panel"],["color","primary","formControlName","checkDeviceInactivity",1,"mat-slide"],["class","tb-form-row no-border no-padding tb-standard-fields column-xs",4,"ngIf"],["matInput","","formControlName","minPackSendDelayMS","type","number","min","0"],["formControlName","qos"],[3,"value"],["matInput","","formControlName","checkConnectorsConfigurationInSeconds","type","number","min","0"],["matInput","","formControlName","maxPayloadSizeBytes","type","number","min","0"],["matInput","","formControlName","minPackSizeToSend","type","number","min","0"],["matInput","","formControlName","inactivityTimeoutSeconds","type","number","min","0"],["matInput","","type","number","min","0","formControlName","inactivityCheckPeriodSeconds"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-tab-group",1,0)(2,"mat-tab",2),t.ɵɵpipe(3,"translate"),t.ɵɵtemplate(4,e6,35,24,"ng-template",3),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-tab",2),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"tb-gateway-logs-configuration",4),t.ɵɵlistener("initialized",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onInitialized(i))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-tab",2),t.ɵɵpipe(9,"translate"),t.ɵɵelementStart(10,"tb-gateway-storage-configuration",5),t.ɵɵlistener("initialized",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onInitialized(i))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-tab",2),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"tb-gateway-grpc-configuration",6),t.ɵɵlistener("initialized",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onInitialized(i))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-tab",2),t.ɵɵpipe(15,"translate"),t.ɵɵtemplate(16,v6,28,8,"ng-template",3),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-tab",2),t.ɵɵpipe(18,"translate"),t.ɵɵtemplate(19,G6,65,28,"ng-template",3),t.ɵɵelementEnd()()}2&e&&(t.ɵɵclassProp("dialog-mode",n.dialogMode),t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(3,9,"gateway.general")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(6,11,"gateway.logs.logs")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(9,13,"gateway.storage")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,15,"gateway.grpc")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(15,17,"gateway.statistics.statistics")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(18,19,"gateway.other")))},dependencies:t.ɵɵgetComponentDepsFactory(z6,[se,_,ni,K4,h5,I5,H5]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:grid;grid-template-rows:min-content minmax(auto,1fr) min-content}[_nghost-%COMP%] .configuration-block[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:16px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{grid-row:1;background:transparent;color:#000000de!important}[_nghost-%COMP%] .tab-group-block[_ngcontent-%COMP%]{min-width:0;height:100%;min-height:0;grid-row:2}[_nghost-%COMP%] .toggle-group[_ngcontent-%COMP%]{margin-right:auto}[_nghost-%COMP%] .first-capital[_ngcontent-%COMP%]{text-transform:capitalize}[_nghost-%COMP%] textarea[_ngcontent-%COMP%]{resize:none}[_nghost-%COMP%] .saving-period[_ngcontent-%COMP%]{flex:1}[_nghost-%COMP%] .statistics-container[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .statistics-container[_ngcontent-%COMP%] .command-container[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] mat-form-field[_ngcontent-%COMP%] mat-error[_ngcontent-%COMP%]{display:none!important}[_nghost-%COMP%] mat-form-field[_ngcontent-%COMP%] mat-error[_ngcontent-%COMP%]:first-child{display:block!important}[_nghost-%COMP%] .pointer-event{pointer-events:all}[_nghost-%COMP%] .toggle-group span{padding:0 25px}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{color:#e0e0e0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix:hover{color:#9e9e9e}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex;align-items:center}']})}}e("GatewayBasicConfigurationComponent",z6),He([I()],z6.prototype,"dialogMode",void 0),He([I()],z6.prototype,"withReportStrategy",void 0);class U6{constructor(e){this.fb=e,this.destroy$=new we,this.advancedFormControl=this.fb.control(""),this.advancedFormControl.valueChanges.pipe(Me(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){this.advancedFormControl.reset(e,{emitEvent:!1})}validate(){return this.advancedFormControl.valid?null:{advancedFormControl:{valid:!1}}}static{this.ɵfac=function(e){return new(e||U6)(t.ɵɵdirectiveInject(de.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:U6,selectors:[["tb-gateway-advanced-configuration"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ue,useExisting:c((()=>U6)),multi:!0},{provide:he,useExisting:c((()=>U6)),multi:!0}]),t.ɵɵStandaloneFeature],decls:2,vars:4,consts:[["fillHeight","true","jsonRequired","",1,"flex","h-full","flex-col","p-2",3,"label","formControl"]],template:function(e,n){1&e&&(t.ɵɵelement(0,"tb-json-object-edit",0),t.ɵɵpipe(1,"translate")),2&e&&(t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(1,2,"gateway.configuration")),t.ɵɵproperty("formControl",n.advancedFormControl))},dependencies:t.ɵɵgetComponentDepsFactory(U6,[se,_]),encapsulation:2})}}function j6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",14),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(1,"mat-icon",15),t.ɵɵtext(2,"close"),t.ɵɵelementEnd()()}}function H6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-basic-configuration",16),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onInitialized(n))})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("device",e.device)("defaultTab",e.defaultTab)("gatewayVersion",e.gatewayVersion)("dialogMode",!!e.dialogRef)("withReportStrategy",t.ɵɵpipeBind1(1,5,e.gatewayVersion))}}function W6(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-advanced-configuration",10)}function $6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",17),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.cancel())})),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()}2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"action.cancel")," "))}e("GatewayAdvancedConfigurationComponent",U6);class K6{constructor(e,t,n,i,a){this.fb=e,this.attributeService=t,this.cd=n,this.gatewayCredentialsService=i,this.destroyRef=a,this.ConfigurationModes=an,this.gatewayConfigAttributeKeys=["general_configuration","grpc_configuration","logs_configuration","storage_configuration","RemoteLoggingLevel","mode"],this.useUpdatedLogs=!1,this.gatewayConfigGroup=this.fb.group({basicConfig:[],advancedConfig:[],mode:[an.BASIC]}),this.observeAlignConfigs()}ngAfterViewInit(){this.fetchConfigAttribute(this.device)}saveConfig(){const{mode:e,advancedConfig:t}=oe(this.removeEmpty(this.gatewayConfigGroup.value)),n={mode:e,...t};n.thingsboard.statistics.commands=Object.values(n.thingsboard.statistics.commands??[]),n.thingsboard.reportStrategy||(n.thingsboard.reportStrategy={type:"DISABLED"});const i=this.generateAttributes(n);this.attributeService.saveEntityAttributes(this.device,D.SHARED_SCOPE,i).pipe(Ve((e=>this.gatewayCredentialsService.updateCredentials(n.thingsboard.security))),In(this.destroyRef)).subscribe((()=>{this.dialogRef?this.dialogRef.close():(this.gatewayConfigGroup.markAsPristine(),this.cd.detectChanges())}))}onInitialized(e){this.gatewayConfigGroup.get("basicConfig").patchValue(e,{emitEvent:!1}),this.gatewayConfigGroup.get("advancedConfig").patchValue(e,{emitEvent:!1})}observeAlignConfigs(){this.gatewayConfigGroup.get("basicConfig").valueChanges.pipe(In(this.destroyRef)).subscribe((e=>{const t=this.gatewayConfigGroup.get("advancedConfig");W(t.value,e)||this.gatewayConfigGroup.get("mode").value!==an.BASIC||t.patchValue(e,{emitEvent:!1})})),this.gatewayConfigGroup.get("advancedConfig").valueChanges.pipe(In(this.destroyRef)).subscribe((e=>{const t=this.gatewayConfigGroup.get("basicConfig");W(t.value,e)||this.gatewayConfigGroup.get("mode").value!==an.ADVANCED||t.patchValue(e,{emitEvent:!1})}))}generateAttributes(e){const t=[],n=(e,n)=>{t.push({key:e,value:n})},i=(e,t)=>{t={...t,ts:(new Date).getTime()},n(e,t)};return n("RemoteLoggingLevel",e.logs?.logLevel??pt.NONE),delete e.connectors,n("logs_configuration",this.generateLogsFile(e.logs)),i("grpc_configuration",e.grpc),i("storage_configuration",e.storage),i("general_configuration",e.thingsboard),n("mode",e.mode),t}cancel(){this.dialogRef&&this.dialogRef.close()}removeEmpty(e){return Object.fromEntries(Object.entries(e).filter((([e,t])=>null!=t)).map((([e,t])=>[e,t===Object(t)?this.removeEmpty(t):t])))}generateLogsFile(e){const t={version:1,disable_existing_loggers:!1,formatters:{LogFormatter:{class:"logging.Formatter",format:e.logFormat,datefmt:e.dateFormat}},handlers:{consoleHandler:{class:"logging.StreamHandler",formatter:"LogFormatter",level:0,stream:"ext://sys.stdout"}},loggers:{...this.useUpdatedLogs?{}:{database:{handlers:["consoleHandler"],level:"DEBUG",propagate:!1}}},root:{level:"ERROR",handlers:["consoleHandler"]},ts:(new Date).getTime()};return this.addLocalLoggers(t,e?.local),t}addLocalLoggers(e,t){if(t)for(const n of Object.keys(t))e.handlers[n+"Handler"]=this.createHandlerObj(t[n],n),e.loggers[n]=this.createLoggerObj(t[n],n)}createHandlerObj(e,t){return{class:this.useUpdatedLogs?n4:i4,formatter:"LogFormatter",filename:`${e.filePath}/${t}.log`,backupCount:e.backupCount,interval:e.savingTime,when:e.savingPeriod,encoding:"utf-8"}}createLoggerObj(e,t){return{handlers:[`${t}Handler`,"consoleHandler"],level:e.logLevel,propagate:!1}}fetchConfigAttribute(e){e.id!==B&&this.attributeService.getEntityAttributes(e,D.CLIENT_SCOPE).pipe(Ge((t=>t.length?_e(t):this.attributeService.getEntityAttributes(e,D.SHARED_SCOPE,this.gatewayConfigAttributeKeys))),In(this.destroyRef)).subscribe((e=>{this.gatewayVersion=e.find((e=>"Version"===e.key))?.value,this.useUpdatedLogs=Ya.parseVersion(this.gatewayVersion??ct.Legacy)>=Ya.parseVersion(ct.v3_6_3),this.updateConfigs(e),this.cd.detectChanges()}))}updateConfigs(e){let t={},n=pt.NONE;this.gatewayCredentialsService.setInitialCredentials(this.device),e.forEach((e=>{switch(e.key){case"general_configuration":e.value&&(t={...t,thingsboard:e.value});break;case"grpc_configuration":e.value&&(t={...t,grpc:e.value});break;case"logs_configuration":e.value&&(t={...t,logs:this.logsToObj(e.value)});break;case"storage_configuration":e.value&&(t={...t,storage:e.value});break;case"mode":t={...t,mode:e.value??an.BASIC};break;case"RemoteLoggingLevel":e.value&&(n=e.value)}})),t.logs&&(t={...t,logs:{...t.logs,logLevel:n}}),t.thingsboard?.security?this.gatewayCredentialsService.initialCredentials$.pipe(Oe(Boolean),Ne(1),In(this.destroyRef)).subscribe((e=>{this.gatewayCredentialsService.shouldUpdateSecurityConfig(t.thingsboard.security)&&(t.thingsboard.security=this.gatewayCredentialsService.credentialsToSecurityConfig(e)),this.gatewayConfigGroup.get("basicConfig").patchValue(t,{emitEvent:!1}),this.gatewayConfigGroup.get("advancedConfig").patchValue(t,{emitEvent:!1})})):(this.gatewayConfigGroup.get("basicConfig").patchValue(t,{emitEvent:!1}),this.gatewayConfigGroup.get("advancedConfig").patchValue(t,{emitEvent:!1}))}logsToObj(e){const{format:t,datefmt:n}=e.formatters.LogFormatter;return{local:Object.keys(Y3).reduce(((t,n)=>{const i=e.handlers[`${n}Handler`]||{},a=e.loggers[n]||{};return t[n]={logLevel:a.level||pt.INFO,filePath:i.filename?.split(`/${n}`)[0]||"./logs",backupCount:i.backupCount||7,savingTime:i.interval||3,savingPeriod:i.when||Q3.days},t}),{}),logFormat:t,dateFormat:n}}static{this.ɵfac=function(e){return new(e||K6)(t.ɵɵdirectiveInject(de.FormBuilder),t.ɵɵdirectiveInject(K.AttributeService),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(L5),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:K6,selectors:[["tb-gateway-configuration"]],inputs:{device:"device",defaultTab:"defaultTab",dialogRef:"dialogRef"},standalone:!0,features:[t.ɵɵProvidersFeature([L5]),t.ɵɵStandaloneFeature],decls:24,vars:24,consts:[[1,"flex","size-full","max-w-full","flex-col",3,"formGroup"],["color","primary"],[1,"tb-flex","space-between","align-center","h-16"],["tbTruncateWithTooltip",""],[1,"flex","items-center"],["formControlName","mode",3,"appearance"],[3,"value"],["mat-icon-button","","type","button",3,"click",4,"ngIf"],[1,"content-wrapper","flex-1"],["formControlName","basicConfig",3,"device","defaultTab","gatewayVersion","dialogMode","withReportStrategy"],["formControlName","advancedConfig"],[1,"flex","h-full","items-center","justify-end","gap-2","pr-4"],["mat-button","","color","primary","type","button"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["formControlName","basicConfig",3,"initialized","device","defaultTab","gatewayVersion","dialogMode","withReportStrategy"],["mat-button","","color","primary","type","button",3,"click"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"mat-toolbar",1)(2,"div",2)(3,"h2")(4,"div",3),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",4)(8,"tb-toggle-select",5)(9,"tb-toggle-option",6),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"tb-toggle-option",6),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(15,j6,3,0,"button",7),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",8),t.ɵɵtemplate(17,H6,2,7,"tb-gateway-basic-configuration",9)(18,W6,1,0,"tb-gateway-advanced-configuration",10),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",11),t.ɵɵtemplate(20,$6,3,3,"button",12),t.ɵɵelementStart(21,"button",13),t.ɵɵlistener("click",(function(){return n.saveConfig()})),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.gatewayConfigGroup),t.ɵɵadvance(),t.ɵɵclassProp("page-header",!n.dialogRef),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,16,"gateway.gateway-configuration")),t.ɵɵadvance(3),t.ɵɵclassProp("dialog-toggle",!!n.dialogRef),t.ɵɵpropertyInterpolate("appearance",n.dialogRef?"stroked":"fill"),t.ɵɵadvance(),t.ɵɵproperty("value",n.ConfigurationModes.BASIC),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,18,"gateway.basic")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",n.ConfigurationModes.ADVANCED),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,20,"gateway.advanced")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.dialogRef),t.ɵɵadvance(2),t.ɵɵconditional(n.gatewayConfigGroup.get("mode").value===n.ConfigurationModes.BASIC?17:18),t.ɵɵadvance(3),t.ɵɵconditional(n.dialogRef?20:-1),t.ɵɵadvance(),t.ɵɵproperty("disabled",n.gatewayConfigGroup.invalid||!n.gatewayConfigGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(23,22,"action.save")," "))},dependencies:t.ɵɵgetComponentDepsFactory(K6,[se,_,tr,z6,U6]),styles:['@charset "UTF-8";[_nghost-%COMP%]{overflow:hidden;padding:0!important;max-height:75vh;height:75vh;width:800px;max-width:80vw}@media screen and (max-width: 599px){[_nghost-%COMP%]{max-height:100%;height:100%;width:100%;max-width:100%}}[_nghost-%COMP%] .page-header.mat-toolbar[_ngcontent-%COMP%]{background:transparent;color:#000000de!important}[_nghost-%COMP%] .content-wrapper[_ngcontent-%COMP%]{min-height:calc(100% - 116px)}.dialog-toggle[_ngcontent-%COMP%] .mat-button-toggle-button{color:#ffffffbf}']})}}e("GatewayConfigurationComponent",K6);class Y6{constructor(){}static{this.ɵfac=function(e){return new(e||Y6)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Y6,selectors:[["tb-lib-styles-entry"]],standalone:!0,features:[t.ɵɵStandaloneFeature],decls:0,vars:0,template:function(e,t){},styles:['@charset "UTF-8";.gw-delete-icon-button{color:#0000008a}.gw-connector-table .mat-mdc-table{table-layout:fixed}.gw-chip-list .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center}.gw-table-toolbar.mat-mdc-table-toolbar.mat-mdc-table-toolbar{padding:0 8px}.see-example{display:flex}.tb-default .h-16{height:4rem}.tb-default .h-\\[500px\\]{height:500px}.tb-default .max-h-full{max-height:100%}.tb-default .min-h-10{min-height:2.5rem}.tb-default .\\!w-1\\/5{width:20%!important}.tb-default .w-1\\/2{width:50%}.tb-default .w-12{width:3rem}.tb-default .w-\\[77vw\\]{width:77vw}.tb-default .min-w-16{min-width:4rem}.tb-default .min-w-24{min-width:6rem}.tb-default .max-w-2xl{max-width:42rem}.tb-default .max-w-3xl{max-width:48rem}.tb-default .max-w-4\\/12{max-width:33.333333%}.tb-default .max-w-\\[11vw\\]{max-width:11vw}.tb-default .max-w-full{max-width:100%}.tb-default .flex-\\[1_1_30px\\]{flex:1 1 30px}.tb-default .flex-\\[1_1_33\\%\\]{flex:1 1 33%}.tb-default .flex-grow{flex-grow:1}.tb-default .gap-1\\.25{gap:.3125rem}.tb-default .p-1{padding:.25rem}.tb-default .\\!pl-0{padding-left:0!important}.tb-default .pr-4{padding-right:1rem}.tb-default .pt-4{padding-top:1rem}.tb-default .text-center{text-align:center}@media (max-width: 599px){.tb-default .lt-sm\\:flex-col{flex-direction:column}}\n'],encapsulation:2})}}const X6=(e,t)=>{const n=e[y];if(n.styles?.length){const e=n.styles[0];let i=document.getElementById(t);if(!i){i=document.createElement("style"),i.id=t;(document.head||document.getElementsByTagName("head")[0]).appendChild(i)}i.innerHTML=e}};class Z6{constructor(e){this.translate=e,At(e),this.translate.onLangChange.pipe(In(),Oe((e=>e.lang!==ft.English))).subscribe((()=>{At(e)})),(e=>{X6(Y6,e)})("tb-gateway-css")}static{this.ɵfac=function(e){return new(e||Z6)(t.ɵɵinject(le.TranslateService))}}static{this.ɵmod=t.ɵɵdefineNgModule({type:Z6})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[se,_,ce,wj,hW,c$,K6,Tn,W3,H3]})}}e("GatewayExtensionModule",Z6),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(Z6,{imports:[se,_,ce,wj,hW,c$,K6,Tn,W3,H3]})}}}));//# sourceMappingURL=gateway-management-extension.js.map diff --git a/application/src/main/data/resources/js_modules/gateway-management-extension.js b/application/src/main/data/resources/js_modules/gateway-management-extension.js deleted file mode 100644 index 183f475e63..0000000000 --- a/application/src/main/data/resources/js_modules/gateway-management-extension.js +++ /dev/null @@ -1,17 +0,0 @@ -System.register(["@angular/core","@angular/material/sort","@angular/material/table","@angular/material/paginator","@shared/public-api","@angular/common","@angular/forms","rxjs","rxjs/operators","@core/public-api","@angular/material/dialog","tslib","@angular/material/tooltip","@ngx-translate/core","@angular/cdk/collections","@ngrx/store","@angular/router","@home/components/public-api","@angular/platform-browser","@home/components/widget/widget.component","@shared/components/dialog/json-object-edit-dialog.component","@shared/import-export/import-export.service","@shared/components/popover.component","@shared/components/popover.service","@shared/decorators/coercion","@angular/material/core"],(function(e){"use strict";var t,n,i,a,r,o,s,l,p,c,d,u,m,h,g,f,y,v,x,b,w,S,C,_,T,I,E,M,k,P,D,O,A,F,R,B,N,L,V,q,G,z,U,j,H,W,$,K,Y,X,Z,Q,J,ee,te,ne,ie,ae,re,oe,se,le,pe,ce,de,ue,me,he,ge,fe,ye,ve,xe,be,we,Se,Ce,_e,Te,Ie,Ee,Me,ke,Pe,De,Oe,Ae,Fe,Re,Be,Ne,Le,Ve,qe,Ge,ze,Ue,je,He,We,$e,Ke,Ye,Xe,Ze,Qe,Je,et,tt,nt,it,at,rt,ot;return{setters:[function(e){t=e,n=e.assertInInjectionContext,i=e.inject,a=e.DestroyRef,e.ɵRuntimeError,e.ɵgetOutputDestroyRef,r=e.Injector,o=e.effect,s=e.untracked,e.assertNotInReactiveContext,e.signal,e.computed,l=e.input,p=e.output,c=e.forwardRef,d=e.ChangeDetectionStrategy,u=e.EventEmitter,m=e.booleanAttribute,h=e.ChangeDetectorRef,g=e.SecurityContext,f=e.KeyValueDiffers,y=e.ɵNG_COMP_DEF},function(e){v=e.MatSort},function(e){x=e.MatTableDataSource},function(e){b=e.MatPaginator},function(e){w=e.Direction,S=e.PageLink,C=e.DataKeyType,_=e.SharedModule,T=e,I=e.coerceBoolean,E=e.emptyPageData,M=e.isClientSideTelemetryType,k=e.TelemetrySubscriber,P=e.coerceNumber,D=e.AttributeScope,O=e.helpBaseUrl,A=e.DialogComponent,F=e.defaultLegendConfig,R=e.widgetType,B=e.NULL_UUID,N=e.DatasourceType,L=e.EntityType,V=e.ContentType,q=e.PageComponent,G=e.TbTableDatasource,z=e.HOUR,U=e.DeviceCredentialsType},function(e){j=e.CommonModule},function(e){H=e,W=e.NG_VALUE_ACCESSOR,$=e.Validators,K=e.NG_VALIDATORS,Y=e.FormBuilder,X=e.FormControl},function(e){Z=e.Observable,Q=e.ReplaySubject,J=e.shareReplay,ee=e.combineLatest,te=e.Subject,ne=e.fromEvent,ie=e.BehaviorSubject,ae=e.of,re=e.zip,oe=e.forkJoin,se=e.merge},function(e){le=e.takeUntil,pe=e.map,ce=e.distinctUntilChanged,de=e.debounceTime,ue=e.filter,me=e.tap,he=e.catchError,ge=e.publishReplay,fe=e.refCount,ye=e.take,ve=e.takeWhile,xe=e.switchMap,be=e.startWith,we=e.mergeMap},function(e){Se=e.isEqual,Ce=e.WINDOW,_e=e,Te=e.deleteNullProperties,Ie=e.DialogService,Ee=e.isNumber,Me=e.isString,ke=e.isDefinedAndNotNull,Pe=e.formatValue,De=e.isLiteralObject,Oe=e.deepClone,Ae=e.isUndefinedOrNull,Fe=e.isObject,Re=e.generateSecret,Be=e.camelCase,Ne=e.deepTrim},function(e){Le=e.MatDialog,Ve=e.MAT_DIALOG_DATA,qe=e},function(e){Ge=e.__decorate,ze=e.__extends},function(e){Ue=e,je=e.MatTooltip},function(e){He=e,We=e.TranslateService,$e=e.TranslateModule},function(e){Ke=e.SelectionModel},function(e){Ye=e},function(e){Xe=e},function(e){Ze=e.calculateAxisSize,Qe=e.measureAxisNameSize},function(e){Je=e},function(e){et=e},function(e){tt=e.JsonObjectEditDialogComponent},function(e){nt=e},function(e){it=e},function(e){at=e},function(e){rt=e.coerceBoolean},function(e){ot=e.ErrorStateMatcher}],execute:function(){e("getDefaultConfig",Ht);const st=e("jsonRequired",(e=>e.value?null:{required:!0}));var lt,pt,ct;e("GatewayLogLevel",lt),function(e){e.NONE="NONE",e.CRITICAL="CRITICAL",e.ERROR="ERROR",e.WARNING="WARNING",e.INFO="INFO",e.DEBUG="DEBUG",e.TRACE="TRACE"}(lt||e("GatewayLogLevel",lt={})),e("GatewayVersion",pt),function(e){e.v3_7_3="3.7.3",e.v3_7_2="3.7.2",e.v3_7_0="3.7",e.v3_6_3="3.6.3",e.v3_6_2="3.6.2",e.v3_6_0="3.6",e.v3_5_2="3.5.2",e.Legacy="legacy"}(pt||e("GatewayVersion",pt={})),e("ConnectorType",ct),function(e){e.MQTT="mqtt",e.MODBUS="modbus",e.GRPC="grpc",e.OPCUA="opcua",e.BLE="ble",e.REQUEST="request",e.CAN="can",e.BACNET="bacnet",e.ODBC="odbc",e.REST="rest",e.SNMP="snmp",e.FTP="ftp",e.SOCKET="socket",e.XMPP="xmpp",e.OCPP="ocpp",e.CUSTOM="custom",e.KNX="knx"}(ct||e("ConnectorType",ct={}));const dt=e("GatewayConnectorDefaultTypesTranslatesMap",new Map([[ct.MQTT,"MQTT"],[ct.MODBUS,"MODBUS"],[ct.GRPC,"GRPC"],[ct.OPCUA,"OPCUA"],[ct.BLE,"BLE"],[ct.REQUEST,"REQUEST"],[ct.CAN,"CAN"],[ct.BACNET,"BACNET"],[ct.ODBC,"ODBC"],[ct.REST,"REST"],[ct.SNMP,"SNMP"],[ct.FTP,"FTP"],[ct.SOCKET,"SOCKET"],[ct.XMPP,"XMPP"],[ct.OCPP,"OCPP"],[ct.CUSTOM,"CUSTOM"],[ct.KNX,"KNX"]])),ut=e("ConnectorsTypesByVersion",new Map([[pt.v3_7_0,Object.values(ct)],[pt.Legacy,Object.values(ct).filter((e=>e!==ct.KNX))]]));var mt,ht;e("SocketEncoding",mt),function(e){e.UTF8="utf-8",e.HEX="hex",e.UTF16="utf-16",e.UTF32="utf-32",e.UTF16BE="utf-16-be",e.UTF16LE="utf-16-le",e.UTF32BE="utf-32-be",e.UTF32LE="utf-32-le"}(mt||e("SocketEncoding",mt={})),e("HTTPMethods",ht),function(e){e.DELETE="DELETE",e.GET="GET",e.HEAD="HEAD",e.OPTIONS="OPTIONS",e.PATCH="PATCH",e.POST="POST",e.PUT="PUT"}(ht||e("HTTPMethods",ht={}));var gt={gateway:{active:"Active",address:"Address","address-required":"Address required","add-entry":"Add configuration","add-attribute":"Add attribute","add-attribute-update":"Add attribute update","add-attribute-request":"Add attribute request","add-key":"Add key","add-timeseries":"Add time series","add-mapping":"Add mapping","add-slave":"Add Slave",arguments:"Arguments","add-rpc-method":"Add method","add-rpc-request":"Add request","add-value":"Add argument","advanced-settings":"Advanced settings",application:"Application",bacnet:{"device-timeout":"Discovery timeout","network-number":"Network number","alt-responses-address":"Alternative responses address","apdu-length":"APDU Length","object-name":"Object Name","object-type":{"analog-input":"Analog Input","analog-output":"Analog Output","analog-value":"Analog Value","binary-input":"Binary Input","binary-output":"Binary Output","binary-value":"Binary Value"},"request-type":{label:"Request Type",write:"Write Property",read:"Read Property"},"property-id":{"present-value":"Present Value","status-flags":"Status Flags","cov-increment":"COV Increment","event-state":"Event State","out-of-service":"Out of Service",polarity:"Polarity","priority-array":"Priority Array","relinquish-default":"Relinquish Default","current-command-priority":"Current Command Priority","event-message-texts":"Event Message Texts","event-message-texts-config":"Event Message Texts Config","event-algorithm-inhibit-reference":"Event Algorithm Inhibit Reference","time-delay-normal":"Time Delay Normal"},segmentation:{label:"Segmentation",no:"None",both:"Both",transmit:"Transmit",receive:"Receive"}},baudrate:"Baudrate","buffer-size":"Buffer size","buffer-size-required":"Buffer size is required","buffer-size-range":"Buffer size should be greater than 0",bytesize:"Bytesize",boolean:"Boolean",bit:"Bit","bit-target-type":"Bit target type","delete-value":"Delete value","delete-argument":"Delete argument","delete-rpc-method":"Delete method","delete-rpc-request":"Delete request","delete-attribute-update":"Delete attribute update","delete-attribute-request":"Delete attribute request",advanced:"Advanced","add-device":"Add device","address-filter":"Address filter","address-filter-required":"Address filter is required","advanced-connection-settings":"Advanced connection settings","advanced-configuration-settings":"Advanced configuration settings",attributes:"Attributes","attribute-updates":"Attribute updates","attribute-on-platform":"Attribute on platform","attribute-requests":"Attribute requests","attribute-filter":"Attribute filter","attribute-filter-hint":"Filter for incoming attribute name from platform, supports regular expression.","attribute-filter-required":"Attribute filter required.","attribute-name-expression":"Attribute name expression","attribute-name-expression-required":"Attribute name expression required.","attribute-name-expression-hint":"Hint for Attribute name expression",basic:"Basic","byte-order":"Byte order","word-order":"Word order",broker:{connection:"Connection to broker",name:"Broker name","name-required":"Broker name required.","security-types":{anonymous:"Anonymous",basic:"Basic",certificates:"Certificates"}},certificate:"Certificate","CA-certificate-path":"Path to CA certificate file","path-to-CA-cert-required":"Path to CA certificate file is required.","change-connector-title":"Confirm connector change","change-connector-text":"Switching connectors will discard any unsaved changes. Continue?","checking-device-activity":"Checking device activity",command:"Docker commands","command-copied-message":"Docker command has been copied to clipboard",configuration:"Configuration","add-connector":"Add connector","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","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","connection-timeout":"Connection timeout (s)","connect-attempt-time":"Connect attempt time (ms)","connect-attempt-count":"Connect attempt count","copy-username":"Copy username","copy-password":"Copy password","copy-client-id":"Copy client ID","connector-created":"Connector created","connector-updated":"Connector updated","create-new-one":"Create new one!","rpc-command-save-template":"Save Template","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 Docker compose with credentials for selected device","install-docker-compose":"Use the instructions to download, install and setup docker compose",integer:"Integer",inactive:"Inactive",device:"Device",devices:"Devices","device-profile":"Device profile","device-info-settings":"Device info settings","device-info":{"entity-field":"Entity field",source:"Source",expression:"Value / Expression","expression-hint":"Show help",name:"Name","profile-name":"Profile name","device-name-expression":"Device name expression","device-name-expression-required":"Device name expression is required.","device-profile-expression-required":"Device profile expression is required."},"device-name-filter":"Device name filter","device-name-filter-hint":"This field supports regular expressions to filter incoming data by device name.","device-name-filter-required":"Device name filter is required.",details:"Details","delete-mapping-title":"Are you sure you want to delete the mapping related with '{{name}}'?","delete-mapping-description":"Be careful, after the confirmation mapping configuration and all related data will become unrecoverable.","delete-slave-title":"Are you sure you want to delete the server '{{name}}'?","delete-slave-description":"Be careful, after the confirmation server configuration and all related data will become unrecoverable.","delete-device-title":"Are you sure you want to delete the device '{{name}}'?","delete-device-description":"Be careful, after the confirmation the device configuration and all related data will become unrecoverable.",divider:"Divider","download-configuration-file":"Download configuration file","download-docker-compose":"Download docker-compose.yml for your gateway","enable-remote-logging":"Enable remote logging","ellipsis-chips-text":"+ {{count}} more","launch-gateway":"Launch gateway","launch-docker-compose":"Start the gateway using the following command in the terminal from folder with docker-compose.yml file","logs-configuration":"Logs configuration","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","connector-duplicate-name":"Connector with such name already exists.","connection-type":"Connection type","connector-side":"Connector side","payload-type":"Payload type","platform-side":"Platform side",JSON:"JSON","JSON-hint":"Converter for this payload type processes MQTT messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and time series from the message. And regular expressions to get device details from topics.",byte:"Byte",bytes:"Bytes","bytes-hint":"Converter for this payload type designed for binary MQTT payloads, this converter directly interprets binary data to retrieve device names and device profile names, along with attributes and time series, using specific byte positions for data extraction.",custom:"Custom","custom-hint":"This option allows you to use a custom converter for specific data tasks. You need to add your custom converter to the extension folder and enter its class name in the UI settings. Any keys you provide will be sent as configuration to your custom converter.","client-cert-path":"Path to client certificate file","path-to-client-cert-required":"Path to client certificate file is required.","client-id":"Client ID","data-conversion":"Data conversion","data-mapping":"Data mapping","data-mapping-hint":"Data mapping provides the capability to parse and convert the data received from a MQTT client in incoming messages into specific attributes and time series data keys.","opcua-data-mapping-hint":"Data mapping provides the capability to parse and convert the data received from a OPCUA server into specific data keys.",delete:"Delete configuration","delete-attribute":"Delete attribute","delete-key":"Delete key","delete-timeseries":"Delete time series",default:"Default","device-node":"Device node","device-node-required":"Device node required.","device-node-hint":"Path or identifier for device node on OPC UA server. Relative paths from it for attributes and time series can be used.","device-name":"Device name","device-profile-label":"Device profile","device-name-required":"Device name required","device-profile-required":"Device profile required","download-tip":"Download configuration file","drop-file":"Drop file here or",enable:"Enable",encoding:"Encoding","enable-subscription":"Enable subscription",extension:"Extension","extension-hint":"Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.","extension-required":"Extension is required.","extension-configuration":"Extension configuration","extension-configuration-hint":"Configuration for converter. These arguments will be passed as a config to convert function.","fill-connector-defaults":"Fill configuration with default values","fill-connector-defaults-hint":"This property allows to fill connector configuration with default values on it's creation.","from-device-request-settings":"Input request parsing","from-device-request-settings-hint":"These fields support JSONPath expressions to extract a name from incoming message.","function-code":"Function code","function-codes":{"read-coils":"01 - Read Coils","read-discrete-inputs":"02 - Read Discrete Inputs","read-multiple-holding-registers":"03 - Read Multiple Holding Registers","read-input-registers":"04 - Read Input Registers","write-single-coil":"05 - Write Single Coil","write-single-holding-register":"06 - Write Single Holding Register","write-multiple-coils":"15 - Write Multiple Coils","write-multiple-holding-registers":"16 - Write Multiple Holding Registers"},"to-device-response-settings":"Output request processing","to-device-response-settings-hint":"For these fields you can use the following variables and they will be replaced with actual values: ${deviceName}, ${attributeKey}, ${attributeValue}",gateway:"Gateway","gateway-exists":"Device with same name is already exists.","gateway-status":"Gateway status","gateway-name":"Gateway name","gateway-name-required":"Gateway name is required.","gateway-saved":"Gateway configuration successfully saved.","generate-client-id":"Generate Client ID",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",info:"Info",identity:"Identity","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","unit-id":"Unit ID",host:"Host","host-required":"Host is required.",holding_registers:"Holding registers",coils_initializer:"Coils initializer",input_registers:"Input registers",discrete_inputs:"Discrete inputs","json-parse":"Not valid JSON.","json-required":"Field cannot be empty.","JSONPath-hint":"Supports constants and JSONPath expressions to extract data.",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"},"max-number-of-workers":"Max number of workers","max-number-of-workers-hint":"Maximal number of workers threads for converters \n(The amount of workers changes dynamically, depending on load) \nRecommended amount 50-150.","max-number-of-workers-required":"Max number of workers is required.","max-messages-queue-for-worker":"Max messages queue per worker","max-messages-queue-for-worker-hint":"Maximal messages count that will be in the queue \nfor each converter worker.","max-messages-queue-for-worker-required":"Max messages queue per worker is required.",method:"Method","method-name":"Method name","method-required":"Method name is 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 10","min-pack-send-delay-pattern":"Min pack send delay is not valid",multiplier:"Multiplier",mode:"Mode","model-name":"Model name",modifier:"Modifier","modifier-invalid":"Modifier is not valid","mqtt-version":"MQTT version",name:"Name","name-required":"Name is required.","network-mask":"Network mask","no-attributes":"No attributes","no-attribute-updates":"No attribute updates","no-attribute-requests":"No attribute requests","no-connectors":"No connectors","no-data":"No configurations","no-gateway-found":"No gateway found.","no-gateway-matching":" '{{item}}' not found.","no-timeseries":"No time series","no-keys":"No keys","no-value":"No arguments","no-rpc-methods":"No RPC methods","no-rpc-requests":"No RPC requests","path-hint":"The path is local to the gateway file system","path-logs":"Path to log files","path-logs-required":"Path is required.",password:"Password","password-required":"Password is required.","permit-without-calls":"Keep alive permit without calls","property-id":"Property ID","poll-period":"Poll period (ms)","poll-period-error":"Poll period should be at least {{min}} (ms).",port:"Port","port-required":"Port is required.","port-limits-error":"Port should be number from {{min}} to {{max}}.","private-key-path":"Path to private key file","path-to-private-key-required":"Path to private key file is required.",parity:"Parity","product-code":"Product code","product-name":"Product name",raw:"Raw",retain:"Retain","retain-hint":"This flag tells the broker to store the message for a topic\nand ensures any new client subscribing to that topic\nwill receive the stored message.",remote:"Remote configuration","remote-logging-level":"Logging level","remove-entry":"Remove configuration","remote-shell":"Remote shell","remote-configuration":"Remote Configuration","request-expression":"Request expression","request-expression-required":"Request expression is required",retries:"Retries","retries-on-empty":"Retries on empty","retries-on-invalid":"Retries on invalid",response:"Response",rest:{"add-http-header":"Add HTTP header","no-http-headers":"No HTTP headers","delete-http-header":"Delete HTTP header","ssl-verify":"SSL verify",endpoint:"Endpoint","http-methods":"HTTP methods","http-method":"HTTP method","on-error":"On error","on-success":"On success",tries:"Tries","http-headers":"HTTP headers","allow-redirects":"Allow redirects","request-url-expression":"Request URL expression","response-attribute":"Response attribute","response-expected":"Expected","response-type":{default:"Default",const:"Constant",advanced:"Advanced"}},rpc:{title:"{{type}} Connector RPC parameters","templates-title":"Connector RPC Templates",methodFilter:"Method filter","method-name":"Method name",requestTopicExpression:"Request topic expression",responseTopicExpression:"Response topic expression",responseTimeout:"Response timeout",valueExpression:"Value expression",tag:"Tag",type:"Type",functionCode:"Function Code",objectsCount:"Objects Count",address:"Address",method:"Method",requestType:"Request Type",requestTimeout:"Request Timeout",objectType:"Object type",identifier:"Identifier",propertyId:"Property ID",methodRPC:"Method RPC name",withResponse:"With Response",characteristicUUID:"Characteristic UUID",methodProcessing:"Method Processing",nodeID:"Node ID",isExtendedID:"Is Extended ID",isFD:"Is FD",bitrateSwitch:"Bitrate Switch",dataInHEX:"Data In HEX",dataLength:"Data Length",dataByteorder:"Data Byte Order",dataBefore:"Data Before",dataAfter:"Data After",dataExpression:"Data Expression",oid:"OID","add-oid":"Add OID","add-header":"Add header","add-security":"Add security",remove:"Remove",requestFilter:"Request Filter",requestUrlExpression:"Request URL Expression",httpMethod:"HTTP Method",timeout:"Timeout",tries:"Tries",httpHeaders:"HTTP Headers","header-name":"Header name",hint:{"modbus-response-reading":"RPC response will return all subtracted values from all connected devices when the reading functions are selected.","modbus-writing-functions":"RPC will write a filled value to all connected devices when the writing functions are selected.","opc-method":"A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)."},"security-name":"Security name",value:"Value",security:"Security",responseValueExpression:"Response Value Expression",requestValueExpression:"Request Value Expression",arguments:"Arguments","add-argument":"Add argument","write-property":"Write property","read-property":"Read property","analog-output":"Analog output","analog-input":"Analog input","binary-output":"Binary output","binary-input":"Binary input","binary-value":"Binary value","analog-value":"Analog value",write:"Write",read:"Read",scan:"Scan",oids:"OIDS",set:"Set",multiset:"Multiset",get:"Get","bulk-walk":"Bulk walk",table:"Table","multi-get":"Multiget","get-next":"Get next","bulk-get":"Bulk get",walk:"Walk","save-template":"Save template","template-name":"Template name","template-name-required":"Template name is required.","template-name-duplicate":"Template with such name already exists, it will be updated.",command:"Command",params:"Params","json-value-invalid":"JSON value has an invalid format"},"rpc-methods":"RPC methods","rpc-requests":"RPC requests",request:{"connect-request":"Connect request","disconnect-request":"Disconnect request","attribute-request":"Attribute request","attribute-update":"Attribute update","rpc-connection":"RPC command"},"request-type":"Request type","request-timeout":"Request timeout (ms)","requests-mapping":"Requests mapping","requests-mapping-hint":"MQTT Connector requests allows you to connect, disconnect, process attribute requests from the device, handle attribute updates on the server and RPC processing configuration.","request-topic-expression":"Request topic expression","request-client-certificate":"Request client certificate","request-topic-expression-required":"Request topic expression is required.","response-timeout":"Response timeout","response-timeout-required":"Response timeout is required.","response-timeout-limits-error":"Timeout must be more then {{min}} ms.","response-topic-Qos":"Response topic QoS","response-topic-Qos-hint":"MQTT Quality of Service (QoS) is an agreement between the message sender and receiver that defines the level of delivery guarantee for a specific message.","response-topic-expression":"Response topic expression","response-topic-expression-required":"Response topic expression is required.","response-value-expression":"Response value expression","response-value-expression-required":"Response value expression is required.","vendor-name":"Vendor name","vendor-url":"Vendor URL",value:"Value",values:"Values","value-required":"Value is required.","value-expression":"Value expression","value-expression-required":"Value expression is required.","with-response":"With response","without-response":"Without response",other:"Other",socket:"Socket","save-tip":"Save configuration file","scan-period":"Scan period (ms)","scan-period-error":"Scan period should be at least {{min}} (ms).","sub-check-period":"Subscription check period (ms)","sub-check-period-error":"Subscription check period should be at least {{min}} (ms).",security:"Security","security-policy":"Security policy","security-type":"Security type","security-types":{"access-token":"Access Token","username-password":"Username and Password",tls:"TLS","tls-access-token":"TLS + Access Token","tls-private-key":"TLS + Private Key"},"select-connector":"Select connector to display config","send-change-data":"Send data only on change","send-data-to-platform":"Send data to platform","send-data-on-change":"Send data only on change","send-change-data-hint":"The values will be saved to the database only if they are different from the corresponding values in the previous converted message. This functionality applies to both attributes and time series in the converter output.",server:"Server","server-hostname":"Server hostname","server-slave":"Server (Slave)","servers-slaves":"Servers (Slaves)","server-port":"Server port","server-url":"Server endpoint url","server-connection":"Server Connection","server-config":"Server configuration","server-slave-config":"Server (Slave) configuration","server-url-required":"Server endpoint url is required.",stopbits:"Stopbits",strict:"Strict",set:"Set","show-map":"Show map",statistics:{entry:"Statistic entry","custom-send-period":"Custom send period (in sec)","custom-send-period-pattern":"Custom send period is not valid","custom-send-period-min":"Custom send period can not be less then 60","custom-send-period-required":"Custom send period is required","create-command":"Create command",attributes:"Attributes","name-already-exists":"Attribute name already exists.",telemetry:"Telemetry","storage-message-count":"Storage message count","messages-from-platform":"Messages from platform","pushed-datapoints":"Pushed datapoints","messages-pulled-from-storage":"Messages pulled from storage","messages-pushed-to-platform":"Messages pushed to platform","messages-sent-to-platform":"Messages sent to platform","process-cpu-usage":"Gateway process CPU usage",memory:"Gateway process memory usage","machine-resources":"Machine resources","free-disk":"Free disk",statistic:"Statistic",statistics:"Statistics","general-statistics":"General statistics","custom-statistics":"Custom statistics","copy-message":"Copy message","statistic-commands-empty":'No configured statistic keys found. You can configure them in "Statistics" tab in general configuration.',"statistics-button":"Go to configuration",commands:"Commands",name:"Time series name","time-series-name-already-exists":"Time series name already exists.","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)",messages:"Messages","max-payload-size-bytes":"Max payload size in bytes","max-payload-size-bytes-required":"Max payload size in bytes is required","max-payload-size-bytes-min":"Max payload size in bytes can not be less then 100","max-payload-size-bytes-pattern":"Max payload size in bytes is not valid","min-pack-size-to-send":"Min packet size to send","min-pack-size-to-send-required":"Min packet size to send is required","min-pack-size-to-send-min":"Min packet size to send can not be less then 100","min-pack-size-to-send-pattern":"Min packet size to send is not valid","no-config-commands-found":"No configuration commands found","delete-command":"Delete command '{{command}}'?","delete-command-data":"All command data will be deleted.","edit-command":"Edit command","change-command-title":"Discard command change","change-command-text":"Cancelling command edit will discard any unsaved changes. Continue?","no-command-found":"No command found","no-commands-matching":"No command matching '{{command}}' were found.","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","install-cmd":"Install command",add:"Add command",timeout:"Timeout (in sec)","timeout-required":"Timeout is required","timeout-min":"Timeout can not be less then 1","timeout-pattern":"Timeout is not valid","time-series-name-required":"Time series name is required","time-series-name-pattern":"Time series name is not valid",command:"Command","command-required":"Command is required","command-pattern":"Command is not valid",remove:"Remove command"},storage:"Storage","storage-max-files":"Maximum number of files","storage-max-files-min":"Minimum number is 1.","storage-max-files-pattern":"Number is not valid.","storage-max-files-required":"Number is required.","storage-max-records":"Maximum records in storage","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.","storage-pack-size-required":"Maximum event pack size is required.","storage-path":"Storage path","storage-path-required":"Storage path is required.","storage-type":"Storage type","storage-types":{"file-storage":"File storage","memory-storage":"Memory storage",sqlite:"SQLITE"},suffix:{s:"s",ms:"ms"},"report-strategy":{label:"Report strategy","on-change":"On value change","on-report-period":"On report period","on-change-or-report-period":"On value change or report period","report-period":"Report period","on-received":"On received"},"source-type":{msg:"Message",topic:"Topic",const:"Constant",identifier:"Identifier",path:"Path",expression:"Expression",request:"From request"},"workers-settings":"Workers settings",thingsboard:"ThingsBoard",general:"General",timeseries:"Time series",key:"Key",keys:"Keys","key-required":"Key is required.","thingsboard-host":"Platform host","thingsboard-host-required":"Host is required.","thingsboard-port":"Platform port","thingsboard-port-max":"Maximum port number is 65535.","thingsboard-port-min":"Minimum port number is 1.","thingsboard-port-pattern":"Port is not valid.","thingsboard-port-required":"Port is required.",tidy:"Tidy","tidy-tip":"Tidy config JSON",timeout:"Timeout","timeout-error":"Timeout should be at least {{min}} (ms).","title-connectors-json":"Connector {{typeName}} configuration",type:"Type","topic-filter":"Topic filter","topic-required":"Topic filter is required.","tls-path-ca-certificate":"Path to CA certificate on gateway","tls-path-client-certificate":"Path to client certificate on gateway","tls-connection":"TLS Connection","master-connections":"Master Connections","method-filter":"Method filter","method-filter-hint":"Regular expression to filter incoming RPC method from platform.","method-filter-required":"Method filter is required.","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",qos:{"at-most-once":"0 - At most once","at-least-once":"1 - At least once","exactly-once":"2 - Exactly once"},"objects-count":"Objects count","object-id":"Object ID","objects-count-required":"Objects count is required","wait-after-failed-attempts":"Wait after failed attempts (ms)","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",username:"Username","username-required":"Username is required.","unit-id-required":"Unit ID is required.","vendor-id":"Vendor ID","write-coil":"Write Coil","write-coils":"Write Coils","write-register":"Write Register","write-registers":"Write Registers",hints:{"enable-general-statistics":"Enables gateway statistics (machine, storage, connectors).","enable-custom-statistics":"Enables collecting statistics using custom commands.","buffer-size":"Buffer size for received data blocks.",encoding:"Encoding used for writing received string data to storage.",method:"Name for method on a platform.","modbus-master":"Configuration sections for connecting to Modbus servers and reading data from them.","modbus-server":"Configuration section for the Modbus server, storing data and sending updates to the platform when changes occur or at fixed intervals.","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 platform server",port:"Port of MQTT service on platform server",token:"Access token for the gateway from platform server","client-id":"MQTT client id for the gateway form platform server",username:"MQTT username for the gateway form platform server",password:"MQTT password for the gateway form platform server","ca-cert":"Path to CA certificate file","date-form":"Date format in log message","object-id-required":"Object ID is required","vendor-id-required":"Vendor ID is required","data-folder":"Path to the folder that will contain 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 number of files that will be created","max-read-count":"Number of messages to retrieve from the storage and send to platform","max-records":"Maximum count of records that will be stored in one file","read-record-count":"Number of messages to retrieve from the storage and send to platform","max-records-count":"Maximum number of data entries in storage before sending to platform","ttl-check-hour":"How often will the Gateway check data for obsolescence","ttl-messages-day":"Maximum number of days that the storage will retain data","username-required-with-password":"Username required if password is specified",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.","path-in-os":"Path in gateway os.",memory:"Your data will be stored in the in-memory queue, it is a fastest but no persistence guarantee.",file:"Your data will be stored in separated files and will be saved even after the gateway restart.",sqlite:"Your data will be stored in file based database. And will be saved even after the gateway restart.","opc-timeout":"Timeout in milliseconds for connecting to OPC-UA server.","security-policy":"Security Policy defines the security mechanisms to be applied.","install-cmd":"Packages that will be installed for command executing.","scan-period":"Period in milliseconds to rescan the server.","sub-check-period":"Period to check the subscriptions in the OPC-UA server.","enable-subscription":"If true - the gateway will subscribe to interesting nodes and wait for data update and if false - the gateway will rescan OPC-UA server every scanPeriodInMillis.","show-map":"Show nodes on scanning.","method-name":"Name of method on OPC-UA server.",arguments:"Arguments for the method (will be overwritten by arguments from the RPC request).","min-pack-size-to-send":"Minimum package size for sending.","max-payload-size-bytes":"Maximum package size in bytes","poll-period":"Period in milliseconds to read data from nodes.","poll-period-required":"Poll period is required.","report-period-required":"Report period is required.","report-period-range":"Report period must be greater than 100.","timeout-pattern":"Timeout is not valid",rest:{"endpoint-required":"Endpoint is required.","endpoint-pattern":"Endpoint is not valid.","device-name-filter-required":"Device name filter is required.","device-name-filter-pattern":"Device name filter is not valid.","request-url-expression-required":"Request URL expression is required.","request-url-expression-pattern":"Request URL expression is not valid.","value-expression-required":"Value expression is required.","value-expression-pattern":"Value expression is not valid.","method-filter-required":"Method filter is required.","method-filter-pattern":"Method filter is not valid.","scope-type":"Attribute scope on platform.","tries-min":"Request tries number should be greater than 0.","tries-pattern":"Request tries is invalid.","http-methods-required":"HTTP methods are required.",host:"Domain or IP address of the server.",port:"Port on the server for listening clients connection.","ssl-verify":"Enable SSL certificate verification.",cert:"Path to the certificate file.",key:"Path to the private key file",endpoint:"Endpoint path on the server.","http-methods":"HTTP methods allowed for endpoint.","http-method":"Request to external URL HTTP method.",JSON:"Converter for this payload type processes REST messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and time series from the request body.",extension:"Custom converter classname. File with this class should be in extension/rest folder.","response-attribute-required":"Response attribute is required.","response-attribute-pattern":"Response attribute is not valid.","update-ssl":"Verify SSL certificate on the external server.","attribute-filter":"Filter for incoming attribute name from platform, support regular expression.","value-expression":"Used to create request body for request to external server. Supports ${deviceName}, ${deviceType}, ${attributeName}, ${attributeValue} variables.","attribute-url-expression":"Used to create request URL for request to external server. Supports ${deviceName}, ${deviceType}, ${attributeName}, ${attributeValue} variables.","method-filter":"Filter for RPC requests from the platform based on the method name. Supports regular expressions.","attribute-name-expression-required":"Attribute name expression is required.","attribute-name-expression-pattern":"Attribute name expression is not valid.","attribute-filter-required":"Attribute filter is required.","attribute-filter-pattern":"Attribute filter is not valid."},socket:{"attribute-on-platform-required":"Attribute on platform is required","attribute-requests-type":"The type of requested attribute can be “shared” or “client.“","with-response":"Boolean flag that specifies whether to send a response back to platform.","key-telemetry":"Name for telemetry on platform.","key-attribute":"Name for attribute on platform."},modbus:{"bit-target-type":"The response type can be either an integer (1/0) or a boolean (True/False).",bit:"Specify the index of the bit to read from the array, or leave it blank to read the entire array.","max-bit":"The bit value must not exceed the objects count.","framer-type":"Type of a framer (Socket, RTU, or ASCII), if needed.",host:"Hostname or IP address of Modbus server.",port:"Modbus server port for connection.","unit-id":"Modbus slave ID.","connection-timeout":"Connection timeout (in seconds) for the Modbus server.","byte-order":"Byte order for reading data.","word-order":"Word order when reading multiple registers.",retries:"Retrying data transmission to the master. Acceptable values: true or false.","retries-on-empty":"Retry sending data to the master if the data is empty.","retries-on-invalid":"Retry sending data to the master if it fails.","poll-period":"Period in milliseconds to check attributes and telemetry on the slave.","connect-attempt-time":"A waiting period in milliseconds before establishing a connection to the master.","connect-attempt-count":"The number of connection attempts made through the gateway.","wait-after-failed-attempts":"A waiting period in milliseconds before attempting to send data to the master.","serial-port":"Serial port for connection.",baudrate:"Baud rate for the serial device.",stopbits:"The number of stop bits sent after each character in a message to indicate the end of the byte.",bytesize:"The number of bits in a byte of serial data. This can be one of 5, 6, 7, or 8.",parity:"The type of checksum used to verify data integrity. Options: (E)ven, (O)dd, (N)one.",strict:"Use inter-character timeout for baudrates ≤ 19200.","objects-count":"Depends on the selected type.",address:"Register address to verify.",key:"Key to be used as the attribute key for the platform instance.",method:"Method name to be used for the platform instance.","data-keys":"For more information about function codes and data types click on help icon",modifier:"The retrieved value will be adjusted (by multiplying or dividing it) based on the specified modifier value."},bacnet:{"object-id":"The gateway object identifier in the BACnet network.","vendor-id":"The gateway vendor identifier in the BACnet network","apdu-length":"Maximal length of the APDU.",segmentation:"Segmentation type for transmitting large BACnet messages.","key-object-id":"Object id in the BACnet device.","property-id":"Property id in the BACnet device.","request-type":"“writeProperty” to write data and “readProperty” to read data.","request-timeout":"Timeout to wait the response from the BACnet device, milliseconds.","device-timeout":"Period of time when the connector will try to discover BACnet devices.","network-number":"Identifier of the network segment."}}}},ft={"add-entry":"إضافة تكوين",advanced:"متقدم","checking-device-activity":"فحص نشاط الجهاز",command:"أوامر Docker","command-copied-message":"تم نسخ أمر Docker إلى الحافظة",configuration:"التكوين","connector-add":"إضافة موصل جديد","connector-enabled":"تمكين الموصل","connector-name":"اسم الموصل","connector-name-required":"اسم الموصل مطلوب.","connector-type":"نوع الموصل","connector-type-required":"نوع الموصل مطلوب.",connectors:"الموصلات","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":"تكوين عام","docker-label":"استخدم التعليمات التالية لتشغيل IoT Gateway في Docker compose مع بيانات اعتماد للجهاز المحدد","install-docker-compose":"استخدم التعليمات لتنزيل وتثبيت وإعداد docker compose","download-configuration-file":"تنزيل ملف التكوين","download-docker-compose":"تنزيل docker-compose.yml لبوابتك","launch-gateway":"تشغيل البوابة","launch-docker-compose":"بدء تشغيل البوابة باستخدام الأمر التالي في الطرفية من المجلد الذي يحتوي على ملف docker-compose.yml","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":"الحد الأدنى للوقت بين البينغات (بالمللي ثانية)","grpc-min-time-between-pings-required":"الحد الأدنى للوقت بين البينغات مطلوب","grpc-min-time-between-pings-min":"الحد الأدنى للوقت بين البينغات لا يمكن أن يكون أقل من 1","grpc-min-time-between-pings-pattern":"الحد الأدنى للوقت بين البينغات غير صالح","grpc-min-ping-interval-without-data":"الحد الأدنى لفاصل البينغ بدون بيانات (بالمللي ثانية)","grpc-min-ping-interval-without-data-required":"الحد الأدنى لفاصل البينغ بدون بيانات مطلوب","grpc-min-ping-interval-without-data-min":"الحد الأدنى لفاصل البينغ بدون بيانات لا يمكن أن يكون أقل من 1","grpc-min-ping-interval-without-data-pattern":"الحد الأدنى لفاصل البينغ بدون بيانات غير صالح","grpc-max-pings-without-data":"الحد الأقصى لعدد البينغات بدون بيانات","grpc-max-pings-without-data-required":"الحد الأقصى لعدد البينغات بدون بيانات مطلوب","grpc-max-pings-without-data-min":"الحد الأقصى لعدد البينغات بدون بيانات لا يمكن أن يكون أقل من 1","grpc-max-pings-without-data-pattern":"الحد الأقصى لعدد البينغات بدون بيانات غير صالح","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":"قشرة عن بُعد","remote-configuration":"التكوين عن بُعد",other:"آخر","save-tip":"حفظ ملف التكوين","security-type":"نوع الأمان","security-types":{"access-token":"رمز الوصول","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-required":"المهلة مطلوبة","timeout-min":"لا يمكن أن تكون المهلة أقل من 1","timeout-pattern":"المهلة غير صالحة","attribute-name":"اسم السمة","attribute-name-required":"اسم السمة مطلوب",command:"الأمر","command-required":"الأمر مطلوب","command-pattern":"الأمر غير صالح",remove:"إزالة الأمر"},storage:"التخزين","storage-max-file-records":"السجلات القصوى في الملف","storage-max-files":"الحد الأقصى لعدد الملفات","storage-max-files-min":"الحد الأدنى هو 1.","storage-max-files-pattern":"العدد غير صالح.","storage-max-files-required":"العدد مطلوب.","storage-max-records":"السجلات القصوى في التخزين","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":"العدد غير صالح.","storage-pack-size-required":"الحجم الأقصى لحزمة الحدث مطلوب.","storage-path":"مسار التخزين","storage-path-required":"مسار التخزين مطلوب.","storage-type":"نوع التخزين","storage-types":{"file-storage":"تخزين الملفات","memory-storage":"تخزين الذاكرة",sqlite:"SQLITE"},thingsboard:"ثينغزبورد",general:"عام","thingsboard-host":"مضيف ثينغزبورد","thingsboard-host-required":"المضيف مطلوب.","thingsboard-port":"منفذ ثينغزبورد","thingsboard-port-max":"الحد الأقصى لرقم المنفذ هو 65535.","thingsboard-port-min":"الحد الأدنى لرقم المنفذ هو 1.","thingsboard-port-pattern":"المنفذ غير صالح.","thingsboard-port-required":"المنفذ مطلوب.",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",hints:{"remote-configuration":"يمكنك تمكين التكوين وإدارة البوابة عن بُعد","remote-shell":"يمكنك تمكين التحكم البعيد في نظام التشغيل مع البوابة من عنصر واجهة المستخدم قشرة عن بُعد",host:"اسم المضيف أو عنوان IP لخادم ثينغزبورد",port:"منفذ خدمة MQTT على خادم ثينغزبورد",token:"رمز الوصول للبوابة من خادم ثينغزبورد","client-id":"معرف عميل MQTT للبوابة من خادم ثينغزبورد",username:"اسم المستخدم MQTT للبوابة من خادم ثينغزبورد",password:"كلمة المرور MQTT للبوابة من خادم ثينغزبورد","ca-cert":"المسار إلى ملف شهادة CA","date-form":"تنسيق التاريخ في رسالة السجل","data-folder":"المسار إلى المجلد الذي سيحتوي على البيانات (نسبي أو مطلق)","log-format":"تنسيق رسالة السجل","remote-log":"يمكنك تمكين التسجيل البعيد وقراءة السجلات من البوابة","backup-count":"إذا كان عدد النسخ الاحتياطية > 0، عند عملية تدوير، لا يتم الاحتفاظ بأكثر من عدد النسخ الاحتياطية المحددة - يتم حذف الأقدم",storage:"يوفر تكوينًا لحفظ البيانات الواردة قبل إرسالها إلى المنصة","max-file-count":"العدد الأقصى لعدد الملفات التي سيتم إنشاؤها","max-read-count":"عدد الرسائل للحصول عليها من التخزين وإرسالها إلى ثينغزبورد","max-records":"العدد الأقصى للسجلات التي ستخزن في ملف واحد","read-record-count":"عدد الرسائل للحصول عليها من التخزين وإرسالها إلى ثينغزبورد","max-records-count":"العدد الأقصى للبيانات في التخزين قبل إرسالها إلى ثينغزبورد","ttl-check-hour":"كم مرة سيتحقق البوابة من البيانات القديمة","ttl-messages-day":"الحد الأقصى لعدد الأيام التي ستحتفظ فيها التخزين بالبيانات",commands:"الأوامر لجمع الإحصائيات الإضافية",attribute:"مفتاح تلقي الإحصائيات",timeout:"مهلة زمنية لتنفيذ الأمر",command:"سيتم استخدام نتيجة تنفيذ الأمر كقيمة لتلقي الإحصائيات","check-device-activity":"يمكنك تمكين مراقبة نشاط كل جهاز متصل","inactivity-timeout":"الوقت بعد الذي ستفصل البوابة الجهاز","inactivity-period":"تكرار فحص نشاط الجهاز","minimal-pack-delay":"التأخير بين إرسال حزم الرسائل (يؤدي تقليل هذا الإعداد إلى زيادة استخدام وحدة المعالجة المركزية)",qos:"جودة الخدمة في رسائل MQTT (0 - على الأكثر مرة واحدة، 1 - على الأقل مرة واحدة)","server-port":"منفذ الشبكة الذي سيستمع فيه خادم GRPC للاستفسارات الواردة.","grpc-keep-alive-timeout":"الحد الأقصى للوقت الذي يجب أن ينتظره الخادم لاستجابة رسالة الحفاظ على الاتصال قبل اعتبار الاتصال ميتًا.","grpc-keep-alive":"المدة بين رسائل حفظ الاتصال المتعاقبة عند عدم وجود استدعاء RPC نشط.","grpc-min-time-between-pings":"الحد الأدنى للوقت الذي يجب فيه أن ينتظر الخادم بين إرسال رسائل حفظ الاتصال","grpc-max-pings-without-data":"الحد الأقصى لعدد رسائل حفظ الاتصال التي يمكن للخادم إرسالها دون تلقي أي بيانات قبل اعتبار الاتصال ميتًا.","grpc-min-ping-interval-without-data":"الحد الأدنى للوقت الذي يجب فيه أن ينتظر الخادم بين إرسال رسائل حفظ الاتصال عند عدم إرسال أو استلام بيانات.","permit-without-calls":"السماح للخادم بإبقاء اتصال GRPC حيًا حتى عندما لا تكون هناك استدعاءات RPC نشطة."}},yt={"add-entry":"Afegir configuració","connector-add":"Afegir conector","connector-enabled":"Activar conector","connector-name":"Nom conector","connector-name-required":"Cal nom conector.","connector-type":"Tipus conector","connector-type-required":"Cal tipus conector.",connectors:"Configuració de conectors","create-new-gateway":"Crear un gateway nou","create-new-gateway-text":"Crear un nou gateway amb el nom: '{{gatewayName}}'?",delete:"Esborrar configuració","download-tip":"Descarregar fitxer de configuració",gateway:"Gateway","gateway-exists":"Ja existeix un dispositiu amb el mateix nom.","gateway-name":"Nom de Gateway","gateway-name-required":"Cal un nom de gateway.","gateway-saved":"Configuració de gateway gravada satisfactòriament.","json-parse":"JSON no vàlid.","json-required":"El camp no pot ser buit.","no-connectors":"No hi ha conectors","no-data":"No hi ha configuracions","no-gateway-found":"No s'ha trobat cap gateway.","no-gateway-matching":" '{{item}}' no trobat.","path-logs":"Ruta als fitxers de log","path-logs-required":"Cal ruta.",remote:"Configuració remota","remote-logging-level":"Nivel de logging","remove-entry":"Esborrar configuració","save-tip":"Gravar fitxer de configuració","security-type":"Tipus de seguretat","security-types":{"access-token":"Token d'accés",tls:"TLS"},storage:"Grabació","storage-max-file-records":"Número màxim de registres en fitxer","storage-max-files":"Número màxim de fitxers","storage-max-files-min":"El número mínim és 1.","storage-max-files-pattern":"Número no vàlid.","storage-max-files-required":"Cal número.","storage-max-records":"Màxim de registres en el magatzem","storage-max-records-min":"El número mínim és 1.","storage-max-records-pattern":"Número no vàlid.","storage-max-records-required":"Cal número.","storage-pack-size":"Mida màxim de esdeveniments","storage-pack-size-min":"El número mínim és 1.","storage-pack-size-pattern":"Número no vàlid.","storage-pack-size-required":"Cal número.","storage-path":"Ruta de magatzem","storage-path-required":"Cal ruta de magatzem.","storage-type":"Tipus de magatzem","storage-types":{"file-storage":"Magatzem fitxer","memory-storage":"Magatzem en memoria"},thingsboard:"ThingsBoard","thingsboard-host":"Host ThingsBoard","thingsboard-host-required":"Cal Host.","thingsboard-port":"Port ThingsBoard","thingsboard-port-max":"El port màxim és 65535.","thingsboard-port-min":"El port mínim és 1.","thingsboard-port-pattern":"Port no vàlid.","thingsboard-port-required":"Cal port.",tidy:"Endreçat","tidy-tip":"Endreçat JSON","title-connectors-json":"Configuració conector {{typeName}}","tls-path-ca-certificate":"Ruta al certificat CA al gateway","tls-path-client-certificate":"Ruta al certificat client al gateway","tls-path-private-key":"Ruta a la clau privada al gateway","toggle-fullscreen":"Pantalla completa fullscreen","transformer-json-config":"Configuració JSON*","update-config":"Afegir/actualizar configuració JSON"},vt={"add-entry":"Přidat konfiguraci","connector-add":"Přidat nový konektor","connector-enabled":"Povolit konektor","connector-name":"Název konektoru","connector-name-required":"Název konektoru je povinný.","connector-type":"Typ konektoru","connector-type-required":"Typ konektoru je povinný.",connectors:"Konfigurace konektoru","create-new-gateway":"Vytvořit novou bránu","create-new-gateway-text":"Jste si jisti, že chcete vytvořit novou bránu s názvem: '{{gatewayName}}'?",delete:"Smazat konfiguraci","download-tip":"Stáhnout soubor konfigurace",gateway:"Brána","gateway-exists":"Zařízení se shodným názvem již existuje.","gateway-name":"Název brány","gateway-name-required":"Název brány je povinný.","gateway-saved":"Konfigurace brány byla úspěšně uložena.","json-parse":"Neplatný JSON.","json-required":"Pole nemůže být prázdné.","no-connectors":"Žádné konektory","no-data":"Žádné konfigurace","no-gateway-found":"Žádné brány nebyly nalezeny.","no-gateway-matching":" '{{item}}' nenalezena.","path-logs":"Cesta k souborům logu","path-logs-required":"Cesta je povinná.",remote:"Vzdálená konfigurace","remote-logging-level":"Úroveň logování","remove-entry":"Odstranit konfiguraci","save-tip":"Uložit soubor konfigurace","security-type":"Typ zabezpečení","security-types":{"access-token":"Přístupový token",tls:"TLS"},storage:"Úložiště","storage-max-file-records":"Maximální počet záznamů v souboru","storage-max-files":"Maximální počet souborů","storage-max-files-min":"Minimální počet je 1.","storage-max-files-pattern":"Počet není platný.","storage-max-files-required":"Počet je povinný.","storage-max-records":"Maximální počet záznamů v úložišti","storage-max-records-min":"Minimální počet záznamů je 1.","storage-max-records-pattern":"Počet není platný.","storage-max-records-required":"Maximální počet záznamů je povinný.","storage-pack-size":"Maximální velikost souboru událostí","storage-pack-size-min":"Minimální počet je 1.","storage-pack-size-pattern":"Počet není platný.","storage-pack-size-required":"Maximální velikost souboru událostí je povinná.","storage-path":"Cesta k úložišti","storage-path-required":"Cesta k úložišti je povinná.","storage-type":"Typ úložiště","storage-types":{"file-storage":"Soubor","memory-storage":"Paměť"},thingsboard:"ThingsBoard","thingsboard-host":"Host ThingsBoard","thingsboard-host-required":"Host je povinný.","thingsboard-port":"Port ThingsBoard","thingsboard-port-max":"Maximální číslo portu je 65535.","thingsboard-port-min":"Minimální číslo portu je 1.","thingsboard-port-pattern":"Port není platný.","thingsboard-port-required":"Port je povinný.",tidy:"Uspořádat","tidy-tip":"Uspořádat JSON konfiguraci","title-connectors-json":"Konfigurace {{typeName}} konektoru","tls-path-ca-certificate":"Cesta k certifikátu CA brány","tls-path-client-certificate":"Cesta k certifikátu klienta brány","tls-path-private-key":"Cesta k privátnímu klíči brány","toggle-fullscreen":"Přepnout do režimu celé obrazovky","transformer-json-config":"JSON* konfigurace","update-config":"Přidat/editovat JSON konfiguraci"},xt={"add-entry":"Tilføj konfiguration","connector-add":"Tilføj ny stikforbindelse","connector-enabled":"Aktivér stikforbindelse","connector-name":"Navn på stikforbindelse","connector-name-required":"Navn på stikforbindelse er påkrævet.","connector-type":"Stikforbindelsestype","connector-type-required":"Stikforbindelsestype er påkrævet.",connectors:"Konfiguration af stikforbindelser","create-new-gateway":"Opret en ny gateway","create-new-gateway-text":"",delete:"Slet konfiguration","download-tip":"Download konfigurationsfil",gateway:"Gateway","gateway-exists":"Enhed med samme navn findes allerede.","gateway-name":"Gateway-navn","gateway-name-required":"Gateway-navn er påkrævet.","gateway-saved":"Gateway-konfigurationen blev gemt.","json-parse":"Ikke gyldig JSON.","json-required":"Feltet må ikke være tomt.","no-connectors":"Ingen stikforbindelser","no-data":"Ingen konfigurationer","no-gateway-found":"Ingen gateway fundet.","no-gateway-matching":"","path-logs":"Sti til logfiler","path-logs-required":"Sti er påkrævet.",remote:"Fjernkonfiguration","remote-logging-level":"Logføringsniveau","remove-entry":"Fjern konfiguration","save-tip":"Gem konfigurationsfil","security-type":"Sikkerhedstype","security-types":{"access-token":"Adgangstoken",tls:"TLS"},storage:"Lagring","storage-max-file-records":"Maks. antal poster i fil","storage-max-files":"Maks. antal filer","storage-max-files-min":"Min. antal er 1.","storage-max-files-pattern":"Antal er ikke gyldigt.","storage-max-files-required":"Antal er påkrævet.","storage-max-records":"Maks. antal poster i lagring","storage-max-records-min":"Min. antal poster er 1.","storage-max-records-pattern":"Antal er ikke gyldigt.","storage-max-records-required":"Maks. antal poster er påkrævet.","storage-pack-size":"Maks. antal pakkestørrelse for begivenhed","storage-pack-size-min":"Min. antal er 1.","storage-pack-size-pattern":"Antal er ikke gyldigt.","storage-pack-size-required":"Maks. antal pakkestørrelse for begivenhed er påkrævet.","storage-path":"Lagringssti","storage-path-required":"Lagringssti er påkrævet.","storage-type":"Lagringstype","storage-types":{"file-storage":"Lagring af filter","memory-storage":"Lagring af hukommelse"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard-vært","thingsboard-host-required":"Vært er påkrævet.","thingsboard-port":"ThingsBoard-port","thingsboard-port-max":"Maks. portnummer er 65535.","thingsboard-port-min":"Min. portnummer er 1.","thingsboard-port-pattern":"Port er ikke gyldig.","thingsboard-port-required":"Port er påkrævet.",tidy:"Tidy","tidy-tip":"Tidy konfig. JSON","title-connectors-json":"","tls-path-ca-certificate":"Sti til CA-certifikat på gateway","tls-path-client-certificate":"Sti til klientcertifikat på gateway","tls-path-private-key":"Sti til privat nøgle på gateway","toggle-fullscreen":"Skift til fuld skærm","transformer-json-config":"Konfiguration JSON*","update-config":"Tilføj/opdater konfiguration JSON"},bt={"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:"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":"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-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","storage-max-files":"Número máximo de ficheros","storage-max-files-min":"El número mínimo es 1.","storage-max-files-pattern":"Número no válido.","storage-max-files-required":"Se requiere número.","storage-max-records":"Máximo de registros en el almacén","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.","storage-pack-size-required":"Se requiere número.","storage-path":"Ruta de almacén","storage-path-required":"Se requiere ruta de almacén.","storage-type":"Tipo de almacén","storage-types":{"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","thingsboard-port-max":"El puerto máximo es 65535.","thingsboard-port-min":"El puerto mínimo es 1.","thingsboard-port-pattern":"Puerto no válido.","thingsboard-port-required":"Se requiere puerto.",tidy:"Tidy","tidy-tip":"Tidy JSON","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",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."}},wt={"add-entry":"설정 추가","connector-add":"새로운 연결자 추가","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","create-new-gateway":"Create a new gateway","create-new-gateway-text":"Are you sure you want create a new gateway with name: '{{gatewayName}}'?",delete:"Delete configuration","download-tip":"Download configuration file",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.","json-parse":"Not valid JSON.","json-required":"Field cannot be empty.","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.",remote:"Remote configuration","remote-logging-level":"Logging level","remove-entry":"Remove configuration","save-tip":"Save configuration file","security-type":"Security type","security-types":{"access-token":"Access Token",tls:"TLS"},storage:"Storage","storage-max-file-records":"Maximum records in file","storage-max-files":"Maximum number of files","storage-max-files-min":"Minimum number is 1.","storage-max-files-pattern":"Number is not valid.","storage-max-files-required":"Number is required.","storage-max-records":"Maximum records in storage","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-pack-size":"Maximum event pack size","storage-pack-size-min":"Minimum number is 1.","storage-pack-size-pattern":"Number is not valid.","storage-pack-size-required":"Maximum event pack size is required.","storage-path":"Storage path","storage-path-required":"Storage path is required.","storage-type":"Storage type","storage-types":{"file-storage":"File storage","memory-storage":"Memory storage"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard host","thingsboard-host-required":"Host is required.","thingsboard-port":"ThingsBoard port","thingsboard-port-max":"Maximum port number is 65535.","thingsboard-port-min":"Minimum port number is 1.","thingsboard-port-pattern":"Port is not valid.","thingsboard-port-required":"Port is required.",tidy:"Tidy","tidy-tip":"Tidy config JSON","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","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"},St={"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","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":"Result","rpc-command-edit-params":"Edit parameters","gateway-configuration":"General Configuration","docker-label":"In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.","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","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-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",remove:"Remove command"},storage:"Storage","storage-max-file-records":"Maximum records in file","storage-max-files":"Maximum number of files","storage-max-files-min":"Minimum number is 1.","storage-max-files-pattern":"Number is not valid.","storage-max-files-required":"Number is required.","storage-max-records":"Maximum records in storage","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.","storage-pack-size-required":"Maximum event pack size is required.","storage-path":"Storage path","storage-path-required":"Storage path is required.","storage-type":"Storage type","storage-types":{"file-storage":"File 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","thingsboard-port-max":"Maximum port number is 65535.","thingsboard-port-min":"Minimum port number is 1.","thingsboard-port-pattern":"Port is not valid.","thingsboard-port-required":"Port is required.",tidy:"Tidy","tidy-tip":"Tidy config JSON","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",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."}},Ct={"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"},_t={"add-entry":"Dodaj konfigurację",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":"Dodaj nowe złącze","connector-enabled":"Włącz złącze","connector-name":"Nazwa złącza","connector-name-required":"Nazwa złącza jest wymagana.","connector-type":"Typ złącza","connector-type-required":"Typ złącza jest wymagany.",connectors:"Konfiguracja złączy","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":"Result","rpc-command-edit-params":"Edit parameters","gateway-configuration":"General Configuration","docker-label":"In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.","create-new-gateway":"Utwórz nowy gateway","create-new-gateway-text":"Czy na pewno chcesz utworzyć nowy gateway o nazwie: '{{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:"Usuń konfigurację","download-tip":"Pobierz plik konfiguracyjny","drop-file":"Drop file here or",gateway:"Wejście","gateway-exists":"Urządzenie o tej samej nazwie już istnieje.","gateway-name":"Nazwa Gateway","gateway-name-required":"Nazwa Gateway'a jest wymagana.","gateway-saved":"Konfiguracja Gatewey'a została pomyślnie zapisana.",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":"Nieprawidłowy JSON.","json-required":"Pole nie może być puste.",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":"Brak złączy","no-data":"Brak konfiguracji","no-gateway-found":"Nie znaleziono gateway'a.","no-gateway-matching":" '{{item}}' nie znaleziono.","path-logs":"Ścieżka do plików dziennika","path-logs-required":"Ścieżka jest wymagana.","permit-without-calls":"Keep alive permit without calls",remote:"Zdalna konfiguracja","remote-logging-level":"Poziom logowania","remove-entry":"Usuń konfigurację","remote-shell":"Remote shell","remote-configuration":"Remote Configuration",other:"Other","save-tip":"Zapisz plik konfiguracyjny","security-type":"Rodzaj zabezpieczenia","security-types":{"access-token":"Token dostępu","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-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",remove:"Remove command"},storage:"Składowanie","storage-max-file-records":"Maksymalna liczba rekordów w pliku","storage-max-files":"Maksymalna liczba plików","storage-max-files-min":"Minimalna liczba to 1.","storage-max-files-pattern":"Numer jest nieprawidłowy.","storage-max-files-required":"Numer jest wymagany.","storage-max-records":"Maksymalna liczba rekordów w pamięci","storage-max-records-min":"Minimalna liczba rekordów to 1.","storage-max-records-pattern":"Numer jest nieprawidłowy.","storage-max-records-required":"Maksymalna liczba rekordów jest wymagana.","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":"Maksymalny rozmiar pakietu wydarzeń","storage-pack-size-min":"Minimalna liczba to 1.","storage-pack-size-pattern":"Numer jest nieprawidłowy.","storage-pack-size-required":"Maksymalny rozmiar pakietu wydarzeń jest wymagany.","storage-path":"Ścieżka przechowywania","storage-path-required":"Ścieżka do przechowywania jest wymagana.","storage-type":"Typ składowania","storage-types":{"file-storage":"Nośnik danych","memory-storage":"Przechowywanie pamięci",sqlite:"SQLITE"},thingsboard:"ThingsBoard",general:"General","thingsboard-host":"Gospodarz ThingsBoard","thingsboard-host-required":"Host jest wymagany.","thingsboard-port":"Port ThingsBoard","thingsboard-port-max":"Maksymalny numer portu to 65535.","thingsboard-port-min":"Minimalny numer portu to 1.","thingsboard-port-pattern":"Port jest nieprawidłowy.","thingsboard-port-required":"Port jest wymagany.",tidy:"Uporządkuj","tidy-tip":"Uporządkowana konfiguracja JSON","title-connectors-json":"Złącze {{typeName}} konfiguracja","tls-path-ca-certificate":"Ścieżka do certyfikatu CA na gateway","tls-path-client-certificate":"Ścieżka do certyfikatu klienta na 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":"Ścieżka do klucza prywatnego na bramce","toggle-fullscreen":"Przełącz tryb pełnoekranowy","transformer-json-config":"Konfiguracja JSON*","update-config":"Dodaj/zaktualizuj konfigurację 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."}},Tt={"add-entry":"Adicionar configuração","connector-add":"Adicionar novo conector","connector-enabled":"Habilitar conector","connector-name":"Nome do conector","connector-name-required":"O nome do conector é obrigatório.","connector-type":"Tipo de conector","connector-type-required":"O tipo de conector é obrigatório.",connectors:"Configuração de conectores","create-new-gateway":"Criar um novo gateway","create-new-gateway-text":"Tem certeza de que deseja criar um novo gateway com o nome: '{{gatewayName}}'?",delete:"Excluir configuração","download-tip":"Download de arquivo de configuração",gateway:"Gateway","gateway-exists":"Já existe um dispositivo com o mesmo nome.","gateway-name":"Nome do gateway","gateway-name-required":"O nome do gateway é obrigatório.","gateway-saved":"A configuração do gateway foi salva corretamente.","json-parse":"JSON inválido.","json-required":"O campo não pode estar em branco.","no-connectors":"Sem conectores","no-data":"Sem configurações","no-gateway-found":"Nenhum gateway encontrado.","no-gateway-matching":" '{{item}}' não encontrado.","path-logs":"Caminho para arquivos de log","path-logs-required":"O caminho é obrigatório",remote:"Configuração remota","remote-logging-level":"Nível de registro em log","remove-entry":"Remover configuração","save-tip":"Salvar arquivo de configuração","security-type":"Tipo de segurança","security-types":{"access-token":"Token de Acesso",tls:"TLS"},storage:"Armazenamento","storage-max-file-records":"Número máximo de registros em arquivo","storage-max-files":"Número máximo de arquivos","storage-max-files-min":"O número mínimo é 1.","storage-max-files-pattern":"O número não é válido.","storage-max-files-required":"O número é obrigatório.","storage-max-records":"Número máximo de registros em armazenamento","storage-max-records-min":"O número mínimo de registros é 1.","storage-max-records-pattern":"O número não é válido.","storage-max-records-required":"O número máximo de registros é obrigatório.","storage-pack-size":"Tamanho máximo de pacote de eventos","storage-pack-size-min":"O número mínimo é 1.","storage-pack-size-pattern":"O número não é válido.","storage-pack-size-required":"O tamanho máximo de pacote de eventos é obrigatório.","storage-path":"Caminho de armazenamento","storage-path-required":"O caminho de armazenamento é obrigatório.","storage-type":"Tipo de armazenamento","storage-types":{"file-storage":"Armazenamento de arquivo","memory-storage":"Armazenamento de memória"},thingsboard:"ThingsBoard","thingsboard-host":"Host ThingsBoard","thingsboard-host-required":"O host é obrigatório.","thingsboard-port":"Porta ThingsBoard","thingsboard-port-max":"O número máximo de portas é 65535.","thingsboard-port-min":"O número mínimo de portas é 1.","thingsboard-port-pattern":"A porta não é válida.","thingsboard-port-required":"A porta é obrigatória.",tidy:"Tidy","tidy-tip":"Config Tidy JSON","title-connectors-json":"Configuração do conector {{typeName}}","tls-path-ca-certificate":"Caminho para certificado de Autoridade de Certificação no gateway","tls-path-client-certificate":"Caminho para certificado de cliente no gateway","tls-path-private-key":"Caminho para chave privada no gateway","toggle-fullscreen":"Alternar tela inteira","transformer-json-config":"Configuração JSON*","update-config":"Adicionar/atualizar configuração de JSON"},It={"add-entry":"Dodaj konfiguracijo","connector-add":"Dodaj nov priključek","connector-enabled":"Omogoči priključek","connector-name":"Ime priključka","connector-name-required":"Ime priključka je obvezno.","connector-type":"Vrsta priključka","connector-type-required":"Zahteva se vrsta priključka.",connectors:"Konfiguracija priključkov","create-new-gateway":"Ustvari nov prehod","create-new-gateway-text":"Ali ste prepričani, da želite ustvariti nov prehod z imenom: '{{gatewayName}}'?",delete:"Izbriši konfiguracijo","download-tip":"Prenos konfiguracijske datoteke",gateway:"Prehod","gateway-exists":"Naprava z istim imenom že obstaja.","gateway-name":"Ime prehoda","gateway-name-required":"Ime prehoda je obvezno.","gateway-saved":"Konfiguracija prehoda je uspešno shranjena.","json-parse":"Neveljaven JSON.","json-required":"Polje ne sme biti prazno.","no-connectors":"Ni priključkov","no-data":"Brez konfiguracij","no-gateway-found":"Prehod ni najden.","no-gateway-matching":" '{{item}}' ni mogoče najti.","path-logs":"Pot do dnevniških datotek","path-logs-required":"Pot je obvezna.",remote:"Oddaljena konfiguracija","remote-logging-level":"Raven beleženja","remove-entry":"Odstrani konfiguracijo","save-tip":"Shrani konfiguracijsko datoteko","security-type":"Vrsta zaščite","security-types":{"access-token":"Dostopni žeton",tls:"TLS"},storage:"Shramba","storage-max-file-records":"Največ zapisov v datoteki","storage-max-files":"Največje število datotek","storage-max-files-min":"Najmanjše število je 1.","storage-max-files-pattern":"Številka ni veljavna.","storage-max-files-required":"Številka je obvezna.","storage-max-records":"Največ zapisov v pomnilniku","storage-max-records-min":"Najmanjše število zapisov je 1.","storage-max-records-pattern":"Številka ni veljavna.","storage-max-records-required":"Zahtevan je največ zapisov.","storage-pack-size":"Največja velikost paketa dogodkov","storage-pack-size-min":"Najmanjše število je 1.","storage-pack-size-pattern":"Številka ni veljavna.","storage-pack-size-required":"Zahtevana je največja velikost paketa dogodkov.","storage-path":"Pot pomnilnika","storage-path-required":"Zahtevana je pot do pomnilnika.","storage-type":"Vrsta pomnilnika","storage-types":{"file-storage":"Shramba datotek","memory-storage":"Spomin pomnilnika"},thingsboard:"ThingsBoard","thingsboard-host":"Gostitelj ThingsBoard","thingsboard-host-required":"Potreben je gostitelj.","thingsboard-port":"Vrata ThingsBoard","thingsboard-port-max":"Največja številka vrat je 65535.","thingsboard-port-min":"Najmanjša številka vrat je 1.","thingsboard-port-pattern":"Vrata niso veljavna.","thingsboard-port-required":"Potrebna so vrata.",tidy:"Urejeno","tidy-tip":"Urejena konfiguracija JSON","title-connectors-json":"Konfiguracija konektorja {{typeName}}","tls-path-ca-certificate":"Pot do potrdila CA na prehodu","tls-path-client-certificate":"Pot do potrdila stranke na prehodu","tls-path-private-key":"Pot do zasebnega ključa na prehodu","toggle-fullscreen":"Preklop na celozaslonski način","transformer-json-config":"Konfiguracija JSON *","update-config":"Dodaj / posodobi konfiguracijo JSON"},Et={"add-entry":"Yapılandırma ekle","connector-add":"Yeni bağlayıcı ekle","connector-enabled":"Bağlayıcıyı etkinleştir","connector-name":"Bağlayıcı adı","connector-name-required":"Bağlayıcı adı gerekli.","connector-type":"Bağlayıcı tipi","connector-type-required":"Bağlayıcı türü gerekli.",connectors:"Bağlayıcıların yapılandırması","create-new-gateway":"Yeni bir ağ geçidi oluştur","create-new-gateway-text":"'{{gatewayName}}' adında yeni bir ağ geçidi oluşturmak istediğinizden emin misiniz?",delete:"Yapılandırmayı sil","download-tip":"Yapılandırma dosyasını indirin",gateway:"Ağ geçidi","gateway-exists":"Aynı ada sahip cihaz zaten var.","gateway-name":"Ağ geçidi adı","gateway-name-required":"Ağ geçidi adı gerekli.","gateway-saved":"Ağ geçidi yapılandırması başarıyla kaydedildi.","json-parse":"Geçerli bir JSON değil.","json-required":"Alan boş olamaz.","no-connectors":"Bağlayıcı yok","no-data":"Yapılandırma yok","no-gateway-found":"Ağ geçidi bulunamadı.","no-gateway-matching":" '{{item}}' bulunamadı.","path-logs":"Log dosyaları yolu","path-logs-required":"Log dosyaları dizini gerekli.",remote:"Uzaktan yapılandırma","remote-logging-level":"Loglama seviyesi","remove-entry":"Yapılandırmayı kaldır","save-tip":"Yapılandırma dosyasını kaydet","security-type":"Güvenlik türü","security-types":{"access-token":"Access Token",tls:"TLS"},storage:"Depolama","storage-max-file-records":"Dosyadaki maksimum kayıt","storage-max-files":"Maksimum dosya sayısı","storage-max-files-min":"Minimum sayı 1'dir.","storage-max-files-pattern":"Sayı geçerli değil.","storage-max-files-required":"Sayı gerekli.","storage-max-records":"Depodaki maksimum kayıt","storage-max-records-min":"Minimum kayıt sayısı 1'dir.","storage-max-records-pattern":"Sayı geçerli değil.","storage-max-records-required":"Maksimum kayıt gerekli.","storage-pack-size":"Maksimum etkinlik paketi boyutu","storage-pack-size-min":"Minimum sayı 1'dir.","storage-pack-size-pattern":"Sayı geçerli değil.","storage-pack-size-required":"Maksimum etkinlik paketi boyutu gerekli.","storage-path":"Depolama yolu","storage-path-required":"Depolama yolu gerekli.","storage-type":"Depolama türü","storage-types":{"file-storage":"Dosya depolama","memory-storage":"Bellek depolama"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard host","thingsboard-host-required":"Host gerekli.","thingsboard-port":"ThingsBoard port","thingsboard-port-max":"Maksimum port numarası 65535.","thingsboard-port-min":"Minimum port numarası 1'dir.","thingsboard-port-pattern":"Port geçerli değil.","thingsboard-port-required":"Port gerekli.",tidy:"Tidy","tidy-tip":"Tidy config JSON","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","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"},Mt={"add-entry":"添加配置",advanced:"高级","checking-device-activity":"检查设备活动",command:"Docker命令","command-copied-message":"Docker命令已复制到剪贴板",configuration:"配置","connector-add":"添加连接器","connector-enabled":"启用连接器","connector-name":"连接器名称","connector-name-required":"连接器名称必填。","connector-type":"连接器类型","connector-type-required":"连接器类型必填。",connectors:"连接器配置","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":"访问令牌","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-required":"超时时间是必需的","timeout-min":"超时时间不能小于1","timeout-pattern":"超时时间无效","attribute-name":"属性名称","attribute-name-required":"属性名称是必需的",command:"命令","command-required":"命令是必需的","command-pattern":"命令无效",remove:"删除命令"},storage:"存储","storage-max-file-records":"文件中的最大记录数","storage-max-files":"最大文件数","storage-max-files-min":"最小值为1。","storage-max-files-pattern":"数字无效。","storage-max-files-required":"数字是必需的。","storage-max-records":"存储中的最大记录数","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":"数字无效。","storage-pack-size-required":"最大事件包大小必填。","storage-path":"存储路径","storage-path-required":"存储路径必填。","storage-type":"存储类型","storage-types":{"file-storage":"文件存储","memory-storage":"内存存储",sqlite:"SQLITE"},thingsboard:"ThingsBoard",general:"常规","thingsboard-host":"ThingsBoard主机","thingsboard-host-required":"主机必填。","thingsboard-port":"ThingsBoard端口","thingsboard-port-max":"最大端口号为65535。","thingsboard-port-min":"最小端口号为1。","thingsboard-port-pattern":"端口无效。","thingsboard-port-required":"端口必填。",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",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连接活动"},"docker-label":"使用以下指令在 Docker compose 中运行 IoT 网关,并为选定的设备提供凭据","install-docker-compose":"使用以下说明下载、安装和设置 Docker Compose","download-configuration-file":"下载配置文件","download-docker-compose":"下载您的网关的 docker-compose.yml 文件","launch-gateway":"启动网关","launch-docker-compose":"在包含 docker-compose.yml 文件的文件夹中,使用以下命令在终端中启动网关"},kt={"add-entry":"增加配置","connector-add":"增加新連接器","connector-enabled":"啟用連接器","connector-name":"連接器名稱","connector-name-required":"需要連接器名稱。","connector-type":"連接器類型","connector-type-required":"需要連接器類型。",connectors:"連接器配置","create-new-gateway":"建立新閘道","create-new-gateway-text":"您確定要建立一個名稱為:'{{gatewayName}}'的新閘道嗎?",delete:"刪除配置","download-tip":"下載配置文件",gateway:"閘道","gateway-exists":"同名設備已存在。","gateway-name":"閘道名稱","gateway-name-required":"需要閘道名稱。","gateway-saved":"閘道配置已成功保存。","json-parse":"無效的JSON","json-required":"欄位不能為空。","no-connectors":"無連接器","no-data":"無配置","no-gateway-found":"未找到閘道。","no-gateway-matching":" 未找到'{{item}}'。","path-logs":"日誌文件的路徑","path-logs-required":"需要路徑。",remote:"移除配置","remote-logging-level":"日誌記錄級別","remove-entry":"移除配置","save-tip":"保存配置文件","security-type":"安全類型","security-types":{"access-token":"訪問Token",tls:"TLS"},storage:"貯存","storage-max-file-records":"文件中的最大紀錄","storage-max-files":"最大文件數","storage-max-files-min":"最小數量為1。","storage-max-files-pattern":"號碼無效。","storage-max-files-required":"需要號碼。","storage-max-records":"存儲中的最大紀錄","storage-max-records-min":"最小紀錄數為1。","storage-max-records-pattern":"號碼無效。","storage-max-records-required":"需要最大紀錄數","storage-pack-size":"最大事件包大小","storage-pack-size-min":"最小數量為1。","storage-pack-size-pattern":"號碼無效.","storage-pack-size-required":"需要最大事件包大小","storage-path":"存儲路徑","storage-path-required":"需要存儲路徑。","storage-type":"存儲類型","storage-types":{"file-storage":"文件存儲","memory-storage":"記憶體存儲"},thingsboard:"ThingsBoard","thingsboard-host":"ThingsBoard主機","thingsboard-host-required":"需要主機。","thingsboard-port":"ThingsBoard連接埠","thingsboard-port-max":"最大埠號為 65535。","thingsboard-port-min":"最小埠號為1。","thingsboard-port-pattern":"連接埠無效。","thingsboard-port-required":"需要連接埠。",tidy:"整理","tidy-tip":"整理配置JSON","title-connectors-json":"連接器{{typeName}}配置","tls-path-ca-certificate":"閘道上CA證書的路徑","tls-path-client-certificate":"閘道上用戶端憑據的路徑","tls-path-private-key":"閘道上的私鑰路徑","toggle-fullscreen":"切換全螢幕","transformer-json-config":"配置JSON*","update-config":"增加/更新配置JSON"};var Pt={3.6:{socket:{type:"TCP",address:"127.0.0.1",port:5e4,bufferSize:1024},devices:[{address:"*:*",deviceName:"Device Example",deviceType:"default",encoding:"utf-8",telemetry:[{key:"temp",byteFrom:0,byteTo:-1},{key:"hum",byteFrom:0,byteTo:2}],attributes:[{key:"name",byteFrom:0,byteTo:-1},{key:"num",byteFrom:2,byteTo:4}],attributeRequests:[{type:"shared",requestExpressionSource:"constant",attributeNameExpressionSource:"constant",requestExpression:"${[0:3]==atr}",attributeNameExpression:"[3:]"}],attributeUpdates:[{encoding:"utf-16",attributeOnThingsBoard:"sharedName"}],serverSideRpc:[{methodRPC:"rpcMethod1",withResponse:!0,encoding:"utf-8"}]}]},legacy:{type:"TCP",address:"127.0.0.1",port:5e4,bufferSize:1024,devices:[{address:"*:*",deviceName:"Device Example",deviceType:"default",encoding:"utf-8",telemetry:[{key:"temp",byteFrom:0,byteTo:-1},{key:"hum",byteFrom:0,byteTo:2}],attributes:[{key:"name",byteFrom:0,byteTo:-1},{key:"num",byteFrom:2,byteTo:4}],attributeRequests:[{type:"shared",requestExpression:"${[0:3]==atr}",attributeNameExpression:"[3:]"}],attributeUpdates:[{encoding:"utf-16",attributeOnThingsBoard:"sharedName"}],serverSideRpc:[{methodRPC:"rpcMethod1",withResponse:!0,methodProcessing:"write",encoding:"utf-8"}]}]}},Dt={"3.5.2":{broker:{host:"127.0.0.1",port:1883,clientId:"ThingsBoard_gateway",version:5,maxMessageNumberPerWorker:10,maxNumberOfWorkers:100,sendDataOnlyOnChange:!1,security:{type:"anonymous"}},mapping:[{topicFilter:"sensor/data",subscriptionQos:1,converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}",deviceProfileExpressionSource:"message",deviceProfileExpression:"${sensorType}"},sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"${sensorModel}",value:"on"}],timeseries:[{type:"string",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"},{type:"string",key:"combine",value:"${hum}:${temp}"}]}},{topicFilter:"sensor/+/data",subscriptionQos:1,converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"topic",deviceNameExpression:"(?<=sensor/)(.*?)(?=/data)",deviceProfileExpressionSource:"constant",deviceProfileExpression:"Thermometer"},sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"string",key:"humidity",value:"${hum}"}]}},{topicFilter:"sensor/raw_data",subscriptionQos:1,converter:{type:"bytes",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"[0:4]",deviceProfileExpressionSource:"constant",deviceProfileExpression:"default"},sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"raw",key:"rawData",value:"[:]"}],timeseries:[{type:"raw",key:"temp",value:"[4:]"}]}},{topicFilter:"custom/sensors/+",subscriptionQos:1,converter:{type:"custom",extension:"CustomMqttUplinkConverter",cached:!0,extensionConfig:{temperature:2,humidity:2,batteryLevel:1}}}],requestsMapping:{connectRequests:[{topicFilter:"sensor/connect",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}",deviceProfileExpressionSource:"constant",deviceProfileExpression:"Thermometer"}},{topicFilter:"sensor/+/connect",deviceInfo:{deviceNameExpressionSource:"topic",deviceNameExpression:"(?<=sensor/)(.*?)(?=/connect)",deviceProfileExpressionSource:"constant",deviceProfileExpression:"Thermometer"}}],disconnectRequests:[{topicFilter:"sensor/disconnect",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}"}},{topicFilter:"sensor/+/disconnect",deviceInfo:{deviceNameExpressionSource:"topic",deviceNameExpression:"(?<=sensor/)(.*?)(?=/connect)"}}],attributeRequests:[{retain:!1,topicFilter:"v1/devices/me/attributes/request",deviceInfo:{deviceNameExpressionSource:"message",deviceNameExpression:"${serialNumber}"},attributeNameExpressionSource:"message",attributeNameExpression:"${versionAttribute}, ${pduAttribute}",topicExpression:"devices/${deviceName}/attrs",valueExpression:"${attributeKey}: ${attributeValue}"}],attributeUpdates:[{retain:!0,deviceNameFilter:".*",attributeFilter:"firmwareVersion",topicExpression:"sensor/${deviceName}/${attributeKey}",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{type:"twoWay",deviceNameFilter:".*",methodFilter:"echo",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",responseTopicExpression:"sensor/${deviceName}/response/${methodName}/${requestId}",responseTopicQoS:1,responseTimeout:1e4,valueExpression:"${params}"},{type:"oneWay",deviceNameFilter:".*",methodFilter:"no-reply",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",valueExpression:"${params}"}]}},legacy:{broker:{name:"Default Local Broker",host:"127.0.0.1",port:1883,clientId:"ThingsBoard_gateway",version:5,maxMessageNumberPerWorker:10,maxNumberOfWorkers:100,sendDataOnlyOnChange:!1,security:{type:"basic",username:"user",password:"password"}},mapping:[{topicFilter:"sensor/data",converter:{type:"json",deviceNameJsonExpression:"${serialNumber}",deviceTypeJsonExpression:"${sensorType}",sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"${sensorModel}",value:"on"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"},{type:"string",key:"combine",value:"${hum}:${temp}"}]}},{topicFilter:"sensor/+/data",converter:{type:"json",deviceNameTopicExpression:"(?<=sensor/)(.*?)(?=/data)",deviceTypeTopicExpression:"Thermometer",sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"string",key:"model",value:"${sensorModel}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{topicFilter:"sensor/raw_data",converter:{type:"bytes",deviceNameExpression:"[0:4]",deviceTypeExpression:"default",sendDataOnlyOnChange:!1,timeout:6e4,attributes:[{type:"raw",key:"rawData",value:"[:]"}],timeseries:[{type:"raw",key:"temp",value:"[4:]"}]}},{topicFilter:"custom/sensors/+",converter:{type:"custom",extension:"CustomMqttUplinkConverter",cached:!0,"extension-config":{temperatureBytes:2,humidityBytes:2,batteryLevelBytes:1}}}],connectRequests:[{topicFilter:"sensor/connect",deviceNameJsonExpression:"${serialNumber}"},{topicFilter:"sensor/+/connect",deviceNameTopicExpression:"(?<=sensor/)(.*?)(?=/connect)"}],disconnectRequests:[{topicFilter:"sensor/disconnect",deviceNameJsonExpression:"${serialNumber}"},{topicFilter:"sensor/+/disconnect",deviceNameTopicExpression:"(?<=sensor/)(.*?)(?=/disconnect)"}],attributeRequests:[{retain:!1,topicFilter:"v1/devices/me/attributes/request",deviceNameJsonExpression:"${serialNumber}",attributeNameJsonExpression:"${versionAttribute}, ${pduAttribute}",topicExpression:"devices/${deviceName}/attrs",valueExpression:"${attributeKey}: ${attributeValue}"}],attributeUpdates:[{retain:!0,deviceNameFilter:".*",attributeFilter:"firmwareVersion",topicExpression:"sensor/${deviceName}/${attributeKey}",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",responseTopicExpression:"sensor/${deviceName}/response/${methodName}/${requestId}",responseTimeout:1e4,valueExpression:"${params}"},{deviceNameFilter:".*",methodFilter:"no-reply",requestTopicExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",valueExpression:"${params}"}]}},Ot={"3.5.2":{master:{slaves:[{host:"127.0.0.1",port:5021,type:"tcp",method:"socket",timeout:35,byteOrder:"LITTLE",wordOrder:"LITTLE",retries:!0,retryOnEmpty:!0,retryOnInvalid:!0,pollPeriod:5e3,unitId:1,deviceName:"Temp Sensor",deviceType:"default",connectAttemptTimeMs:5e3,connectAttemptCount:5,waitAfterFailedAttemptsMs:3e5,reportStrategy:{type:"ON_REPORT_PERIOD",reportPeriod:3e4},attributes:[{tag:"string_read",type:"string",functionCode:4,objectsCount:4,address:1,reportStrategy:{type:"ON_REPORT_PERIOD",reportPeriod:15e3}},{tag:"bits_read",type:"bits",functionCode:4,objectsCount:1,address:5},{tag:"8int_read",type:"8int",functionCode:4,objectsCount:1,address:6},{tag:"16int_read",type:"16int",functionCode:4,objectsCount:1,address:7},{tag:"32int_read_divider",type:"32int",functionCode:4,objectsCount:2,address:8,divider:10},{tag:"8int_read_multiplier",type:"8int",functionCode:4,objectsCount:1,address:10,multiplier:10},{tag:"32int_read",type:"32int",functionCode:4,objectsCount:2,address:11},{tag:"64int_read",type:"64int",functionCode:4,objectsCount:4,address:13}],timeseries:[{tag:"8uint_read",type:"8uint",functionCode:4,objectsCount:1,address:17,reportStrategy:{type:"ON_REPORT_PERIOD",reportPeriod:15e3}},{tag:"16uint_read",type:"16uint",functionCode:4,objectsCount:2,address:18},{tag:"32uint_read",type:"32uint",functionCode:4,objectsCount:4,address:20},{tag:"64uint_read",type:"64uint",functionCode:4,objectsCount:1,address:24},{tag:"16float_read",type:"16float",functionCode:4,objectsCount:1,address:25},{tag:"32float_read",type:"32float",functionCode:4,objectsCount:2,address:26},{tag:"64float_read",type:"64float",functionCode:4,objectsCount:4,address:28}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31},{tag:"getValue",type:"bits",functionCode:1,objectsCount:1,address:31},{tag:"setCPUFanSpeed",type:"32int",functionCode:16,objectsCount:2,address:33},{tag:"getCPULoad",type:"32int",functionCode:4,objectsCount:2,address:35}]}]},slave:{type:"tcp",host:"127.0.0.1",port:5026,method:"socket",deviceName:"Modbus Slave Example",deviceType:"default",pollPeriod:5e3,sendDataToThingsBoard:!1,byteOrder:"LITTLE",wordOrder:"LITTLE",unitId:0,values:{holding_registers:{attributes:[{address:1,type:"string",tag:"sm",objectsCount:1,value:"ON"}],timeseries:[{address:2,type:"8int",tag:"smm",objectsCount:1,value:"12334"}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29,value:1243}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31,value:1}]},coils_initializer:{attributes:[{address:5,type:"8int",tag:"coil",objectsCount:1,value:0}],timeseries:[],attributeUpdates:[],rpc:[]}}}},legacy:{master:{slaves:[{host:"127.0.0.1",port:5021,type:"tcp",method:"socket",timeout:35,byteOrder:"LITTLE",wordOrder:"LITTLE",retries:!0,retryOnEmpty:!0,retryOnInvalid:!0,pollPeriod:5e3,unitId:1,deviceName:"Temp Sensor",deviceType:"default",sendDataOnlyOnChange:!0,connectAttemptTimeMs:5e3,connectAttemptCount:5,waitAfterFailedAttemptsMs:3e5,attributes:[{tag:"string_read",type:"string",functionCode:4,objectsCount:4,address:1},{tag:"bits_read",type:"bits",functionCode:4,objectsCount:1,address:5},{tag:"16int_read",type:"16int",functionCode:4,objectsCount:1,address:7},{tag:"32int_read_divider",type:"32int",functionCode:4,objectsCount:2,address:8,divider:10},{tag:"32int_read",type:"32int",functionCode:4,objectsCount:2,address:11},{tag:"64int_read",type:"64int",functionCode:4,objectsCount:4,address:13}],timeseries:[{tag:"16uint_read",type:"16uint",functionCode:4,objectsCount:2,address:18},{tag:"32uint_read",type:"32uint",functionCode:4,objectsCount:4,address:20},{tag:"64uint_read",type:"64uint",functionCode:4,objectsCount:1,address:24},{tag:"16float_read",type:"16float",functionCode:4,objectsCount:1,address:25},{tag:"32float_read",type:"32float",functionCode:4,objectsCount:2,address:26},{tag:"64float_read",type:"64float",functionCode:4,objectsCount:4,address:28}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31},{tag:"getValue",type:"bits",functionCode:1,objectsCount:1,address:31},{tag:"setCPUFanSpeed",type:"32int",functionCode:16,objectsCount:2,address:33},{tag:"getCPULoad",type:"32int",functionCode:4,objectsCount:2,address:35}]}]},slave:{type:"tcp",host:"127.0.0.1",port:5026,method:"socket",deviceName:"Modbus Slave Example",deviceType:"default",pollPeriod:5e3,sendDataToThingsBoard:!1,byteOrder:"LITTLE",wordOrder:"LITTLE",unitId:0,values:{holding_registers:[{attributes:[{address:1,type:"string",tag:"sm",objectsCount:1,value:"ON"}],timeseries:[{address:2,type:"int",tag:"smm",objectsCount:1,value:"12334"}],attributeUpdates:[{tag:"shared_attribute_write",type:"32int",functionCode:6,objectsCount:2,address:29,value:1243}],rpc:[{tag:"setValue",type:"bits",functionCode:5,objectsCount:1,address:31,value:1}]}],coils_initializer:[{attributes:[{address:5,type:"string",tag:"sm",objectsCount:1,value:"12"}],timeseries:[],attributeUpdates:[],rpc:[]}]}}}},At={"3.5.2":{server:{url:"localhost:4840/freeopcua/server/",timeoutInMillis:5e3,scanPeriodInMillis:36e5,pollPeriodInMillis:5e3,enableSubscriptions:!0,subCheckPeriodInMillis:100,showMap:!1,security:"Basic128Rsa15",identity:{type:"anonymous"}},mapping:[{deviceNodePattern:"Root\\.Objects\\.Device1",deviceNodeSource:"path",deviceInfo:{deviceNameExpression:"Device ${Root\\.Objects\\.Device1\\.serialNumber}",deviceNameExpressionSource:"path",deviceProfileExpression:"Device",deviceProfileExpressionSource:"constant"},attributes:[{key:"temperature °C",type:"path",value:"${ns=2;i=5}"}],timeseries:[{key:"humidity",type:"path",value:"${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"},{key:"batteryLevel",type:"path",value:"${Battery\\.batteryLevel}"}],rpc_methods:[{method:"multiply",arguments:[{type:"integer",value:2},{type:"integer",value:4}]}],attributes_updates:[{key:"deviceName",type:"path",value:"Root\\.Objects\\.Device1\\.serialNumber"}]}]},legacy:{server:{name:"OPC-UA Default Server",url:"localhost:4840/freeopcua/server/",timeoutInMillis:5e3,scanPeriodInMillis:5e3,disableSubscriptions:!1,subCheckPeriodInMillis:100,showMap:!1,security:"Basic128Rsa15",identity:{type:"anonymous"},mapping:[{deviceNodePattern:"Root\\.Objects\\.Device1",deviceNamePattern:"Device ${Root\\.Objects\\.Device1\\.serialNumber}",attributes:[{key:"temperature °C",path:"${ns=2;i=5}"}],timeseries:[{key:"humidity",path:"${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"},{key:"batteryLevel",path:"${Battery\\.batteryLevel}"}],rpc_methods:[{method:"multiply",arguments:[2,4]}],attributes_updates:[{attributeOnThingsBoard:"deviceName",attributeOnDevice:"Root\\.Objects\\.Device1\\.serialNumber"}]}]}}},Ft={passiveScanMode:!0,showMap:!1,scanner:{timeout:1e4,deviceName:"Device name"},devices:[{name:"Temperature and humidity sensor",MACAddress:"4C:65:A8:DF:85:C0",pollPeriod:5e5,showMap:!1,timeout:1e4,connectRetry:5,connectRetryInSeconds:0,waitAfterConnectRetries:10,telemetry:[{key:"temperature",method:"notify",characteristicUUID:"226CAA55-6476-4566-7562-66734470666D",valueExpression:"[2]"},{key:"humidity",method:"notify",characteristicUUID:"226CAA55-6476-4566-7562-66734470666D",valueExpression:"[0]"}],attributes:[{key:"name",method:"read",characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",valueExpression:"[0:2]cm [2:]A"},{key:"values",method:"read",characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",valueExpression:"All values: [:]"}],attributeUpdates:[{attributeOnThingsBoard:"sharedName",characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB"}],serverSideRpc:[{methodRPC:"rpcMethod1",withResponse:!0,characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",methodProcessing:"read"},{methodRPC:"rpcMethod2",withResponse:!0,characteristicUUID:"00002A00-0000-1000-8000-00805F9B34FB",methodProcessing:"write"},{methodRPC:"rpcMethod3",withResponse:!0,methodProcessing:"scan"}]}]},Rt={host:"http://127.0.0.1:5000",SSLVerify:!0,security:{type:"basic",username:"user",password:"password"},mapping:[{url:"getdata",httpMethod:"GET",httpHeaders:{ACCEPT:"application/json"},content:{name:"morpheus",job:"leader"},allowRedirects:!0,timeout:.5,scanPeriod:5,converter:{type:"json",deviceNameJsonExpression:"SD8500",deviceTypeJsonExpression:"SD",attributes:[{key:"serialNumber",type:"string",value:"${serial}"}],telemetry:[{key:"Maintainer",type:"string",value:"${Developer}"},{key:"combine",type:"string",value:"${Developer}:${hum}"}]}},{url:"get_info",httpMethod:"GET",httpHeaders:{ACCEPT:"application/json"},allowRedirects:!0,timeout:.5,scanPeriod:100,converter:{type:"custom",deviceNameJsonExpression:"SD8500",deviceTypeJsonExpression:"SD",extension:"CustomRequestUplinkConverter","extension-config":[{key:"Totaliser",type:"float",fromByte:0,toByte:4,byteorder:"big",signed:!0,multiplier:1},{key:"Flow",type:"int",fromByte:4,toByte:6,byteorder:"big",signed:!0,multiplier:.01},{key:"Temperature",type:"int",fromByte:8,toByte:10,byteorder:"big",signed:!0,multiplier:.01},{key:"Pressure",type:"int",fromByte:12,toByte:14,byteorder:"big",signed:!0,multiplier:.01},{key:"deviceStatus",type:"int",byteAddress:15,fromBit:4,toBit:8,byteorder:"big",signed:!1},{key:"OUT2",type:"int",byteAddress:15,fromBit:1,toBit:2,byteorder:"big"},{key:"OUT1",type:"int",byteAddress:15,fromBit:0,toBit:1,byteorder:"big"}]}}],attributeUpdates:[{httpMethod:"POST",httpHeaders:{"CONTENT-TYPE":"application/json"},timeout:.5,tries:3,allowRedirects:!0,deviceNameFilter:"SD.*",attributeFilter:"send_data",requestUrlExpression:"sensor/${deviceName}/${attributeKey}",requestValueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",responseTimeout:1,httpMethod:"GET",requestValueExpression:"${params}",responseValueExpression:"${temp}",timeout:.5,tries:3,httpHeaders:{"Content-Type":"application/json"}},{deviceNameFilter:".*",methodFilter:"no-reply",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",httpMethod:"POST",requestValueExpression:"${params}",httpHeaders:{"Content-Type":"application/json"}}]},Bt={interface:"socketcan",channel:"vcan0",backend:{fd:!0},reconnectPeriod:5,devices:[{name:"Car",sendDataOnlyOnChange:!1,enableUnknownRpc:!0,strictEval:!1,attributes:[{key:"isDriverDoorOpened",nodeId:41,command:"2:2:big:8717",value:"4:1:int",expression:"bool(value & 0b00000100)",polling:{type:"once",dataInHex:"AB CD AB CD"}}],timeseries:[{key:"rpm",nodeId:1918,isExtendedId:!0,command:"2:2:big:48059",value:"4:2:big:int",expression:"value / 4",polling:{type:"always",period:5,dataInHex:"aaaa bbbb aaaa bbbb"}},{key:"milliage",nodeId:1918,isExtendedId:!0,value:"4:2:little:int",expression:"value * 10",polling:{type:"always",period:30,dataInHex:"aa bb cc dd ee ff aa bb"}}],attributeUpdates:[{attributeOnThingsBoard:"softwareVersion",nodeId:64,isExtendedId:!0,dataLength:4,dataExpression:"value + 5",dataByteorder:"little"}],serverSideRpc:[{method:"sendSameData",nodeId:4,isExtendedId:!0,isFd:!0,bitrateSwitch:!0,dataInHex:"aa bb cc dd ee ff aa bb aa bb cc d ee ff"},{method:"setLightLevel",nodeId:5,dataLength:2,dataByteorder:"little",dataBefore:"00AA"},{method:"setSpeed",nodeId:16,dataAfter:"0102",dataExpression:"userSpeed if maxAllowedSpeed > userSpeed else maxAllowedSpeed"}]}]},Nt={"3.6.2":{application:{objectName:"TB_gateway",host:"0.0.0.0",port:"47808",objectIdentifier:599,maxApduLengthAccepted:1476,segmentationSupported:"segmentedBoth",vendorIdentifier:15},devices:[{deviceInfo:{deviceNameExpression:"BACnet Device ${objectName}",deviceProfileExpression:"default",deviceNameExpressionSource:"expression",deviceProfileExpressionSource:"constant"},host:"192.168.2.110",port:"47808",pollPeriod:1e4,attributes:[{key:"temperature",objectType:"analogOutput",objectId:"1",propertyId:"presentValue"}],timeseries:[{key:"state",objectType:"binaryValue",objectId:"1",propertyId:"presentValue"}],attributeUpdates:[{key:"brightness",objectType:"analogOutput",objectId:"1",propertyId:"presentValue"}],serverSideRpc:[{method:"set_state",requestType:"writeProperty",requestTimeout:1e4,objectType:"binaryOutput",objectId:"1",propertyId:"presentValue"},{method:"get_state",requestType:"readProperty",requestTimeout:1e4,objectType:"binaryOutput",objectId:"1",propertyId:"presentValue"}]}]},legacy:{general:{objectName:"TB_gateway",address:"0.0.0.0:47808",objectIdentifier:599,maxApduLengthAccepted:1476,segmentationSupported:"segmentedBoth",vendorIdentifier:15},devices:[{deviceName:"BACnet Device ${objectName}",deviceType:"default",address:"192.168.2.110:47808",pollPeriod:1e4,attributes:[{key:"temperature",type:"string",objectId:"analogOutput:1",propertyId:"presentValue"}],timeseries:[{key:"state",type:"bool",objectId:"binaryValue:1",propertyId:"presentValue"}],attributeUpdates:[{key:"brightness",requestType:"writeProperty",objectId:"analogOutput:1",propertyId:"presentValue"}],serverSideRpc:[{method:"set_state",requestType:"writeProperty",requestTimeout:1e4,objectId:"binaryOutput:1",propertyId:"presentValue"},{method:"get_state",requestType:"readProperty",requestTimeout:1e4,objectId:"binaryOutput:1",propertyId:"presentValue"}]}]}},Lt={connection:{str:"Driver={PostgreSQL};Server=localhost;Port=5432;Database=thingsboard;Uid=postgres;Pwd=postgres;",attributes:{autocommit:!0,timeout:0},encoding:"utf-8",decoding:{char:"utf-8",wchar:"utf-8",metadata:"utf-16le"},reconnect:!0,reconnectPeriod:60},pyodbc:{pooling:!1},polling:{query:"SELECT bool_v, str_v, dbl_v, long_v, entity_id, ts FROM ts_kv WHERE ts > ? ORDER BY ts ASC LIMIT 10",period:10,iterator:{column:"ts",query:"SELECT MIN(ts) - 1 FROM ts_kv",persistent:!1}},mapping:{device:{type:"postgres",name:"'ODBC ' + entity_id"},sendDataOnlyOnChange:!1,attributes:"*",timeseries:[{name:"value",value:"[i for i in [str_v, long_v, dbl_v,bool_v] if i is not None][0]"}]},serverSideRpc:{enableUnknownRpc:!1,overrideRpcConfig:!0,methods:["procedureOne",{name:"procedureTwo",args:["One",2,3]}]}},Vt={"3.7.2":{server:{host:"127.0.0.1",port:"5000",SSL:!1,security:{cert:"~/ssl/cert.pem",key:"~/ssl/key.pem"}},mapping:[{endpoint:"/my_devices",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"request",deviceNameExpression:"${sensorName}",deviceProfileExpressionSource:"request",deviceProfileExpression:"${sensorType}"},attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"certificateNumber",value:"${certificateNumber}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon1",HTTPMethods:["GET","POST"],security:{type:"anonymous"},converter:{type:"json",deviceInfo:{deviceNameExpressionSource:"constant",deviceNameExpression:"Device 2",deviceProfileExpressionSource:"constant",deviceProfileExpression:"default"},attributes:[{type:"string",key:"model",value:"Model2"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon2",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"custom",deviceInfo:{deviceNameExpressionSource:"constant",deviceNameExpression:"SuperAnonDevice",deviceProfileExpressionSource:"constant",deviceProfileExpression:"default"},extension:"CustomRestUplinkConverter",extensionConfig:{key:"Totaliser",datatype:"float",fromByte:0,toByte:4,byteorder:"big",signed:!0,multiplier:1}}}],requestsMapping:{attributeRequests:[{endpoint:"/sharedAttributes",type:"shared",HTTPMethods:["POST"],security:{type:"anonymous"},timeout:10,deviceNameExpression:"${deviceName}",attributeNameExpression:"${attribute}${attribute1}"}],attributeUpdates:[{HTTPMethod:"POST",SSLVerify:!1,httpHeaders:{"CONTENT-TYPE":"application/json"},security:{type:"anonymous"},timeout:.5,tries:3,allowRedirects:!0,deviceNameFilter:"SN.*",attributeFilter:".*",requestUrlExpression:"http://127.0.0.1:5001/",valueExpression:'{"deviceName":"${deviceName}","${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestUrlExpression:"http://127.0.0.1:5001/${deviceName}",responseTimeout:1,HTTPMethod:"GET",valueExpression:"${params}",timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:"SN.*",methodFilter:"post_attributes",requestUrlExpression:"http://127.0.0.1:5000/my_devices",responseTimeout:1,HTTPMethod:"POST",valueExpression:'{"sensorName":"${deviceName}", "sensorModel":"${params.sensorModel}", "certificateNumber":"${params.certificateNumber}", "temp":"${params.temp}", "hum":"${params.hum}"}',timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:".*",methodFilter:"no-reply",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",HTTPMethod:"POST",valueExpression:"${params}",httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}}]}},legacy:{host:"127.0.0.1",port:"5000",SSL:!1,security:{cert:"~/ssl/cert.pem",key:"~/ssl/key.pem"},mapping:[{endpoint:"/my_devices",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"json",deviceNameExpression:"${sensorName}",deviceTypeExpression:"${sensorType}",attributes:[{type:"string",key:"model",value:"${sensorModel}"},{type:"string",key:"certificateNumber",value:"${certificateNumber}"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon1",HTTPMethods:["GET","POST"],security:{type:"anonymous"},converter:{type:"json",deviceNameExpression:"Device 2",deviceTypeExpression:"default",attributes:[{type:"string",key:"model",value:"Model2"}],timeseries:[{type:"double",key:"temperature",value:"${temp}"},{type:"double",key:"humidity",value:"${hum}"}]}},{endpoint:"/anon2",HTTPMethods:["POST"],security:{type:"anonymous"},converter:{type:"custom",deviceNameExpression:"SuperAnonDevice",deviceTypeExpression:"default",extension:"CustomRestUplinkConverter","extension-config":{key:"Totaliser",datatype:"float",fromByte:0,toByte:4,byteorder:"big",signed:!0,multiplier:1}}}],attributeRequests:[{endpoint:"/sharedAttributes",type:"shared",HTTPMethods:["POST"],security:{type:"anonymous"},timeout:10,deviceNameExpression:"${deviceName}",attributeNameExpression:"${attribute}${attribute1}"}],attributeUpdates:[{HTTPMethod:"POST",SSLVerify:!1,httpHeaders:{"CONTENT-TYPE":"application/json"},security:{type:"anonymous"},timeout:.5,tries:3,allowRedirects:!0,deviceNameFilter:"SN.*",attributeFilter:".*",requestUrlExpression:"http://127.0.0.1:5001/",valueExpression:'{"deviceName":"${deviceName}","${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"echo",requestUrlExpression:"http://127.0.0.1:5001/${deviceName}",responseTimeout:1,HTTPMethod:"GET",valueExpression:"${params}",timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:"SN.*",methodFilter:"post_attributes",requestUrlExpression:"http://127.0.0.1:5000/my_devices",responseTimeout:1,HTTPMethod:"POST",valueExpression:'{"sensorName":"${deviceName}", "sensorModel":"${params.sensorModel}", "certificateNumber":"${params.certificateNumber}", "temp":"${params.temp}", "hum":"${params.hum}"}',timeout:10,tries:3,httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}},{deviceNameFilter:".*",methodFilter:"no-reply",requestUrlExpression:"sensor/${deviceName}/request/${methodName}/${requestId}",HTTPMethod:"POST",valueExpression:"${params}",httpHeaders:{"Content-Type":"application/json"},security:{type:"anonymous"}}]}},qt={devices:[{deviceName:"SNMP router",deviceType:"snmp",ip:"snmp.live.gambitcommunications.com",port:161,pollPeriod:5e3,community:"public",attributes:[{key:"ReceivedFromGet",method:"get",oid:"1.3.6.1.2.1.1.1.0",timeout:6},{key:"ReceivedFromMultiGet",method:"multiget",oid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"],timeout:6},{key:"ReceivedFromGetNext",method:"getnext",oid:"1.3.6.1.2.1.1.1.0",timeout:6},{key:"ReceivedFromMultiWalk",method:"multiwalk",oid:["1.3.6.1.2.1.1.1.0","1.3.6.0.1.2.1"]},{key:"ReceivedFromBulkWalk",method:"bulkwalk",oid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"]},{key:"ReceivedFromBulkGet",method:"bulkget",scalarOid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"],repeatingOid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"],maxListSize:10}],telemetry:[{key:"ReceivedFromWalk",community:"private",method:"walk",oid:"1.3.6.1.2.1.1.1.0"},{key:"ReceivedFromTable",method:"table",oid:"1.3.6.1.2.1.1"}],attributeUpdateRequests:[{attributeFilter:"dataToSet",method:"set",oid:"1.3.6.1.2.1.1.1.0"},{attributeFilter:"dataToMultiSet",method:"multiset",mappings:{"1.2.3":"10","2.3.4":"${attribute}"}}],serverSideRpcRequests:[{requestFilter:"setData",method:"set",oid:"1.3.6.1.2.1.1.1.0"},{requestFilter:"multiSetData",method:"multiset"},{requestFilter:"getData",method:"get",oid:"1.3.6.1.2.1.1.1.0"},{requestFilter:"runBulkWalk",method:"bulkwalk",oid:["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"]}]},{deviceName:"SNMP router",deviceType:"snmp",ip:"127.0.0.1",pollPeriod:5e3,community:"public",converter:"CustomSNMPConverter",attributes:[{key:"ReceivedFromGetWithCustomConverter",method:"get",oid:"1.3.6.1.2.1.1.1.0"}],telemetry:[{key:"ReceivedFromTableWithCustomConverter",method:"table",oid:"1.3.6.1.2.1.1.1.0"}]}]},Gt={host:"0.0.0.0",port:21,TLSSupport:!1,security:{type:"basic",username:"admin",password:"admin"},paths:[{devicePatternName:"asd",devicePatternType:"Device",delimiter:",",path:"fol/*_hello*.txt",readMode:"FULL",maxFileSize:5,pollPeriod:500,txtFileDataView:"SLICED",withSortingFiles:!0,attributes:[{key:"temp",value:"[1:]"},{key:"tmp",value:"[0:1]"}],timeseries:[{type:"int",key:"[0:1]",value:"[0:1]"},{type:"int",key:"temp",value:"[1:]"}]}],attributeUpdates:[{path:"fol/hello.json",deviceNameFilter:".*",writingMode:"WRITE",valueExpression:"{'${attributeKey}':'${attributeValue}'}"}],serverSideRpc:[{deviceNameFilter:".*",methodFilter:"read",valueExpression:"${params}"},{deviceNameFilter:".*",methodFilter:"write",valueExpression:"${params}"}]},zt={server:{jid:"gateway@localhost",password:"password",host:"localhost",port:5222,use_ssl:!1,disable_starttls:!1,force_starttls:!0,timeout:1e4,plugins:["xep_0030","xep_0323","xep_0325"]},devices:[{jid:"device@localhost/TMP_1101",deviceNameExpression:"${serialNumber}",deviceTypeExpression:"default",attributes:[{key:"temperature",value:"${temp}"}],timeseries:[{key:"humidity",value:"${hum}"},{key:"combination",value:"${temp}:${hum}"}],attributeUpdates:[{attributeOnThingsBoard:"shared",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{methodRPC:"rpc1",withResponse:!0,valueExpression:"${params}"}]}]},Ut={centralSystem:{name:"Central System",host:"127.0.0.1",port:9e3,connection:{type:"insecure"},security:[{type:"token",tokens:["Bearer ACCESS_TOKEN"]},{type:"basic",credentials:[{username:"admin",password:"admin"}]}]},chargePoints:[{idRegexpPattern:"bidon/hello/CP_1",deviceNameExpression:"${Vendor} ${Model}",deviceTypeExpression:"default",attributes:[{messageTypeFilter:"MeterValues,",key:"temp1",value:"${meter_value[:].sampled_value[:].value}"},{messageTypeFilter:"MeterValues,",key:"vendorId",value:"${connector_id}"}],timeseries:[{messageTypeFilter:"DataTransfer,",key:"temp",value:"${data.temp}"}],attributeUpdates:[{attributeOnThingsBoard:"shared",valueExpression:'{"${attributeKey}":"${attributeValue}"}'}],serverSideRpc:[{methodRPC:"rpc1",withResponse:!0,valueExpression:"${params}"}]}]};const jt=e("connectorConfigs",{[ct.MQTT]:Dt,[ct.MODBUS]:Ot,[ct.OPCUA]:At,[ct.BLE]:Ft,[ct.REQUEST]:Rt,[ct.CAN]:Bt,[ct.BACNET]:Nt,[ct.ODBC]:Lt,[ct.REST]:Vt,[ct.SNMP]:qt,[ct.FTP]:Gt,[ct.SOCKET]:Pt,[ct.XMPP]:zt,[ct.OCPP]:Ut,[ct.KNX]:{3.7:{logLevel:"INFO",client:{type:"AUTOMATIC",addressFormat:"LONG",gatewayIP:"127.0.0.1",gatewayPort:3671,autoReconnect:!0,autoReconnectWait:3,gatewaysScanner:{enabled:!0,scanPeriod:3,stopOnFound:!1}},devices:[{deviceInfo:{deviceNameDataType:"string",deviceNameExpressionSource:"expression",deviceNameExpression:"Device ${1/0/5}",deviceProfileDataType:"none",deviceProfileExpressionSource:"constant",deviceProfileNameExpression:"default"},pollPeriod:5e3,attributes:[{type:"temperature",key:"temperature",groupAddress:"1/0/6"}],timeseries:[{type:"humidity",key:"humidity",groupAddress:"1/0/7"}]}],attributeUpdates:[{deviceNameFilter:".*",dataType:"precent_U8",groupAddress:"1/0/9",key:"brightness"}],serverSideRpc:[{requestType:"read",deviceNameFilter:".*",method:"get_name",dataType:"string",groupAddress:"1/0/5"},{requestType:"write",deviceNameFilter:".*",method:"set_name",dataType:"string",groupAddress:"1/0/5"}]}}});function Ht(e){const t=jt[e];if(!t)throw new Error("No default config found");return t}var Wt;e("ModbusDataType",Wt),function(e){e.STRING="string",e.BYTES="bytes",e.BITS="bits",e.INT8="8int",e.UINT8="8uint",e.INT16="16int",e.UINT16="16uint",e.FLOAT16="16float",e.INT32="32int",e.UINT32="32uint",e.FLOAT32="32float",e.INT64="64int",e.UINT64="64uint",e.FLOAT64="64float"}(Wt||e("ModbusDataType",Wt={}));const $t=e("ModbusEditableDataTypes",[Wt.BYTES,Wt.BITS,Wt.STRING]);var Kt,Yt;e("ModbusObjectCountByDataType",Kt),function(e){e[e["8int"]=1]="8int",e[e["8uint"]=1]="8uint",e[e["16int"]=1]="16int",e[e["16uint"]=1]="16uint",e[e["16float"]=1]="16float",e[e["32int"]=2]="32int",e[e["32uint"]=2]="32uint",e[e["32float"]=2]="32float",e[e["64int"]=4]="64int",e[e["64uint"]=4]="64uint",e[e["64float"]=4]="64float"}(Kt||e("ModbusObjectCountByDataType",Kt={})),e("MappingValueType",Yt),function(e){e.STRING="string",e.INTEGER="integer",e.DOUBLE="double",e.BOOLEAN="boolean"}(Yt||e("MappingValueType",Yt={}));const Xt=e("mappingValueTypesMap",new Map([[Yt.STRING,{name:"value.string",icon:"mdi:format-text"}],[Yt.INTEGER,{name:"value.integer",icon:"mdi:numeric"}],[Yt.DOUBLE,{name:"value.double",icon:"mdi:numeric"}],[Yt.BOOLEAN,{name:"value.boolean",icon:"mdi:checkbox-marked-outline"}]])),Zt=e("ModbusFunctionCodeTranslationsMap",new Map([[1,"gateway.function-codes.read-coils"],[2,"gateway.function-codes.read-discrete-inputs"],[3,"gateway.function-codes.read-multiple-holding-registers"],[4,"gateway.function-codes.read-input-registers"],[5,"gateway.function-codes.write-single-coil"],[6,"gateway.function-codes.write-single-holding-register"],[15,"gateway.function-codes.write-multiple-coils"],[16,"gateway.function-codes.write-multiple-holding-registers"]]));var Qt,Jt,en;e("ConfigurationModes",Qt),function(e){e.BASIC="basic",e.ADVANCED="advanced"}(Qt||e("ConfigurationModes",Qt={})),e("ReportStrategyType",Jt),function(e){e.OnChange="ON_CHANGE",e.OnReportPeriod="ON_REPORT_PERIOD",e.OnChangeOrReportPeriod="ON_CHANGE_OR_REPORT_PERIOD",e.OnReceived="ON_RECEIVED"}(Jt||e("ReportStrategyType",Jt={})),e("ReportStrategyDefaultValue",en),function(e){e[e.Gateway=6e4]="Gateway",e[e.Connector=6e4]="Connector",e[e.Device=3e4]="Device",e[e.Key=15e3]="Key"}(en||e("ReportStrategyDefaultValue",en={}));const tn=e("ReportStrategyTypeTranslationsMap",new Map([[Jt.OnChange,"gateway.report-strategy.on-change"],[Jt.OnReportPeriod,"gateway.report-strategy.on-report-period"],[Jt.OnChangeOrReportPeriod,"gateway.report-strategy.on-change-or-report-period"],[Jt.OnReceived,"gateway.report-strategy.on-received"]])),nn=e("numberInputPattern",new RegExp(/^\d{1,15}(\.\d{1,15})?$/)),an=e("noLeadTrailSpacesRegex",/^\S+(?: \S+)*$/),rn=e("integerRegex",/^[-+]?\d+$/),on=e("nonZeroFloat",/^-?(?!0(\.0+)?$)\d+(\.\d+)?$/);var sn;!function(e){e.EXCEPTION="EXCEPTION"}(sn||(sn={}));const ln={...lt,...sn},pn=()=>[10,20,30];function cn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"a",19),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).$implicit,i=t.ɵɵnextContext();return t.ɵɵresetView(i.onTabChanged(n))})),t.ɵɵtext(1),t.ɵɵelementEnd()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("active",i.activeLink.name===e.name),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}function dn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",20),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.created-time")))}function un(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtext(1),t.ɵɵpipe(2,"date"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,1,e.ts,"yyyy-MM-dd HH:mm:ss")," ")}}function mn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.level")))}function hn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell")(1,"span"),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassMap(i.statusClass(e.status)),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.status)}}function gn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.message")))}function fn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵclassMap(i.statusClassMsg(e.status)),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.message," ")}}function yn(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",23)}function vn(e,n){1&e&&t.ɵɵelement(0,"mat-row",23)}class xn{constructor(){this.displayedColumns=["ts","status","message"],this.gatewayLogLinks=[{name:"General",key:"LOGS"},{name:"Service",key:"SERVICE_LOGS"},{name:"Connection",key:"CONNECTION_LOGS"},{name:"Storage",key:"STORAGE_LOGS"},{key:"EXTENSIONS_LOGS",name:"Extension"}];const e={property:"ts",direction:w.DESC};this.pageLink=new S(10,0,null,e),this.dataSource=new x([])}ngOnInit(){this.updateWidgetTitle()}ngAfterViewInit(){if(this.dataSource.sort=this.sort,this.dataSource.paginator=this.paginator,this.ctx.defaultSubscription.onTimewindowChangeFunction=e=>(this.ctx.defaultSubscription.options.timeWindowConfig=e,this.ctx.defaultSubscription.updateDataSubscriptions(),e),this.ctx.settings.isConnectorLog&&this.ctx.settings.connectorLogState){const e=this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState];this.logLinks=[{key:`${e.key}_LOGS`,name:"Connector",filterFn:e=>!e.message.includes("_converter.py")},{key:`${e.key}_converter_LOGS`,name:"Converter",filterFn:e=>e.message.includes("_converter.py")}]}else this.logLinks=this.gatewayLogLinks;this.activeLink=this.logLinks[0],this.changeSubscription()}updateWidgetTitle(){if(this.ctx.settings.isConnectorLog&&this.ctx.settings.connectorLogState){const e=this.ctx.widgetConfig.title,t="${connectorName}";if(e.includes(t)){const n=this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState];this.ctx.widgetTitle=e.replace(t,n.key)}}}updateData(){if(this.ctx.defaultSubscription.data.length&&this.ctx.defaultSubscription.data[0]){let e=this.ctx.defaultSubscription.data[0].data.map((e=>{const t={ts:e[0],key:this.activeLink.key,message:e[1],status:"INVALID LOG FORMAT"};try{t.message=/\[(.*)/.exec(e[1])[0]}catch(n){t.message=e[1]}try{t.status=e[1].match(/\|(\w+)\|/)[1]}catch(e){t.status="INVALID LOG FORMAT"}return t}));this.activeLink.filterFn&&(e=e.filter((e=>this.activeLink.filterFn(e)))),this.dataSource.data=e}}onTabChanged(e){this.activeLink=e,this.changeSubscription()}statusClass(e){switch(e){case ln.DEBUG:return"status status-debug";case ln.WARNING:return"status status-warning";case ln.ERROR:case ln.EXCEPTION:return"status status-error";default:return"status status-info"}}statusClassMsg(e){if(e===ln.EXCEPTION)return"msg-status-exception"}trackByLogTs(e,t){return t.ts}changeSubscription(){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:C.timeseries,settings:{}}],this.ctx.defaultSubscription.unsubscribe(),this.ctx.defaultSubscription.updateDataSubscriptions(),this.ctx.defaultSubscription.callbacks.onDataUpdated=()=>{this.updateData()})}static{this.ɵfac=function(e){return new(e||xn)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:xn,selectors:[["tb-gateway-logs"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(v,5),t.ɵɵviewQuery(b,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.sort=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.paginator=e.first)}},inputs:{ctx:"ctx",dialogRef:"dialogRef"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:23,vars:21,consts:[["tabPanel",""],[1,"flex","h-full","flex-col"],["mat-tab-nav-bar","",3,"tabPanel"],["mat-tab-link","",3,"active","click",4,"ngFor","ngForOf"],[1,"flex-1","overflow-auto"],["mat-table","","matSort","","matSortDisableClear","",3,"dataSource","trackBy","matSortActive","matSortDirection"],["matColumnDef","ts"],["mat-sort-header","","style","width: 20%",4,"matHeaderCellDef"],[4,"matCellDef"],["matColumnDef","status"],["mat-sort-header","","style","width: 10%",4,"matHeaderCellDef"],["matColumnDef","message"],["mat-sort-header","","style","width: 70%",4,"matHeaderCellDef"],[3,"class",4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],["class","mat-row-select",4,"matRowDef","matRowDefColumns"],[1,"no-data-found","flex-1","items-center","justify-center"],[1,"flex-1"],["showFirstLastButtons","",3,"length","pageIndex","pageSize","pageSizeOptions"],["mat-tab-link","",3,"click","active"],["mat-sort-header","",2,"width","20%"],["mat-sort-header","",2,"width","10%"],["mat-sort-header","",2,"width","70%"],[1,"mat-row-select"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"nav",2),t.ɵɵtemplate(2,cn,2,2,"a",3),t.ɵɵelementEnd(),t.ɵɵelement(3,"mat-tab-nav-panel",null,0),t.ɵɵelementStart(5,"div",4)(6,"table",5),t.ɵɵelementContainerStart(7,6),t.ɵɵtemplate(8,dn,3,3,"mat-header-cell",7)(9,un,3,4,"mat-cell",8),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(10,9),t.ɵɵtemplate(11,mn,3,3,"mat-header-cell",10)(12,hn,3,3,"mat-cell",8),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(13,11),t.ɵɵtemplate(14,gn,3,3,"mat-header-cell",12)(15,fn,2,3,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(16,yn,1,0,"mat-header-row",14)(17,vn,1,0,"mat-row",15),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"span",16),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(21,"span",17),t.ɵɵelementEnd(),t.ɵɵelement(22,"mat-paginator",18),t.ɵɵelementEnd()),2&e){const e=t.ɵɵreference(4);t.ɵɵadvance(),t.ɵɵproperty("tabPanel",e),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.logLinks),t.ɵɵadvance(4),t.ɵɵproperty("dataSource",n.dataSource)("trackBy",n.trackByLogTs)("matSortActive",n.pageLink.sortOrder.property)("matSortDirection",n.pageLink.sortDirection()),t.ɵɵadvance(10),t.ɵɵproperty("matHeaderRowDef",n.displayedColumns)("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",n.displayedColumns),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",0!==n.dataSource.data.length),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,18,"attribute.no-telemetry-text")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",0===n.dataSource.data.length),t.ɵɵadvance(),t.ɵɵproperty("length",n.dataSource.data.length)("pageIndex",n.pageLink.page)("pageSize",n.pageLink.pageSize)("pageSizeOptions",t.ɵɵpureFunction0(20,pn))}},dependencies:t.ɵɵgetComponentDepsFactory(xn,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex;flex-direction:column;overflow-x:auto;padding:0}[_nghost-%COMP%] .status[_ngcontent-%COMP%]{border-radius:20px;font-weight:500;padding:5px 15px}[_nghost-%COMP%] .status-debug[_ngcontent-%COMP%]{color:green;background:#0080001a}[_nghost-%COMP%] .status-warning[_ngcontent-%COMP%]{color:orange;background:#ffa5001a}[_nghost-%COMP%] .status-error[_ngcontent-%COMP%]{color:red;background:#ff00001a}[_nghost-%COMP%] .status-info[_ngcontent-%COMP%]{color:#00f;background:#0000801a}[_nghost-%COMP%] .msg-status-exception[_ngcontent-%COMP%]{color:red}']})}} -/** - * @license Angular v18.2.6 - * (c) 2010-2024 Google LLC. https://angular.io/ - * License: MIT - */ -function bn(e){e||(n(bn),e=i(a));const t=new Z((t=>e.onDestroy(t.next.bind(t))));return e=>e.pipe(le(t))}e("GatewayLogsComponent",xn);function wn(e,t){!t?.injector&&n(wn);const l=t?.injector??i(r),p=new Q(1),c=o((()=>{let t;try{t=e()}catch(e){return void s((()=>p.error(e)))}s((()=>p.next(t)))}),{injector:l,manualCleanup:!0});return l.get(a).onDestroy((()=>{c.destroy(),p.complete()})),p.asObservable()}const Sn=["commandInput"],Cn=(e,t)=>t.attributeOnGateway,_n=e=>({command:e});function Tn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",10),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.clear())})),t.ɵɵelementStart(2,"mat-icon",11),t.ɵɵtext(3,"close"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"action.clear"))}function In(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",12),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onCreateNewClick(n))})),t.ɵɵelementStart(1,"span",13),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"notification.create-new")))}function En(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵelement(1,"span",14),t.ɵɵpipe(2,"async"),t.ɵɵpipe(3,"highlight"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(3,4,e.attributeOnGateway,t.ɵɵpipeBind1(2,2,i.searchText$)),t.ɵɵsanitizeHtml)}}function Mn(e,n){if(1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"async"),t.ɵɵpipe(2,"translate")),2&e){const e=t.ɵɵnextContext(2);t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,3,"gateway.statistics.no-commands-matching",t.ɵɵpureFunction1(6,_n,e.truncate.transform(t.ɵɵpipeBind1(1,1,e.searchText$),!0,6,"...")))," ")}}function kn(e,n){1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(1,1,"gateway.statistics.no-command-found")," ")}function Pn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-option",9)(1,"div",15),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(2,"span"),t.ɵɵtemplate(3,Mn,3,8),t.ɵɵpipe(4,"async"),t.ɵɵtemplate(5,kn,2,3),t.ɵɵelementStart(6,"a",16),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onCreateNewClick(n))})),t.ɵɵtext(7,"gateway.create-new-one"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("value",null),t.ɵɵadvance(3),t.ɵɵconditional(t.ɵɵpipeBind1(4,2,e.searchText$)?3:5)}}class Dn{constructor(e,t){this.truncate=e,this.fb=t,this.commands=l(),this.onCreateNewClicked=p(),this.selectStatisticsCommandControl=this.fb.control({}),this.searchText$=this.selectStatisticsCommandControl.valueChanges.pipe(pe((e=>e?"string"==typeof e?e:e?.attributeOnGateway:"")),ce(),J(1)),this.filteredCommands$=ee([this.searchText$,wn(this.commands)]).pipe(de(150),pe((([e,t])=>{const n=t.find((t=>t.attributeOnGateway===e))??null,i=this.selectStatisticsCommandControl.value;return"string"==typeof i&&n?.attributeOnGateway!==e||this.selectStatisticsCommandControl.patchValue(n,{emitEvent:!Se(n,i)}),t.filter((t=>t.attributeOnGateway.toLowerCase().includes(e?.toLowerCase()??"")))})),J(1)),this.onChanges=e=>{},this.selectStatisticsCommandControl.valueChanges.pipe(bn()).subscribe((e=>this.onChanges(e)))}registerOnChange(e){this.onChanges=e}registerOnTouched(e){}writeValue(e){this.selectStatisticsCommandControl.patchValue(e)}displayCommandFn(e){return e?e.attributeOnGateway:null}clear(){this.selectStatisticsCommandControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.commandInput.nativeElement.blur(),this.commandInput.nativeElement.focus()}),0)}onCreateNewClick(e){e.stopPropagation(),this.onCreateNewClicked.emit()}static{this.ɵfac=function(e){return new(e||Dn)(t.ɵɵdirectiveInject(T.TruncatePipe),t.ɵɵdirectiveInject(H.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Dn,selectors:[["tb-statistics-commands-autocomplete"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(Sn,7),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.commandInput=e.first)}},inputs:{commands:[1,"commands"]},outputs:{onCreateNewClicked:"onCreateNewClicked"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>Dn)),multi:!0}]),t.ɵɵStandaloneFeature],decls:14,vars:10,consts:[["commandInput",""],["commandAutocomplete","matAutocomplete"],["appearance","outline",1,"mat-block"],["translate",""],["matInput","","type","text",3,"formControl","matAutocomplete"],["type","button","matTooltipPosition","above","matSuffix","","mat-icon-button","","aria-label","Clear",1,"action-button",3,"matTooltip"],["mat-button","","color","primary","matSuffix","",1,"mr-2"],[1,"tb-autocomplete",3,"displayWith"],[3,"value"],[1,"tb-not-found",3,"value"],["type","button","matTooltipPosition","above","matSuffix","","mat-icon-button","","aria-label","Clear",1,"action-button",3,"click","matTooltip"],[1,"material-icons"],["mat-button","","color","primary","matSuffix","",1,"mr-2",3,"click"],[1,"whitespace-nowrap"],[3,"innerHTML"],[1,"tb-not-found-content",3,"click"],["translate","",3,"click"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",2)(1,"mat-label",3),t.ɵɵtext(2,"gateway.statistics.name"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",4,0),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,Tn,4,3,"button",5)(7,In,4,3,"button",6),t.ɵɵelementStart(8,"mat-autocomplete",7,1),t.ɵɵrepeaterCreate(10,En,4,7,"mat-option",8,Cn,!1,Pn,8,4,"mat-option",9),t.ɵɵpipe(13,"async"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵreference(9);t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.selectStatisticsCommandControl)("matAutocomplete",e),t.ɵɵattribute("aria-label",t.ɵɵpipeBind1(5,6,"gateway.statistics.command")),t.ɵɵadvance(3),t.ɵɵconditional(n.selectStatisticsCommandControl.value?6:7),t.ɵɵadvance(2),t.ɵɵproperty("displayWith",n.displayCommandFn),t.ɵɵadvance(2),t.ɵɵrepeater(t.ɵɵpipeBind1(13,8,n.filteredCommands$))}},dependencies:t.ɵɵgetComponentDepsFactory(Dn,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%] a[_ngcontent-%COMP%]{border-bottom:none;color:inherit}[_nghost-%COMP%] .action-button[_ngcontent-%COMP%]{opacity:.7}']})}}const On=()=>["createdTime","message"];function An(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",12),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"widgets.gateway.created-time")))}function Fn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",13),t.ɵɵtext(1),t.ɵɵpipe(2,"date"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(2,1,e[0],"yyyy-MM-dd HH:mm:ss"))}}function Rn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",14),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"widgets.gateway.message")," "))}function Bn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell")(1,"div",15),t.ɵɵtext(2),t.ɵɵelement(3,"tb-copy-button",16),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",e[1]," "),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(4,3,"gateway.statistics.copy-message")),t.ɵɵproperty("copyText",e[1])}}function Nn(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",17)}function Ln(e,n){1&e&&t.ɵɵelement(0,"mat-row",17)}class Vn{constructor(){this.data=l([]),this.defaultPageSizes=[10,20,30],this.defaultSortOrder={property:"0",direction:w.DESC},this.pageLink=new S(this.defaultPageSizes[0],0,null,this.defaultSortOrder),this.dataSource=new x([]),o((()=>{this.dataSource.data=this.data()}))}ngAfterViewInit(){this.dataSource.sort=this.sort,this.dataSource.paginator=this.paginator,this.dataSource.sortingDataAccessor=e=>e[Number(this.sort?.active)||0]}static{this.ɵfac=function(e){return new(e||Vn)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Vn,selectors:[["tb-custom-statistics-table"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(v,5),t.ɵɵviewQuery(b,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.sort=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.paginator=e.first)}},inputs:{data:[1,"data"]},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:12,vars:13,consts:[[1,"flex","h-full","flex-col"],[1,"flex-1","overflow-auto"],["mat-table","","matSort","","matSortDisableClear","",3,"dataSource","matSortActive","matSortDirection"],["matColumnDef","createdTime"],["mat-sort-header","","class","w-1/5",4,"matHeaderCellDef"],["class","!w-1/5",4,"matCellDef"],["matColumnDef","message"],["mat-sort-header","",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],["class","mat-row-select",4,"matRowDef","matRowDefColumns"],["showFirstLastButtons","",3,"length","pageSize","pageSizeOptions"],["mat-sort-header","",1,"w-1/5"],[1,"!w-1/5"],["mat-sort-header",""],[1,"flex","items-center","justify-between"],["tooltipPosition","above","icon","content_copy",1,"copy-content",3,"copyText","tooltipText"],[1,"mat-row-select"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"table",2),t.ɵɵelementContainerStart(3,3),t.ɵɵtemplate(4,An,3,3,"mat-header-cell",4)(5,Fn,3,4,"mat-cell",5),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(6,6),t.ɵɵtemplate(7,Rn,3,3,"mat-header-cell",7)(8,Bn,5,5,"mat-cell",8),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(9,Nn,1,0,"mat-header-row",9)(10,Ln,1,0,"mat-row",10),t.ɵɵelementEnd()(),t.ɵɵelement(11,"mat-paginator",11),t.ɵɵelementEnd()),2&e){let e;t.ɵɵadvance(2),t.ɵɵproperty("dataSource",n.dataSource)("matSortActive",n.pageLink.sortOrder.property)("matSortDirection",n.pageLink.sortDirection()),t.ɵɵadvance(7),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(11,On))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(12,On)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",!n.dataSource.data.length),t.ɵɵproperty("length",null!==(e=null==n.dataSource||null==n.dataSource.data?null:n.dataSource.data.length)&&void 0!==e?e:0)("pageSize",n.defaultPageSizes[0])("pageSizeOptions",n.defaultPageSizes)}},dependencies:t.ɵɵgetComponentDepsFactory(Vn,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .copy-content .mat-icon{padding:4px;font-size:18px}']})}}class qn{constructor(e,t,n){this.elementRef=e,this.renderer=t,this.tooltip=n,this.tooltipEnabled=!0,this.position="above",this.destroy$=new te}ngOnInit(){this.observeMouseEvents(),this.applyTruncationStyles()}ngAfterViewInit(){this.tooltip.position=this.position}ngOnDestroy(){this.tooltip._isTooltipVisible()&&this.hideTooltip(),this.destroy$.next(),this.destroy$.complete()}observeMouseEvents(){ne(this.elementRef.nativeElement,"mouseenter").pipe(ue((()=>this.tooltipEnabled)),ue((()=>this.isOverflown(this.elementRef.nativeElement))),me((()=>this.showTooltip())),le(this.destroy$)).subscribe(),ne(this.elementRef.nativeElement,"mouseleave").pipe(ue((()=>this.tooltipEnabled)),ue((()=>this.tooltip._isTooltipVisible())),me((()=>this.hideTooltip())),le(this.destroy$)).subscribe()}applyTruncationStyles(){this.renderer.setStyle(this.elementRef.nativeElement,"white-space","nowrap"),this.renderer.setStyle(this.elementRef.nativeElement,"overflow","hidden"),this.renderer.setStyle(this.elementRef.nativeElement,"text-overflow","ellipsis")}isOverflown(e){return e.clientWidth{this.adjustChips()}),0))}constructor(e,t,n,i){this.el=e,this.renderer=t,this.translate=n,this.window=i,this.destroy$=new te,this.renderer.setStyle(this.el.nativeElement,"max-height","48px"),this.renderer.setStyle(this.el.nativeElement,"overflow","auto"),ne(i,"resize").pipe(le(this.destroy$)).subscribe((()=>{this.adjustChips()})),this.observeIntersection()}observeIntersection(){this.intersectionObserver=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.adjustChips()}))})),this.intersectionObserver.observe(this.el.nativeElement)}adjustChips(){const e=this.el.nativeElement,t=this.el.nativeElement.querySelector(".ellipsis-chip"),n=parseFloat(this.window.getComputedStyle(t).marginLeft)||0,i=e.querySelectorAll("mat-chip:not(.ellipsis-chip)");if(this.chipsValue.length>1){const a=this.el.nativeElement.querySelector(".ellipsis-text");this.renderer.setStyle(t,"display","inline-flex"),a.innerHTML=this.translate.instant("gateway.ellipsis-chips-text",{count:this.chipsValue.length});const r=e.offsetWidth-(t.offsetWidth+n);let o=0,s=0;i.forEach((e=>{this.renderer.setStyle(e,"display","inline-flex");const t=e.querySelector(".mdc-evolution-chip__text-label");this.applyMaxChipTextWidth(t,r/3),o+(e.offsetWidth+n)<=r&&sae(E())))).subscribe((e=>{this.attributesSubject.next(e.data),this.pageDataSubject.next(e),a.next(e)})),a}fetchAttributes(e,t,n){return this.getAllAttributes(e,t).pipe(pe((e=>{const t=e.filter((e=>0!==e.lastUpdateTs));return n.filterData(t)})))}getAllAttributes(e,t){if(!this.allAttributes){let n;M.get(t)?(this.telemetrySubscriber=k.createEntityAttributesSubscription(this.telemetryWsService,e,t,this.zone),this.telemetrySubscriber.subscribe(),n=this.telemetrySubscriber.attributeData$()):n=this.attributeService.getEntityAttributes(e,t),this.allAttributes=n.pipe(ge(1),fe())}return this.allAttributes}isAllSelected(){const e=this.selection.selected.length;return this.attributesSubject.pipe(pe((t=>e===t.length)))}isEmpty(){return this.attributesSubject.pipe(pe((e=>!e.length)))}total(){return this.pageDataSubject.pipe(pe((e=>e.totalElements)))}masterToggle(){this.attributesSubject.pipe(me((e=>{this.selection.selected.length===e.length?this.selection.clear():e.forEach((e=>{this.selection.select(e)}))})),ye(1)).subscribe()}}e("AttributeDatasource",zn);const Un=()=>({maxWidth:"970px"});function jn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-expansion-panel",4)(1,"mat-expansion-panel-header",5)(2,"mat-panel-title")(3,"mat-slide-toggle",6),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(4,"mat-label"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementContainer(7,7),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(),n=t.ɵɵreference(5);t.ɵɵproperty("expanded",e.showStrategyControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showStrategyControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,4,"gateway.report-strategy.label")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n)}}function Hn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",8),t.ɵɵtext(1,"gateway.report-strategy.label"),t.ɵɵelementEnd(),t.ɵɵelementContainer(2,7)),2&e){t.ɵɵnextContext();const e=t.ɵɵreference(5);t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",e)}}function Wn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",16),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.ReportTypeTranslateMap.get(e)))}}function $n(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",19),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.reportStrategyFormGroup.get("reportPeriod").hasError("min")?"gateway.hints.report-period-range":"gateway.hints.report-period-required"))}}function Kn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",17),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",18),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,$n,3,3,"mat-icon",19),t.ɵɵelementStart(8,"span",20),t.ɵɵtext(9,"gateway.suffix.ms"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"gateway.report-strategy.report-period")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.reportStrategyFormGroup.get("reportPeriod").hasError("required")||e.reportStrategyFormGroup.get("reportPeriod").hasError("min")&&e.reportStrategyFormGroup.get("reportPeriod").touched?7:-1)}}function Yn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelement(4,"div",11),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",12)(6,"mat-select",13),t.ɵɵtemplate(7,Wn,3,4,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵtemplate(8,Kn,10,7,"div",15)),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"gateway.type")," "),t.ɵɵadvance(2),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/report-strategy_fn")("tb-help-popup-style",t.ɵɵpureFunction0(7,Un)),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.reportStrategyTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.reportStrategyFormGroup.get("type").value!==e.ReportStrategyType.OnChange&&e.reportStrategyFormGroup.get("type").value!==e.ReportStrategyType.OnReceived)}}class Xn{constructor(e){this.fb=e,this.isExpansionMode=!1,this.defaultValue=en.Key,this.reportStrategyTypes=Object.values(Jt),this.ReportTypeTranslateMap=tn,this.ReportStrategyType=Jt,this.destroy$=new te,this.showStrategyControl=this.fb.control(!1),this.reportStrategyFormGroup=this.fb.group({type:[{value:Jt.OnReportPeriod,disabled:!0},[]],reportPeriod:[{value:this.defaultValue,disabled:!0},[$.required,$.min(100)]]}),this.observeStrategyFormChange(),this.observeStrategyToggle()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.isExpansionMode&&this.showStrategyControl.setValue(!!e,{emitEvent:!1}),e&&this.reportStrategyFormGroup.enable({emitEvent:!1});const{type:t=Jt.OnReportPeriod,reportPeriod:n=this.defaultValue}=e??{};this.reportStrategyFormGroup.setValue({type:t,reportPeriod:n},{emitEvent:!1}),this.onTypeChange(t)}validate(){return this.reportStrategyFormGroup.valid||this.reportStrategyFormGroup.disabled?null:{reportStrategyForm:{valid:!1}}}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}observeStrategyFormChange(){this.reportStrategyFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()})),this.reportStrategyFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.onTypeChange(e)))}observeStrategyToggle(){this.showStrategyControl.valueChanges.pipe(le(this.destroy$),ue((()=>this.isExpansionMode))).subscribe((e=>{e?(this.reportStrategyFormGroup.enable({emitEvent:!1}),this.reportStrategyFormGroup.get("reportPeriod").addValidators($.required),this.onChange(this.reportStrategyFormGroup.value)):(this.reportStrategyFormGroup.disable({emitEvent:!1}),this.reportStrategyFormGroup.get("reportPeriod").removeValidators($.required),this.onChange(null)),this.reportStrategyFormGroup.updateValueAndValidity({emitEvent:!1})}))}onTypeChange(e){const t=this.reportStrategyFormGroup.get("reportPeriod");e===Jt.OnChange||e===Jt.OnReceived?t.disable({emitEvent:!1}):this.isExpansionMode&&!this.showStrategyControl.value||t.enable({emitEvent:!1})}static{this.ɵfac=function(e){return new(e||Xn)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Xn,selectors:[["tb-report-strategy"]],inputs:{isExpansionMode:"isExpansionMode",defaultValue:"defaultValue"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>Xn)),multi:!0},{provide:K,useExisting:c((()=>Xn)),multi:!0}]),t.ɵɵStandaloneFeature],decls:6,vars:3,consts:[["defaultMode",""],["strategyFields",""],[3,"formGroup"],["class","tb-settings",3,"expanded",4,"ngIf","ngIfElse"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide",3,"click","formControl"],[3,"ngTemplateOutlet"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","flex","items-center","gap-2"],["matSuffix","","tb-help-popup-placement","right",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],[3,"value"],["tbTruncateWithTooltip","",1,"fixed-title-width","tb-required"],["matInput","","type","number","min","100","name","value","formControlName","reportPeriod",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["translate","","matSuffix","",1,"block","pr-2"]],template:function(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,2),t.ɵɵtemplate(1,jn,8,6,"mat-expansion-panel",3)(2,Hn,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor)(4,Yn,9,8,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(3);t.ɵɵproperty("formGroup",n.reportStrategyFormGroup),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isExpansionMode)("ngIfElse",e)}},dependencies:t.ɵɵgetComponentDepsFactory(Xn,[j,_,qn]),encapsulation:2,changeDetection:d.OnPush})}}e("ReportStrategyComponent",Xn),Ge([I()],Xn.prototype,"isExpansionMode",void 0),Ge([P()],Xn.prototype,"defaultValue",void 0);class Zn{constructor(e,t){this.attributeService=e,this.cd=t,this.isGatewayActive=!1}ngAfterViewInit(){this.ctx.$scope.gatewayStatus=this,this.loadGatewayState()}loadGatewayState(){this.attributeService.getEntityAttributes(this.deviceId,D.SERVER_SCOPE,["active","lastDisconnectTime","lastConnectTime"]).subscribe((e=>{const t=e.find((e=>"active"===e.key)).value,n=e.find((e=>"lastDisconnectTime"===e.key))?.value,i=e.find((e=>"lastConnectTime"===e.key))?.value;this.isGatewayActive=this.getGatewayStatus(t,i,n),this.cd.detectChanges()}))}getGatewayStatus(e,t,n){return!!e&&(!n||t>n)}onDataUpdated(){const e=this.ctx.defaultSubscription.data,t=e.find((e=>"active"===e.dataKey.name)).data[0][1],n=e.find((e=>"lastDisconnectTime"===e.dataKey.name)).data[0][1],i=e.find((e=>"lastConnectTime"===e.dataKey.name)).data[0][1];this.isGatewayActive=this.getGatewayStatus(t,i,n),this.cd.detectChanges()}static{this.ɵfac=function(e){return new(e||Zn)(t.ɵɵdirectiveInject(_e.AttributeService),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Zn,selectors:[["tb-gateway-status"]],inputs:{ctx:"ctx",deviceId:"deviceId"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:9,vars:10,consts:[[1,"flex","min-h-10","flex-1","justify-center"],[1,"divider"],[1,"whitespace-nowrap"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-card",0),t.ɵɵelement(1,"div",1),t.ɵɵelementStart(2,"mat-card-header")(3,"mat-card-subtitle",2),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"mat-card-content"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵclassProp("divider-red",!n.isGatewayActive)("divider-green",n.isGatewayActive),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,6,"gateway.gateway-status")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,8,n.isGatewayActive?"gateway.active":"gateway.inactive")))},dependencies:t.ɵɵgetComponentDepsFactory(Zn,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex}[_nghost-%COMP%] .divider[_ngcontent-%COMP%]{position:absolute;width:3px;top:12px;border-radius:2px;bottom:4px;left:10px}[_nghost-%COMP%] .divider-green[_ngcontent-%COMP%] .divider[_ngcontent-%COMP%]{border:1px solid rgb(25,128,56);background-color:#198038}[_nghost-%COMP%] .divider-green[_ngcontent-%COMP%] .mat-mdc-card-content[_ngcontent-%COMP%]{color:#198038}[_nghost-%COMP%] .divider-red[_ngcontent-%COMP%] .divider[_ngcontent-%COMP%]{border:1px solid rgb(203,37,48);background-color:#cb2530}[_nghost-%COMP%] .divider-red[_ngcontent-%COMP%] .mat-mdc-card-content[_ngcontent-%COMP%]{color:#cb2530}.mdc-card[_ngcontent-%COMP%]{position:relative;padding-left:10px;box-shadow:none}.mat-mdc-card-subtitle[_ngcontent-%COMP%]{font-weight:400;font-size:12px}.mat-mdc-card-header[_ngcontent-%COMP%]{padding:8px 16px 0}.mat-mdc-card-content[_ngcontent-%COMP%]:last-child{padding-bottom:8px;font-size:16px}'],changeDetection:d.OnPush})}}e("GatewayStatusComponent",Zn);class Qn{constructor(){this.tooltipText=""}static{this.ɵfac=function(e){return new(e||Qn)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Qn,selectors:[["tb-error-icon"]],inputs:{tooltipText:"tooltipText"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:2,vars:1,consts:[["matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",0),t.ɵɵtext(1," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",n.tooltipText)},dependencies:t.ɵɵgetComponentDepsFactory(Qn,[_,j]),styles:["[_nghost-%COMP%]{display:flex;width:40px;height:40px;padding:10px}mat-icon[_ngcontent-%COMP%]{font-size:20px}"]})}}var Jn,ei;e("ErrorTooltipIconComponent",Qn),e("MqttConverterType",Jn),function(e){e.JSON="json",e.BYTES="bytes",e.CUSTOM="custom"}(Jn||e("MqttConverterType",Jn={})),e("MQTTSourceType",ei),function(e){e.MSG="message",e.TOPIC="topic",e.CONST="constant"}(ei||e("MQTTSourceType",ei={}));const ti=e("MqttVersions",[{name:3.1,value:3},{name:3.11,value:4},{name:5,value:5}]),ni=e("QualityTypeTranslationsMap",new Map([[0,"gateway.qos.at-most-once"],[1,"gateway.qos.at-least-once"],[2,"gateway.qos.exactly-once"]])),ii=e("ConvertorTypeTranslationsMap",new Map([[Jn.JSON,"gateway.JSON"],[Jn.BYTES,"gateway.bytes"],[Jn.CUSTOM,"gateway.custom"]]));var ai;e("RequestType",ai),function(e){e.CONNECT_REQUEST="connectRequests",e.DISCONNECT_REQUEST="disconnectRequests",e.ATTRIBUTE_REQUEST="attributeRequests",e.ATTRIBUTE_UPDATE="attributeUpdates",e.SERVER_SIDE_RPC="serverSideRpc"}(ai||e("RequestType",ai={}));const ri=e("RequestTypesTranslationsMap",new Map([[ai.CONNECT_REQUEST,"gateway.request.connect-request"],[ai.DISCONNECT_REQUEST,"gateway.request.disconnect-request"],[ai.ATTRIBUTE_REQUEST,"gateway.request.attribute-request"],[ai.ATTRIBUTE_UPDATE,"gateway.request.attribute-update"],[ai.SERVER_SIDE_RPC,"gateway.request.rpc-connection"]])),oi=e("DataConversionTranslationsMap",new Map([[Jn.JSON,"gateway.JSON-hint"],[Jn.BYTES,"gateway.bytes-hint"],[Jn.CUSTOM,"gateway.custom-hint"]]));var si,li;e("SocketType",si),function(e){e.TCP="TCP",e.UDP="UDP"}(si||e("SocketType",si={})),e("SocketValueKey",li),function(e){e.TIMESERIES="telemetry",e.ATTRIBUTES="attributes",e.ATTRIBUTES_REQUESTS="attributeRequests",e.ATTRIBUTES_UPDATES="attributeUpdates",e.RPC_METHODS="serverSideRpc"}(li||e("SocketValueKey",li={}));const pi=e("SocketKeysPanelTitleTranslationsMap",new Map([[li.ATTRIBUTES,"gateway.attributes"],[li.TIMESERIES,"gateway.timeseries"],[li.ATTRIBUTES_REQUESTS,"gateway.attribute-requests"],[li.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[li.RPC_METHODS,"gateway.rpc-methods"]]));var ci,di;e("RequestsType",ci),function(e){e.Shared="shared",e.Client="client"}(ci||e("RequestsType",ci={})),e("ExpressionType",di),function(e){e.Constant="constant",e.Expression="expression"}(di||e("ExpressionType",di={}));const ui=e("SocketKeysAddKeyTranslationsMap",new Map([[li.ATTRIBUTES,"gateway.add-attribute"],[li.TIMESERIES,"gateway.add-timeseries"],[li.ATTRIBUTES_REQUESTS,"gateway.add-attribute-request"],[li.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[li.RPC_METHODS,"gateway.add-rpc-method"]])),mi=e("SocketKeysDeleteKeyTranslationsMap",new Map([[li.ATTRIBUTES,"gateway.delete-attribute"],[li.TIMESERIES,"gateway.delete-timeseries"],[li.ATTRIBUTES_REQUESTS,"gateway.delete-attribute-request"],[li.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[li.RPC_METHODS,"gateway.delete-rpc-method"]])),hi=e("SocketKeysNoKeysTextTranslationsMap",new Map([[li.ATTRIBUTES,"gateway.no-attributes"],[li.TIMESERIES,"gateway.no-timeseries"],[li.ATTRIBUTES_REQUESTS,"gateway.no-attribute-requests"],[li.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[li.RPC_METHODS,"gateway.no-rpc-methods"]]));var gi,fi,yi,vi;e("RestConverterType",gi),function(e){e.JSON="json",e.CUSTOM="custom"}(gi||e("RestConverterType",gi={})),e("RestSourceType",fi),function(e){e.REQUEST="request",e.CONST="constant"}(fi||e("RestSourceType",fi={})),e("ResponseType",yi),function(e){e.DEFAULT="default",e.CONST="constant",e.ADVANCED="advanced"}(yi||e("ResponseType",yi={})),e("ResponseStatus",vi),function(e){e.OK="OK",e.ERROR="Error"}(vi||e("ResponseStatus",vi={}));const xi=e("ResponseTypeTranslationsMap",new Map([[yi.DEFAULT,"gateway.rest.response-type.default"],[yi.CONST,"gateway.rest.response-type.const"],[yi.ADVANCED,"gateway.rest.response-type.advanced"]]));var bi;e("RestRequestType",bi),function(e){e.ATTRIBUTE_REQUEST="attributeRequests",e.ATTRIBUTE_UPDATE="attributeUpdates",e.SERVER_SIDE_RPC="serverSideRpc"}(bi||e("RestRequestType",bi={}));const wi=e("RestRequestTypesTranslationsMap",new Map([[bi.ATTRIBUTE_REQUEST,"gateway.request.attribute-request"],[bi.ATTRIBUTE_UPDATE,"gateway.request.attribute-update"],[bi.SERVER_SIDE_RPC,"gateway.request.rpc-connection"]]));var Si;e("RestRequestsScopeType",Si),function(e){e.Shared="shared",e.Client="client"}(Si||e("RestRequestsScopeType",Si={}));const Ci=e("RestRequestTypeFieldsMap",new Map([[bi.ATTRIBUTE_REQUEST,["HTTPMethods","endpoint","type","deviceNameExpression","attributeNameExpression"]],[bi.ATTRIBUTE_UPDATE,["HTTPMethod","SSLVerify","deviceNameFilter","attributeFilter","requestUrlExpression","valueExpression","httpHeaders","tries","allowRedirects"]],[bi.SERVER_SIDE_RPC,["HTTPMethod","deviceNameFilter","methodFilter","requestUrlExpression","valueExpression","responseTimeout","httpHeaders","tries"]],["all",["requestType","timeout","security"]]])),_i=e("RestConvertorTypeTranslationsMap",new Map([[gi.JSON,"gateway.JSON"],[gi.CUSTOM,"gateway.custom"]])),Ti=e("RestDataConversionTranslationsMap",new Map([[gi.JSON,"gateway.hints.rest.JSON"],[gi.CUSTOM,"gateway.custom-hint"]]));var Ii;e("PortLimits",Ii),function(e){e[e.MIN=1]="MIN",e[e.MAX=65535]="MAX"}(Ii||e("PortLimits",Ii={}));const Ei=e("GatewayConnectorConfigVersionMap",new Map([[ct.REST,pt.v3_7_2],[ct.KNX,pt.v3_7_0],[ct.BACNET,pt.v3_6_2],[ct.SOCKET,pt.v3_6_0],[ct.MQTT,pt.v3_5_2],[ct.OPCUA,pt.v3_5_2],[ct.MODBUS,pt.v3_5_2]]));var Mi,ki,Pi,Di;e("OPCUaSourceType",Mi),function(e){e.PATH="path",e.IDENTIFIER="identifier",e.CONST="constant"}(Mi||e("OPCUaSourceType",Mi={})),e("SecurityType",ki),function(e){e.ANONYMOUS="anonymous",e.BASIC="basic",e.CERTIFICATES="certificates"}(ki||e("SecurityType",ki={})),e("ModeType",Pi),function(e){e.NONE="None",e.SIGN="Sign",e.SIGNANDENCRYPT="SignAndEncrypt"}(Pi||e("ModeType",Pi={})),e("MappingType",Di),function(e){e.DATA="data",e.REQUESTS="requests",e.OPCUA="OPCua"}(Di||e("MappingType",Di={}));const Oi=e("MappingTypeTranslationsMap",new Map([[Di.DATA,"gateway.data-mapping"],[Di.REQUESTS,"gateway.requests-mapping"],[Di.OPCUA,"gateway.data-mapping"]]));var Ai;e("SecurityPolicy",Ai),function(e){e.BASIC128="Basic128Rsa15",e.BASIC256="Basic256",e.BASIC256SHA="Basic256Sha256"}(Ai||e("SecurityPolicy",Ai={}));const Fi=e("SecurityPolicyTypes",[{value:Ai.BASIC128,name:"Basic128RSA15"},{value:Ai.BASIC256,name:"Basic256"},{value:Ai.BASIC256SHA,name:"Basic256SHA256"}]),Ri=e("SecurityTypeTranslationsMap",new Map([[ki.ANONYMOUS,"gateway.broker.security-types.anonymous"],[ki.BASIC,"gateway.broker.security-types.basic"],[ki.CERTIFICATES,"gateway.broker.security-types.certificates"]])),Bi=e("SourceTypeTranslationsMap",new Map([[ei.MSG,"gateway.source-type.msg"],[ei.TOPIC,"gateway.source-type.topic"],[ei.CONST,"gateway.source-type.const"],[Mi.PATH,"gateway.source-type.path"],[Mi.IDENTIFIER,"gateway.source-type.identifier"],[Mi.CONST,"gateway.source-type.const"],[di.Expression,"gateway.source-type.expression"],[fi.REQUEST,"gateway.source-type.request"]]));var Ni;e("MappingKeysType",Ni),function(e){e.ATTRIBUTES="attributes",e.TIMESERIES="timeseries",e.CUSTOM="extensionConfig",e.RPC_METHODS="rpc_methods",e.ATTRIBUTES_UPDATES="attributes_updates"}(Ni||e("MappingKeysType",Ni={}));const Li=e("MappingKeysPanelTitleTranslationsMap",new Map([[Ni.ATTRIBUTES,"gateway.attributes"],[Ni.TIMESERIES,"gateway.timeseries"],[Ni.CUSTOM,"gateway.keys"],[Ni.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[Ni.RPC_METHODS,"gateway.rpc-methods"]])),Vi=e("MappingKeysAddKeyTranslationsMap",new Map([[Ni.ATTRIBUTES,"gateway.add-attribute"],[Ni.TIMESERIES,"gateway.add-timeseries"],[Ni.CUSTOM,"gateway.add-key"],[Ni.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[Ni.RPC_METHODS,"gateway.add-rpc-method"]])),qi=e("MappingKeysDeleteKeyTranslationsMap",new Map([[Ni.ATTRIBUTES,"gateway.delete-attribute"],[Ni.TIMESERIES,"gateway.delete-timeseries"],[Ni.CUSTOM,"gateway.delete-key"],[Ni.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[Ni.RPC_METHODS,"gateway.delete-rpc-method"]])),Gi=e("MappingKeysNoKeysTextTranslationsMap",new Map([[Ni.ATTRIBUTES,"gateway.no-attributes"],[Ni.TIMESERIES,"gateway.no-timeseries"],[Ni.CUSTOM,"gateway.no-keys"],[Ni.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[Ni.RPC_METHODS,"gateway.no-rpc-methods"]])),zi=e("QualityTypes",[0,1,2]);var Ui;e("ServerSideRpcType",Ui),function(e){e.WithResponse="twoWay",e.WithoutResponse="oneWay"}(Ui||e("ServerSideRpcType",Ui={}));const ji=e("HelpLinkByMappingTypeMap",new Map([[Di.DATA,O+"/docs/iot-gateway/config/mqtt/#section-mapping"],[Di.OPCUA,O+"/docs/iot-gateway/config/opc-ua/#section-mapping"],[Di.REQUESTS,O+"/docs/iot-gateway/config/mqtt/#requests-mapping"]])),Hi=e("MappingHintTranslationsMap",new Map([[Di.DATA,"gateway.data-mapping-hint"],[Di.OPCUA,"gateway.opcua-data-mapping-hint"],[Di.REQUESTS,"gateway.requests-mapping-hint"]]));var Wi,$i,Ki,Yi,Xi,Zi,Qi,Ji,ea;e("ServerSideRPCType",Wi),function(e){e.ONE_WAY="oneWay",e.TWO_WAY="twoWay"}(Wi||e("ServerSideRPCType",Wi={})),e("SecurityMode",$i),function(e){e.basic="basic",e.certificates="certificates",e.extendedCertificates="extendedCertificates"}($i||e("SecurityMode",$i={})),e("ModbusProtocolType",Ki),function(e){e.TCP="tcp",e.UDP="udp",e.Serial="serial"}(Ki||e("ModbusProtocolType",Ki={})),e("ModbusMethodType",Yi),function(e){e.SOCKET="socket",e.RTU="rtu"}(Yi||e("ModbusMethodType",Yi={})),e("ModbusSerialMethodType",Xi),function(e){e.RTU="rtu",e.ASCII="ascii"}(Xi||e("ModbusSerialMethodType",Xi={})),e("ModbusParity",Zi),function(e){e.Even="E",e.Odd="O",e.None="N"}(Zi||e("ModbusParity",Zi={})),e("ModbusOrderType",Qi),function(e){e.BIG="BIG",e.LITTLE="LITTLE"}(Qi||e("ModbusOrderType",Qi={})),e("ModbusRegisterType",Ji),function(e){e.HoldingRegisters="holding_registers",e.CoilsInitializer="coils_initializer",e.InputRegisters="input_registers",e.DiscreteInputs="discrete_inputs"}(Ji||e("ModbusRegisterType",Ji={})),e("ModbusValueKey",ea),function(e){e.ATTRIBUTES="attributes",e.TIMESERIES="timeseries",e.ATTRIBUTES_UPDATES="attributeUpdates",e.RPC_REQUESTS="rpc"}(ea||e("ModbusValueKey",ea={}));const ta=e("ModbusBaudrates",[4800,9600,19200,38400,57600,115200,230400,460800,921600]),na=e("ModbusByteSizes",[5,6,7,8]),ia=e("ModbusRegisterTranslationsMap",new Map([[Ji.HoldingRegisters,"gateway.holding_registers"],[Ji.CoilsInitializer,"gateway.coils_initializer"],[Ji.InputRegisters,"gateway.input_registers"],[Ji.DiscreteInputs,"gateway.discrete_inputs"]]));var aa;e("ModbusBitTargetType",aa),function(e){e.BooleanType="bool",e.IntegerType="int"}(aa||e("ModbusBitTargetType",aa={}));const ra=e("ModbusBitTargetTypeTranslationMap",new Map([[aa.BooleanType,"gateway.boolean"],[aa.IntegerType,"gateway.integer"]])),oa=e("ModbusMethodLabelsMap",new Map([[Yi.SOCKET,"Socket"],[Yi.RTU,"RTU"],[Xi.ASCII,"ASCII"]])),sa=e("ModbusProtocolLabelsMap",new Map([[Ki.TCP,"TCP"],[Ki.UDP,"UDP"],[Ki.Serial,"Serial"]])),la=e("ModbusParityLabelsMap",new Map([[Zi.Even,"Even"],[Zi.Odd,"Odd"],[Zi.None,"None"]])),pa=e("ModbusKeysPanelTitleTranslationsMap",new Map([[ea.ATTRIBUTES,"gateway.attributes"],[ea.TIMESERIES,"gateway.timeseries"],[ea.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[ea.RPC_REQUESTS,"gateway.rpc-requests"]])),ca=e("ModbusKeysAddKeyTranslationsMap",new Map([[ea.ATTRIBUTES,"gateway.add-attribute"],[ea.TIMESERIES,"gateway.add-timeseries"],[ea.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[ea.RPC_REQUESTS,"gateway.add-rpc-request"]])),da=e("ModbusKeysDeleteKeyTranslationsMap",new Map([[ea.ATTRIBUTES,"gateway.delete-attribute"],[ea.TIMESERIES,"gateway.delete-timeseries"],[ea.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[ea.RPC_REQUESTS,"gateway.delete-rpc-request"]])),ua=e("ModbusKeysNoKeysTextTranslationsMap",new Map([[ea.ATTRIBUTES,"gateway.no-attributes"],[ea.TIMESERIES,"gateway.no-timeseries"],[ea.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[ea.RPC_REQUESTS,"gateway.no-rpc-requests"]]));var ma;e("ModifierType",ma),function(e){e.DIVIDER="divider",e.MULTIPLIER="multiplier"}(ma||e("ModifierType",ma={}));const ha=e("ModifierTypesMap",new Map([[ma.DIVIDER,{name:"gateway.divider",icon:"mdi:division"}],[ma.MULTIPLIER,{name:"gateway.multiplier",icon:"mdi:multiplication"}]]));var ga,fa;e("DeviceInfoType",ga),function(e){e.FULL="full",e.PARTIAL="partial"}(ga||e("DeviceInfoType",ga={})),e("SegmentationType",fa),function(e){e.BOTH="segmentedBoth",e.TRANSMIT="segmentedTransmit",e.RECEIVE="segmentedReceive",e.NO="noSegmentation"}(fa||e("SegmentationType",fa={}));const ya=e("SegmentationTypeTranslationsMap",new Map([[fa.BOTH,"gateway.bacnet.segmentation.both"],[fa.TRANSMIT,"gateway.bacnet.segmentation.transmit"],[fa.RECEIVE,"gateway.bacnet.segmentation.receive"],[fa.NO,"gateway.bacnet.segmentation.no"]]));var va;e("BacnetDeviceKeysType",va),function(e){e.ATTRIBUTES="attributes",e.TIMESERIES="timeseries",e.RPC_METHODS="serverSideRpc",e.ATTRIBUTES_UPDATES="attributeUpdates"}(va||e("BacnetDeviceKeysType",va={}));const xa=e("BacnetDeviceKeysPanelTitleTranslationsMap",new Map([[va.ATTRIBUTES,"gateway.attributes"],[va.TIMESERIES,"gateway.timeseries"],[va.ATTRIBUTES_UPDATES,"gateway.attribute-updates"],[va.RPC_METHODS,"gateway.rpc-methods"]])),ba=e("BacnetDeviceKeysAddKeyTranslationsMap",new Map([[va.ATTRIBUTES,"gateway.add-attribute"],[va.TIMESERIES,"gateway.add-timeseries"],[va.ATTRIBUTES_UPDATES,"gateway.add-attribute-update"],[va.RPC_METHODS,"gateway.add-rpc-method"]])),wa=e("BacnetDeviceKeysDeleteKeyTranslationsMap",new Map([[va.ATTRIBUTES,"gateway.delete-attribute"],[va.TIMESERIES,"gateway.delete-timeseries"],[va.ATTRIBUTES_UPDATES,"gateway.delete-attribute-update"],[va.RPC_METHODS,"gateway.delete-rpc-method"]])),Sa=e("BacnetDeviceKeysNoKeysTextTranslationsMap",new Map([[va.ATTRIBUTES,"gateway.no-attributes"],[va.TIMESERIES,"gateway.no-timeseries"],[va.ATTRIBUTES_UPDATES,"gateway.no-attribute-updates"],[va.RPC_METHODS,"gateway.no-rpc-methods"]]));var Ca;e("BacnetKeyObjectType",Ca),function(e){e.analogInput="analogInput",e.analogOutput="analogOutput",e.analogValue="analogValue",e.binaryInput="binaryInput",e.binaryOutput="binaryOutput",e.binaryValue="binaryValue"}(Ca||e("BacnetKeyObjectType",Ca={}));const _a=e("BacnetKeyObjectTypeTranslationsMap",new Map([[Ca.analogInput,"gateway.bacnet.object-type.analog-input"],[Ca.analogOutput,"gateway.bacnet.object-type.analog-output"],[Ca.analogValue,"gateway.bacnet.object-type.analog-value"],[Ca.binaryInput,"gateway.bacnet.object-type.binary-input"],[Ca.binaryOutput,"gateway.bacnet.object-type.binary-output"],[Ca.binaryValue,"gateway.bacnet.object-type.binary-value"]]));var Ta;e("BacnetPropertyId",Ta),function(e){e.presentValue="presentValue",e.statusFlags="statusFlags",e.covIncrement="covIncrement",e.eventState="eventState",e.outOfService="outOfService",e.polarity="polarity",e.priorityArray="priorityArray",e.relinquishDefault="relinquishDefault",e.currentCommandPriority="currentCommandPriority",e.eventMessageTexts="eventMessageTexts",e.eventMessageTextsConfig="eventMessageTextsConfig",e.eventAlgorithmInhibitReference="eventAlgorithmInhibitReference",e.timeDelayNormal="timeDelayNormal"}(Ta||e("BacnetPropertyId",Ta={}));const Ia=e("BacnetPropertyIdByObjectType",new Map([[Ca.analogInput,[Ta.presentValue,Ta.statusFlags,Ta.covIncrement]],[Ca.analogOutput,[Ta.presentValue,Ta.statusFlags,Ta.covIncrement]],[Ca.analogValue,[Ta.presentValue,Ta.statusFlags,Ta.covIncrement]],[Ca.binaryInput,[Ta.presentValue,Ta.statusFlags,Ta.eventState,Ta.outOfService,Ta.polarity]],[Ca.binaryOutput,[Ta.presentValue,Ta.statusFlags,Ta.eventState,Ta.outOfService,Ta.polarity,Ta.priorityArray,Ta.relinquishDefault,Ta.currentCommandPriority,Ta.eventMessageTexts,Ta.eventMessageTextsConfig,Ta.eventAlgorithmInhibitReference,Ta.timeDelayNormal]],[Ca.binaryValue,[Ta.presentValue,Ta.statusFlags,Ta.eventState,Ta.outOfService]]])),Ea=e("BacnetPropertyIdTranslationsMap",new Map([[Ta.presentValue,"gateway.bacnet.property-id.present-value"],[Ta.statusFlags,"gateway.bacnet.property-id.status-flags"],[Ta.covIncrement,"gateway.bacnet.property-id.cov-increment"],[Ta.eventState,"gateway.bacnet.property-id.event-state"],[Ta.outOfService,"gateway.bacnet.property-id.out-of-service"],[Ta.polarity,"gateway.bacnet.property-id.polarity"],[Ta.priorityArray,"gateway.bacnet.property-id.priority-array"],[Ta.relinquishDefault,"gateway.bacnet.property-id.relinquish-default"],[Ta.currentCommandPriority,"gateway.bacnet.property-id.current-command-priority"],[Ta.eventMessageTexts,"gateway.bacnet.property-id.event-message-texts"],[Ta.eventMessageTextsConfig,"gateway.bacnet.property-id.event-message-texts-config"],[Ta.eventAlgorithmInhibitReference,"gateway.bacnet.property-id.event-algorithm-inhibit-reference"],[Ta.timeDelayNormal,"gateway.bacnet.property-id.time-delay-normal"]]));var Ma;e("BacnetRequestType",Ma),function(e){e.Write="writeProperty",e.Read="readProperty"}(Ma||e("BacnetRequestType",Ma={}));const ka=e("BacnetRequestTypeTranslationsMap",new Map([[Ma.Write,"gateway.bacnet.request-type.write"],[Ma.Read,"gateway.bacnet.request-type.read"]]));class Pa{static{this.mqttRequestTypeKeys=Object.values(ai)}static{this.mqttRequestMappingOldFields=["attributeNameJsonExpression","deviceNameJsonExpression","deviceNameTopicExpression","extension-config"]}static{this.mqttRequestMappingNewFields=["attributeNameExpressionSource","responseTopicQoS","extensionConfig"]}static mapMappingToUpgradedVersion(e){return e?.map((({converter:e,topicFilter:t,subscriptionQos:n=1})=>{const i=e.deviceInfo??this.extractConverterDeviceInfo(e),a={...e,deviceInfo:i,extensionConfig:e.extensionConfig||e["extension-config"]||null};return this.cleanUpOldFields(a),{converter:a,topicFilter:t,subscriptionQos:n}}))}static mapRequestsToUpgradedVersion(e){return this.mqttRequestTypeKeys.reduce(((t,n)=>e[n]?(t[n]=e[n].map((e=>{const t=this.mapRequestToUpgradedVersion(e,n);return this.cleanUpOldFields(t),t})),t):t),{})}static mapRequestsToDowngradedVersion(e){return this.mqttRequestTypeKeys.reduce(((t,n)=>e[n]?(t[n]=e[n].map((e=>{n===ai.SERVER_SIDE_RPC&&delete e.type;const{attributeNameExpression:t,deviceInfo:i,...a}=e,r={...a,attributeNameJsonExpression:t||null,deviceNameJsonExpression:i?.deviceNameExpressionSource!==ei.TOPIC?i?.deviceNameExpression:null,deviceNameTopicExpression:i?.deviceNameExpressionSource===ei.TOPIC?i?.deviceNameExpression:null};return this.cleanUpNewFields(r),r})),t):t),{})}static mapMappingToDowngradedVersion(e){return e?.map((e=>{const t=this.mapConverterToDowngradedVersion(e.converter);return this.cleanUpNewFields(t),{converter:t,topicFilter:e.topicFilter}}))}static mapConverterToDowngradedVersion(e){const{deviceInfo:t,...n}=e;return e.type!==Jn.BYTES?{...n,deviceNameJsonExpression:t?.deviceNameExpressionSource===ei.MSG?t.deviceNameExpression:null,deviceTypeJsonExpression:t?.deviceProfileExpressionSource===ei.MSG?t.deviceProfileExpression:null,deviceNameTopicExpression:t?.deviceNameExpressionSource!==ei.MSG?t?.deviceNameExpression:null,deviceTypeTopicExpression:t?.deviceProfileExpressionSource!==ei.MSG?t?.deviceProfileExpression:null}:{...n,deviceNameExpression:t.deviceNameExpression,deviceTypeExpression:t.deviceProfileExpression,"extension-config":e.extensionConfig}}static cleanUpOldFields(e){this.mqttRequestMappingOldFields.forEach((t=>delete e[t])),Te(e)}static cleanUpNewFields(e){this.mqttRequestMappingNewFields.forEach((t=>delete e[t])),Te(e)}static getTypeSourceByValue(e){return e.includes("${")?ei.MSG:e.includes("/")?ei.TOPIC:ei.CONST}static extractConverterDeviceInfo(e){const t=e.deviceNameExpression||e.deviceNameJsonExpression||e.deviceNameTopicExpression||null,n=e.deviceNameExpressionSource?e.deviceNameExpressionSource:t?this.getTypeSourceByValue(t):null,i=e.deviceProfileExpression||e.deviceTypeTopicExpression||e.deviceTypeJsonExpression||"default",a=e.deviceProfileExpressionSource?e.deviceProfileExpressionSource:i?this.getTypeSourceByValue(i):null;return t||i?{deviceNameExpression:t,deviceNameExpressionSource:n,deviceProfileExpression:i,deviceProfileExpressionSource:a}:null}static mapRequestToUpgradedVersion(e,t){const n=e.deviceNameJsonExpression||e.deviceNameTopicExpression||null,i=e.deviceTypeTopicExpression||e.deviceTypeJsonExpression||"default",a=i?this.getTypeSourceByValue(i):null,r=e.attributeNameExpressionSource||e.attributeNameJsonExpression||null,o=t===ai.SERVER_SIDE_RPC?1:null,s=t===ai.SERVER_SIDE_RPC?e.responseTopicExpression?Ui.WithResponse:Ui.WithoutResponse:null;return{...e,attributeNameExpression:r,attributeNameExpressionSource:r?this.getTypeSourceByValue(r):null,deviceInfo:e.deviceInfo?e.deviceInfo:n?{deviceNameExpression:n,deviceNameExpressionSource:this.getTypeSourceByValue(n),deviceProfileExpression:i,deviceProfileExpressionSource:a}:null,responseTopicQoS:o,type:s}}}e("MqttVersionMappingUtil",Pa);class Da{constructor(e,t){this.gatewayVersionIn=e,this.connector=t,this.gatewayVersion=za.parseVersion(this.gatewayVersionIn),this.configVersion=za.parseVersion(this.connector.configVersion??this.connector.configurationJson.configVersion)}getProcessedByVersion(){return this.isVersionUpdateNeeded()?this.processVersionUpdate():this.connector}processVersionUpdate(){return this.isVersionUpgradeNeeded()?this.getUpgradedVersion():this.isVersionDowngradeNeeded()?this.getDowngradedVersion():this.connector}isVersionUpdateNeeded(){return!!this.gatewayVersion&&this.configVersion!==this.gatewayVersion}isVersionUpgradeNeeded(){const e=za.parseVersion(Ei.get(this.connector.type)),t=this.gatewayVersion>=e,n=!this.configVersion||this.configVersion=e&&e>this.gatewayVersion}}e("GatewayConnectorVersionProcessor",Da);class Oa extends Da{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t,this.mqttRequestTypeKeys=Object.values(ai)}getUpgradedVersion(){const{connectRequests:e,disconnectRequests:t,attributeRequests:n,attributeUpdates:i,serverSideRpc:a}=this.connector.configurationJson;let r={...this.connector.configurationJson,requestsMapping:Pa.mapRequestsToUpgradedVersion({connectRequests:e,disconnectRequests:t,attributeRequests:n,attributeUpdates:i,serverSideRpc:a}),mapping:Pa.mapMappingToUpgradedVersion(this.connector.configurationJson.mapping)};return this.mqttRequestTypeKeys.forEach((e=>{const{[e]:t,...n}=r;r={...n}})),this.cleanUpConfigJson(r),{...this.connector,configurationJson:r,configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const{requestsMapping:e,mapping:t,...n}=this.connector.configurationJson,i=e?Pa.mapRequestsToDowngradedVersion(e):{},a=Pa.mapMappingToDowngradedVersion(t);return{...this.connector,configurationJson:{...n,...i,mapping:a},configVersion:this.gatewayVersionIn}}cleanUpConfigJson(e){Se(e.requestsMapping,{})&&delete e.requestsMapping,Se(e.mapping,[])&&delete e.mapping}}e("MqttVersionProcessor",Oa);class Aa extends Da{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{master:e.master?.slaves?Ua.mapMasterToUpgradedVersion(e.master):{slaves:[]},slave:e.slave?Ua.mapSlaveToUpgradedVersion(e.slave):{}},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{...e,slave:e.slave?Ua.mapSlaveToDowngradedVersion(e.slave):{},master:e.master?.slaves?Ua.mapMasterToDowngradedVersion(e.master):{slaves:[]}},configVersion:this.gatewayVersionIn}}}e("ModbusVersionProcessor",Aa);class Fa extends Da{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson.server;return{...this.connector,configurationJson:{server:e?ja.mapServerToUpgradedVersion(e):{},mapping:e?.mapping?ja.mapMappingToUpgradedVersion(e.mapping):[]},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){return{...this.connector,configurationJson:{server:ja.mapServerToDowngradedVersion(this.connector.configurationJson)},configVersion:this.gatewayVersionIn}}}e("OpcVersionProcessor",Fa);class Ra{constructor(){this.fb=i(Y),this.destroyRef=i(a),this.formGroup=this.initFormGroup(),this.observeValueChanges()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.formGroup.valid?null:{formGroup:{valid:!1}}}writeValue(e){this.onWriteValue(e)}onWriteValue(e){this.formGroup.patchValue(e,{emitEvent:!1})}mapOnChangeValue(e){return e}observeValueChanges(){this.formGroup.valueChanges.pipe(bn(this.destroyRef)).subscribe((e=>this.onChange(this.mapOnChangeValue(e))))}static{this.ɵfac=function(e){return new(e||Ra)}}static{this.ɵdir=t.ɵɵdefineDirective({type:Ra})}}e("ControlValueAccessorBaseAbstract",Ra);class Ba extends Ra{constructor(){super(...arguments),this.withReportStrategy=!0,this.initialized=new u,this.isLegacy=!1,this.fb=i(Y)}get basicFormGroup(){return this.formGroup}ngAfterViewInit(){this.initialized.emit()}onWriteValue(e){this.formGroup.setValue(this.mapConfigToFormValue(e),{emitEvent:!1})}mapOnChangeValue(e){return this.getMappedValue(e)}initFormGroup(){return this.initBasicFormGroup()}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(Ba)))(n||Ba)}})()}static{this.ɵdir=t.ɵɵdefineDirective({type:Ba,inputs:{generalTabContent:"generalTabContent",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},outputs:{initialized:"initialized"},features:[t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature]})}}e("GatewayConnectorBasicConfigDirective",Ba);class Na extends Da{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{socket:e?Ha.mapSocketToUpgradedVersion(e):{},devices:e?.devices?Ha.mapDevicesToUpgradedVersion(e.devices):[]},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){return{...this.connector,configurationJson:Ha.mapSocketToDowngradedVersion(this.connector.configurationJson),configVersion:this.gatewayVersionIn}}}e("SocketVersionProcessor",Na);class La extends Da{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t}getUpgradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{application:e?.general?Wa.mapApplicationToUpgradedVersion(e.general):{},devices:e?.devices?Wa.mapDevicesToUpgradedVersion(e.devices):[]},configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const e=this.connector.configurationJson;return{...this.connector,configurationJson:{general:e?.application?Wa.mapApplicationToDowngradedVersion(e.application):{},devices:e?.devices?Wa.mapDevicesToDowngradedVersion(e.devices):[]},configVersion:this.gatewayVersionIn}}}e("BacnetVersionProcessor",La);const Va=["searchInput"];class qa{constructor(){this.withReportStrategy=!0,this.textSearchMode=!1,this.onChange=()=>{},this.translate=i(We),this.dialog=i(Le),this.dialogService=i(Ie),this.fb=i(Y),this.cd=i(h),this.destroyRef=i(a),this.textSearch=this.fb.control("",{nonNullable:!0}),this.entityFormArray=this.fb.array([]),this.entityFormArray.valueChanges.pipe(bn()).subscribe((e=>{this.updateTableData(e),this.onChange(e)})),this.dataSource=this.getDatasource()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(de(150),ce(((e,t)=>(e??"")===t.trim())),bn(this.destroyRef)).subscribe((e=>this.updateTableData(this.entityFormArray.value,e.trim())))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){this.entityFormArray.clear(),this.pushDataAsFormArrays(e)}enterFilterMode(){this.textSearchMode=!0,this.cd.detectChanges();const e=this.searchInputField.nativeElement;e.focus(),e.setSelectionRange(0,0)}exitFilterMode(){this.updateTableData(this.entityFormArray.value),this.textSearchMode=!1,this.textSearch.reset()}validate(){return this.entityFormArray.controls.length?null:{devicesFormGroup:{valid:!1}}}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.entityFormArray.push(this.fb.control(e))))}static{this.ɵfac=function(e){return new(e||qa)}}static{this.ɵdir=t.ɵɵdefineDirective({type:qa,viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(Va,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},features:[t.ɵɵInputTransformsFeature]})}}e("AbstractDevicesConfigTableComponent",qa);class Ga extends Da{constructor(e,t){super(e,t),this.gatewayVersionIn=e,this.connector=t,this.restRequestTypeKeys=Object.values(bi)}getUpgradedVersion(){const{attributeRequests:e=[],attributeUpdates:t=[],serverSideRpc:n=[],mapping:i=[],...a}=this.connector.configurationJson;let r={server:za.cleanUpConfigBaseInfo(a),requestsMapping:{attributeRequests:e,attributeUpdates:t,serverSideRpc:n},mapping:$a.mapMappingToUpgradedVersion(i)};return this.restRequestTypeKeys.forEach((e=>{const{[e]:t,...n}=r;r={...n}})),{...this.connector,configurationJson:r,configVersion:this.gatewayVersionIn}}getDowngradedVersion(){const{requestsMapping:e={},mapping:t,server:n,...i}=this.connector.configurationJson,{attributeRequests:a=[],attributeUpdates:r=[],serverSideRpc:o=[]}=e;return{...this.connector,configurationJson:{...i,...n,attributeRequests:a,attributeUpdates:r,serverSideRpc:o,mapping:$a.mapMappingToDowngradedVersion(t)},configVersion:this.gatewayVersionIn}}}e("RestVersionProcessor",Ga);class za{static getConfig(e,t){switch(e.type){case ct.MQTT:return new Oa(t,e).getProcessedByVersion();case ct.OPCUA:return new Fa(t,e).getProcessedByVersion();case ct.MODBUS:return new Aa(t,e).getProcessedByVersion();case ct.SOCKET:return new Na(t,e).getProcessedByVersion();case ct.BACNET:return new La(t,e).getProcessedByVersion();case ct.REST:return new Ga(t,e).getProcessedByVersion();default:return e}}static parseVersion(e){if(Ee(e))return e;if(Me(e)){const[t,n="0",i="0"]=e.split(".");return parseFloat(`${t}.${n}${i.slice(0,1)}`)}return 0}static cleanUpConfigBaseInfo(e){const{name:t,id:n,enableRemoteLogging:i,logLevel:a,reportStrategy:r,configVersion:o,...s}=e;return s}}e("GatewayConnectorVersionMappingUtil",za);class Ua{static mapMasterToUpgradedVersion(e){return{slaves:e.slaves.map((e=>{const{sendDataOnlyOnChange:t,...n}=e;return{...n,deviceType:e.deviceType??"default",reportStrategy:t?{type:Jt.OnChange}:{type:Jt.OnReportPeriod,reportPeriod:e.pollPeriod}}}))}}static mapMasterToDowngradedVersion(e){return{slaves:e.slaves.map((e=>{const{reportStrategy:t,...n}=e;return{...n,sendDataOnlyOnChange:t?.type!==Jt.OnReportPeriod}}))}}static mapSlaveToDowngradedVersion(e){if(!e?.values)return e;const t=Object.keys(e.values).reduce(((t,n)=>t={...t,[n]:[e.values[n]]}),{});return{...e,values:t}}static mapSlaveToUpgradedVersion(e){if(!e?.values)return e;const t=Object.keys(e.values).reduce(((t,n)=>t={...t,[n]:this.mapValuesToUpgradedVersion(e.values[n][0]??{})}),{});return{...e,values:t}}static mapValuesToUpgradedVersion(e){return Object.keys(e).reduce(((t,n)=>t={...t,[n]:e[n].map((e=>({...e,type:"int"===e.type?Wt.INT16:e.type})))}),{})}}e("ModbusVersionMappingUtil",Ua);class ja{static mapServerToUpgradedVersion(e){const{mapping:t,disableSubscriptions:n,pollPeriodInMillis:i,...a}=e;return{...a,pollPeriodInMillis:i??5e3,enableSubscriptions:!n}}static mapServerToDowngradedVersion(e){const{mapping:t,server:n}=e,{enableSubscriptions:i,...a}=n??{};return{...a,mapping:t?this.mapMappingToDowngradedVersion(t):[],disableSubscriptions:!i}}static mapMappingToUpgradedVersion(e){return e.map((e=>({deviceNodePattern:e.deviceNodePattern,deviceNodeSource:this.getDeviceNodeSourceByValue(e.deviceNodePattern),deviceInfo:{deviceNameExpression:e.deviceNamePattern,deviceNameExpressionSource:this.getTypeSourceByValue(e.deviceNamePattern),deviceProfileExpression:e.deviceTypePattern??"default",deviceProfileExpressionSource:this.getTypeSourceByValue(e.deviceTypePattern??"default")},attributes:e.attributes?.map((e=>({key:e.key,type:this.getTypeSourceByValue(e.path),value:e.path})))??[],attributes_updates:e.attributes_updates?.map((e=>({key:e.attributeOnThingsBoard,type:this.getTypeSourceByValue(e.attributeOnDevice),value:e.attributeOnDevice})))??[],timeseries:e.timeseries?.map((e=>({key:e.key,type:this.getTypeSourceByValue(e.path),value:e.path})))??[],rpc_methods:e.rpc_methods?.map((e=>({method:e.method,arguments:e.arguments.map((e=>({value:e,type:this.getArgumentType(e)})))})))??[]})))}static mapMappingToDowngradedVersion(e){return e.map((e=>({deviceNodePattern:e.deviceNodePattern,deviceNamePattern:e.deviceInfo.deviceNameExpression,deviceTypePattern:e.deviceInfo.deviceProfileExpression,attributes:e.attributes.map((e=>({key:e.key,path:e.value}))),attributes_updates:e.attributes_updates.map((e=>({attributeOnThingsBoard:e.key,attributeOnDevice:e.value}))),timeseries:e.timeseries.map((e=>({key:e.key,path:e.value}))),rpc_methods:e.rpc_methods.map((e=>({method:e.method,arguments:e.arguments.map((e=>e.value))})))})))}static getTypeSourceByValue(e){return e.includes("${")?Mi.IDENTIFIER:e.includes("/")||e.includes("\\")?Mi.PATH:Mi.CONST}static getDeviceNodeSourceByValue(e){return e.includes("${")?Mi.IDENTIFIER:Mi.PATH}static getArgumentType(e){switch(typeof e){case"boolean":return"boolean";case"number":return Number.isInteger(e)?"integer":"float";default:return"string"}}}e("OpcVersionMappingUtil",ja);class Ha{static mapSocketToUpgradedVersion(e){const{devices:t,...n}=e??{};return za.cleanUpConfigBaseInfo(n)}static mapSocketToDowngradedVersion(e){const{devices:t,socket:n}=e??{};return{...n,devices:this.mapDevicesToDowngradedVersion(t??[])}}static mapDevicesToUpgradedVersion(e){return e?.map((e=>({...e,attributeRequests:e.attributeRequests?.map((e=>({...e,requestExpressionSource:this.getExpressionSource(e.requestExpression),attributeNameExpressionSource:this.getExpressionSource(e.attributeNameExpression)})))??[]})))??[]}static mapDevicesToDowngradedVersion(e){return e.map((e=>({...e,attributeRequests:e.attributeRequests?.map((({requestExpressionSource:e,attributeNameExpressionSource:t,...n})=>n))??[]})))}static getExpressionSource(e){return e.includes("${")||e.includes("[")?di.Expression:di.Constant}}e("SocketVersionMappingUtil",Ha);class Wa{static mapApplicationToUpgradedVersion(e){const{address:t="",...n}=e,[i,a]=t.split(":"),[r,o]=i.split("/");return{host:r,port:a,mask:o,...n}}static mapApplicationToDowngradedVersion(e){const{host:t="",port:n="",mask:i="",...a}=e;return{address:i?`${t}/${i}:${n}`:`${t}:${n}`,...a}}static mapDevicesToUpgradedVersion(e){return e?.map((({address:e="",deviceName:t,deviceType:n,attributes:i,timeseries:a,attributeUpdates:r,serverSideRpc:o,...s})=>({...s,host:e.split(":")[0],port:e.split(":")[1],deviceInfo:{deviceNameExpression:t,deviceProfileExpression:n,deviceNameExpressionSource:this.getExpressionSource(t),deviceProfileExpressionSource:this.getExpressionSource(n)},attributes:this.getUpdateKeys(i),timeseries:this.getUpdateKeys(a),attributeUpdates:this.getUpdateKeys(r),serverSideRpc:this.getUpdateKeys(o)})))??[]}static mapDevicesToDowngradedVersion(e){return e?.map((({port:e,host:t,deviceInfo:n,attributes:i,timeseries:a,attributeUpdates:r,serverSideRpc:o,...s})=>({...s,address:`${t}:${e}`,deviceName:n?.deviceNameExpression,deviceType:n?.deviceProfileExpression,attributes:this.getDowngradedKeys(i),timeseries:this.getDowngradedKeys(a),attributeUpdates:this.getDowngradedKeys(r),serverSideRpc:this.getDowngradedKeys(o)})))??[]}static getExpressionSource(e){return e.includes("${")||e.includes("[")?di.Expression:di.Constant}static getUpdateKeys(e){return e?.map((({objectId:e="",...t})=>({objectType:e.split(":")[0],objectId:e.split(":")[1],...t})))??[]}static getDowngradedKeys(e){return e?.map((({objectId:e="",objectType:t="",...n})=>({objectId:`${t}:${e}`,...n})))??[]}}e("BacnetVersionMappingUtil",Wa);class $a{static mapMappingToUpgradedVersion(e){return e?.map((({converter:e,...t})=>{const{deviceNameExpression:n,deviceTypeExpression:i,...a}=e;return{converter:{deviceInfo:{deviceNameExpressionSource:this.getTypeSourceByValue(n),deviceNameExpression:n,deviceProfileExpressionSource:this.getTypeSourceByValue(i),deviceProfileExpression:i},...a},...t}}))}static mapMappingToDowngradedVersion(e){return e?.map((({converter:e,...t})=>{const{deviceInfo:n,...i}=e;return{converter:{deviceNameExpression:n?.deviceNameExpression,deviceTypeExpression:n?.deviceProfileExpression,...i},...t}}))}static getTypeSourceByValue(e=""){return e.includes("${")?fi.REQUEST:fi.CONST}}e("RestVersionMappingUtil",$a);class Ka{transform(e,t){const n=za.parseVersion(e);return t===ct.MODBUS?n>=za.parseVersion(pt.v3_5_2):n>=za.parseVersion(pt.v3_6_0)}static{this.ɵfac=function(e){return new(e||Ka)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"withReportStrategy",type:Ka,pure:!0,standalone:!0})}}function Ya(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-required")," "))}function Xa(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-pattern")," "))}function Za(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.name-already-exists")," "))}function Qa(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-required")," "))}function Ja(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-pattern")," "))}function er(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-min")," "))}function tr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.command-required")," "))}function nr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.command-pattern")," "))}e("ReportStrategyVersionPipe",Ka);class ir extends A{constructor(e,t,n,i,a,r,o,s){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.dialogService=r,this.translate=o,this.destroyRef=s,this.commandHelpLink=O+"/docs/iot-gateway/configuration/#subsection-statistics",this.editCommandFormGroup=this.fb.group({attributeOnGateway:["",[$.required,$.pattern(an),this.uniqNameRequired()]],command:["",[$.required,$.pattern(/^(?=\S).*\S$/)]],timeout:[10,[$.required,$.min(1),$.pattern(nn),$.pattern(/^[^.\s]+$/)]],installCmd:["",$.pattern(an)]}),this.editCommandFormGroup.patchValue(this.data.command,{emitEvent:!1})}cancel(){this.confirmConnectorChange().pipe(ve(Boolean),bn(this.destroyRef)).subscribe((()=>this.dialogRef.close(null)))}apply(){this.dialogRef.close({current:this.editCommandFormGroup.value,prev:this.data.command})}confirmConnectorChange(){return this.editCommandFormGroup.dirty?this.dialogService.confirm(this.translate.instant("gateway.statistics.change-command-title"),this.translate.instant("gateway.statistics.change-command-text"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0):ae(!0)}uniqNameRequired(){return e=>{const t=e.value?.trim().toLowerCase(),n=t&&this.data.existingCommands.some((e=>e.toLowerCase()===t))&&t!==this.data.command.attributeOnGateway.toLowerCase();return n?{duplicateName:{valid:!1}}:null}}static{this.ɵfac=function(e){return new(e||ir)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(_e.DialogService),t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ir,selectors:[["tb-edit-custom-command-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:64,vars:27,consts:[[1,"edit-command-container",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel","stroked","no-padding-bottom","no-gap","command-container"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["translate",""],["matInput","","formControlName","attributeOnGateway"],["matIconSuffix","",1,"cursor-pointer",3,"matTooltip"],["matInput","","formControlName","timeout","type","number","min","1"],["appearance","outline",1,"mat-block"],["matInput","","formControlName","command"],[1,"tb-settings","pb-4"],["translate","",1,"tb-form-panel-title"],["matInput","","formControlName","installCmd"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"mat-toolbar",1)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",2)(6,"div",3),t.ɵɵelementStart(7,"button",4),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",5),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",6)(11,"div",7)(12,"section",8)(13,"section",9)(14,"mat-form-field",10)(15,"mat-label",11),t.ɵɵtext(16,"gateway.statistics.name"),t.ɵɵelementEnd(),t.ɵɵelement(17,"input",12),t.ɵɵtemplate(18,Ya,3,3,"mat-error")(19,Xa,3,3,"mat-error")(20,Za,3,3,"mat-error"),t.ɵɵelementStart(21,"mat-icon",13),t.ɵɵpipe(22,"translate"),t.ɵɵtext(23,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"mat-form-field",10)(25,"mat-label",11),t.ɵɵtext(26,"gateway.statistics.timeout"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",14),t.ɵɵtemplate(28,Qa,3,3,"mat-error")(29,Ja,3,3,"mat-error")(30,er,3,3,"mat-error"),t.ɵɵelementStart(31,"mat-icon",13),t.ɵɵpipe(32,"translate"),t.ɵɵtext(33,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(34,"section")(35,"mat-form-field",15)(36,"mat-label",11),t.ɵɵtext(37,"gateway.statistics.command"),t.ɵɵelementEnd(),t.ɵɵelement(38,"input",16),t.ɵɵtemplate(39,tr,3,3,"mat-error")(40,nr,3,3,"mat-error"),t.ɵɵelementStart(41,"mat-icon",13),t.ɵɵpipe(42,"translate"),t.ɵɵtext(43,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(44,"section")(45,"mat-expansion-panel",17)(46,"mat-expansion-panel-header")(47,"mat-panel-title")(48,"div",18),t.ɵɵtext(49,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(50,"mat-form-field",10)(51,"mat-label",11),t.ɵɵtext(52,"gateway.statistics.install-cmd"),t.ɵɵelementEnd(),t.ɵɵelement(53,"input",19),t.ɵɵelementStart(54,"mat-icon",13),t.ɵɵpipe(55,"translate"),t.ɵɵtext(56,"info_outlined "),t.ɵɵelementEnd()()()()()()(),t.ɵɵelementStart(57,"div",20)(58,"button",21),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(59),t.ɵɵpipe(60,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(61,"button",22),t.ɵɵlistener("click",(function(){return n.apply()})),t.ɵɵtext(62),t.ɵɵpipe(63,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.editCommandFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,13,n.data.titleText)),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.commandHelpLink),t.ɵɵadvance(12),t.ɵɵconditional(n.editCommandFormGroup.get("attributeOnGateway").hasError("required")?18:n.editCommandFormGroup.get("attributeOnGateway").hasError("pattern")?19:n.editCommandFormGroup.get("attributeOnGateway").hasError("duplicateName")?20:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(22,15,"gateway.hints.attribute")),t.ɵɵadvance(7),t.ɵɵconditional(n.editCommandFormGroup.get("timeout").hasError("required")?28:n.editCommandFormGroup.get("timeout").hasError("pattern")?29:n.editCommandFormGroup.get("timeout").hasError("min")?30:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(32,17,"gateway.hints.timeout")),t.ɵɵadvance(8),t.ɵɵconditional(n.editCommandFormGroup.get("command").hasError("required")?39:n.editCommandFormGroup.get("command").hasError("pattern")?40:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(42,19,"gateway.hints.command")),t.ɵɵadvance(13),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(55,21,"gateway.hints.install-cmd")),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(60,23,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.editCommandFormGroup.invalid||!n.editCommandFormGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(63,25,n.data.buttonText)," "))},dependencies:t.ɵɵgetComponentDepsFactory(ir,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .edit-command-container[_ngcontent-%COMP%]{min-width:40vw;width:50vw}[_nghost-%COMP%] .pointer-event{pointer-events:all}[_nghost-%COMP%] .toggle-group span{padding:0 25px}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{color:#e0e0e0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix:hover{color:#9e9e9e}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex;align-items:center}']})}}var ar=function(){this.firefox=!1,this.ie=!1,this.edge=!1,this.newEdge=!1,this.weChat=!1},rr=new function(){this.browser=new ar,this.node=!1,this.wxa=!1,this.worker=!1,this.svgSupported=!1,this.touchEventsSupported=!1,this.pointerEventsSupported=!1,this.domSupported=!1,this.transformSupported=!1,this.transform3dSupported=!1,this.hasGlobalWindow="undefined"!=typeof window};"object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?(rr.wxa=!0,rr.touchEventsSupported=!0):"undefined"==typeof document&&"undefined"!=typeof self?rr.worker=!0:"undefined"==typeof navigator||0===navigator.userAgent.indexOf("Node.js")?(rr.node=!0,rr.svgSupported=!0):function(e,t){var n=t.browser,i=e.match(/Firefox\/([\d.]+)/),a=e.match(/MSIE\s([\d.]+)/)||e.match(/Trident\/.+?rv:(([\d.]+))/),r=e.match(/Edge?\/([\d.]+)/),o=/micromessenger/i.test(e);i&&(n.firefox=!0,n.version=i[1]);a&&(n.ie=!0,n.version=a[1]);r&&(n.edge=!0,n.version=r[1],n.newEdge=+r[1].split(".")[0]>18);o&&(n.weChat=!0);t.svgSupported="undefined"!=typeof SVGRect,t.touchEventsSupported="ontouchstart"in window&&!n.ie&&!n.edge,t.pointerEventsSupported="onpointerdown"in window&&(n.edge||n.ie&&+n.version>=11),t.domSupported="undefined"!=typeof document;var s=document.documentElement.style;t.transform3dSupported=(n.ie&&"transition"in s||n.edge||"WebKitCSSMatrix"in window&&"m11"in new WebKitCSSMatrix||"MozPerspective"in s)&&!("OTransition"in s),t.transformSupported=t.transform3dSupported||n.ie&&+n.version>=9}(navigator.userAgent,rr);var or="sans-serif",sr="12px "+or;var lr,pr,cr=function(e){var t={};if("undefined"==typeof JSON)return t;for(var n=0;n=0)r=a*e.length;else for(var o=0;o>1)%2;o.style.cssText=["position: absolute","visibility: hidden","padding: 0","margin: 0","border-width: 0","user-select: none","width:0","height:0",i[s]+":0",a[l]+":0",i[1-s]+":auto",a[1-l]+":auto",""].join("!important;"),e.appendChild(o),n.push(o)}return n}(t,r),s=function(e,t,n){for(var i=n?"invTrans":"trans",a=t[i],r=t.srcCoords,o=[],s=[],l=!0,p=0;p<4;p++){var c=e[p].getBoundingClientRect(),d=2*p,u=c.left,m=c.top;o.push(u,m),l=l&&r&&u===r[d]&&m===r[d+1],s.push(e[p].offsetLeft,e[p].offsetTop)}return l&&a?a:(t.srcCoords=o,t[i]=n?Wo(s,o):Wo(o,s))}(o,r,a);if(s)return s(e,n,i),!0}return!1}function Xo(e){return"CANVAS"===e.nodeName.toUpperCase()}var Zo=/([&<>"'])/g,Qo={"&":"&","<":"<",">":">",'"':""","'":"'"};function Jo(e){return null==e?"":(e+"").replace(Zo,(function(e,t){return Qo[t]}))}var es=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ts=[],ns=rr.browser.firefox&&+rr.browser.version.split(".")[0]<39;function is(e,t,n,i){return n=n||{},i?as(e,t,n):ns&&null!=t.layerX&&t.layerX!==t.offsetX?(n.zrX=t.layerX,n.zrY=t.layerY):null!=t.offsetX?(n.zrX=t.offsetX,n.zrY=t.offsetY):as(e,t,n),n}function as(e,t,n){if(rr.domSupported&&e.getBoundingClientRect){var i=t.clientX,a=t.clientY;if(Xo(e)){var r=e.getBoundingClientRect();return n.zrX=i-r.left,void(n.zrY=a-r.top)}if(Yo(ts,e,i,a))return n.zrX=ts[0],void(n.zrY=ts[1])}n.zrX=n.zrY=0}function rs(e){return e||window.event}function os(e,t,n){if(null!=(t=rs(t)).zrX)return t;var i=t.type;if(i&&i.indexOf("touch")>=0){var a="touchend"!==i?t.targetTouches[0]:t.changedTouches[0];a&&is(e,a,t,n)}else{is(e,t,t,n);var r=function(e){var t=e.wheelDelta;if(t)return t;var n=e.deltaX,i=e.deltaY;if(null==n||null==i)return t;return 3*(0!==i?Math.abs(i):Math.abs(n))*(i>0?-1:i<0?1:n>0?-1:1)}(t);t.zrDelta=r?r/120:-(t.detail||0)/3}var o=t.button;return null==t.which&&void 0!==o&&es.test(t.type)&&(t.which=1&o?1:2&o?3:4&o?2:0),t}function ss(e,t,n,i){e.addEventListener(t,n,i)}var ls=function(e){e.preventDefault(),e.stopPropagation(),e.cancelBubble=!0};function ps(e){return 2===e.which||3===e.which}var cs=function(){function e(){this._track=[]}return e.prototype.recognize=function(e,t,n){return this._doTrack(e,t,n),this._recognize(e)},e.prototype.clear=function(){return this._track.length=0,this},e.prototype._doTrack=function(e,t,n){var i=e.touches;if(i){for(var a={points:[],touches:[],target:t,event:e},r=0,o=i.length;r1&&a&&a.length>1){var o=ds(a)/ds(r);!isFinite(o)&&(o=1),t.pinchScale=o;var s=[((i=a)[0][0]+i[1][0])/2,(i[0][1]+i[1][1])/2];return t.pinchX=s[0],t.pinchY=s[1],{type:"pinch",target:e[0].target,event:t}}}}};function ms(e){return e[0]=1,e[1]=0,e[2]=0,e[3]=1,e[4]=0,e[5]=0,e}function hs(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e}function gs(e,t,n){var i=t[0]*n[0]+t[2]*n[1],a=t[1]*n[0]+t[3]*n[1],r=t[0]*n[2]+t[2]*n[3],o=t[1]*n[2]+t[3]*n[3],s=t[0]*n[4]+t[2]*n[5]+t[4],l=t[1]*n[4]+t[3]*n[5]+t[5];return e[0]=i,e[1]=a,e[2]=r,e[3]=o,e[4]=s,e[5]=l,e}function fs(e,t,n){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4]+n[0],e[5]=t[5]+n[1],e}function ys(e,t,n,i){void 0===i&&(i=[0,0]);var a=t[0],r=t[2],o=t[4],s=t[1],l=t[3],p=t[5],c=Math.sin(n),d=Math.cos(n);return e[0]=a*d+s*c,e[1]=-a*c+s*d,e[2]=r*d+l*c,e[3]=-r*c+d*l,e[4]=d*(o-i[0])+c*(p-i[1])+i[0],e[5]=d*(p-i[1])-c*(o-i[0])+i[1],e}function vs(e,t,n){var i=n[0],a=n[1];return e[0]=t[0]*i,e[1]=t[1]*a,e[2]=t[2]*i,e[3]=t[3]*a,e[4]=t[4]*i,e[5]=t[5]*a,e}function xs(e,t){var n=t[0],i=t[2],a=t[4],r=t[1],o=t[3],s=t[5],l=n*o-r*i;return l?(l=1/l,e[0]=o*l,e[1]=-r*l,e[2]=-i*l,e[3]=n*l,e[4]=(i*s-o*a)*l,e[5]=(r*a-n*s)*l,e):null}function bs(e){var t=[1,0,0,1,0,0];return hs(t,e),t}var ws=function(){function e(e,t){this.x=e||0,this.y=t||0}return e.prototype.copy=function(e){return this.x=e.x,this.y=e.y,this},e.prototype.clone=function(){return new e(this.x,this.y)},e.prototype.set=function(e,t){return this.x=e,this.y=t,this},e.prototype.equal=function(e){return e.x===this.x&&e.y===this.y},e.prototype.add=function(e){return this.x+=e.x,this.y+=e.y,this},e.prototype.scale=function(e){this.x*=e,this.y*=e},e.prototype.scaleAndAdd=function(e,t){this.x+=e.x*t,this.y+=e.y*t},e.prototype.sub=function(e){return this.x-=e.x,this.y-=e.y,this},e.prototype.dot=function(e){return this.x*e.x+this.y*e.y},e.prototype.len=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},e.prototype.lenSquare=function(){return this.x*this.x+this.y*this.y},e.prototype.normalize=function(){var e=this.len();return this.x/=e,this.y/=e,this},e.prototype.distance=function(e){var t=this.x-e.x,n=this.y-e.y;return Math.sqrt(t*t+n*n)},e.prototype.distanceSquare=function(e){var t=this.x-e.x,n=this.y-e.y;return t*t+n*n},e.prototype.negate=function(){return this.x=-this.x,this.y=-this.y,this},e.prototype.transform=function(e){if(e){var t=this.x,n=this.y;return this.x=e[0]*t+e[2]*n+e[4],this.y=e[1]*t+e[3]*n+e[5],this}},e.prototype.toArray=function(e){return e[0]=this.x,e[1]=this.y,e},e.prototype.fromArray=function(e){this.x=e[0],this.y=e[1]},e.set=function(e,t,n){e.x=t,e.y=n},e.copy=function(e,t){e.x=t.x,e.y=t.y},e.len=function(e){return Math.sqrt(e.x*e.x+e.y*e.y)},e.lenSquare=function(e){return e.x*e.x+e.y*e.y},e.dot=function(e,t){return e.x*t.x+e.y*t.y},e.add=function(e,t,n){e.x=t.x+n.x,e.y=t.y+n.y},e.sub=function(e,t,n){e.x=t.x-n.x,e.y=t.y-n.y},e.scale=function(e,t,n){e.x=t.x*n,e.y=t.y*n},e.scaleAndAdd=function(e,t,n,i){e.x=t.x+n.x*i,e.y=t.y+n.y*i},e.lerp=function(e,t,n,i){var a=1-i;e.x=a*t.x+i*n.x,e.y=a*t.y+i*n.y},e}(),Ss=Math.min,Cs=Math.max,_s=new ws,Ts=new ws,Is=new ws,Es=new ws,Ms=new ws,ks=new ws,Ps=function(){function e(e,t,n,i){n<0&&(e+=n,n=-n),i<0&&(t+=i,i=-i),this.x=e,this.y=t,this.width=n,this.height=i}return e.prototype.union=function(e){var t=Ss(e.x,this.x),n=Ss(e.y,this.y);isFinite(this.x)&&isFinite(this.width)?this.width=Cs(e.x+e.width,this.x+this.width)-t:this.width=e.width,isFinite(this.y)&&isFinite(this.height)?this.height=Cs(e.y+e.height,this.y+this.height)-n:this.height=e.height,this.x=t,this.y=n},e.prototype.applyTransform=function(t){e.applyTransform(this,this,t)},e.prototype.calculateTransform=function(e){var t=this,n=e.width/t.width,i=e.height/t.height,a=[1,0,0,1,0,0];return fs(a,a,[-t.x,-t.y]),vs(a,a,[n,i]),fs(a,a,[e.x,e.y]),a},e.prototype.intersect=function(t,n){if(!t)return!1;t instanceof e||(t=e.create(t));var i=this,a=i.x,r=i.x+i.width,o=i.y,s=i.y+i.height,l=t.x,p=t.x+t.width,c=t.y,d=t.y+t.height,u=!(rh&&(h=x,gh&&(h=b,y=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height},e.prototype.clone=function(){return new e(this.x,this.y,this.width,this.height)},e.prototype.copy=function(t){e.copy(this,t)},e.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},e.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},e.prototype.isZero=function(){return 0===this.width||0===this.height},e.create=function(t){return new e(t.x,t.y,t.width,t.height)},e.copy=function(e,t){e.x=t.x,e.y=t.y,e.width=t.width,e.height=t.height},e.applyTransform=function(t,n,i){if(i){if(i[1]<1e-5&&i[1]>-1e-5&&i[2]<1e-5&&i[2]>-1e-5){var a=i[0],r=i[3],o=i[4],s=i[5];return t.x=n.x*a+o,t.y=n.y*r+s,t.width=n.width*a,t.height=n.height*r,t.width<0&&(t.x+=t.width,t.width=-t.width),void(t.height<0&&(t.y+=t.height,t.height=-t.height))}_s.x=Is.x=n.x,_s.y=Es.y=n.y,Ts.x=Es.x=n.x+n.width,Ts.y=Is.y=n.y+n.height,_s.transform(i),Es.transform(i),Ts.transform(i),Is.transform(i),t.x=Ss(_s.x,Ts.x,Is.x,Es.x),t.y=Ss(_s.y,Ts.y,Is.y,Es.y);var l=Cs(_s.x,Ts.x,Is.x,Es.x),p=Cs(_s.y,Ts.y,Is.y,Es.y);t.width=l-t.x,t.height=p-t.y}else t!==n&&e.copy(t,n)},e}(),Ds="silent";function Os(){ls(this.event)}var As=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.handler=null,t}return ze(t,e),t.prototype.dispose=function(){},t.prototype.setCursor=function(){},t}(Uo),Fs=function(e,t){this.x=e,this.y=t},Rs=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],Bs=new Ps(0,0,0,0),Ns=function(e){function t(t,n,i,a,r){var o=e.call(this)||this;return o._hovered=new Fs(0,0),o.storage=t,o.painter=n,o.painterRoot=a,o._pointerSize=r,i=i||new As,o.proxy=null,o.setHandlerProxy(i),o._draggingMgr=new zo(o),o}return ze(t,e),t.prototype.setHandlerProxy=function(e){this.proxy&&this.proxy.dispose(),e&&(Rr(Rs,(function(t){e.on&&e.on(t,this[t],this)}),this),e.handler=this),this.proxy=e},t.prototype.mousemove=function(e){var t=e.zrX,n=e.zrY,i=qs(this,t,n),a=this._hovered,r=a.target;r&&!r.__zr&&(r=(a=this.findHover(a.x,a.y)).target);var o=this._hovered=i?new Fs(t,n):this.findHover(t,n),s=o.target,l=this.proxy;l.setCursor&&l.setCursor(s?s.cursor:"default"),r&&s!==r&&this.dispatchToElement(a,"mouseout",e),this.dispatchToElement(o,"mousemove",e),s&&s!==r&&this.dispatchToElement(o,"mouseover",e)},t.prototype.mouseout=function(e){var t=e.zrEventControl;"only_globalout"!==t&&this.dispatchToElement(this._hovered,"mouseout",e),"no_globalout"!==t&&this.trigger("globalout",{type:"globalout",event:e})},t.prototype.resize=function(){this._hovered=new Fs(0,0)},t.prototype.dispatch=function(e,t){var n=this[e];n&&n.call(this,t)},t.prototype.dispose=function(){this.proxy.dispose(),this.storage=null,this.proxy=null,this.painter=null},t.prototype.setCursorStyle=function(e){var t=this.proxy;t.setCursor&&t.setCursor(e)},t.prototype.dispatchToElement=function(e,t,n){var i=(e=e||{}).target;if(!i||!i.silent){for(var a="on"+t,r=function(e,t,n){return{type:e,event:n,target:t.target,topTarget:t.topTarget,cancelBubble:!1,offsetX:n.zrX,offsetY:n.zrY,gestureEvent:n.gestureEvent,pinchX:n.pinchX,pinchY:n.pinchY,pinchScale:n.pinchScale,wheelDelta:n.zrDelta,zrByTouch:n.zrByTouch,which:n.which,stop:Os}}(t,e,n);i&&(i[a]&&(r.cancelBubble=!!i[a].call(i,r)),i.trigger(t,r),i=i.__hostTarget?i.__hostTarget:i.parent,!r.cancelBubble););r.cancelBubble||(this.trigger(t,r),this.painter&&this.painter.eachOtherLayer&&this.painter.eachOtherLayer((function(e){"function"==typeof e[a]&&e[a].call(e,r),e.trigger&&e.trigger(t,r)})))}},t.prototype.findHover=function(e,t,n){var i=this.storage.getDisplayList(),a=new Fs(e,t);if(Vs(i,a,e,t,n),this._pointerSize&&!a.target){for(var r=[],o=this._pointerSize,s=o/2,l=new Ps(e-s,t-s,o,o),p=i.length-1;p>=0;p--){var c=i[p];c===n||c.ignore||c.ignoreCoarsePointer||c.parent&&c.parent.ignoreCoarsePointer||(Bs.copy(c.getBoundingRect()),c.transform&&Bs.applyTransform(c.transform),Bs.intersect(l)&&r.push(c))}if(r.length)for(var d=Math.PI/12,u=2*Math.PI,m=0;m=0;r--){var o=e[r],s=void 0;if(o!==a&&!o.ignore&&(s=Ls(o,n,i))&&(!t.topTarget&&(t.topTarget=o),s!==Ds)){t.target=o;break}}}function qs(e,t,n){var i=e.painter;return t<0||t>i.getWidth()||n<0||n>i.getHeight()}Rr(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],(function(e){Ns.prototype[e]=function(t){var n,i,a=t.zrX,r=t.zrY,o=qs(this,a,r);if("mouseup"===e&&o||(i=(n=this.findHover(a,r)).target),"mousedown"===e)this._downEl=i,this._downPoint=[t.zrX,t.zrY],this._upEl=i;else if("mouseup"===e)this._upEl=i;else if("click"===e){if(this._downEl!==this._upEl||!this._downPoint||Fo(this._downPoint,[t.zrX,t.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,e,t)}}));function Gs(e,t,n,i){var a=t+1;if(a===n)return 1;if(i(e[a++],e[t])<0){for(;a=0;)a++;return a-t}function zs(e,t,n,i,a){for(i===t&&i++;i>>1])<0?l=r:s=r+1;var p=i-s;switch(p){case 3:e[s+3]=e[s+2];case 2:e[s+2]=e[s+1];case 1:e[s+1]=e[s];break;default:for(;p>0;)e[s+p]=e[s+p-1],p--}e[s]=o}}function Us(e,t,n,i,a,r){var o=0,s=0,l=1;if(r(e,t[n+a])>0){for(s=i-a;l0;)o=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),o+=a,l+=a}else{for(s=a+1;ls&&(l=s);var p=o;o=a-l,l=a-p}for(o++;o>>1);r(e,t[n+c])>0?o=c+1:l=c}return l}function js(e,t,n,i,a,r){var o=0,s=0,l=1;if(r(e,t[n+a])<0){for(s=a+1;ls&&(l=s);var p=o;o=a-l,l=a-p}else{for(s=i-a;l=0;)o=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),o+=a,l+=a}for(o++;o>>1);r(e,t[n+c])<0?l=c:o=c+1}return l}function Hs(e,t){var n,i,a=7,r=0,o=[];function s(s){var l=n[s],p=i[s],c=n[s+1],d=i[s+1];i[s]=p+d,s===r-3&&(n[s+1]=n[s+2],i[s+1]=i[s+2]),r--;var u=js(e[c],e,l,p,0,t);l+=u,0!==(p-=u)&&0!==(d=Us(e[l+p-1],e,c,d,d-1,t))&&(p<=d?function(n,i,r,s){var l=0;for(l=0;l=7||m>=7);if(h)break;g<0&&(g=0),g+=2}if((a=g)<1&&(a=1),1===i){for(l=0;l=0;l--)e[m+l]=e[u+l];return void(e[d]=o[c])}var h=a;for(;;){var g=0,f=0,y=!1;do{if(t(o[c],e[p])<0){if(e[d--]=e[p--],g++,f=0,0==--i){y=!0;break}}else if(e[d--]=o[c--],f++,g=0,1==--s){y=!0;break}}while((g|f)=0;l--)e[m+l]=e[u+l];if(0===i){y=!0;break}}if(e[d--]=o[c--],1==--s){y=!0;break}if(0!==(f=s-Us(e[p],o,0,s,s-1,t))){for(s-=f,m=(d-=f)+1,u=(c-=f)+1,l=0;l=7||f>=7);if(y)break;h<0&&(h=0),h+=2}(a=h)<1&&(a=1);if(1===s){for(m=(d-=i)+1,u=(p-=i)+1,l=i-1;l>=0;l--)e[m+l]=e[u+l];e[d]=o[c]}else{if(0===s)throw new Error;for(u=d-(s-1),l=0;l1;){var e=r-2;if(e>=1&&i[e-1]<=i[e]+i[e+1]||e>=2&&i[e-2]<=i[e]+i[e-1])i[e-1]i[e+1])break;s(e)}},forceMergeRuns:function(){for(;r>1;){var e=r-2;e>0&&i[e-1]=32;)t|=1&e,e>>=1;return e+t}(a);do{if((r=Gs(e,n,i,t))s&&(l=s),zs(e,n,n+l,n+r,t),r=l}o.pushRun(n,r),o.mergeRuns(),a-=r,n+=r}while(0!==a);o.forceMergeRuns()}}}var $s=!1;function Ks(){$s||($s=!0,console.warn("z / z2 / zlevel of displayable is invalid, which may cause unexpected errors"))}function Ys(e,t){return e.zlevel===t.zlevel?e.z===t.z?e.z2-t.z2:e.z-t.z:e.zlevel-t.zlevel}var Xs=function(){function e(){this._roots=[],this._displayList=[],this._displayListLen=0,this.displayableSortFunc=Ys}return e.prototype.traverse=function(e,t){for(var n=0;n0&&(p.__clipPaths=[]),isNaN(p.z)&&(Ks(),p.z=0),isNaN(p.z2)&&(Ks(),p.z2=0),isNaN(p.zlevel)&&(Ks(),p.zlevel=0),this._displayList[this._displayListLen++]=p}var c=e.getDecalElement&&e.getDecalElement();c&&this._updateAndAddDisplayable(c,t,n);var d=e.getTextGuideLine();d&&this._updateAndAddDisplayable(d,t,n);var u=e.getTextContent();u&&this._updateAndAddDisplayable(u,t,n)}},e.prototype.addRoot=function(e){e.__zr&&e.__zr.storage===this||this._roots.push(e)},e.prototype.delRoot=function(e){if(e instanceof Array)for(var t=0,n=e.length;t=0&&this._roots.splice(i,1)}},e.prototype.delAllRoots=function(){this._roots=[],this._displayList=[],this._displayListLen=0},e.prototype.getRoots=function(){return this._roots},e.prototype.dispose=function(){this._displayList=null,this._roots=null},e}(),Zs=rr.hasGlobalWindow&&(window.requestAnimationFrame&&window.requestAnimationFrame.bind(window)||window.msRequestAnimationFrame&&window.msRequestAnimationFrame.bind(window)||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame)||function(e){return setTimeout(e,16)},Qs={linear:function(e){return e},quadraticIn:function(e){return e*e},quadraticOut:function(e){return e*(2-e)},quadraticInOut:function(e){return(e*=2)<1?.5*e*e:-.5*(--e*(e-2)-1)},cubicIn:function(e){return e*e*e},cubicOut:function(e){return--e*e*e+1},cubicInOut:function(e){return(e*=2)<1?.5*e*e*e:.5*((e-=2)*e*e+2)},quarticIn:function(e){return e*e*e*e},quarticOut:function(e){return 1- --e*e*e*e},quarticInOut:function(e){return(e*=2)<1?.5*e*e*e*e:-.5*((e-=2)*e*e*e-2)},quinticIn:function(e){return e*e*e*e*e},quinticOut:function(e){return--e*e*e*e*e+1},quinticInOut:function(e){return(e*=2)<1?.5*e*e*e*e*e:.5*((e-=2)*e*e*e*e+2)},sinusoidalIn:function(e){return 1-Math.cos(e*Math.PI/2)},sinusoidalOut:function(e){return Math.sin(e*Math.PI/2)},sinusoidalInOut:function(e){return.5*(1-Math.cos(Math.PI*e))},exponentialIn:function(e){return 0===e?0:Math.pow(1024,e-1)},exponentialOut:function(e){return 1===e?1:1-Math.pow(2,-10*e)},exponentialInOut:function(e){return 0===e?0:1===e?1:(e*=2)<1?.5*Math.pow(1024,e-1):.5*(2-Math.pow(2,-10*(e-1)))},circularIn:function(e){return 1-Math.sqrt(1-e*e)},circularOut:function(e){return Math.sqrt(1- --e*e)},circularInOut:function(e){return(e*=2)<1?-.5*(Math.sqrt(1-e*e)-1):.5*(Math.sqrt(1-(e-=2)*e)+1)},elasticIn:function(e){var t,n=.1;return 0===e?0:1===e?1:(!n||n<1?(n=1,t=.1):t=.4*Math.asin(1/n)/(2*Math.PI),-n*Math.pow(2,10*(e-=1))*Math.sin((e-t)*(2*Math.PI)/.4))},elasticOut:function(e){var t,n=.1;return 0===e?0:1===e?1:(!n||n<1?(n=1,t=.1):t=.4*Math.asin(1/n)/(2*Math.PI),n*Math.pow(2,-10*e)*Math.sin((e-t)*(2*Math.PI)/.4)+1)},elasticInOut:function(e){var t,n=.1,i=.4;return 0===e?0:1===e?1:(!n||n<1?(n=1,t=.1):t=i*Math.asin(1/n)/(2*Math.PI),(e*=2)<1?n*Math.pow(2,10*(e-=1))*Math.sin((e-t)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(e-=1))*Math.sin((e-t)*(2*Math.PI)/i)*.5+1)},backIn:function(e){var t=1.70158;return e*e*((t+1)*e-t)},backOut:function(e){var t=1.70158;return--e*e*((t+1)*e+t)+1},backInOut:function(e){var t=2.5949095;return(e*=2)<1?e*e*((t+1)*e-t)*.5:.5*((e-=2)*e*((t+1)*e+t)+2)},bounceIn:function(e){return 1-Qs.bounceOut(1-e)},bounceOut:function(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375},bounceInOut:function(e){return e<.5?.5*Qs.bounceIn(2*e):.5*Qs.bounceOut(2*e-1)+.5}},Js=Math.pow,el=Math.sqrt,tl=1e-8,nl=1e-4,il=el(3),al=1/3,rl=Co(),ol=Co(),sl=Co();function ll(e){return e>-1e-8&&etl||e<-1e-8}function cl(e,t,n,i,a){var r=1-a;return r*r*(r*e+3*a*t)+a*a*(a*i+3*r*n)}function dl(e,t,n,i,a){var r=1-a;return 3*(((t-e)*r+2*(n-t)*a)*r+(i-n)*a*a)}function ul(e,t,n,i,a,r){var o=i+3*(t-n)-e,s=3*(n-2*t+e),l=3*(t-e),p=e-a,c=s*s-3*o*l,d=s*l-9*o*p,u=l*l-3*s*p,m=0;if(ll(c)&&ll(d)){if(ll(s))r[0]=0;else(_=-l/s)>=0&&_<=1&&(r[m++]=_)}else{var h=d*d-4*c*u;if(ll(h)){var g=d/c,f=-g/2;(_=-s/o+g)>=0&&_<=1&&(r[m++]=_),f>=0&&f<=1&&(r[m++]=f)}else if(h>0){var y=el(h),v=c*s+1.5*o*(-d+y),x=c*s+1.5*o*(-d-y);(_=(-s-((v=v<0?-Js(-v,al):Js(v,al))+(x=x<0?-Js(-x,al):Js(x,al))))/(3*o))>=0&&_<=1&&(r[m++]=_)}else{var b=(2*c*s-3*o*d)/(2*el(c*c*c)),w=Math.acos(b)/3,S=el(c),C=Math.cos(w),_=(-s-2*S*C)/(3*o),T=(f=(-s+S*(C+il*Math.sin(w)))/(3*o),(-s+S*(C-il*Math.sin(w)))/(3*o));_>=0&&_<=1&&(r[m++]=_),f>=0&&f<=1&&(r[m++]=f),T>=0&&T<=1&&(r[m++]=T)}}return m}function ml(e,t,n,i,a){var r=6*n-12*t+6*e,o=9*t+3*i-3*e-9*n,s=3*t-3*e,l=0;if(ll(o)){if(pl(r))(c=-s/r)>=0&&c<=1&&(a[l++]=c)}else{var p=r*r-4*o*s;if(ll(p))a[0]=-r/(2*o);else if(p>0){var c,d=el(p),u=(-r-d)/(2*o);(c=(-r+d)/(2*o))>=0&&c<=1&&(a[l++]=c),u>=0&&u<=1&&(a[l++]=u)}}return l}function hl(e,t,n,i,a,r){var o=(t-e)*a+e,s=(n-t)*a+t,l=(i-n)*a+n,p=(s-o)*a+o,c=(l-s)*a+s,d=(c-p)*a+p;r[0]=e,r[1]=o,r[2]=p,r[3]=d,r[4]=d,r[5]=c,r[6]=l,r[7]=i}function gl(e,t,n,i,a,r,o,s,l,p,c){var d,u,m,h,g,f=.005,y=1/0;rl[0]=l,rl[1]=p;for(var v=0;v<1;v+=.05)ol[0]=cl(e,n,a,o,v),ol[1]=cl(t,i,r,s,v),(h=Bo(rl,ol))=0&&h=0&&f=1?1:ul(0,i,r,1,e,s)&&cl(0,a,o,1,s[0])}}}var Tl=function(){function e(e){this._inited=!1,this._startTime=0,this._pausedTime=0,this._paused=!1,this._life=e.life||1e3,this._delay=e.delay||0,this.loop=e.loop||!1,this.onframe=e.onframe||wo,this.ondestroy=e.ondestroy||wo,this.onrestart=e.onrestart||wo,e.easing&&this.setEasing(e.easing)}return e.prototype.step=function(e,t){if(this._inited||(this._startTime=e+this._delay,this._inited=!0),!this._paused){var n=this._life,i=e-this._startTime-this._pausedTime,a=i/n;a<0&&(a=0),a=Math.min(a,1);var r=this.easingFunc,o=r?r(a):a;if(this.onframe(o),1===a){if(!this.loop)return!0;var s=i%n;this._startTime=e-s,this._pausedTime=0,this.onrestart()}return!1}this._pausedTime+=t},e.prototype.pause=function(){this._paused=!0},e.prototype.resume=function(){this._paused=!1},e.prototype.setEasing=function(e){this.easing=e,this.easingFunc=jr(e)?e:Qs[e]||_l(e)},e}(),Il=function(e){this.value=e},El=function(){function e(){this._len=0}return e.prototype.insert=function(e){var t=new Il(e);return this.insertEntry(t),t},e.prototype.insertEntry=function(e){this.head?(this.tail.next=e,e.prev=this.tail,e.next=null,this.tail=e):this.head=this.tail=e,this._len++},e.prototype.remove=function(e){var t=e.prev,n=e.next;t?t.next=n:this.head=n,n?n.prev=t:this.tail=t,e.next=e.prev=null,this._len--},e.prototype.len=function(){return this._len},e.prototype.clear=function(){this.head=this.tail=null,this._len=0},e}(),Ml=function(){function e(e){this._list=new El,this._maxSize=10,this._map={},this._maxSize=e}return e.prototype.put=function(e,t){var n=this._list,i=this._map,a=null;if(null==i[e]){var r=n.len(),o=this._lastRemovedEntry;if(r>=this._maxSize&&r>0){var s=n.head;n.remove(s),delete i[s.key],a=s.value,this._lastRemovedEntry=s}o?o.value=t:o=new Il(t),o.key=e,n.insertEntry(o),i[e]=o}return a},e.prototype.get=function(e){var t=this._map[e],n=this._list;if(null!=t)return t!==n.tail&&(n.remove(t),n.insertEntry(t)),t.value},e.prototype.clear=function(){this._list.clear(),this._map={}},e.prototype.len=function(){return this._list.len()},e}(),kl={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function Pl(e){return(e=Math.round(e))<0?0:e>255?255:e}function Dl(e){return e<0?0:e>1?1:e}function Ol(e){var t=e;return t.length&&"%"===t.charAt(t.length-1)?Pl(parseFloat(t)/100*255):Pl(parseInt(t,10))}function Al(e){var t=e;return t.length&&"%"===t.charAt(t.length-1)?Dl(parseFloat(t)/100):Dl(parseFloat(t))}function Fl(e,t,n){return n<0?n+=1:n>1&&(n-=1),6*n<1?e+(t-e)*n*6:2*n<1?t:3*n<2?e+(t-e)*(2/3-n)*6:e}function Rl(e,t,n){return e+(t-e)*n}function Bl(e,t,n,i,a){return e[0]=t,e[1]=n,e[2]=i,e[3]=a,e}function Nl(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}var Ll=new Ml(20),Vl=null;function ql(e,t){Vl&&Nl(Vl,t),Vl=Ll.put(e,Vl||t.slice())}function Gl(e,t){if(e){t=t||[];var n=Ll.get(e);if(n)return Nl(t,n);var i=(e+="").replace(/ /g,"").toLowerCase();if(i in kl)return Nl(t,kl[i]),ql(e,t),t;var a,r=i.length;if("#"===i.charAt(0))return 4===r||5===r?(a=parseInt(i.slice(1,4),16))>=0&&a<=4095?(Bl(t,(3840&a)>>4|(3840&a)>>8,240&a|(240&a)>>4,15&a|(15&a)<<4,5===r?parseInt(i.slice(4),16)/15:1),ql(e,t),t):void Bl(t,0,0,0,1):7===r||9===r?(a=parseInt(i.slice(1,7),16))>=0&&a<=16777215?(Bl(t,(16711680&a)>>16,(65280&a)>>8,255&a,9===r?parseInt(i.slice(7),16)/255:1),ql(e,t),t):void Bl(t,0,0,0,1):void 0;var o=i.indexOf("("),s=i.indexOf(")");if(-1!==o&&s+1===r){var l=i.substr(0,o),p=i.substr(o+1,s-(o+1)).split(","),c=1;switch(l){case"rgba":if(4!==p.length)return 3===p.length?Bl(t,+p[0],+p[1],+p[2],1):Bl(t,0,0,0,1);c=Al(p.pop());case"rgb":return p.length>=3?(Bl(t,Ol(p[0]),Ol(p[1]),Ol(p[2]),3===p.length?c:Al(p[3])),ql(e,t),t):void Bl(t,0,0,0,1);case"hsla":return 4!==p.length?void Bl(t,0,0,0,1):(p[3]=Al(p[3]),zl(p,t),ql(e,t),t);case"hsl":return 3!==p.length?void Bl(t,0,0,0,1):(zl(p,t),ql(e,t),t);default:return}}Bl(t,0,0,0,1)}}function zl(e,t){var n=(parseFloat(e[0])%360+360)%360/360,i=Al(e[1]),a=Al(e[2]),r=a<=.5?a*(i+1):a+i-a*i,o=2*a-r;return Bl(t=t||[],Pl(255*Fl(o,r,n+1/3)),Pl(255*Fl(o,r,n)),Pl(255*Fl(o,r,n-1/3)),1),4===e.length&&(t[3]=e[3]),t}function Ul(e,t){var n=Gl(e);if(n){for(var i=0;i<3;i++)n[i]=t<0?n[i]*(1-t)|0:(255-n[i])*t+n[i]|0,n[i]>255?n[i]=255:n[i]<0&&(n[i]=0);return Kl(n,4===n.length?"rgba":"rgb")}}function jl(e,t,n){if(t&&t.length&&e>=0&&e<=1){n=n||[];var i=e*(t.length-1),a=Math.floor(i),r=Math.ceil(i),o=t[a],s=t[r],l=i-a;return n[0]=Pl(Rl(o[0],s[0],l)),n[1]=Pl(Rl(o[1],s[1],l)),n[2]=Pl(Rl(o[2],s[2],l)),n[3]=Dl(Rl(o[3],s[3],l)),n}}function Hl(e,t,n){if(t&&t.length&&e>=0&&e<=1){var i=e*(t.length-1),a=Math.floor(i),r=Math.ceil(i),o=Gl(t[a]),s=Gl(t[r]),l=i-a,p=Kl([Pl(Rl(o[0],s[0],l)),Pl(Rl(o[1],s[1],l)),Pl(Rl(o[2],s[2],l)),Dl(Rl(o[3],s[3],l))],"rgba");return n?{color:p,leftIndex:a,rightIndex:r,value:i}:p}}function Wl(e,t,n,i){var a=Gl(e);if(e)return a=function(e){if(e){var t,n,i=e[0]/255,a=e[1]/255,r=e[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o,p=(s+o)/2;if(0===l)t=0,n=0;else{n=p<.5?l/(s+o):l/(2-s-o);var c=((s-i)/6+l/2)/l,d=((s-a)/6+l/2)/l,u=((s-r)/6+l/2)/l;i===s?t=u-d:a===s?t=1/3+c-u:r===s&&(t=2/3+d-c),t<0&&(t+=1),t>1&&(t-=1)}var m=[360*t,n,p];return null!=e[3]&&m.push(e[3]),m}}(a),null!=t&&(a[0]=function(e){return(e=Math.round(e))<0?0:e>360?360:e}(t)),null!=n&&(a[1]=Al(n)),null!=i&&(a[2]=Al(i)),Kl(zl(a),"rgba")}function $l(e,t){var n=Gl(e);if(n&&null!=t)return n[3]=Dl(t),Kl(n,"rgba")}function Kl(e,t){if(e&&e.length){var n=e[0]+","+e[1]+","+e[2];return"rgba"!==t&&"hsva"!==t&&"hsla"!==t||(n+=","+e[3]),t+"("+n+")"}}function Yl(e,t){var n=Gl(e);return n?(.299*n[0]+.587*n[1]+.114*n[2])*n[3]/255+(1-n[3])*t:0}var Xl=new Ml(100);function Zl(e){if(Hr(e)){var t=Xl.get(e);return t||(t=Ul(e,-.1),Xl.put(e,t)),t}if(Qr(e)){var n=kr({},e);return n.colorStops=Br(e.colorStops,(function(e){return{offset:e.offset,color:Ul(e.color,-.1)}})),n}return e}var Ql=Math.round;function Jl(e){var t;if(e&&"transparent"!==e){if("string"==typeof e&&e.indexOf("rgba")>-1){var n=Gl(e);n&&(e="rgb("+n[0]+","+n[1]+","+n[2]+")",t=n[3])}}else e="none";return{color:e,opacity:null==t?1:t}}var ep=1e-4;function tp(e){return e-1e-4}function np(e){return Ql(1e3*e)/1e3}function ip(e){return Ql(1e4*e)/1e4}var ap={left:"start",right:"end",center:"middle",middle:"middle"};function rp(e){return e&&!!e.image}function op(e){return rp(e)||function(e){return e&&!!e.svgElement}(e)}function sp(e){return"linear"===e.type}function lp(e){return"radial"===e.type}function pp(e){return e&&("linear"===e.type||"radial"===e.type)}function cp(e){return"url(#"+e+")"}function dp(e){var t=e.getGlobalScale(),n=Math.max(t[0],t[1]);return Math.max(Math.ceil(Math.log(n)/Math.log(10)),1)}function up(e){var t=e.x||0,n=e.y||0,i=(e.rotation||0)*So,a=io(e.scaleX,1),r=io(e.scaleY,1),o=e.skewX||0,s=e.skewY||0,l=[];return(t||n)&&l.push("translate("+t+"px,"+n+"px)"),i&&l.push("rotate("+i+")"),1===a&&1===r||l.push("scale("+a+","+r+")"),(o||s)&&l.push("skew("+Ql(o*So)+"deg, "+Ql(s*So)+"deg)"),l.join(" ")}var mp=rr.hasGlobalWindow&&jr(window.btoa)?function(e){return window.btoa(unescape(encodeURIComponent(e)))}:"undefined"!=typeof Buffer?function(e){return Buffer.from(e).toString("base64")}:function(e){return null},hp=Array.prototype.slice;function gp(e,t,n){return(t-e)*n+e}function fp(e,t,n,i){for(var a=t.length,r=0;ri?t:e,r=Math.min(n,i),o=a[r-1]||{color:[0,0,0,0],offset:0},s=r;so)i.length=o;else for(var s=r;s=1},e.prototype.getAdditiveTrack=function(){return this._additiveTrack},e.prototype.addKeyframe=function(e,t,n){this._needsSort=!0;var i=this.keyframes,a=i.length,r=!1,o=6,s=t;if(Fr(t)){var l=function(e){return Fr(e&&e[0])?2:1}(t);o=l,(1===l&&!$r(t[0])||2===l&&!$r(t[0][0]))&&(r=!0)}else if($r(t)&&!to(t))o=0;else if(Hr(t))if(isNaN(+t)){var p=Gl(t);p&&(s=p,o=3)}else o=0;else if(Qr(t)){var c=kr({},s);c.colorStops=Br(t.colorStops,(function(e){return{offset:e.offset,color:Gl(e.color)}})),sp(t)?o=4:lp(t)&&(o=5),s=c}0===a?this.valType=o:o===this.valType&&6!==o||(r=!0),this.discrete=this.discrete||r;var d={time:e,value:s,rawValue:t,percent:0};return n&&(d.easing=n,d.easingFunc=jr(n)?n:Qs[n]||_l(n)),i.push(d),d},e.prototype.prepare=function(e,t){var n=this.keyframes;this._needsSort&&n.sort((function(e,t){return e.time-t.time}));for(var i=this.valType,a=n.length,r=n[a-1],o=this.discrete,s=_p(i),l=Cp(i),p=0;p=0&&!(l[n].percent<=t);n--);n=m(n,p-2)}else{for(n=u;nt);n++);n=m(n-1,p-2)}a=l[n+1],i=l[n]}if(i&&a){this._lastFr=n,this._lastFrP=t;var h=a.percent-i.percent,g=0===h?1:m((t-i.percent)/h,1);a.easingFunc&&(g=a.easingFunc(g));var f=r?this._additiveValue:d?Tp:e[c];if(!_p(s)&&!d||f||(f=this._additiveValue=[]),this.discrete)e[c]=g<1?i.rawValue:a.rawValue;else if(_p(s))1===s?fp(f,i[o],a[o],g):function(e,t,n,i){for(var a=t.length,r=a&&t[0].length,o=0;o0&&s.addKeyframe(0,wp(l),i),this._trackKeys.push(o)}s.addKeyframe(e,wp(t[o]),i)}return this._maxTime=Math.max(this._maxTime,e),this},e.prototype.pause=function(){this._clip.pause(),this._paused=!0},e.prototype.resume=function(){this._clip.resume(),this._paused=!1},e.prototype.isPaused=function(){return!!this._paused},e.prototype.duration=function(e){return this._maxTime=e,this._force=!0,this},e.prototype._doneCallback=function(){this._setTracksFinished(),this._clip=null;var e=this._doneCbs;if(e)for(var t=e.length,n=0;n0)){this._started=1;for(var t=this,n=[],i=this._maxTime||0,a=0;a1){var o=r.pop();a.addKeyframe(o.time,e[i]),a.prepare(this._maxTime,a.getAdditiveTrack())}}}},e}();function Mp(){return(new Date).getTime()}var kp,Pp,Dp=function(e){function t(t){var n=e.call(this)||this;return n._running=!1,n._time=0,n._pausedTime=0,n._pauseStart=0,n._paused=!1,t=t||{},n.stage=t.stage||{},n}return ze(t,e),t.prototype.addClip=function(e){e.animation&&this.removeClip(e),this._head?(this._tail.next=e,e.prev=this._tail,e.next=null,this._tail=e):this._head=this._tail=e,e.animation=this},t.prototype.addAnimator=function(e){e.animation=this;var t=e.getClip();t&&this.addClip(t)},t.prototype.removeClip=function(e){if(e.animation){var t=e.prev,n=e.next;t?t.next=n:this._head=n,n?n.prev=t:this._tail=t,e.next=e.prev=e.animation=null}},t.prototype.removeAnimator=function(e){var t=e.getClip();t&&this.removeClip(t),e.animation=null},t.prototype.update=function(e){for(var t=Mp()-this._pausedTime,n=t-this._time,i=this._head;i;){var a=i.next;i.step(t,n)?(i.ondestroy(),this.removeClip(i),i=a):i=a}this._time=t,e||(this.trigger("frame",n),this.stage.update&&this.stage.update())},t.prototype._startLoop=function(){var e=this;this._running=!0,Zs((function t(){e._running&&(Zs(t),!e._paused&&e.update())}))},t.prototype.start=function(){this._running||(this._time=Mp(),this._pausedTime=0,this._startLoop())},t.prototype.stop=function(){this._running=!1},t.prototype.pause=function(){this._paused||(this._pauseStart=Mp(),this._paused=!0)},t.prototype.resume=function(){this._paused&&(this._pausedTime+=Mp()-this._pauseStart,this._paused=!1)},t.prototype.clear=function(){for(var e=this._head;e;){var t=e.next;e.prev=e.next=e.animation=null,e=t}this._head=this._tail=null},t.prototype.isFinished=function(){return null==this._head},t.prototype.animate=function(e,t){t=t||{},this.start();var n=new Ep(e,t.loop);return this.addAnimator(n),n},t}(Uo),Op=rr.domSupported,Ap=(Pp={pointerdown:1,pointerup:1,pointermove:1,pointerout:1},{mouse:kp=["click","dblclick","mousewheel","wheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],touch:["touchstart","touchend","touchmove"],pointer:Br(kp,(function(e){var t=e.replace("mouse","pointer");return Pp.hasOwnProperty(t)?t:e}))}),Fp=["mousemove","mouseup"],Rp=["pointermove","pointerup"],Bp=!1;function Np(e){var t=e.pointerType;return"pen"===t||"touch"===t}function Lp(e){e&&(e.zrByTouch=!0)}function Vp(e,t){for(var n=t,i=!1;n&&9!==n.nodeType&&!(i=n.domBelongToZr||n!==t&&n===e.painterRoot);)n=n.parentNode;return i}var qp=function(e,t){this.stopPropagation=wo,this.stopImmediatePropagation=wo,this.preventDefault=wo,this.type=t.type,this.target=this.currentTarget=e.dom,this.pointerType=t.pointerType,this.clientX=t.clientX,this.clientY=t.clientY},Gp={mousedown:function(e){e=os(this.dom,e),this.__mayPointerCapture=[e.zrX,e.zrY],this.trigger("mousedown",e)},mousemove:function(e){e=os(this.dom,e);var t=this.__mayPointerCapture;!t||e.zrX===t[0]&&e.zrY===t[1]||this.__togglePointerCapture(!0),this.trigger("mousemove",e)},mouseup:function(e){e=os(this.dom,e),this.__togglePointerCapture(!1),this.trigger("mouseup",e)},mouseout:function(e){Vp(this,(e=os(this.dom,e)).toElement||e.relatedTarget)||(this.__pointerCapturing&&(e.zrEventControl="no_globalout"),this.trigger("mouseout",e))},wheel:function(e){Bp=!0,e=os(this.dom,e),this.trigger("mousewheel",e)},mousewheel:function(e){Bp||(e=os(this.dom,e),this.trigger("mousewheel",e))},touchstart:function(e){Lp(e=os(this.dom,e)),this.__lastTouchMoment=new Date,this.handler.processGesture(e,"start"),Gp.mousemove.call(this,e),Gp.mousedown.call(this,e)},touchmove:function(e){Lp(e=os(this.dom,e)),this.handler.processGesture(e,"change"),Gp.mousemove.call(this,e)},touchend:function(e){Lp(e=os(this.dom,e)),this.handler.processGesture(e,"end"),Gp.mouseup.call(this,e),+new Date-+this.__lastTouchMoment<300&&Gp.click.call(this,e)},pointerdown:function(e){Gp.mousedown.call(this,e)},pointermove:function(e){Np(e)||Gp.mousemove.call(this,e)},pointerup:function(e){Gp.mouseup.call(this,e)},pointerout:function(e){Np(e)||Gp.mouseout.call(this,e)}};Rr(["click","dblclick","contextmenu"],(function(e){Gp[e]=function(t){t=os(this.dom,t),this.trigger(e,t)}}));var zp={pointermove:function(e){Np(e)||zp.mousemove.call(this,e)},pointerup:function(e){zp.mouseup.call(this,e)},mousemove:function(e){this.trigger("mousemove",e)},mouseup:function(e){var t=this.__pointerCapturing;this.__togglePointerCapture(!1),this.trigger("mouseup",e),t&&(e.zrEventControl="only_globalout",this.trigger("mouseout",e))}};function Up(e,t){var n=t.domHandlers;rr.pointerEventsSupported?Rr(Ap.pointer,(function(i){Hp(t,i,(function(t){n[i].call(e,t)}))})):(rr.touchEventsSupported&&Rr(Ap.touch,(function(i){Hp(t,i,(function(a){n[i].call(e,a),function(e){e.touching=!0,null!=e.touchTimer&&(clearTimeout(e.touchTimer),e.touchTimer=null),e.touchTimer=setTimeout((function(){e.touching=!1,e.touchTimer=null}),700)}(t)}))})),Rr(Ap.mouse,(function(i){Hp(t,i,(function(a){a=rs(a),t.touching||n[i].call(e,a)}))})))}function jp(e,t){function n(n){Hp(t,n,(function(i){i=rs(i),Vp(e,i.target)||(i=function(e,t){return os(e.dom,new qp(e,t),!0)}(e,i),t.domHandlers[n].call(e,i))}),{capture:!0})}rr.pointerEventsSupported?Rr(Rp,n):rr.touchEventsSupported||Rr(Fp,n)}function Hp(e,t,n,i){e.mounted[t]=n,e.listenerOpts[t]=i,ss(e.domTarget,t,n,i)}function Wp(e){var t,n,i,a,r=e.mounted;for(var o in r)r.hasOwnProperty(o)&&(t=e.domTarget,n=o,i=r[o],a=e.listenerOpts[o],t.removeEventListener(n,i,a));e.mounted={}}var $p=function(e,t){this.mounted={},this.listenerOpts={},this.touching=!1,this.domTarget=e,this.domHandlers=t},Kp=function(e){function t(t,n){var i=e.call(this)||this;return i.__pointerCapturing=!1,i.dom=t,i.painterRoot=n,i._localHandlerScope=new $p(t,Gp),Op&&(i._globalHandlerScope=new $p(document,zp)),Up(i,i._localHandlerScope),i}return ze(t,e),t.prototype.dispose=function(){Wp(this._localHandlerScope),Op&&Wp(this._globalHandlerScope)},t.prototype.setCursor=function(e){this.dom.style&&(this.dom.style.cursor=e||"default")},t.prototype.__togglePointerCapture=function(e){if(this.__mayPointerCapture=null,Op&&+this.__pointerCapturing^+e){this.__pointerCapturing=e;var t=this._globalHandlerScope;e?jp(this,t):Wp(t)}},t}(Uo),Yp=1;rr.hasGlobalWindow&&(Yp=Math.max(window.devicePixelRatio||window.screen&&window.screen.deviceXDPI/window.screen.logicalXDPI||1,1));var Xp=Yp,Zp="#333",Qp="#ccc",Jp=ms,ec=5e-5;function tc(e){return e>ec||e<-5e-5}var nc=[],ic=[],ac=[1,0,0,1,0,0],rc=Math.abs,oc=function(){function e(){}return e.prototype.getLocalTransform=function(t){return e.getLocalTransform(this,t)},e.prototype.setPosition=function(e){this.x=e[0],this.y=e[1]},e.prototype.setScale=function(e){this.scaleX=e[0],this.scaleY=e[1]},e.prototype.setSkew=function(e){this.skewX=e[0],this.skewY=e[1]},e.prototype.setOrigin=function(e){this.originX=e[0],this.originY=e[1]},e.prototype.needLocalTransform=function(){return tc(this.rotation)||tc(this.x)||tc(this.y)||tc(this.scaleX-1)||tc(this.scaleY-1)||tc(this.skewX)||tc(this.skewY)},e.prototype.updateTransform=function(){var e=this.parent&&this.parent.transform,t=this.needLocalTransform(),n=this.transform;t||e?(n=n||[1,0,0,1,0,0],t?this.getLocalTransform(n):Jp(n),e&&(t?gs(n,e,n):hs(n,e)),this.transform=n,this._resolveGlobalScaleRatio(n)):n&&(Jp(n),this.invTransform=null)},e.prototype._resolveGlobalScaleRatio=function(e){var t=this.globalScaleRatio;if(null!=t&&1!==t){this.getGlobalScale(nc);var n=nc[0]<0?-1:1,i=nc[1]<0?-1:1,a=((nc[0]-n)*t+n)/nc[0]||0,r=((nc[1]-i)*t+i)/nc[1]||0;e[0]*=a,e[1]*=a,e[2]*=r,e[3]*=r}this.invTransform=this.invTransform||[1,0,0,1,0,0],xs(this.invTransform,e)},e.prototype.getComputedTransform=function(){for(var e=this,t=[];e;)t.push(e),e=e.parent;for(;e=t.pop();)e.updateTransform();return this.transform},e.prototype.setLocalTransform=function(e){if(e){var t=e[0]*e[0]+e[1]*e[1],n=e[2]*e[2]+e[3]*e[3],i=Math.atan2(e[1],e[0]),a=Math.PI/2+i-Math.atan2(e[3],e[2]);n=Math.sqrt(n)*Math.cos(a),t=Math.sqrt(t),this.skewX=a,this.skewY=0,this.rotation=-i,this.x=+e[4],this.y=+e[5],this.scaleX=t,this.scaleY=n,this.originX=0,this.originY=0}},e.prototype.decomposeTransform=function(){if(this.transform){var e=this.parent,t=this.transform;e&&e.transform&&(e.invTransform=e.invTransform||[1,0,0,1,0,0],gs(ic,e.invTransform,t),t=ic);var n=this.originX,i=this.originY;(n||i)&&(ac[4]=n,ac[5]=i,gs(ic,t,ac),ic[4]-=n,ic[5]-=i,t=ic),this.setLocalTransform(t)}},e.prototype.getGlobalScale=function(e){var t=this.transform;return e=e||[],t?(e[0]=Math.sqrt(t[0]*t[0]+t[1]*t[1]),e[1]=Math.sqrt(t[2]*t[2]+t[3]*t[3]),t[0]<0&&(e[0]=-e[0]),t[3]<0&&(e[1]=-e[1]),e):(e[0]=1,e[1]=1,e)},e.prototype.transformCoordToLocal=function(e,t){var n=[e,t],i=this.invTransform;return i&&Lo(n,n,i),n},e.prototype.transformCoordToGlobal=function(e,t){var n=[e,t],i=this.transform;return i&&Lo(n,n,i),n},e.prototype.getLineScale=function(){var e=this.transform;return e&&rc(e[0]-1)>1e-10&&rc(e[3]-1)>1e-10?Math.sqrt(rc(e[0]*e[3]-e[2]*e[1])):1},e.prototype.copyTransform=function(e){lc(this,e)},e.getLocalTransform=function(e,t){t=t||[];var n=e.originX||0,i=e.originY||0,a=e.scaleX,r=e.scaleY,o=e.anchorX,s=e.anchorY,l=e.rotation||0,p=e.x,c=e.y,d=e.skewX?Math.tan(e.skewX):0,u=e.skewY?Math.tan(-e.skewY):0;if(n||i||o||s){var m=n+o,h=i+s;t[4]=-m*a-d*h*r,t[5]=-h*r-u*m*a}else t[4]=t[5]=0;return t[0]=a,t[3]=r,t[1]=u*a,t[2]=d*r,l&&ys(t,t,l),t[4]+=n+p,t[5]+=i+c,t},e.initDefaultProps=function(){var t=e.prototype;t.scaleX=t.scaleY=t.globalScaleRatio=1,t.x=t.y=t.originX=t.originY=t.skewX=t.skewY=t.rotation=t.anchorX=t.anchorY=0}(),e}(),sc=["x","y","originX","originY","anchorX","anchorY","rotation","scaleX","scaleY","skewX","skewY"];function lc(e,t){for(var n=0;n=0?parseFloat(e)/100*t:parseFloat(e):e}function yc(e,t,n){var i=t.position||"inside",a=null!=t.distance?t.distance:5,r=n.height,o=n.width,s=r/2,l=n.x,p=n.y,c="left",d="top";if(i instanceof Array)l+=fc(i[0],n.width),p+=fc(i[1],n.height),c=null,d=null;else switch(i){case"left":l-=a,p+=s,c="right",d="middle";break;case"right":l+=a+o,p+=s,d="middle";break;case"top":l+=o/2,p-=a,c="center",d="bottom";break;case"bottom":l+=o/2,p+=r+a,c="center";break;case"inside":l+=o/2,p+=s,c="center",d="middle";break;case"insideLeft":l+=a,p+=s,d="middle";break;case"insideRight":l+=o-a,p+=s,c="right",d="middle";break;case"insideTop":l+=o/2,p+=a,c="center";break;case"insideBottom":l+=o/2,p+=r-a,c="center",d="bottom";break;case"insideTopLeft":l+=a,p+=a;break;case"insideTopRight":l+=o-a,p+=a,c="right";break;case"insideBottomLeft":l+=a,p+=r-a,d="bottom";break;case"insideBottomRight":l+=o-a,p+=r-a,c="right",d="bottom"}return(e=e||{}).x=l,e.y=p,e.align=c,e.verticalAlign=d,e}var vc="__zr_normal__",xc=sc.concat(["ignore"]),bc=Nr(sc,(function(e,t){return e[t]=!0,e}),{ignore:!1}),wc={},Sc=new Ps(0,0,0,0),Cc=function(){function e(e){this.id=_r(),this.animators=[],this.currentStates=[],this.states={},this._init(e)}return e.prototype._init=function(e){this.attr(e)},e.prototype.drift=function(e,t,n){switch(this.draggable){case"horizontal":t=0;break;case"vertical":e=0}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=e,i[5]+=t,this.decomposeTransform(),this.markRedraw()},e.prototype.beforeUpdate=function(){},e.prototype.afterUpdate=function(){},e.prototype.update=function(){this.updateTransform(),this.__dirty&&this.updateInnerText()},e.prototype.updateInnerText=function(e){var t=this._textContent;if(t&&(!t.ignore||e)){this.textConfig||(this.textConfig={});var n=this.textConfig,i=n.local,a=t.innerTransformable,r=void 0,o=void 0,s=!1;a.parent=i?this:null;var l=!1;if(a.copyTransform(t),null!=n.position){var p=Sc;n.layoutRect?p.copy(n.layoutRect):p.copy(this.getBoundingRect()),i||p.applyTransform(this.transform),this.calculateTextPosition?this.calculateTextPosition(wc,n,p):yc(wc,n,p),a.x=wc.x,a.y=wc.y,r=wc.align,o=wc.verticalAlign;var c=n.origin;if(c&&null!=n.rotation){var d=void 0,u=void 0;"center"===c?(d=.5*p.width,u=.5*p.height):(d=fc(c[0],p.width),u=fc(c[1],p.height)),l=!0,a.originX=-a.x+d+(i?0:p.x),a.originY=-a.y+u+(i?0:p.y)}}null!=n.rotation&&(a.rotation=n.rotation);var m=n.offset;m&&(a.x+=m[0],a.y+=m[1],l||(a.originX=-m[0],a.originY=-m[1]));var h=null==n.inside?"string"==typeof n.position&&n.position.indexOf("inside")>=0:n.inside,g=this._innerTextDefaultStyle||(this._innerTextDefaultStyle={}),f=void 0,y=void 0,v=void 0;h&&this.canBeInsideText()?(f=n.insideFill,y=n.insideStroke,null!=f&&"auto"!==f||(f=this.getInsideTextFill()),null!=y&&"auto"!==y||(y=this.getInsideTextStroke(f),v=!0)):(f=n.outsideFill,y=n.outsideStroke,null!=f&&"auto"!==f||(f=this.getOutsideFill()),null!=y&&"auto"!==y||(y=this.getOutsideStroke(f),v=!0)),(f=f||"#000")===g.fill&&y===g.stroke&&v===g.autoStroke&&r===g.align&&o===g.verticalAlign||(s=!0,g.fill=f,g.stroke=y,g.autoStroke=v,g.align=r,g.verticalAlign=o,t.setDefaultTextStyle(g)),t.__dirty|=1,s&&t.dirtyStyle(!0)}},e.prototype.canBeInsideText=function(){return!0},e.prototype.getInsideTextFill=function(){return"#fff"},e.prototype.getInsideTextStroke=function(e){return"#000"},e.prototype.getOutsideFill=function(){return this.__zr&&this.__zr.isDarkMode()?Qp:Zp},e.prototype.getOutsideStroke=function(e){var t=this.__zr&&this.__zr.getBackgroundColor(),n="string"==typeof t&&Gl(t);n||(n=[255,255,255,1]);for(var i=n[3],a=this.__zr.isDarkMode(),r=0;r<3;r++)n[r]=n[r]*i+(a?0:255)*(1-i);return n[3]=1,Kl(n,"rgba")},e.prototype.traverse=function(e,t){},e.prototype.attrKV=function(e,t){"textConfig"===e?this.setTextConfig(t):"textContent"===e?this.setTextContent(t):"clipPath"===e?this.setClipPath(t):"extra"===e?(this.extra=this.extra||{},kr(this.extra,t)):this[e]=t},e.prototype.hide=function(){this.ignore=!0,this.markRedraw()},e.prototype.show=function(){this.ignore=!1,this.markRedraw()},e.prototype.attr=function(e,t){if("string"==typeof e)this.attrKV(e,t);else if(Kr(e))for(var n=qr(e),i=0;i0},e.prototype.getState=function(e){return this.states[e]},e.prototype.ensureState=function(e){var t=this.states;return t[e]||(t[e]={}),t[e]},e.prototype.clearStates=function(e){this.useState(vc,!1,e)},e.prototype.useState=function(e,t,n,i){var a=e===vc;if(this.hasState()||!a){var r=this.currentStates,o=this.stateTransition;if(!(Dr(r,e)>=0)||!t&&1!==r.length){var s;if(this.stateProxy&&!a&&(s=this.stateProxy(e)),s||(s=this.states&&this.states[e]),s||a){a||this.saveCurrentToNormalState(s);var l=!!(s&&s.hoverLayer||i);l&&this._toggleHoverLayerFlag(!0),this._applyStateObj(e,s,this._normalState,t,!n&&!this.__inHover&&o&&o.duration>0,o);var p=this._textContent,c=this._textGuide;return p&&p.useState(e,t,n,l),c&&c.useState(e,t,n,l),a?(this.currentStates=[],this._normalState={}):t?this.currentStates.push(e):this.currentStates=[e],this._updateAnimationTargets(),this.markRedraw(),!l&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2),s}Tr("State "+e+" not exists.")}}},e.prototype.useStates=function(e,t,n){if(e.length){var i=[],a=this.currentStates,r=e.length,o=r===a.length;if(o)for(var s=0;s0,m);var h=this._textContent,g=this._textGuide;h&&h.useStates(e,t,d),g&&g.useStates(e,t,d),this._updateAnimationTargets(),this.currentStates=e.slice(),this.markRedraw(),!d&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2)}else this.clearStates()},e.prototype.isSilent=function(){for(var e=this.silent,t=this.parent;!e&&t;){if(t.silent){e=!0;break}t=t.parent}return e},e.prototype._updateAnimationTargets=function(){for(var e=0;e=0){var n=this.currentStates.slice();n.splice(t,1),this.useStates(n)}},e.prototype.replaceState=function(e,t,n){var i=this.currentStates.slice(),a=Dr(i,e),r=Dr(i,t)>=0;a>=0?r?i.splice(a,1):i[a]=t:n&&!r&&i.push(t),this.useStates(i)},e.prototype.toggleState=function(e,t){t?this.useState(e,!0):this.removeState(e)},e.prototype._mergeStates=function(e){for(var t,n={},i=0;i=0&&t.splice(n,1)})),this.animators.push(e),n&&n.animation.addAnimator(e),n&&n.wakeUp()},e.prototype.updateDuringAnimation=function(e){this.markRedraw()},e.prototype.stopAnimation=function(e,t){for(var n=this.animators,i=n.length,a=[],r=0;r0&&n.during&&r[0].during((function(e,t){n.during(t)}));for(var u=0;u0||a.force&&!o.length){var S,C=void 0,_=void 0,T=void 0;if(s){_={},u&&(C={});for(b=0;b=0&&(n.splice(i,0,e),this._doAdd(e))}return this},t.prototype.replace=function(e,t){var n=Dr(this._children,e);return n>=0&&this.replaceAt(t,n),this},t.prototype.replaceAt=function(e,t){var n=this._children,i=n[t];if(e&&e!==this&&e.parent!==this&&e!==i){n[t]=e,i.parent=null;var a=this.__zr;a&&i.removeSelfFromZr(a),this._doAdd(e)}return this},t.prototype._doAdd=function(e){e.parent&&e.parent.remove(e),e.parent=this;var t=this.__zr;t&&t!==e.__zr&&e.addSelfToZr(t),t&&t.refresh()},t.prototype.remove=function(e){var t=this.__zr,n=this._children,i=Dr(n,e);return i<0||(n.splice(i,1),e.parent=null,t&&e.removeSelfFromZr(t),t&&t.refresh()),this},t.prototype.removeAll=function(){for(var e=this._children,t=this.__zr,n=0;n0&&(this._stillFrameAccum++,this._stillFrameAccum>this._sleepAfterStill&&this.animation.stop())},e.prototype.setSleepAfterStill=function(e){this._sleepAfterStill=e},e.prototype.wakeUp=function(){this._disposed||(this.animation.start(),this._stillFrameAccum=0)},e.prototype.refreshHover=function(){this._needsRefreshHover=!0},e.prototype.refreshHoverImmediately=function(){this._disposed||(this._needsRefreshHover=!1,this.painter.refreshHover&&"canvas"===this.painter.getType()&&this.painter.refreshHover())},e.prototype.resize=function(e){this._disposed||(e=e||{},this.painter.resize(e.width,e.height),this.handler.resize())},e.prototype.clearAnimation=function(){this._disposed||this.animation.clear()},e.prototype.getWidth=function(){if(!this._disposed)return this.painter.getWidth()},e.prototype.getHeight=function(){if(!this._disposed)return this.painter.getHeight()},e.prototype.setCursorStyle=function(e){this._disposed||this.handler.setCursorStyle(e)},e.prototype.findHover=function(e,t){if(!this._disposed)return this.handler.findHover(e,t)},e.prototype.on=function(e,t,n){return this._disposed||this.handler.on(e,t,n),this},e.prototype.off=function(e,t){this._disposed||this.handler.off(e,t)},e.prototype.trigger=function(e,t){this._disposed||this.handler.trigger(e,t)},e.prototype.clear=function(){if(!this._disposed){for(var e=this.storage.getRoots(),t=0;t0){if(e<=a)return o;if(e>=r)return s}else{if(e>=a)return o;if(e<=r)return s}else{if(e===a)return o;if(e===r)return s}return(e-a)/l*p+o}function Vc(e,t){switch(e){case"center":case"middle":e="50%";break;case"left":case"top":e="0%";break;case"right":case"bottom":e="100%"}return Hr(e)?(n=e,n.replace(/^\s+|\s+$/g,"")).match(/%$/)?parseFloat(e)/100*t:parseFloat(e):null==e?NaN:+e;var n}function qc(e,t,n){return null==t&&(t=10),t=Math.min(Math.max(0,t),20),e=(+e).toFixed(t),n?e:+e}function Gc(e){return e.sort((function(e,t){return e-t})),e}function zc(e){if(e=+e,isNaN(e))return 0;if(e>1e-14)for(var t=1,n=0;n<15;n++,t*=10)if(Math.round(e*t)/t===e)return n;return Uc(e)}function Uc(e){var t=e.toString().toLowerCase(),n=t.indexOf("e"),i=n>0?+t.slice(n+1):0,a=n>0?n:t.length,r=t.indexOf("."),o=r<0?0:a-1-r;return Math.max(0,o-i)}function jc(e,t){var n=Math.log,i=Math.LN10,a=Math.floor(n(e[1]-e[0])/i),r=Math.round(n(Math.abs(t[1]-t[0]))/i),o=Math.min(Math.max(-a+r,0),20);return isFinite(o)?o:20}function Hc(e,t){var n=Nr(e,(function(e,t){return e+(isNaN(t)?0:t)}),0);if(0===n)return[];for(var i=Math.pow(10,t),a=Br(e,(function(e){return(isNaN(e)?0:e)/n*i*100})),r=100*i,o=Br(a,(function(e){return Math.floor(e)})),s=Nr(o,(function(e,t){return e+t}),0),l=Br(a,(function(e,t){return e-o[t]}));sp&&(p=l[d],c=d);++o[c],l[c]=0,++s}return Br(o,(function(e){return e/i}))}function Wc(e,t){var n=Math.max(zc(e),zc(t)),i=e+t;return n>20?i:qc(i,n)}function $c(e){var t=2*Math.PI;return(e%t+t)%t}function Kc(e){return e>-1e-4&&e=10&&t++,t}function Jc(e,t){var n=Qc(e),i=Math.pow(10,n),a=e/i;return e=(t?a<1.5?1:a<2.5?2:a<4?3:a<7?5:10:a<1?1:a<2?2:a<3?3:a<5?5:10)*i,n>=-20?+e.toFixed(n<0?-n:0):e}function ed(e){e.sort((function(e,t){return s(e,t,0)?-1:1}));for(var t=-1/0,n=1,i=0;i=0||a&&Dr(a,s)<0)){var l=n.getShallow(s,t);null!=l&&(r[e[o][0]]=l)}}return r}}var Ud=zd([["fill","color"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["opacity"],["shadowColor"]]),jd=function(){function e(){}return e.prototype.getAreaStyle=function(e,t){return Ud(this,e,t)},e}(),Hd=new Ml(50);function Wd(e){if("string"==typeof e){var t=Hd.get(e);return t&&t.image}return e}function $d(e,t,n,i,a){if(e){if("string"==typeof e){if(t&&t.__zrImageSrc===e||!n)return t;var r=Hd.get(e),o={hostEl:n,cb:i,cbPayload:a};return r?!Yd(t=r.image)&&r.pending.push(o):((t=dr.loadImage(e,Kd,Kd)).__zrImageSrc=e,Hd.put(e,t.__cachedImgObj={image:t,pending:[o]})),t}return e}return t}function Kd(){var e=this.__cachedImgObj;this.onload=this.onerror=this.__cachedImgObj=null;for(var t=0;t=o;l++)s-=o;var p=cc(n,t);return p>s&&(n="",p=0),s=e-p,a.ellipsis=n,a.ellipsisWidth=p,a.contentWidth=s,a.containerWidth=e,a}function Jd(e,t){var n=t.containerWidth,i=t.font,a=t.contentWidth;if(!n)return"";var r=cc(e,i);if(r<=n)return e;for(var o=0;;o++){if(r<=a||o>=t.maxIterations){e+=t.ellipsis;break}var s=0===o?eu(e,a,t.ascCharWidth,t.cnCharWidth):r>0?Math.floor(e.length*a/r):0;r=cc(e=e.substr(0,s),i)}return""===e&&(e=t.placeholder),e}function eu(e,t,n,i){for(var a=0,r=0,o=e.length;r0&&h+i.accumWidth>i.width&&(r=t.split("\n"),d=!0),i.accumWidth=h}else{var g=su(t,c,i.width,i.breakAll,i.accumWidth);i.accumWidth=g.accumWidth+m,o=g.linesWidths,r=g.lines}}else r=t.split("\n");for(var f=0;f=32&&t<=591||t>=880&&t<=4351||t>=4608&&t<=5119||t>=7680&&t<=8303}(e)||!!ru[e]}function su(e,t,n,i,a){for(var r=[],o=[],s="",l="",p=0,c=0,d=0;dn:a+c+m>n)?c?(s||l)&&(h?(s||(s=l,l="",c=p=0),r.push(s),o.push(c-p),l+=u,s="",c=p+=m):(l&&(s+=l,l="",p=0),r.push(s),o.push(c),s=u,c=m)):h?(r.push(l),o.push(p),l=u,p=m):(r.push(u),o.push(m)):(c+=m,h?(l+=u,p+=m):(l&&(s+=l,l="",p=0),s+=u))}else l&&(s+=l,c+=p),r.push(s),o.push(c),s="",l="",p=0,c=0}return r.length||s||(s=e,l="",p=0),l&&(s+=l),s&&(r.push(s),o.push(c)),1===r.length&&(c+=a),{accumWidth:c,lines:r,linesWidths:o}}var lu="__zr_style_"+Math.round(10*Math.random()),pu={shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,shadowColor:"#000",opacity:1,blend:"source-over"},cu={style:{shadowBlur:!0,shadowOffsetX:!0,shadowOffsetY:!0,shadowColor:!0,opacity:!0}};pu[lu]=!0;var du=["z","z2","invisible"],uu=["invisible"],mu=function(e){function t(t){return e.call(this,t)||this}var n;return ze(t,e),t.prototype._init=function(t){for(var n=qr(t),i=0;i1e-4)return s[0]=e-n,s[1]=t-i,l[0]=e+n,void(l[1]=t+i);if(wu[0]=xu(a)*n+e,wu[1]=vu(a)*i+t,Su[0]=xu(r)*n+e,Su[1]=vu(r)*i+t,p(s,wu,Su),c(l,wu,Su),(a%=bu)<0&&(a+=bu),(r%=bu)<0&&(r+=bu),a>r&&!o?r+=bu:aa&&(Cu[0]=xu(m)*n+e,Cu[1]=vu(m)*i+t,p(s,Cu,s),c(l,Cu,l))}var Du={M:1,L:2,C:3,Q:4,A:5,Z:6,R:7},Ou=[],Au=[],Fu=[],Ru=[],Bu=[],Nu=[],Lu=Math.min,Vu=Math.max,qu=Math.cos,Gu=Math.sin,zu=Math.abs,Uu=Math.PI,ju=2*Uu,Hu="undefined"!=typeof Float32Array,Wu=[];function $u(e){return Math.round(e/Uu*1e8)/1e8%2*Uu}function Ku(e,t){var n=$u(e[0]);n<0&&(n+=ju);var i=n-e[0],a=e[1];a+=i,!t&&a-n>=ju?a=n+ju:t&&n-a>=ju?a=n-ju:!t&&n>a?a=n+(ju-$u(n-a)):t&&n0&&(this._ux=zu(n/Xp/e)||0,this._uy=zu(n/Xp/t)||0)},e.prototype.setDPR=function(e){this.dpr=e},e.prototype.setContext=function(e){this._ctx=e},e.prototype.getContext=function(){return this._ctx},e.prototype.beginPath=function(){return this._ctx&&this._ctx.beginPath(),this.reset(),this},e.prototype.reset=function(){this._saveData&&(this._len=0),this._pathSegLen&&(this._pathSegLen=null,this._pathLen=0),this._version++},e.prototype.moveTo=function(e,t){return this._drawPendingPt(),this.addData(Du.M,e,t),this._ctx&&this._ctx.moveTo(e,t),this._x0=e,this._y0=t,this._xi=e,this._yi=t,this},e.prototype.lineTo=function(e,t){var n=zu(e-this._xi),i=zu(t-this._yi),a=n>this._ux||i>this._uy;if(this.addData(Du.L,e,t),this._ctx&&a&&this._ctx.lineTo(e,t),a)this._xi=e,this._yi=t,this._pendingPtDist=0;else{var r=n*n+i*i;r>this._pendingPtDist&&(this._pendingPtX=e,this._pendingPtY=t,this._pendingPtDist=r)}return this},e.prototype.bezierCurveTo=function(e,t,n,i,a,r){return this._drawPendingPt(),this.addData(Du.C,e,t,n,i,a,r),this._ctx&&this._ctx.bezierCurveTo(e,t,n,i,a,r),this._xi=a,this._yi=r,this},e.prototype.quadraticCurveTo=function(e,t,n,i){return this._drawPendingPt(),this.addData(Du.Q,e,t,n,i),this._ctx&&this._ctx.quadraticCurveTo(e,t,n,i),this._xi=n,this._yi=i,this},e.prototype.arc=function(e,t,n,i,a,r){this._drawPendingPt(),Wu[0]=i,Wu[1]=a,Ku(Wu,r),i=Wu[0];var o=(a=Wu[1])-i;return this.addData(Du.A,e,t,n,n,i,o,0,r?0:1),this._ctx&&this._ctx.arc(e,t,n,i,a,r),this._xi=qu(a)*n+e,this._yi=Gu(a)*n+t,this},e.prototype.arcTo=function(e,t,n,i,a){return this._drawPendingPt(),this._ctx&&this._ctx.arcTo(e,t,n,i,a),this},e.prototype.rect=function(e,t,n,i){return this._drawPendingPt(),this._ctx&&this._ctx.rect(e,t,n,i),this.addData(Du.R,e,t,n,i),this},e.prototype.closePath=function(){this._drawPendingPt(),this.addData(Du.Z);var e=this._ctx,t=this._x0,n=this._y0;return e&&e.closePath(),this._xi=t,this._yi=n,this},e.prototype.fill=function(e){e&&e.fill(),this.toStatic()},e.prototype.stroke=function(e){e&&e.stroke(),this.toStatic()},e.prototype.len=function(){return this._len},e.prototype.setData=function(e){var t=e.length;this.data&&this.data.length===t||!Hu||(this.data=new Float32Array(t));for(var n=0;np.length&&(this._expandData(),p=this.data);for(var c=0;c0&&(this._ctx&&this._ctx.lineTo(this._pendingPtX,this._pendingPtY),this._pendingPtDist=0)},e.prototype._expandData=function(){if(!(this.data instanceof Array)){for(var e=[],t=0;t11&&(this.data=new Float32Array(e)))}},e.prototype.getBoundingRect=function(){Fu[0]=Fu[1]=Bu[0]=Bu[1]=Number.MAX_VALUE,Ru[0]=Ru[1]=Nu[0]=Nu[1]=-Number.MAX_VALUE;var e,t=this.data,n=0,i=0,a=0,r=0;for(e=0;en||zu(f)>i||d===t-1)&&(h=Math.sqrt(k*k+f*f),a=g,r=x);break;case Du.C:var y=e[d++],v=e[d++],x=(g=e[d++],e[d++]),b=e[d++],w=e[d++];h=fl(a,r,y,v,g,x,b,w,10),a=b,r=w;break;case Du.Q:h=Sl(a,r,y=e[d++],v=e[d++],g=e[d++],x=e[d++],10),a=g,r=x;break;case Du.A:var S=e[d++],C=e[d++],_=e[d++],T=e[d++],I=e[d++],E=e[d++],M=E+I;d+=1,m&&(o=qu(I)*_+S,s=Gu(I)*T+C),h=Vu(_,T)*Lu(ju,Math.abs(E)),a=qu(M)*_+S,r=Gu(M)*T+C;break;case Du.R:o=a=e[d++],s=r=e[d++],h=2*e[d++]+2*e[d++];break;case Du.Z:var k=o-a;f=s-r;h=Math.sqrt(k*k+f*f),a=o,r=s}h>=0&&(l[c++]=h,p+=h)}return this._pathLen=p,p},e.prototype.rebuildPath=function(e,t){var n,i,a,r,o,s,l,p,c,d,u=this.data,m=this._ux,h=this._uy,g=this._len,f=t<1,y=0,v=0,x=0;if(!f||(this._pathSegLen||this._calculateLength(),l=this._pathSegLen,p=t*this._pathLen))e:for(var b=0;b0&&(e.lineTo(c,d),x=0),w){case Du.M:n=a=u[b++],i=r=u[b++],e.moveTo(a,r);break;case Du.L:o=u[b++],s=u[b++];var C=zu(o-a),_=zu(s-r);if(C>m||_>h){if(f){if(y+(K=l[v++])>p){var T=(p-y)/K;e.lineTo(a*(1-T)+o*T,r*(1-T)+s*T);break e}y+=K}e.lineTo(o,s),a=o,r=s,x=0}else{var I=C*C+_*_;I>x&&(c=o,d=s,x=I)}break;case Du.C:var E=u[b++],M=u[b++],k=u[b++],P=u[b++],D=u[b++],O=u[b++];if(f){if(y+(K=l[v++])>p){hl(a,E,k,D,T=(p-y)/K,Ou),hl(r,M,P,O,T,Au),e.bezierCurveTo(Ou[1],Au[1],Ou[2],Au[2],Ou[3],Au[3]);break e}y+=K}e.bezierCurveTo(E,M,k,P,D,O),a=D,r=O;break;case Du.Q:E=u[b++],M=u[b++],k=u[b++],P=u[b++];if(f){if(y+(K=l[v++])>p){bl(a,E,k,T=(p-y)/K,Ou),bl(r,M,P,T,Au),e.quadraticCurveTo(Ou[1],Au[1],Ou[2],Au[2]);break e}y+=K}e.quadraticCurveTo(E,M,k,P),a=k,r=P;break;case Du.A:var A=u[b++],F=u[b++],R=u[b++],B=u[b++],N=u[b++],L=u[b++],V=u[b++],q=!u[b++],G=R>B?R:B,z=zu(R-B)>.001,U=N+L,j=!1;if(f)y+(K=l[v++])>p&&(U=N+L*(p-y)/K,j=!0),y+=K;if(z&&e.ellipse?e.ellipse(A,F,R,B,V,N,U,q):e.arc(A,F,G,N,U,q),j)break e;S&&(n=qu(N)*R+A,i=Gu(N)*B+F),a=qu(U)*R+A,r=Gu(U)*B+F;break;case Du.R:n=a=u[b],i=r=u[b+1],o=u[b++],s=u[b++];var H=u[b++],W=u[b++];if(f){if(y+(K=l[v++])>p){var $=p-y;e.moveTo(o,s),e.lineTo(o+Lu($,H),s),($-=H)>0&&e.lineTo(o+H,s+Lu($,W)),($-=W)>0&&e.lineTo(o+Vu(H-$,0),s+W),($-=H)>0&&e.lineTo(o,s+Vu(W-$,0));break e}y+=K}e.rect(o,s,H,W);break;case Du.Z:if(f){var K;if(y+(K=l[v++])>p){T=(p-y)/K;e.lineTo(a*(1-T)+n*T,r*(1-T)+i*T);break e}y+=K}e.closePath(),a=n,r=i}}},e.prototype.clone=function(){var t=new e,n=this.data;return t.data=n.slice?n.slice():Array.prototype.slice.call(n),t._len=this._len,t},e.CMD=Du,e.initDefaultProps=function(){var t=e.prototype;t._saveData=!0,t._ux=0,t._uy=0,t._pendingPtDist=0,t._version=0}(),e}();function Xu(e,t,n,i,a,r,o){if(0===a)return!1;var s=a,l=0;if(o>t+s&&o>i+s||oe+s&&r>n+s||rt+d&&c>i+d&&c>r+d&&c>s+d||ce+d&&p>n+d&&p>a+d&&p>o+d||pt+p&&l>i+p&&l>r+p||le+p&&s>n+p&&s>a+p||sn||c+pa&&(a+=tm);var u=Math.atan2(l,s);return u<0&&(u+=tm),u>=i&&u<=a||u+tm>=i&&u+tm<=a}function im(e,t,n,i,a,r){if(r>t&&r>i||ra?s:0}var am=Yu.CMD,rm=2*Math.PI;var om=[-1,-1,-1],sm=[-1,-1];function lm(e,t,n,i,a,r,o,s,l,p){if(p>t&&p>i&&p>r&&p>s||p1&&(c=void 0,c=sm[0],sm[0]=sm[1],sm[1]=c),h=cl(t,i,r,s,sm[0]),m>1&&(g=cl(t,i,r,s,sm[1]))),2===m?yt&&s>i&&s>r||s=0&&c<=1&&(a[l++]=c);else{var p=o*o-4*r*s;if(ll(p))(c=-o/(2*r))>=0&&c<=1&&(a[l++]=c);else if(p>0){var c,d=el(p),u=(-o-d)/(2*r);(c=(-o+d)/(2*r))>=0&&c<=1&&(a[l++]=c),u>=0&&u<=1&&(a[l++]=u)}}return l}(t,i,r,s,om);if(0===l)return 0;var p=xl(t,i,r);if(p>=0&&p<=1){for(var c=0,d=yl(t,i,r,p),u=0;un||s<-n)return 0;var l=Math.sqrt(n*n-s*s);om[0]=-l,om[1]=l;var p=Math.abs(i-a);if(p<1e-4)return 0;if(p>=rm-1e-4){i=0,a=rm;var c=r?1:-1;return o>=om[0]+e&&o<=om[1]+e?c:0}if(i>a){var d=i;i=a,a=d}i<0&&(i+=rm,a+=rm);for(var u=0,m=0;m<2;m++){var h=om[m];if(h+e>o){var g=Math.atan2(s,h);c=r?1:-1;g<0&&(g=rm+g),(g>=i&&g<=a||g+rm>=i&&g+rm<=a)&&(g>Math.PI/2&&g<1.5*Math.PI&&(c=-c),u+=c)}}return u}function dm(e,t,n,i,a){for(var r,o,s,l,p=e.data,c=e.len(),d=0,u=0,m=0,h=0,g=0,f=0;f1&&(n||(d+=im(u,m,h,g,i,a))),v&&(h=u=p[f],g=m=p[f+1]),y){case am.M:u=h=p[f++],m=g=p[f++];break;case am.L:if(n){if(Xu(u,m,p[f],p[f+1],t,i,a))return!0}else d+=im(u,m,p[f],p[f+1],i,a)||0;u=p[f++],m=p[f++];break;case am.C:if(n){if(Zu(u,m,p[f++],p[f++],p[f++],p[f++],p[f],p[f+1],t,i,a))return!0}else d+=lm(u,m,p[f++],p[f++],p[f++],p[f++],p[f],p[f+1],i,a)||0;u=p[f++],m=p[f++];break;case am.Q:if(n){if(Qu(u,m,p[f++],p[f++],p[f],p[f+1],t,i,a))return!0}else d+=pm(u,m,p[f++],p[f++],p[f],p[f+1],i,a)||0;u=p[f++],m=p[f++];break;case am.A:var x=p[f++],b=p[f++],w=p[f++],S=p[f++],C=p[f++],_=p[f++];f+=1;var T=!!(1-p[f++]);r=Math.cos(C)*w+x,o=Math.sin(C)*S+b,v?(h=r,g=o):d+=im(u,m,r,o,i,a);var I=(i-x)*S/w+x;if(n){if(nm(x,b,S,C,C+_,T,t,I,a))return!0}else d+=cm(x,b,S,C,C+_,T,I,a);u=Math.cos(C+_)*w+x,m=Math.sin(C+_)*S+b;break;case am.R:if(h=u=p[f++],g=m=p[f++],r=h+p[f++],o=g+p[f++],n){if(Xu(h,g,r,g,t,i,a)||Xu(r,g,r,o,t,i,a)||Xu(r,o,h,o,t,i,a)||Xu(h,o,h,g,t,i,a))return!0}else d+=im(r,g,r,o,i,a),d+=im(h,o,h,g,i,a);break;case am.Z:if(n){if(Xu(u,m,h,g,t,i,a))return!0}else d+=im(u,m,h,g,i,a);u=h,m=g}}return n||(s=m,l=g,Math.abs(s-l)<1e-4)||(d+=im(u,m,h,g,i,a)||0),0!==d}var um=Pr({fill:"#000",stroke:null,strokePercent:1,fillOpacity:1,strokeOpacity:1,lineDashOffset:0,lineWidth:1,lineCap:"butt",miterLimit:10,strokeNoScale:!1,strokeFirst:!1},pu),mm={style:Pr({fill:!0,stroke:!0,strokePercent:!0,fillOpacity:!0,strokeOpacity:!0,lineDashOffset:!0,lineWidth:!0,miterLimit:!0},cu.style)},hm=sc.concat(["invisible","culling","z","z2","zlevel","parent"]),gm=function(e){function t(t){return e.call(this,t)||this}var n;return ze(t,e),t.prototype.update=function(){var n=this;e.prototype.update.call(this);var i=this.style;if(i.decal){var a=this._decalEl=this._decalEl||new t;a.buildPath===t.prototype.buildPath&&(a.buildPath=function(e){n.buildPath(e,n.shape)}),a.silent=!0;var r=a.style;for(var o in i)r[o]!==i[o]&&(r[o]=i[o]);r.fill=i.fill?i.decal:null,r.decal=null,r.shadowColor=null,i.strokeFirst&&(r.stroke=null);for(var s=0;s.5?Zp:t>.2?"#eee":Qp}if(e)return Qp}return Zp},t.prototype.getInsideTextStroke=function(e){var t=this.style.fill;if(Hr(t)){var n=this.__zr;if(!(!n||!n.isDarkMode())===Yl(e,0)<.4)return t}},t.prototype.buildPath=function(e,t,n){},t.prototype.pathUpdated=function(){this.__dirty&=-5},t.prototype.getUpdatedPathProxy=function(e){return!this.path&&this.createPathProxy(),this.path.beginPath(),this.buildPath(this.path,this.shape,e),this.path},t.prototype.createPathProxy=function(){this.path=new Yu(!1)},t.prototype.hasStroke=function(){var e=this.style,t=e.stroke;return!(null==t||"none"===t||!(e.lineWidth>0))},t.prototype.hasFill=function(){var e=this.style.fill;return null!=e&&"none"!==e},t.prototype.getBoundingRect=function(){var e=this._rect,t=this.style,n=!e;if(n){var i=!1;this.path||(i=!0,this.createPathProxy());var a=this.path;(i||4&this.__dirty)&&(a.beginPath(),this.buildPath(a,this.shape,!1),this.pathUpdated()),e=a.getBoundingRect()}if(this._rect=e,this.hasStroke()&&this.path&&this.path.len()>0){var r=this._rectStroke||(this._rectStroke=e.clone());if(this.__dirty||n){r.copy(e);var o=t.strokeNoScale?this.getLineScale():1,s=t.lineWidth;if(!this.hasFill()){var l=this.strokeContainThreshold;s=Math.max(s,null==l?4:l)}o>1e-10&&(r.width+=s/o,r.height+=s/o,r.x-=s/o/2,r.y-=s/o/2)}return r}return e},t.prototype.contain=function(e,t){var n=this.transformCoordToLocal(e,t),i=this.getBoundingRect(),a=this.style;if(e=n[0],t=n[1],i.contain(e,t)){var r=this.path;if(this.hasStroke()){var o=a.lineWidth,s=a.strokeNoScale?this.getLineScale():1;if(s>1e-10&&(this.hasFill()||(o=Math.max(o,this.strokeContainThreshold)),function(e,t,n,i){return dm(e,t,!0,n,i)}(r,o/s,e,t)))return!0}if(this.hasFill())return function(e,t,n){return dm(e,0,!1,t,n)}(r,e,t)}return!1},t.prototype.dirtyShape=function(){this.__dirty|=4,this._rect&&(this._rect=null),this._decalEl&&this._decalEl.dirtyShape(),this.markRedraw()},t.prototype.dirty=function(){this.dirtyStyle(),this.dirtyShape()},t.prototype.animateShape=function(e){return this.animate("shape",e)},t.prototype.updateDuringAnimation=function(e){"style"===e?this.dirtyStyle():"shape"===e?this.dirtyShape():this.markRedraw()},t.prototype.attrKV=function(t,n){"shape"===t?this.setShape(n):e.prototype.attrKV.call(this,t,n)},t.prototype.setShape=function(e,t){var n=this.shape;return n||(n=this.shape={}),"string"==typeof e?n[e]=t:kr(n,e),this.dirtyShape(),this},t.prototype.shapeChanged=function(){return!!(4&this.__dirty)},t.prototype.createStyle=function(e){return vo(um,e)},t.prototype._innerSaveToNormal=function(t){e.prototype._innerSaveToNormal.call(this,t);var n=this._normalState;t.shape&&!n.shape&&(n.shape=kr({},this.shape))},t.prototype._applyStateObj=function(t,n,i,a,r,o){e.prototype._applyStateObj.call(this,t,n,i,a,r,o);var s,l=!(n&&a);if(n&&n.shape?r?a?s=n.shape:(s=kr({},i.shape),kr(s,n.shape)):(s=kr({},a?this.shape:i.shape),kr(s,n.shape)):l&&(s=i.shape),s)if(r){this.shape=kr({},this.shape);for(var p={},c=qr(s),d=0;d0},t.prototype.hasFill=function(){var e=this.style.fill;return null!=e&&"none"!==e},t.prototype.createStyle=function(e){return vo(fm,e)},t.prototype.setBoundingRect=function(e){this._rect=e},t.prototype.getBoundingRect=function(){var e=this.style;if(!this._rect){var t=e.text;null!=t?t+="":t="";var n=uc(t,e.font,e.textAlign,e.textBaseline);if(n.x+=e.x||0,n.y+=e.y||0,this.hasStroke()){var i=e.lineWidth;n.x-=i/2,n.y-=i/2,n.width+=i,n.height+=i}this._rect=n}return this._rect},t.initDefaultProps=void(t.prototype.dirtyRectTolerance=10),t}(mu);ym.prototype.type="tspan";var vm=Pr({x:0,y:0},pu),xm={style:Pr({x:!0,y:!0,width:!0,height:!0,sx:!0,sy:!0,sWidth:!0,sHeight:!0},cu.style)};var bm=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.createStyle=function(e){return vo(vm,e)},t.prototype._getSize=function(e){var t=this.style,n=t[e];if(null!=n)return n;var i,a=(i=t.image)&&"string"!=typeof i&&i.width&&i.height?t.image:this.__image;if(!a)return 0;var r="width"===e?"height":"width",o=t[r];return null==o?a[e]:a[e]/a[r]*o},t.prototype.getWidth=function(){return this._getSize("width")},t.prototype.getHeight=function(){return this._getSize("height")},t.prototype.getAnimationStyleProps=function(){return xm},t.prototype.getBoundingRect=function(){var e=this.style;return this._rect||(this._rect=new Ps(e.x||0,e.y||0,this.getWidth(),this.getHeight())),this._rect},t}(mu);bm.prototype.type="image";var wm=Math.round;function Sm(e,t,n){if(t){var i=t.x1,a=t.x2,r=t.y1,o=t.y2;e.x1=i,e.x2=a,e.y1=r,e.y2=o;var s=n&&n.lineWidth;return s?(wm(2*i)===wm(2*a)&&(e.x1=e.x2=_m(i,s,!0)),wm(2*r)===wm(2*o)&&(e.y1=e.y2=_m(r,s,!0)),e):e}}function Cm(e,t,n){if(t){var i=t.x,a=t.y,r=t.width,o=t.height;e.x=i,e.y=a,e.width=r,e.height=o;var s=n&&n.lineWidth;return s?(e.x=_m(i,s,!0),e.y=_m(a,s,!0),e.width=Math.max(_m(i+r,s,!1)-e.x,0===r?0:1),e.height=Math.max(_m(a+o,s,!1)-e.y,0===o?0:1),e):e}}function _m(e,t,n){if(!t)return e;var i=wm(2*e);return(i+wm(t))%2==0?i/2:(i+(n?1:-1))/2}var Tm=function(){this.x=0,this.y=0,this.width=0,this.height=0},Im={},Em=function(e){function t(t){return e.call(this,t)||this}return ze(t,e),t.prototype.getDefaultShape=function(){return new Tm},t.prototype.buildPath=function(e,t){var n,i,a,r;if(this.subPixelOptimize){var o=Cm(Im,t,this.style);n=o.x,i=o.y,a=o.width,r=o.height,o.r=t.r,t=o}else n=t.x,i=t.y,a=t.width,r=t.height;t.r?function(e,t){var n,i,a,r,o,s=t.x,l=t.y,p=t.width,c=t.height,d=t.r;p<0&&(s+=p,p=-p),c<0&&(l+=c,c=-c),"number"==typeof d?n=i=a=r=d:d instanceof Array?1===d.length?n=i=a=r=d[0]:2===d.length?(n=a=d[0],i=r=d[1]):3===d.length?(n=d[0],i=r=d[1],a=d[2]):(n=d[0],i=d[1],a=d[2],r=d[3]):n=i=a=r=0,n+i>p&&(n*=p/(o=n+i),i*=p/o),a+r>p&&(a*=p/(o=a+r),r*=p/o),i+a>c&&(i*=c/(o=i+a),a*=c/o),n+r>c&&(n*=c/(o=n+r),r*=c/o),e.moveTo(s+n,l),e.lineTo(s+p-i,l),0!==i&&e.arc(s+p-i,l+i,i,-Math.PI/2,0),e.lineTo(s+p,l+c-a),0!==a&&e.arc(s+p-a,l+c-a,a,0,Math.PI/2),e.lineTo(s+r,l+c),0!==r&&e.arc(s+r,l+c-r,r,Math.PI/2,Math.PI),e.lineTo(s,l+n),0!==n&&e.arc(s+n,l+n,n,Math.PI,1.5*Math.PI)}(e,t):e.rect(n,i,a,r)},t.prototype.isZeroArea=function(){return!this.shape.width||!this.shape.height},t}(gm);Em.prototype.type="rect";var Mm={fill:"#000"},km={style:Pr({fill:!0,stroke:!0,fillOpacity:!0,strokeOpacity:!0,lineWidth:!0,fontSize:!0,lineHeight:!0,width:!0,height:!0,textShadowColor:!0,textShadowBlur:!0,textShadowOffsetX:!0,textShadowOffsetY:!0,backgroundColor:!0,padding:!0,borderColor:!0,borderWidth:!0,borderRadius:!0},cu.style)},Pm=function(e){function t(t){var n=e.call(this)||this;return n.type="text",n._children=[],n._defaultStyle=Mm,n.attr(t),n}return ze(t,e),t.prototype.childrenRef=function(){return this._children},t.prototype.update=function(){e.prototype.update.call(this),this.styleChanged()&&this._updateSubTexts();for(var t=0;tm&&c){var h=Math.floor(m/l);n=n.slice(0,h)}if(e&&o&&null!=d)for(var g=Qd(d,r,t.ellipsis,{minChar:t.truncateMinChar,placeholder:t.placeholder}),f=0;f0,T=null!=e.width&&("truncate"===e.overflow||"break"===e.overflow||"breakAll"===e.overflow),I=i.calculatedLineHeight,E=0;El&&au(n,e.substring(l,p),t,s),au(n,i[2],t,s,i[1]),l=Xd.lastIndex}lr){w>0?(v.tokens=v.tokens.slice(0,w),f(v,b,x),n.lines=n.lines.slice(0,y+1)):n.lines=n.lines.slice(0,y);break e}var E=S.width,M=null==E||"auto"===E;if("string"==typeof E&&"%"===E.charAt(E.length-1))O.percentWidth=E,c.push(O),O.contentWidth=cc(O.text,T);else{if(M){var k=S.backgroundColor,P=k&&k.image;P&&Yd(P=Wd(P))&&(O.width=Math.max(O.width,P.width*I/P.height))}var D=h&&null!=a?a-b:null;null!=D&&D=0&&"right"===(E=x[I]).align;)this._placeToken(E,e,w,h,T,"right",f),S-=E.width,T-=E.width,I--;for(_+=(n-(_-m)-(g-T)-S)/2;C<=I;)E=x[C],this._placeToken(E,e,w,h,_+E.width/2,"center",f),_+=E.width,C++;h+=w}},t.prototype._placeToken=function(e,t,n,i,a,r,o){var s=t.rich[e.styleName]||{};s.text=e.text;var l=e.verticalAlign,p=i+n/2;"top"===l?p=i+e.height/2:"bottom"===l&&(p=i+n-e.height/2),!e.isLineHolder&&zm(s)&&this._renderBackground(s,t,"right"===r?a-e.width:"center"===r?a-e.width/2:a,p-e.height/2,e.width,e.height);var c=!!s.backgroundColor,d=e.textPadding;d&&(a=qm(a,r,d),p-=e.height/2-d[0]-e.innerHeight/2);var u=this._getOrCreateChild(ym),m=u.createStyle();u.useStyle(m);var h=this._defaultStyle,g=!1,f=0,y=Vm("fill"in s?s.fill:"fill"in t?t.fill:(g=!0,h.fill)),v=Lm("stroke"in s?s.stroke:"stroke"in t?t.stroke:c||o||h.autoStroke&&!g?null:(f=2,h.stroke)),x=s.textShadowBlur>0||t.textShadowBlur>0;m.text=e.text,m.x=a,m.y=p,x&&(m.shadowBlur=s.textShadowBlur||t.textShadowBlur||0,m.shadowColor=s.textShadowColor||t.textShadowColor||"transparent",m.shadowOffsetX=s.textShadowOffsetX||t.textShadowOffsetX||0,m.shadowOffsetY=s.textShadowOffsetY||t.textShadowOffsetY||0),m.textAlign=r,m.textBaseline="middle",m.font=e.font||sr,m.opacity=ao(s.opacity,t.opacity,1),Rm(m,s),v&&(m.lineWidth=ao(s.lineWidth,t.lineWidth,f),m.lineDash=io(s.lineDash,t.lineDash),m.lineDashOffset=t.lineDashOffset||0,m.stroke=v),y&&(m.fill=y);var b=e.contentWidth,w=e.contentHeight;u.setBoundingRect(new Ps(mc(m.x,b,m.textAlign),hc(m.y,w,m.textBaseline),b,w))},t.prototype._renderBackground=function(e,t,n,i,a,r){var o,s,l,p=e.backgroundColor,c=e.borderWidth,d=e.borderColor,u=p&&p.image,m=p&&!u,h=e.borderRadius,g=this;if(m||e.lineHeight||c&&d){(o=this._getOrCreateChild(Em)).useStyle(o.createStyle()),o.style.fill=null;var f=o.shape;f.x=n,f.y=i,f.width=a,f.height=r,f.r=h,o.dirtyShape()}if(m)(l=o.style).fill=p||null,l.fillOpacity=io(e.fillOpacity,1);else if(u){(s=this._getOrCreateChild(bm)).onload=function(){g.dirtyStyle()};var y=s.style;y.image=p.image,y.x=n,y.y=i,y.width=a,y.height=r}c&&d&&((l=o.style).lineWidth=c,l.stroke=d,l.strokeOpacity=io(e.strokeOpacity,1),l.lineDash=e.borderDash,l.lineDashOffset=e.borderDashOffset||0,o.strokeContainThreshold=0,o.hasFill()&&o.hasStroke()&&(l.strokeFirst=!0,l.lineWidth*=2));var v=(o||s).style;v.shadowBlur=e.shadowBlur||0,v.shadowColor=e.shadowColor||"transparent",v.shadowOffsetX=e.shadowOffsetX||0,v.shadowOffsetY=e.shadowOffsetY||0,v.opacity=ao(e.opacity,t.opacity,1)},t.makeFont=function(e){var t="";return Bm(e)&&(t=[e.fontStyle,e.fontWeight,Fm(e.fontSize),e.fontFamily||"sans-serif"].join(" ")),t&&lo(t)||e.textFont||e.font},t}(mu),Dm={left:!0,right:1,center:1},Om={top:1,bottom:1,middle:1},Am=["fontStyle","fontWeight","fontSize","fontFamily"];function Fm(e){return"string"!=typeof e||-1===e.indexOf("px")&&-1===e.indexOf("rem")&&-1===e.indexOf("em")?isNaN(+e)?"12px":e+"px":e}function Rm(e,t){for(var n=0;n=0,r=!1;if(e instanceof gm){var o=$m(e),s=a&&o.selectFill||o.normalFill,l=a&&o.selectStroke||o.normalStroke;if(ih(s)||ih(l)){var p=(i=i||{}).style||{};"inherit"===p.fill?(r=!0,i=kr({},i),(p=kr({},p)).fill=s):!ih(p.fill)&&ih(s)?(r=!0,i=kr({},i),(p=kr({},p)).fill=Zl(s)):!ih(p.stroke)&&ih(l)&&(r||(i=kr({},i),p=kr({},p)),p.stroke=Zl(l)),i.style=p}}if(i&&null==i.z2){r||(i=kr({},i));var c=e.z2EmphasisLift;i.z2=e.z2+(null!=c?c:Zm)}return i}(this,0,t,n);if("blur"===e)return function(e,t,n){var i=Dr(e.currentStates,t)>=0,a=e.style.opacity,r=i?null:function(e,t,n,i){for(var a=e.style,r={},o=0;o0){var r={dataIndex:a,seriesIndex:e.seriesIndex};null!=i&&(r.dataType=i),t.push(r)}}))})),t}function Dh(e,t,n){Nh(e,!0),uh(e,gh),Ah(e,t,n)}function Oh(e,t,n,i){i?function(e){Nh(e,!1)}(e):Dh(e,t,n)}function Ah(e,t,n){var i=Um(e);null!=t?(i.focus=t,i.blurScope=n):i.focus&&(i.focus=null)}var Fh=["emphasis","blur","select"],Rh={itemStyle:"getItemStyle",lineStyle:"getLineStyle",areaStyle:"getAreaStyle"};function Bh(e,t,n,i){n=n||"itemStyle";for(var a=0;a1&&(o*=Wh(h),s*=Wh(h));var g=(a===r?-1:1)*Wh((o*o*(s*s)-o*o*(m*m)-s*s*(u*u))/(o*o*(m*m)+s*s*(u*u)))||0,f=g*o*m/s,y=g*-s*u/o,v=(e+n)/2+Kh(d)*f-$h(d)*y,x=(t+i)/2+$h(d)*f+Kh(d)*y,b=Qh([1,0],[(u-f)/o,(m-y)/s]),w=[(u-f)/o,(m-y)/s],S=[(-1*u-f)/o,(-1*m-y)/s],C=Qh(w,S);if(Zh(w,S)<=-1&&(C=Yh),Zh(w,S)>=1&&(C=0),C<0){var _=Math.round(C/Yh*1e6)/1e6;C=2*Yh+_%2*Yh}c.addData(p,v,x,o,s,b,C,d,r)}var eg=/([mlvhzcqtsa])([^mlvhzcqtsa]*)/gi,tg=/-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;var ng=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.applyTransform=function(e){},t}(gm);function ig(e){return null!=e.setData}function ag(e,t){var n=function(e){var t=new Yu;if(!e)return t;var n,i=0,a=0,r=i,o=a,s=Yu.CMD,l=e.match(eg);if(!l)return t;for(var p=0;pP*P+D*D&&(_=I,T=E),{cx:_,cy:T,x0:-c,y0:-d,x1:_*(a/w-1),y1:T*(a/w-1)}}function Sg(e,t){var n,i=vg(t.r,0),a=vg(t.r0||0,0),r=i>0;if(r||a>0){if(r||(i=a,a=0),a>i){var o=i;i=a,a=o}var s=t.startAngle,l=t.endAngle;if(!isNaN(s)&&!isNaN(l)){var p=t.cx,c=t.cy,d=!!t.clockwise,u=fg(l-s),m=u>dg&&u%dg;if(m>bg&&(u=m),i>bg)if(u>dg-bg)e.moveTo(p+i*mg(s),c+i*ug(s)),e.arc(p,c,i,s,l,!d),a>bg&&(e.moveTo(p+a*mg(l),c+a*ug(l)),e.arc(p,c,a,l,s,d));else{var h=void 0,g=void 0,f=void 0,y=void 0,v=void 0,x=void 0,b=void 0,w=void 0,S=void 0,C=void 0,_=void 0,T=void 0,I=void 0,E=void 0,M=void 0,k=void 0,P=i*mg(s),D=i*ug(s),O=a*mg(l),A=a*ug(l),F=u>bg;if(F){var R=t.cornerRadius;R&&(n=function(e){var t;if(Ur(e)){var n=e.length;if(!n)return e;t=1===n?[e[0],e[0],0,0]:2===n?[e[0],e[0],e[1],e[1]]:3===n?e.concat(e[2]):e}else t=[e,e,e,e];return t}(R),h=n[0],g=n[1],f=n[2],y=n[3]);var B=fg(i-a)/2;if(v=xg(B,f),x=xg(B,y),b=xg(B,h),w=xg(B,g),_=S=vg(v,x),T=C=vg(b,w),(S>bg||C>bg)&&(I=i*mg(l),E=i*ug(l),M=a*mg(s),k=a*ug(s),ubg){var j=xg(f,_),H=xg(y,_),W=wg(M,k,P,D,i,j,d),$=wg(I,E,O,A,i,H,d);e.moveTo(p+W.cx+W.x0,c+W.cy+W.y0),_0&&e.arc(p+W.cx,c+W.cy,j,gg(W.y0,W.x0),gg(W.y1,W.x1),!d),e.arc(p,c,i,gg(W.cy+W.y1,W.cx+W.x1),gg($.cy+$.y1,$.cx+$.x1),!d),H>0&&e.arc(p+$.cx,c+$.cy,H,gg($.y1,$.x1),gg($.y0,$.x0),!d))}else e.moveTo(p+P,c+D),e.arc(p,c,i,s,l,!d);else e.moveTo(p+P,c+D);if(a>bg&&F)if(T>bg){j=xg(h,T),W=wg(O,A,I,E,a,-(H=xg(g,T)),d),$=wg(P,D,M,k,a,-j,d);e.lineTo(p+W.cx+W.x0,c+W.cy+W.y0),T0&&e.arc(p+W.cx,c+W.cy,H,gg(W.y0,W.x0),gg(W.y1,W.x1),!d),e.arc(p,c,a,gg(W.cy+W.y1,W.cx+W.x1),gg($.cy+$.y1,$.cx+$.x1),d),j>0&&e.arc(p+$.cx,c+$.cy,j,gg($.y1,$.x1),gg($.y0,$.x0),!d))}else e.lineTo(p+O,c+A),e.arc(p,c,a,l,s,d);else e.lineTo(p+O,c+A)}else e.moveTo(p,c);e.closePath()}}}var Cg=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0,this.cornerRadius=0},_g=function(e){function t(t){return e.call(this,t)||this}return ze(t,e),t.prototype.getDefaultShape=function(){return new Cg},t.prototype.buildPath=function(e,t){Sg(e,t)},t.prototype.isZeroArea=function(){return this.shape.startAngle===this.shape.endAngle||this.shape.r===this.shape.r0},t}(gm);_g.prototype.type="sector";var Tg=function(){this.cx=0,this.cy=0,this.r=0,this.r0=0},Ig=function(e){function t(t){return e.call(this,t)||this}return ze(t,e),t.prototype.getDefaultShape=function(){return new Tg},t.prototype.buildPath=function(e,t){var n=t.cx,i=t.cy,a=2*Math.PI;e.moveTo(n+t.r,i),e.arc(n,i,t.r,0,a,!1),e.moveTo(n+t.r0,i),e.arc(n,i,t.r0,0,a,!0)},t}(gm);function Eg(e,t,n){var i=t.smooth,a=t.points;if(a&&a.length>=2){if(i){var r=function(e,t,n,i){var a,r,o,s,l=[],p=[],c=[],d=[];if(i){o=[1/0,1/0],s=[-1/0,-1/0];for(var u=0,m=e.length;uWg[1]){if(o=!1,a)return o;var p=Math.abs(Wg[0]-Hg[1]),c=Math.abs(Hg[0]-Wg[1]);Math.min(p,c)>i.len()&&(p0){var d={duration:c.duration,delay:c.delay||0,easing:c.easing,done:r,force:!!r||!!o,setToFinal:!p,scope:e,during:o};l?t.animateFrom(n,d):t.animateTo(n,d)}else t.stopAnimation(),!l&&t.attr(n),o&&o(1),r&&r()}function tf(e,t,n,i,a,r){ef("update",e,t,n,i,a,r)}function nf(e,t,n,i,a,r){ef("enter",e,t,n,i,a,r)}function af(e){if(!e.__zr)return!0;for(var t=0;tMath.abs(r[1])?r[0]>0?"right":"left":r[1]>0?"bottom":"top"}function Ef(e){return!e.isGroup}function Mf(e,t,n){if(e&&t){var i,a=(i={},e.traverse((function(e){Ef(e)&&e.anid&&(i[e.anid]=e)})),i);t.traverse((function(e){if(Ef(e)&&e.anid){var t=a[e.anid];if(t){var i=r(e);e.attr(r(t)),tf(e,i,n,Um(e).dataIndex)}}}))}function r(e){var t={x:e.x,y:e.y,rotation:e.rotation};return function(e){return null!=e.shape}(e)&&(t.shape=kr({},e.shape)),t}}function kf(e,t){return Br(e,(function(e){var n=e[0];n=pf(n,t.x),n=cf(n,t.x+t.width);var i=e[1];return i=pf(i,t.y),[n,i=cf(i,t.y+t.height)]}))}function Pf(e,t){var n=pf(e.x,t.x),i=cf(e.x+e.width,t.x+t.width),a=pf(e.y,t.y),r=cf(e.y+e.height,t.y+t.height);if(i>=n&&r>=a)return{x:n,y:a,width:i-n,height:r-a}}function Df(e,t,n){var i=kr({rectHover:!0},t),a=i.style={strokeNoScale:!0};if(n=n||{x:-1,y:-1,width:2,height:2},e)return 0===e.indexOf("image://")?(a.image=e.slice(8),Pr(a,n),new bm(i)):yf(e.replace("path://",""),i,n,"center")}function Of(e,t,n,i,a){for(var r=0,o=a[a.length-1];r=-1e-6)return!1;var h=e-a,g=t-r,f=Ff(h,g,p,c)/m;if(f<0||f>1)return!1;var y=Ff(h,g,d,u)/m;return!(y<0||y>1)}function Ff(e,t,n,i){return e*i-n*t}function Rf(e){var t=e.itemTooltipOption,n=e.componentModel,i=e.itemName,a=Hr(t)?{formatter:t}:t,r=n.mainType,o=n.componentIndex,s={componentType:r,name:i,$vars:["name"]};s[r+"Index"]=o;var l=e.formatterParamsExtra;l&&Rr(qr(l),(function(e){bo(s,e)||(s[e]=l[e],s.$vars.push(e))}));var p=Um(e.el);p.componentMainType=r,p.componentIndex=o,p.tooltipConfig={name:i,option:Pr({content:i,formatterParams:s},a)}}function Bf(e,t){var n;e.isGroup&&(n=t(e)),n||e.traverse(t)}function Nf(e,t){if(e)if(Ur(e))for(var n=0;n-1?fy:vy;function Sy(e,t){e=e.toUpperCase(),by[e]=new uy(t),xy[e]=t}function Cy(e){return by[e]}Sy(yy,{time:{month:["January","February","March","April","May","June","July","August","September","October","November","December"],monthAbbr:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayOfWeekAbbr:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},legend:{selector:{all:"All",inverse:"Inv"}},toolbox:{brush:{title:{rect:"Box Select",polygon:"Lasso Select",lineX:"Horizontally Select",lineY:"Vertically Select",keep:"Keep Selections",clear:"Clear Selections"}},dataView:{title:"Data View",lang:["Data View","Close","Refresh"]},dataZoom:{title:{zoom:"Zoom",back:"Zoom Reset"}},magicType:{title:{line:"Switch to Line Chart",bar:"Switch to Bar Chart",stack:"Stack",tiled:"Tile"}},restore:{title:"Restore"},saveAsImage:{title:"Save as Image",lang:["Right Click to Save Image"]}},series:{typeNames:{pie:"Pie chart",bar:"Bar chart",line:"Line chart",scatter:"Scatter plot",effectScatter:"Ripple scatter plot",radar:"Radar chart",tree:"Tree",treemap:"Treemap",boxplot:"Boxplot",candlestick:"Candlestick",k:"K line chart",heatmap:"Heat map",map:"Map",parallel:"Parallel coordinate map",lines:"Line graph",graph:"Relationship graph",sankey:"Sankey diagram",funnel:"Funnel chart",gauge:"Gauge",pictorialBar:"Pictorial bar",themeRiver:"Theme River Map",sunburst:"Sunburst",custom:"Custom chart",chart:"Chart"}},aria:{general:{withTitle:'This is a chart about "{title}"',withoutTitle:"This is a chart"},series:{single:{prefix:"",withName:" with type {seriesType} named {seriesName}.",withoutName:" with type {seriesType}."},multiple:{prefix:". It consists of {seriesCount} series count.",withName:" The {seriesId} series is a {seriesType} representing {seriesName}.",withoutName:" The {seriesId} series is a {seriesType}.",separator:{middle:"",end:""}}},data:{allData:"The data is as follows: ",partialData:"The first {displayCnt} items are: ",withName:"the data for {name} is {value}",withoutName:"{value}",separator:{middle:", ",end:". "}}}}),Sy(fy,{time:{month:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthAbbr:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayOfWeekAbbr:["日","一","二","三","四","五","六"]},legend:{selector:{all:"全选",inverse:"反选"}},toolbox:{brush:{title:{rect:"矩形选择",polygon:"圈选",lineX:"横向选择",lineY:"纵向选择",keep:"保持选择",clear:"清除选择"}},dataView:{title:"数据视图",lang:["数据视图","关闭","刷新"]},dataZoom:{title:{zoom:"区域缩放",back:"区域缩放还原"}},magicType:{title:{line:"切换为折线图",bar:"切换为柱状图",stack:"切换为堆叠",tiled:"切换为平铺"}},restore:{title:"还原"},saveAsImage:{title:"保存为图片",lang:["右键另存为图片"]}},series:{typeNames:{pie:"饼图",bar:"柱状图",line:"折线图",scatter:"散点图",effectScatter:"涟漪散点图",radar:"雷达图",tree:"树图",treemap:"矩形树图",boxplot:"箱型图",candlestick:"K线图",k:"K线图",heatmap:"热力图",map:"地图",parallel:"平行坐标图",lines:"线图",graph:"关系图",sankey:"桑基图",funnel:"漏斗图",gauge:"仪表盘图",pictorialBar:"象形柱图",themeRiver:"主题河流图",sunburst:"旭日图",custom:"自定义图表",chart:"图表"}},aria:{general:{withTitle:"这是一个关于“{title}”的图表。",withoutTitle:"这是一个图表,"},series:{single:{prefix:"",withName:"图表类型是{seriesType},表示{seriesName}。",withoutName:"图表类型是{seriesType}。"},multiple:{prefix:"它由{seriesCount}个图表系列组成。",withName:"第{seriesId}个系列是一个表示{seriesName}的{seriesType},",withoutName:"第{seriesId}个系列是一个{seriesType},",separator:{middle:";",end:"。"}}},data:{allData:"其数据是——",partialData:"其中,前{displayCnt}项是——",withName:"{name}的数据是{value}",withoutName:"{value}",separator:{middle:",",end:""}}}});var _y=1e3,Ty=6e4,Iy=36e5,Ey=864e5,My=31536e6,ky={year:"{yyyy}",month:"{MMM}",day:"{d}",hour:"{HH}:{mm}",minute:"{HH}:{mm}",second:"{HH}:{mm}:{ss}",millisecond:"{HH}:{mm}:{ss} {SSS}",none:"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}"},Py="{yyyy}-{MM}-{dd}",Dy={year:"{yyyy}",month:"{yyyy}-{MM}",day:Py,hour:Py+" "+ky.hour,minute:Py+" "+ky.minute,second:Py+" "+ky.second,millisecond:ky.none},Oy=["year","month","day","hour","minute","second","millisecond"],Ay=["year","half-year","quarter","month","week","half-week","day","half-day","quarter-day","hour","minute","second","millisecond"];function Fy(e,t){return"0000".substr(0,t-(e+="").length)+e}function Ry(e){switch(e){case"half-year":case"quarter":return"month";case"week":case"half-week":return"day";case"half-day":case"quarter-day":return"hour";default:return e}}function By(e){return e===Ry(e)}function Ny(e,t,n,i){var a=Xc(e),r=a[qy(n)](),o=a[Gy(n)]()+1,s=Math.floor((o-1)/3)+1,l=a[zy(n)](),p=a["get"+(n?"UTC":"")+"Day"](),c=a[Uy(n)](),d=(c-1)%12+1,u=a[jy(n)](),m=a[Hy(n)](),h=a[Wy(n)](),g=(i instanceof uy?i:Cy(i||wy)||by[vy]).getModel("time"),f=g.get("month"),y=g.get("monthAbbr"),v=g.get("dayOfWeek"),x=g.get("dayOfWeekAbbr");return(t||"").replace(/{yyyy}/g,r+"").replace(/{yy}/g,Fy(r%100+"",2)).replace(/{Q}/g,s+"").replace(/{MMMM}/g,f[o-1]).replace(/{MMM}/g,y[o-1]).replace(/{MM}/g,Fy(o,2)).replace(/{M}/g,o+"").replace(/{dd}/g,Fy(l,2)).replace(/{d}/g,l+"").replace(/{eeee}/g,v[p]).replace(/{ee}/g,x[p]).replace(/{e}/g,p+"").replace(/{HH}/g,Fy(c,2)).replace(/{H}/g,c+"").replace(/{hh}/g,Fy(d+"",2)).replace(/{h}/g,d+"").replace(/{mm}/g,Fy(u,2)).replace(/{m}/g,u+"").replace(/{ss}/g,Fy(m,2)).replace(/{s}/g,m+"").replace(/{SSS}/g,Fy(h,3)).replace(/{S}/g,h+"")}function Ly(e,t){var n=Xc(e),i=n[Gy(t)]()+1,a=n[zy(t)](),r=n[Uy(t)](),o=n[jy(t)](),s=n[Hy(t)](),l=0===n[Wy(t)](),p=l&&0===s,c=p&&0===o,d=c&&0===r,u=d&&1===a;return u&&1===i?"year":u?"month":d?"day":c?"hour":p?"minute":l?"second":"millisecond"}function Vy(e,t,n){var i=$r(e)?Xc(e):e;switch(t=t||Ly(e,n)){case"year":return i[qy(n)]();case"half-year":return i[Gy(n)]()>=6?1:0;case"quarter":return Math.floor((i[Gy(n)]()+1)/4);case"month":return i[Gy(n)]();case"day":return i[zy(n)]();case"half-day":return i[Uy(n)]()/24;case"hour":return i[Uy(n)]();case"minute":return i[jy(n)]();case"second":return i[Hy(n)]();case"millisecond":return i[Wy(n)]()}}function qy(e){return e?"getUTCFullYear":"getFullYear"}function Gy(e){return e?"getUTCMonth":"getMonth"}function zy(e){return e?"getUTCDate":"getDate"}function Uy(e){return e?"getUTCHours":"getHours"}function jy(e){return e?"getUTCMinutes":"getMinutes"}function Hy(e){return e?"getUTCSeconds":"getSeconds"}function Wy(e){return e?"getUTCMilliseconds":"getMilliseconds"}function $y(e){return e?"setUTCFullYear":"setFullYear"}function Ky(e){return e?"setUTCMonth":"setMonth"}function Yy(e){return e?"setUTCDate":"setDate"}function Xy(e){return e?"setUTCHours":"setHours"}function Zy(e){return e?"setUTCMinutes":"setMinutes"}function Qy(e){return e?"setUTCSeconds":"setSeconds"}function Jy(e){return e?"setUTCMilliseconds":"setMilliseconds"}function ev(e){if(!nd(e))return Hr(e)?e:"-";var t=(e+"").split(".");return t[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(t.length>1?"."+t[1]:"")}function tv(e,t){return e=(e||"").toLowerCase().replace(/-(.)/g,(function(e,t){return t.toUpperCase()})),t&&e&&(e=e.charAt(0).toUpperCase()+e.slice(1)),e}var nv=oo;function iv(e,t,n){function i(e){return e&&lo(e)?e:"-"}function a(e){return!(null==e||isNaN(e)||!isFinite(e))}var r="time"===t,o=e instanceof Date;if(r||o){var s=r?Xc(e):e;if(!isNaN(+s))return Ny(s,"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}",n);if(o)return"-"}if("ordinal"===t)return Wr(e)?i(e):$r(e)&&a(e)?e+"":"-";var l=td(e);return a(l)?ev(l):Wr(e)?i(e):"boolean"==typeof e?e+"":"-"}var av=["a","b","c","d","e","f","g"],rv=function(e,t){return"{"+e+(null==t?"":t)+"}"};function ov(e,t,n){Ur(t)||(t=[t]);var i=t.length;if(!i)return"";for(var a=t[0].$vars||[],r=0;r':'':{renderMode:r,content:"{"+(n.markerId||"markerX")+"|} ",style:"subItem"===a?{width:4,height:4,borderRadius:2,backgroundColor:i}:{width:10,height:10,borderRadius:5,backgroundColor:i}}:""}function lv(e,t){return t=t||"transparent",Hr(e)?e:Kr(e)&&e.colorStops&&(e.colorStops[0]||{}).color||t}function pv(e,t){if("_blank"===t||"blank"===t){var n=window.open();n.opener=null,n.location.href=e}else window.open(e,t)}var cv=Rr,dv=["left","right","top","bottom","width","height"],uv=[["width","left","right"],["height","top","bottom"]];function mv(e,t,n,i,a){var r=0,o=0;null==i&&(i=1/0),null==a&&(a=1/0);var s=0;t.eachChild((function(l,p){var c,d,u=l.getBoundingRect(),m=t.childAt(p+1),h=m&&m.getBoundingRect();if("horizontal"===e){var g=u.width+(h?-h.x+u.x:0);(c=r+g)>i||l.newline?(r=0,c=g,o+=s+n,s=u.height):s=Math.max(s,u.height)}else{var f=u.height+(h?-h.y+u.y:0);(d=o+f)>a||l.newline?(r+=s+n,o=0,d=f,s=u.width):s=Math.max(s,u.width)}l.newline||(l.x=r,l.y=o,l.markRedraw(),"horizontal"===e?r=c+n:o=d+n)}))}var hv=mv;zr(mv,"vertical"),zr(mv,"horizontal");function gv(e,t,n){n=nv(n||0);var i=t.width,a=t.height,r=Vc(e.left,i),o=Vc(e.top,a),s=Vc(e.right,i),l=Vc(e.bottom,a),p=Vc(e.width,i),c=Vc(e.height,a),d=n[2]+n[0],u=n[1]+n[3],m=e.aspect;switch(isNaN(p)&&(p=i-s-u-r),isNaN(c)&&(c=a-l-d-o),null!=m&&(isNaN(p)&&isNaN(c)&&(m>i/a?p=.8*i:c=.8*a),isNaN(p)&&(p=m*c),isNaN(c)&&(c=p/m)),isNaN(r)&&(r=i-s-p-u),isNaN(o)&&(o=a-l-c-d),e.left||e.right){case"center":r=i/2-p/2-n[3];break;case"right":r=i-p-u}switch(e.top||e.bottom){case"middle":case"center":o=a/2-c/2-n[0];break;case"bottom":o=a-c-d}r=r||0,o=o||0,isNaN(p)&&(p=i-u-r-(s||0)),isNaN(c)&&(c=a-d-o-(l||0));var h=new Ps(r+n[3],o+n[0],p,c);return h.margin=n,h}function fv(e,t,n,i,a,r){var o,s=!a||!a.hv||a.hv[0],l=!a||!a.hv||a.hv[1],p=a&&a.boundingMode||"all";if((r=r||e).x=e.x,r.y=e.y,!s&&!l)return!1;if("raw"===p)o="group"===e.type?new Ps(0,0,+t.width||0,+t.height||0):e.getBoundingRect();else if(o=e.getBoundingRect(),e.needLocalTransform()){var c=e.getLocalTransform();(o=o.clone()).applyTransform(c)}var d=gv(Pr({width:o.width,height:o.height},t),n,i),u=s?d.x-o.x:0,m=l?d.y-o.y:0;return"raw"===p?(r.x=u,r.y=m):(r.x+=u,r.y+=m),r===e&&e.markRedraw(),!0}function yv(e){var t=e.layoutMode||e.constructor.layoutMode;return Kr(t)?t:t?{type:t}:null}function vv(e,t,n){var i=n&&n.ignoreSize;!Ur(i)&&(i=[i,i]);var a=o(uv[0],0),r=o(uv[1],1);function o(n,a){var r={},o=0,p={},c=0;if(cv(n,(function(t){p[t]=e[t]})),cv(n,(function(e){s(t,e)&&(r[e]=p[e]=t[e]),l(r,e)&&o++,l(p,e)&&c++})),i[a])return l(t,n[1])?p[n[2]]=null:l(t,n[2])&&(p[n[1]]=null),p;if(2!==c&&o){if(o>=2)return r;for(var d=0;d=0;o--)r=Er(r,n[o],!0);t.defaultOption=r}return t.defaultOption},t.prototype.getReferringComponents=function(e,t){var n=e+"Index",i=e+"Id";return kd(this.ecModel,e,{index:this.get(n,!0),id:this.get(i,!0)},t)},t.prototype.getBoxLayoutParams=function(){var e=this;return{left:e.get("left"),top:e.get("top"),right:e.get("right"),bottom:e.get("bottom"),width:e.get("width"),height:e.get("height")}},t.prototype.getZLevelKey=function(){return""},t.prototype.setZLevel=function(e){this.option.zlevel=e},t.protoInitialize=function(){var e=t.prototype;e.type="component",e.id="",e.name="",e.mainType="",e.subType="",e.componentIndex=0}(),t}(uy);Nd(Sv,uy),Gd(Sv),function(e){var t={};e.registerSubTypeDefaulter=function(e,n){var i=Rd(e);t[i.main]=n},e.determineSubType=function(n,i){var a=i.type;if(!a){var r=Rd(n).main;e.hasSubTypes(n)&&t[r]&&(a=t[r](i))}return a}}(Sv),function(e,t){function n(e,t){return e[t]||(e[t]={predecessor:[],successor:[]}),e[t]}e.topologicalTravel=function(e,i,a,r){if(e.length){var o=function(e){var i={},a=[];return Rr(e,(function(r){var o=n(i,r),s=function(e,t){var n=[];return Rr(e,(function(e){Dr(t,e)>=0&&n.push(e)})),n}(o.originalDeps=t(r),e);o.entryCount=s.length,0===o.entryCount&&a.push(r),Rr(s,(function(e){Dr(o.predecessor,e)<0&&o.predecessor.push(e);var t=n(i,e);Dr(t.successor,e)<0&&t.successor.push(r)}))})),{graph:i,noEntryList:a}}(i),s=o.graph,l=o.noEntryList,p={};for(Rr(e,(function(e){p[e]=!0}));l.length;){var c=l.pop(),d=s[c],u=!!p[c];u&&(a.call(r,c,d.originalDeps.slice()),delete p[c]),Rr(d.successor,u?h:m)}Rr(p,(function(){var e="";throw new Error(e)}))}function m(e){s[e].entryCount--,0===s[e].entryCount&&l.push(e)}function h(e){p[e]=!0,m(e)}}}(Sv,(function(e){var t=[];Rr(Sv.getClassesByMainType(e),(function(e){t=t.concat(e.dependencies||e.prototype.dependencies||[])})),t=Br(t,(function(e){return Rd(e).main})),"dataset"!==e&&Dr(t,"dataset")<=0&&t.unshift("dataset");return t}));var Cv="";"undefined"!=typeof navigator&&(Cv=navigator.platform||"");var _v="rgba(0, 0, 0, 0.2)",Tv={darkMode:"auto",colorBy:"series",color:["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],gradientColor:["#f6efa6","#d88273","#bf444c"],aria:{decal:{decals:[{color:_v,dashArrayX:[1,0],dashArrayY:[2,5],symbolSize:1,rotation:Math.PI/6},{color:_v,symbol:"circle",dashArrayX:[[8,8],[0,8,8,0]],dashArrayY:[6,0],symbolSize:.8},{color:_v,dashArrayX:[1,0],dashArrayY:[4,3],rotation:-Math.PI/4},{color:_v,dashArrayX:[[6,6],[0,6,6,0]],dashArrayY:[6,0]},{color:_v,dashArrayX:[[1,0],[1,6]],dashArrayY:[1,0,6,0],rotation:Math.PI/4},{color:_v,symbol:"triangle",dashArrayX:[[9,9],[0,9,9,0]],dashArrayY:[7,2],symbolSize:.75}]}},textStyle:{fontFamily:Cv.match(/^Win/)?"Microsoft YaHei":"sans-serif",fontSize:12,fontStyle:"normal",fontWeight:"normal"},blendMode:null,stateAnimation:{duration:300,easing:"cubicOut"},animation:"auto",animationDuration:1e3,animationDurationUpdate:500,animationEasing:"cubicInOut",animationEasingUpdate:"cubicInOut",animationThreshold:2e3,progressiveThreshold:3e3,progressive:400,hoverLayerThreshold:3e3,useUTC:!1},Iv=fo(["tooltip","label","itemName","itemId","itemGroupId","itemChildGroupId","seriesName"]),Ev="original",Mv="arrayRows",kv="objectRows",Pv="keyedColumns",Dv="typedArray",Ov="unknown",Av="column",Fv="row",Rv=1,Bv=2,Nv=3,Lv=Cd();function Vv(e,t,n){var i={},a=Gv(t);if(!a||!e)return i;var r,o,s=[],l=[],p=t.ecModel,c=Lv(p).datasetMap,d=a.uid+"_"+n.seriesLayoutBy;Rr(e=e.slice(),(function(t,n){var a=Kr(t)?t:e[n]={name:t};"ordinal"===a.type&&null==r&&(r=n,o=h(a)),i[a.name]=[]}));var u=c.get(d)||c.set(d,{categoryWayDim:o,valueWayDim:0});function m(e,t,n){for(var i=0;it)return e[i];return e[n-1]}(i,o):n;if((c=c||n)&&c.length){var d=c[l];return a&&(p[a]=d),s.paletteIdx=(l+1)%c.length,d}}var Jv="\0_ec_inner";var ex=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.init=function(e,t,n,i,a,r){i=i||{},this.option=null,this._theme=new uy(i),this._locale=new uy(a),this._optionManager=r},t.prototype.setOption=function(e,t,n){var i=ix(t);this._optionManager.setOption(e,n,i),this._resetOption(null,i)},t.prototype.resetOption=function(e,t){return this._resetOption(e,ix(t))},t.prototype._resetOption=function(e,t){var n=!1,i=this._optionManager;if(!e||"recreate"===e){var a=i.mountOption("recreate"===e);0,this.option&&"recreate"!==e?(this.restoreData(),this._mergeOption(a,t)):$v(this,a),n=!0}if("timeline"!==e&&"media"!==e||this.restoreData(),!e||"recreate"===e||"timeline"===e){var r=i.getTimelineOption(this);r&&(n=!0,this._mergeOption(r,t))}if(!e||"recreate"===e||"media"===e){var o=i.getMediaOption(this);o.length&&Rr(o,(function(e){n=!0,this._mergeOption(e,t)}),this)}return n},t.prototype.mergeOption=function(e){this._mergeOption(e,null)},t.prototype._mergeOption=function(e,t){var n=this.option,i=this._componentsMap,a=this._componentsCount,r=[],o=fo(),s=t&&t.replaceMergeMainTypeMap;Lv(this).datasetMap=fo(),Rr(e,(function(e,t){null!=e&&(Sv.hasClass(t)?t&&(r.push(t),o.set(t,!0)):n[t]=null==n[t]?Ir(e):Er(n[t],e,!0))})),s&&s.each((function(e,t){Sv.hasClass(t)&&!o.get(t)&&(r.push(t),o.set(t,!0))})),Sv.topologicalTravel(r,Sv.getAllClassMainTypes(),(function(t){var r=function(e,t,n){var i=jv.get(t);if(!i)return n;var a=i(e);return a?n.concat(a):n}(this,t,dd(e[t])),o=i.get(t),l=o?s&&s.get(t)?"replaceMerge":"normalMerge":"replaceAll",p=fd(o,r,l);(function(e,t,n){Rr(e,(function(e){var i=e.newOption;Kr(i)&&(e.keyInfo.mainType=t,e.keyInfo.subType=function(e,t,n,i){return t.type?t.type:n?n.subType:i.determineSubType(e,t)}(t,i,e.existing,n))}))})(p,t,Sv),n[t]=null,i.set(t,null),a.set(t,0);var c,d=[],u=[],m=0;Rr(p,(function(e,n){var i=e.existing,a=e.newOption;if(a){var r="series"===t,o=Sv.getClass(t,e.keyInfo.subType,!r);if(!o)return;if("tooltip"===t){if(c)return void 0;c=!0}if(i&&i.constructor===o)i.name=e.keyInfo.name,i.mergeOption(a,this),i.optionUpdated(a,!1);else{var s=kr({componentIndex:n},e.keyInfo);kr(i=new o(a,this,this,s),s),e.brandNew&&(i.__requireNewView=!0),i.init(a,this,this),i.optionUpdated(null,!0)}}else i&&(i.mergeOption({},this),i.optionUpdated({},!1));i?(d.push(i.option),u.push(i),m++):(d.push(void 0),u.push(void 0))}),this),n[t]=d,i.set(t,u),a.set(t,m),"series"===t&&Hv(this)}),this),this._seriesIndices||Hv(this)},t.prototype.getOption=function(){var e=Ir(this.option);return Rr(e,(function(t,n){if(Sv.hasClass(n)){for(var i=dd(t),a=i.length,r=!1,o=a-1;o>=0;o--)i[o]&&!wd(i[o])?r=!0:(i[o]=null,!r&&a--);i.length=a,e[n]=i}})),delete e[Jv],e},t.prototype.getTheme=function(){return this._theme},t.prototype.getLocaleModel=function(){return this._locale},t.prototype.setUpdatePayload=function(e){this._payload=e},t.prototype.getUpdatePayload=function(){return this._payload},t.prototype.getComponent=function(e,t){var n=this._componentsMap.get(e);if(n){var i=n[t||0];if(i)return i;if(null==t)for(var a=0;a=t:"max"===n?e<=t:e===t})(i[o],e,r)||(a=!1)}})),a}var dx=Rr,ux=Kr,mx=["areaStyle","lineStyle","nodeStyle","linkStyle","chordStyle","label","labelLine"];function hx(e){var t=e&&e.itemStyle;if(t)for(var n=0,i=mx.length;n=0;g--){var f=e[g];if(s||(u=f.data.rawIndexOf(f.stackedByDimension,d)),u>=0){var y=f.data.getByRawIndex(f.stackResultDimension,u);if("all"===l||"positive"===l&&y>0||"negative"===l&&y<0||"samesign"===l&&m>=0&&y>0||"samesign"===l&&m<=0&&y<0){m=Wc(m,y),h=y;break}}}return i[0]=m,i[1]=h,i}))}))}var Ax,Fx,Rx,Bx,Nx,Lx=function(e){this.data=e.data||(e.sourceFormat===Pv?{}:[]),this.sourceFormat=e.sourceFormat||Ov,this.seriesLayoutBy=e.seriesLayoutBy||Av,this.startIndex=e.startIndex||0,this.dimensionsDetectedCount=e.dimensionsDetectedCount,this.metaRawOption=e.metaRawOption;var t=this.dimensionsDefine=e.dimensionsDefine;if(t)for(var n=0;np&&(p=m)}s[0]=l,s[1]=p}},i=function(){return this._data?this._data.length/this._dimSize:0};function a(e){for(var t=0;t=0&&(s=r.interpolatedValue[l])}return null!=s?s+"":""})):void 0},e.prototype.getRawValue=function(e,t){return ib(this.getData(t),e)},e.prototype.formatTooltip=function(e,t,n){},e}();function ob(e){var t,n;return Kr(e)?e.type&&(n=e):t=e,{text:t,frag:n}}function sb(e){return new lb(e)}var lb=function(){function e(e){e=e||{},this._reset=e.reset,this._plan=e.plan,this._count=e.count,this._onDirty=e.onDirty,this._dirty=!0}return e.prototype.perform=function(e){var t,n=this._upstream,i=e&&e.skip;if(this._dirty&&n){var a=this.context;a.data=a.outputData=n.context.outputData}this.__pipeline&&(this.__pipeline.currentTask=this),this._plan&&!i&&(t=this._plan(this.context));var r,o=c(this._modBy),s=this._modDataCount||0,l=c(e&&e.modBy),p=e&&e.modDataCount||0;function c(e){return!(e>=1)&&(e=1),e}o===l&&s===p||(t="reset"),(this._dirty||"reset"===t)&&(this._dirty=!1,r=this._doReset(i)),this._modBy=l,this._modDataCount=p;var d=e&&e.step;if(this._dueEnd=n?n._outputDueEnd:this._count?this._count(this.context):1/0,this._progress){var u=this._dueIndex,m=Math.min(null!=d?this._dueIndex+d:1/0,this._dueEnd);if(!i&&(r||u1&&i>0?s:o}};return r;function o(){return t=e?null:rt},gte:function(e,t){return e>=t}},hb=function(){function e(e,t){if(!$r(t)){var n="";0,sd(n)}this._opFn=mb[e],this._rvalFloat=td(t)}return e.prototype.evaluate=function(e){return $r(e)?this._opFn(e,this._rvalFloat):this._opFn(td(e),this._rvalFloat)},e}(),gb=function(){function e(e,t){var n="desc"===e;this._resultLT=n?1:-1,null==t&&(t=n?"min":"max"),this._incomparable="min"===t?-1/0:1/0}return e.prototype.evaluate=function(e,t){var n=$r(e)?e:td(e),i=$r(t)?t:td(t),a=isNaN(n),r=isNaN(i);if(a&&(n=this._incomparable),r&&(i=this._incomparable),a&&r){var o=Hr(e),s=Hr(t);o&&(n=s?e:0),s&&(i=o?t:0)}return ni?-this._resultLT:0},e}(),fb=function(){function e(e,t){this._rval=t,this._isEQ=e,this._rvalTypeof=typeof t,this._rvalFloat=td(t)}return e.prototype.evaluate=function(e){var t=e===this._rval;if(!t){var n=typeof e;n===this._rvalTypeof||"number"!==n&&"number"!==this._rvalTypeof||(t=td(e)===this._rvalFloat)}return this._isEQ?t:!t},e}();function yb(e,t){return"eq"===e||"ne"===e?new fb("eq"===e,t):bo(mb,e)?new hb(e,t):null}var vb=function(){function e(){}return e.prototype.getRawData=function(){throw new Error("not supported")},e.prototype.getRawDataItem=function(e){throw new Error("not supported")},e.prototype.cloneRawData=function(){},e.prototype.getDimensionInfo=function(e){},e.prototype.cloneAllDimensionInfo=function(){},e.prototype.count=function(){},e.prototype.retrieveValue=function(e,t){},e.prototype.retrieveValueFromItem=function(e,t){},e.prototype.convertValue=function(e,t){return cb(e,t)},e}();function xb(e){var t=e.sourceFormat;if(!Tb(t)){var n="";0,sd(n)}return e.data}function bb(e){var t=e.sourceFormat,n=e.data;if(!Tb(t)){var i="";0,sd(i)}if(t===Mv){for(var a=[],r=0,o=n.length;r65535?Mb:kb}function Fb(e,t,n,i,a){var r=Ob[n||"float"];if(a){var o=e[t],s=o&&o.length;if(s!==i){for(var l=new r(i),p=0;pg[1]&&(g[1]=h)}return this._rawCount=this._count=s,{start:o,end:s}},e.prototype._initDataFromProvider=function(e,t,n){for(var i=this._provider,a=this._chunks,r=this._dimensions,o=r.length,s=this._rawExtent,l=Br(r,(function(e){return e.property})),p=0;pf[1]&&(f[1]=g)}}!i.persistent&&i.clean&&i.clean(),this._rawCount=this._count=t,this._extent=[]},e.prototype.count=function(){return this._count},e.prototype.get=function(e,t){if(!(t>=0&&t=0&&t=this._rawCount||e<0)return-1;if(!this._indices)return e;var t=this._indices,n=t[e];if(null!=n&&ne))return r;a=r-1}}return-1},e.prototype.indicesOfNearest=function(e,t,n){var i=this._chunks[e],a=[];if(!i)return a;null==n&&(n=1/0);for(var r=1/0,o=-1,s=0,l=0,p=this.count();l=0&&o<0)&&(r=d,o=c,s=0),c===o&&(a[s++]=l))}return a.length=s,a},e.prototype.getIndices=function(){var e,t=this._indices;if(t){var n=t.constructor,i=this._count;if(n===Array){e=new n(i);for(var a=0;a=p&&x<=c||isNaN(x))&&(o[s++]=m),m++}u=!0}else if(2===a){h=d[i[0]];var f=d[i[1]],y=e[i[1]][0],v=e[i[1]][1];for(g=0;g=p&&x<=c||isNaN(x))&&(b>=y&&b<=v||isNaN(b))&&(o[s++]=m),m++}u=!0}}if(!u)if(1===a)for(g=0;g=p&&x<=c||isNaN(x))&&(o[s++]=w)}else for(g=0;ge[_][1])&&(S=!1)}S&&(o[s++]=t.getRawIndex(g))}return sf[1]&&(f[1]=g)}}}},e.prototype.lttbDownSample=function(e,t){var n,i,a,r=this.clone([e],!0),o=r._chunks[e],s=this.count(),l=0,p=Math.floor(1/t),c=this.getRawIndex(0),d=new(Ab(this._rawCount))(Math.min(2*(Math.ceil(s/p)+2),s));d[l++]=c;for(var u=1;un&&(n=i,a=T)}_>0&&_p-m&&(s=p-m,o.length=s);for(var h=0;hc[1]&&(c[1]=f),d[u++]=y}return a._count=u,a._indices=d,a._updateGetRawIdx(),a},e.prototype.each=function(e,t){if(this._count)for(var n=e.length,i=this._chunks,a=0,r=this.count();ao&&(o=l)}return i=[r,o],this._extent[e]=i,i},e.prototype.getRawDataItem=function(e){var t=this.getRawIndex(e);if(this._provider.persistent)return this._provider.getItem(t);for(var n=[],i=this._chunks,a=0;a=0?this._indices[e]:-1},e.prototype._updateGetRawIdx=function(){this.getRawIndex=this._indices?this._getRawIdx:this._getRawIdxIdentity},e.internalField=function(){function e(e,t,n,i){return cb(e[i],this._dimensions[i])}Ib={arrayRows:e,objectRows:function(e,t,n,i){return cb(e[t],this._dimensions[i])},keyedColumns:e,original:function(e,t,n,i){var a=e&&(null==e.value?e:e.value);return cb(a instanceof Array?a[i]:a,this._dimensions[i])},typedArray:function(e,t,n,i){return e[i]}}}(),e}(),Bb=function(){function e(e){this._sourceList=[],this._storeList=[],this._upstreamSignList=[],this._versionSignBase=0,this._dirty=!0,this._sourceHost=e}return e.prototype.dirty=function(){this._setLocalSource([],[]),this._storeList=[],this._dirty=!0},e.prototype._setLocalSource=function(e,t){this._sourceList=e,this._upstreamSignList=t,this._versionSignBase++,this._versionSignBase>9e10&&(this._versionSignBase=0)},e.prototype._getVersionSign=function(){return this._sourceHost.uid+"_"+this._versionSignBase},e.prototype.prepareSource=function(){this._isDirty()&&(this._createSource(),this._dirty=!1)},e.prototype._createSource=function(){this._setLocalSource([],[]);var e,t,n=this._sourceHost,i=this._getUpstreamSourceManagers(),a=!!i.length;if(Lb(n)){var r=n,o=void 0,s=void 0,l=void 0;if(a){var p=i[0];p.prepareSource(),o=(l=p.getSource()).data,s=l.sourceFormat,t=[p._getVersionSign()]}else s=Xr(o=r.get("data",!0))?Dv:Ev,t=[];var c=this._getSourceMetaRawOption()||{},d=l&&l.metaRawOption||{},u=io(c.seriesLayoutBy,d.seriesLayoutBy)||null,m=io(c.sourceHeader,d.sourceHeader),h=io(c.dimensions,d.dimensions);e=u!==d.seriesLayoutBy||!!m!=!!d.sourceHeader||h?[qx(o,{seriesLayoutBy:u,sourceHeader:m,dimensions:h},s)]:[]}else{var g=n;if(a){var f=this._applyTransform(i);e=f.sourceList,t=f.upstreamSignList}else{e=[qx(g.get("source",!0),this._getSourceMetaRawOption(),null)],t=[]}}this._setLocalSource(e,t)},e.prototype._applyTransform=function(e){var t,n=this._sourceHost,i=n.get("transform",!0),a=n.get("fromTransformResult",!0);if(null!=a){var r="";1!==e.length&&Vb(r)}var o,s=[],l=[];return Rr(e,(function(e){e.prepareSource();var t=e.getSource(a||0),n="";null==a||t||Vb(n),s.push(t),l.push(e._getVersionSign())})),i?t=function(e,t){var n=dd(e),i=n.length,a="";i||sd(a);for(var r=0,o=i;r1||n>0&&!e.noHeader;return Rr(e.blocks,(function(e){var n=$b(e);n>=t&&(t=n+ +(i&&(!n||Hb(e)&&!e.noHeader)))})),t}return 0}function Kb(e,t,n,i){var a,r=t.noHeader,o=(a=$b(t),{html:zb[a],richText:Ub[a]}),s=[],l=t.blocks||[];so(!l||Ur(l)),l=l||[];var p=e.orderMode;if(t.sortBlocks&&p){l=l.slice();var c={valueAsc:"asc",valueDesc:"desc"};if(bo(c,p)){var d=new gb(c[p],null);l.sort((function(e,t){return d.evaluate(e.sortParam,t.sortParam)}))}else"seriesDesc"===p&&l.reverse()}Rr(l,(function(n,a){var r=t.valueFormatter,l=Wb(n)(r?kr(kr({},e),{valueFormatter:r}):e,n,a>0?o.html:0,i);null!=l&&s.push(l)}));var u="richText"===e.renderMode?s.join(o.richText):Zb(s.join(""),r?n:o.html);if(r)return u;var m=iv(t.header,"ordinal",e.useUTC),h=Gb(i,e.renderMode).nameStyle;return"richText"===e.renderMode?Qb(e,m,h)+o.richText+u:Zb('
'+Jo(m)+"
"+u,n)}function Yb(e,t,n,i){var a=e.renderMode,r=t.noName,o=t.noValue,s=!t.markerType,l=t.name,p=e.useUTC,c=t.valueFormatter||e.valueFormatter||function(e){return Br(e=Ur(e)?e:[e],(function(e,t){return iv(e,Ur(m)?m[t]:m,p)}))};if(!r||!o){var d=s?"":e.markupStyleCreator.makeTooltipMarker(t.markerType,t.markerColor||"#333",a),u=r?"":iv(l,"ordinal",p),m=t.valueType,h=o?[]:c(t.value,t.dataIndex),g=!s||!r,f=!s&&r,y=Gb(i,a),v=y.nameStyle,x=y.valueStyle;return"richText"===a?(s?"":d)+(r?"":Qb(e,u,v))+(o?"":function(e,t,n,i,a){var r=[a],o=i?10:20;return n&&r.push({padding:[0,0,0,o],align:"right"}),e.markupStyleCreator.wrapRichTextStyle(Ur(t)?t.join(" "):t,r)}(e,h,g,f,x)):Zb((s?"":d)+(r?"":function(e,t,n){return''+Jo(e)+""}(u,!s,v))+(o?"":function(e,t,n,i){var a=n?"10px":"20px",r=t?"float:right;margin-left:"+a:"";return e=Ur(e)?e:[e],''+Br(e,(function(e){return Jo(e)})).join("  ")+""}(h,g,f,x)),n)}}function Xb(e,t,n,i,a,r){if(e)return Wb(e)({useUTC:a,renderMode:n,orderMode:i,markupStyleCreator:t,valueFormatter:e.valueFormatter},e,0,r)}function Zb(e,t){return'
'+e+'
'}function Qb(e,t,n){return e.markupStyleCreator.wrapRichTextStyle(t,n)}function Jb(e,t){return lv(e.getData().getItemVisual(t,"style")[e.visualDrawType])}function ew(e,t){var n=e.get("padding");return null!=n?n:"richText"===t?[8,10]:10}var tw=function(){function e(){this.richTextStyles={},this._nextStyleNameId=id()}return e.prototype._generateStyleName=function(){return"__EC_aUTo_"+this._nextStyleNameId++},e.prototype.makeTooltipMarker=function(e,t,n){var i="richText"===n?this._generateStyleName():null,a=sv({color:t,type:e,renderMode:n,markerId:i});return Hr(a)?a:(this.richTextStyles[i]=a.style,a.content)},e.prototype.wrapRichTextStyle=function(e,t){var n={};Ur(t)?Rr(t,(function(e){return kr(n,e)})):kr(n,t);var i=this._generateStyleName();return this.richTextStyles[i]=n,"{"+i+"|"+e+"}"},e}();function nw(e){var t,n,i,a,r=e.series,o=e.dataIndex,s=e.multipleSeries,l=r.getData(),p=l.mapDimensionsAll("defaultedTooltip"),c=p.length,d=r.getRawValue(o),u=Ur(d),m=Jb(r,o);if(c>1||u&&!c){var h=function(e,t,n,i,a){var r=t.getData(),o=Nr(e,(function(e,t,n){var i=r.getDimensionInfo(n);return e||i&&!1!==i.tooltip&&null!=i.displayName}),!1),s=[],l=[],p=[];function c(e,t){var n=r.getDimensionInfo(t);n&&!1!==n.otherDims.tooltip&&(o?p.push(jb("nameValue",{markerType:"subItem",markerColor:a,name:n.displayName,value:e,valueType:n.type})):(s.push(e),l.push(n.type)))}return i.length?Rr(i,(function(e){c(ib(r,n,e),e)})):Rr(e,c),{inlineValues:s,inlineValueTypes:l,blocks:p}}(d,r,o,p,m);t=h.inlineValues,n=h.inlineValueTypes,i=h.blocks,a=h.inlineValues[0]}else if(c){var g=l.getDimensionInfo(p[0]);a=t=ib(l,o,p[0]),n=g.type}else a=t=u?d[0]:d;var f=bd(r),y=f&&r.name||"",v=l.getName(o),x=s?y:v;return jb("section",{header:y,noHeader:s||!f,sortParam:a,blocks:[jb("nameValue",{markerType:"item",markerColor:m,name:x,noName:!lo(x),value:t,valueType:n,dataIndex:o})].concat(i||[])})}var iw=Cd();function aw(e,t){return e.getName(t)||e.getId(t)}var rw="__universalTransitionEnabled",ow=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t._selectedDataIndicesMap={},t}return ze(t,e),t.prototype.init=function(e,t,n){this.seriesIndex=this.componentIndex,this.dataTask=sb({count:lw,reset:pw}),this.dataTask.context={model:this},this.mergeDefaultAndTheme(e,n),(iw(this).sourceManager=new Bb(this)).prepareSource();var i=this.getInitialData(e,n);dw(i,this),this.dataTask.context.data=i,iw(this).dataBeforeProcessed=i,sw(this),this._initSelectedMapFromData(i)},t.prototype.mergeDefaultAndTheme=function(e,t){var n=yv(this),i=n?xv(e):{},a=this.subType;Sv.hasClass(a)&&(a+="Series"),Er(e,t.getTheme().get(this.subType)),Er(e,this.getDefaultOption()),ud(e,"label",["show"]),this.fillDataTextStyle(e.data),n&&vv(e,i,n)},t.prototype.mergeOption=function(e,t){e=Er(this.option,e,!0),this.fillDataTextStyle(e.data);var n=yv(this);n&&vv(this.option,e,n);var i=iw(this).sourceManager;i.dirty(),i.prepareSource();var a=this.getInitialData(e,t);dw(a,this),this.dataTask.dirty(),this.dataTask.context.data=a,iw(this).dataBeforeProcessed=a,sw(this),this._initSelectedMapFromData(a)},t.prototype.fillDataTextStyle=function(e){if(e&&!Xr(e))for(var t=["show"],n=0;nthis.getShallow("animationThreshold")&&(t=!1),!!t},t.prototype.restoreData=function(){this.dataTask.dirty()},t.prototype.getColorFromPalette=function(e,t,n){var i=this.ecModel,a=Xv.prototype.getColorFromPalette.call(this,e,t,n);return a||(a=i.getColorFromPalette(e,t,n)),a},t.prototype.coordDimToDataDim=function(e){return this.getRawData().mapDimensionsAll(e)},t.prototype.getProgressive=function(){return this.get("progressive")},t.prototype.getProgressiveThreshold=function(){return this.get("progressiveThreshold")},t.prototype.select=function(e,t){this._innerSelect(this.getData(t),e)},t.prototype.unselect=function(e,t){var n=this.option.selectedMap;if(n){var i=this.option.selectedMode,a=this.getData(t);if("series"===i||"all"===n)return this.option.selectedMap={},void(this._selectedDataIndicesMap={});for(var r=0;r=0&&n.push(a)}return n},t.prototype.isSelected=function(e,t){var n=this.option.selectedMap;if(!n)return!1;var i=this.getData(t);return("all"===n||n[aw(i,e)])&&!i.getItemModel(e).get(["select","disabled"])},t.prototype.isUniversalTransitionEnabled=function(){if(this[rw])return!0;var e=this.option.universalTransition;return!!e&&(!0===e||e&&e.enabled)},t.prototype._innerSelect=function(e,t){var n,i,a=this.option,r=a.selectedMode,o=t.length;if(r&&o)if("series"===r)a.selectedMap="all";else if("multiple"===r){Kr(a.selectedMap)||(a.selectedMap={});for(var s=a.selectedMap,l=0;l0&&this._innerSelect(e,t)}},t.registerClass=function(e){return Sv.registerClass(e)},t.protoInitialize=function(){var e=t.prototype;e.type="series.__base__",e.seriesIndex=0,e.ignoreStyleOnData=!1,e.hasSymbolVisual=!1,e.defaultSymbol="circle",e.visualStyleAccessPath="itemStyle",e.visualDrawType="fill"}(),t}(Sv);function sw(e){var t=e.name;bd(e)||(e.name=function(e){var t=e.getRawData(),n=t.mapDimensionsAll("seriesName"),i=[];return Rr(n,(function(e){var n=t.getDimensionInfo(e);n.displayName&&i.push(n.displayName)})),i.join(" ")}(e)||t)}function lw(e){return e.model.getRawData().count()}function pw(e){var t=e.model;return t.setData(t.getRawData().cloneShallow()),cw}function cw(e,t){t.outputData&&e.end>t.outputData.count()&&t.model.getRawData().cloneShallow(t.outputData)}function dw(e,t){Rr(yo(e.CHANGABLE_METHODS,e.DOWNSAMPLE_METHODS),(function(n){e.wrapMethod(n,zr(uw,t))}))}function uw(e,t){var n=mw(e);return n&&n.setOutputEnd((t||this).count()),t}function mw(e){var t=(e.ecModel||{}).scheduler,n=t&&t.getPipeline(e.uid);if(n){var i=n.currentTask;if(i){var a=i.agentStubMap;a&&(i=a.get(e.uid))}return i}}Ar(ow,rb),Ar(ow,Xv),Nd(ow,Sv);var hw=function(){function e(){this.group=new Mc,this.uid=hy("viewComponent")}return e.prototype.init=function(e,t){},e.prototype.render=function(e,t,n,i){},e.prototype.dispose=function(e,t){},e.prototype.updateView=function(e,t,n,i){},e.prototype.updateLayout=function(e,t,n,i){},e.prototype.updateVisual=function(e,t,n,i){},e.prototype.toggleBlurSeries=function(e,t,n){},e.prototype.eachRendered=function(e){var t=this.group;t&&t.traverse(e)},e}();function gw(){var e=Cd();return function(t){var n=e(t),i=t.pipelineContext,a=!!n.large,r=!!n.progressiveRender,o=n.large=!(!i||!i.large),s=n.progressiveRender=!(!i||!i.progressiveRender);return!(a===o&&r===s)&&"reset"}}Bd(hw),Gd(hw);var fw=Cd(),yw=gw(),vw=function(){function e(){this.group=new Mc,this.uid=hy("viewChart"),this.renderTask=sb({plan:ww,reset:Sw}),this.renderTask.context={view:this}}return e.prototype.init=function(e,t){},e.prototype.render=function(e,t,n,i){0},e.prototype.highlight=function(e,t,n,i){var a=e.getData(i&&i.dataType);a&&bw(a,i,"emphasis")},e.prototype.downplay=function(e,t,n,i){var a=e.getData(i&&i.dataType);a&&bw(a,i,"normal")},e.prototype.remove=function(e,t){this.group.removeAll()},e.prototype.dispose=function(e,t){},e.prototype.updateView=function(e,t,n,i){this.render(e,t,n,i)},e.prototype.updateLayout=function(e,t,n,i){this.render(e,t,n,i)},e.prototype.updateVisual=function(e,t,n,i){this.render(e,t,n,i)},e.prototype.eachRendered=function(e){Nf(this.group,e)},e.markUpdateMethod=function(e,t){fw(e).updateMethod=t},e.protoInitialize=void(e.prototype.type="chart"),e}();function xw(e,t,n){e&&Lh(e)&&("emphasis"===t?vh:xh)(e,n)}function bw(e,t,n){var i=Sd(e,t),a=t&&null!=t.highlightKey?function(e){var t=Wm[e];return null==t&&Hm<=32&&(t=Wm[e]=Hm++),t}(t.highlightKey):null;null!=i?Rr(dd(i),(function(t){xw(e.getItemGraphicEl(t),n,a)})):e.eachItemGraphicEl((function(e){xw(e,n,a)}))}function ww(e){return yw(e.model)}function Sw(e){var t=e.model,n=e.ecModel,i=e.api,a=e.payload,r=t.pipelineContext.progressiveRender,o=e.view,s=a&&fw(a).updateMethod,l=r?"incrementalPrepareRender":s&&o[s]?s:"render";return"render"!==l&&o[l](t,n,i,a),Cw[l]}Bd(vw),Gd(vw);var Cw={incrementalPrepareRender:{progress:function(e,t){t.view.incrementalRender(e,t.model,t.ecModel,t.api,t.payload)}},render:{forceFirstProgress:!0,progress:function(e,t){t.view.render(t.model,t.ecModel,t.api,t.payload)}}},_w="\0__throttleOriginMethod",Tw="\0__throttleRate",Iw="\0__throttleType";function Ew(e,t,n){var i,a,r,o,s,l=0,p=0,c=null;function d(){p=(new Date).getTime(),c=null,e.apply(r,o||[])}t=t||0;var u=function(){for(var e=[],u=0;u=0?d():c=setTimeout(d,-a),l=i};return u.clear=function(){c&&(clearTimeout(c),c=null)},u.debounceNextCall=function(e){s=e},u}function Mw(e,t,n,i){var a=e[t];if(a){var r=a[_w]||a,o=a[Iw];if(a[Tw]!==n||o!==i){if(null==n||!i)return e[t]=r;(a=e[t]=Ew(r,n,"debounce"===i))[_w]=r,a[Iw]=i,a[Tw]=n}return a}}function kw(e,t){var n=e[t];n&&n[_w]&&(n.clear&&n.clear(),e[t]=n[_w])}var Pw=Cd(),Dw={itemStyle:zd(py,!0),lineStyle:zd(oy,!0)},Ow={lineStyle:"stroke",itemStyle:"fill"};function Aw(e,t){var n=e.visualStyleMapper||Dw[t];return n||(console.warn("Unknown style type '"+t+"'."),Dw.itemStyle)}function Fw(e,t){var n=e.visualDrawType||Ow[t];return n||(console.warn("Unknown style type '"+t+"'."),"fill")}var Rw={createOnAllSeries:!0,performRawSeries:!0,reset:function(e,t){var n=e.getData(),i=e.visualStyleAccessPath||"itemStyle",a=e.getModel(i),r=Aw(e,i)(a),o=a.getShallow("decal");o&&(n.setVisual("decal",o),o.dirty=!0);var s=Fw(e,i),l=r[s],p=jr(l)?l:null,c="auto"===r.fill||"auto"===r.stroke;if(!r[s]||p||c){var d=e.getColorFromPalette(e.name,null,t.getSeriesCount());r[s]||(r[s]=d,n.setVisual("colorFromPalette",!0)),r.fill="auto"===r.fill||jr(r.fill)?d:r.fill,r.stroke="auto"===r.stroke||jr(r.stroke)?d:r.stroke}if(n.setVisual("style",r),n.setVisual("drawType",s),!t.isSeriesFiltered(e)&&p)return n.setVisual("colorFromPalette",!1),{dataEach:function(t,n){var i=e.getDataParams(n),a=kr({},r);a[s]=p(i),t.setItemVisual(n,"style",a)}}}},Bw=new uy,Nw={createOnAllSeries:!0,performRawSeries:!0,reset:function(e,t){if(!e.ignoreStyleOnData&&!t.isSeriesFiltered(e)){var n=e.getData(),i=e.visualStyleAccessPath||"itemStyle",a=Aw(e,i),r=n.getVisual("drawType");return{dataEach:n.hasItemOption?function(e,t){var n=e.getRawDataItem(t);if(n&&n[i]){Bw.option=n[i];var o=a(Bw);kr(e.ensureUniqueItemVisual(t,"style"),o),Bw.option.decal&&(e.setItemVisual(t,"decal",Bw.option.decal),Bw.option.decal.dirty=!0),r in o&&e.setItemVisual(t,"colorFromPalette",!1)}}:null}}}},Lw={performRawSeries:!0,overallReset:function(e){var t=fo();e.eachSeries((function(e){var n=e.getColorBy();if(!e.isColorBySeries()){var i=e.type+"-"+n,a=t.get(i);a||(a={},t.set(i,a)),Pw(e).scope=a}})),e.eachSeries((function(t){if(!t.isColorBySeries()&&!e.isSeriesFiltered(t)){var n=t.getRawData(),i={},a=t.getData(),r=Pw(t).scope,o=t.visualStyleAccessPath||"itemStyle",s=Fw(t,o);a.each((function(e){var t=a.getRawIndex(e);i[t]=e})),n.each((function(e){var o=i[e];if(a.getItemVisual(o,"colorFromPalette")){var l=a.ensureUniqueItemVisual(o,"style"),p=n.getName(e)||e+"",c=n.count();l[s]=t.getColorFromPalette(p,r,c)}}))}}))}},Vw=Math.PI;var qw=function(){function e(e,t,n,i){this._stageTaskMap=fo(),this.ecInstance=e,this.api=t,n=this._dataProcessorHandlers=n.slice(),i=this._visualHandlers=i.slice(),this._allHandlers=n.concat(i)}return e.prototype.restoreData=function(e,t){e.restoreData(t),this._stageTaskMap.each((function(e){var t=e.overallTask;t&&t.dirty()}))},e.prototype.getPerformArgs=function(e,t){if(e.__pipeline){var n=this._pipelineMap.get(e.__pipeline.id),i=n.context,a=!t&&n.progressiveEnabled&&(!i||i.progressiveRender)&&e.__idxInPipeline>n.blockIndex?n.step:null,r=i&&i.modDataCount;return{step:a,modBy:null!=r?Math.ceil(r/a):null,modDataCount:r}}},e.prototype.getPipeline=function(e){return this._pipelineMap.get(e)},e.prototype.updateStreamModes=function(e,t){var n=this._pipelineMap.get(e.uid),i=e.getData().count(),a=n.progressiveEnabled&&t.incrementalPrepareRender&&i>=n.threshold,r=e.get("large")&&i>=e.get("largeThreshold"),o="mod"===e.get("progressiveChunkMode")?i:null;e.pipelineContext=n.context={progressiveRender:a,modDataCount:o,large:r}},e.prototype.restorePipelines=function(e){var t=this,n=t._pipelineMap=fo();e.eachSeries((function(e){var i=e.getProgressive(),a=e.uid;n.set(a,{id:a,head:null,tail:null,threshold:e.getProgressiveThreshold(),progressiveEnabled:i&&!(e.preventIncremental&&e.preventIncremental()),blockIndex:-1,step:Math.round(i||700),count:0}),t._pipe(e,e.dataTask)}))},e.prototype.prepareStageTasks=function(){var e=this._stageTaskMap,t=this.api.getModel(),n=this.api;Rr(this._allHandlers,(function(i){var a=e.get(i.uid)||e.set(i.uid,{}),r="";so(!(i.reset&&i.overallReset),r),i.reset&&this._createSeriesStageTask(i,a,t,n),i.overallReset&&this._createOverallStageTask(i,a,t,n)}),this)},e.prototype.prepareView=function(e,t,n,i){var a=e.renderTask,r=a.context;r.model=t,r.ecModel=n,r.api=i,a.__block=!e.incrementalPrepareRender,this._pipe(t,a)},e.prototype.performDataProcessorTasks=function(e,t){this._performStageTasks(this._dataProcessorHandlers,e,t,{block:!0})},e.prototype.performVisualTasks=function(e,t,n){this._performStageTasks(this._visualHandlers,e,t,n)},e.prototype._performStageTasks=function(e,t,n,i){i=i||{};var a=!1,r=this;function o(e,t){return e.setDirty&&(!e.dirtyMap||e.dirtyMap.get(t.__pipeline.id))}Rr(e,(function(e,s){if(!i.visualType||i.visualType===e.visualType){var l=r._stageTaskMap.get(e.uid),p=l.seriesTaskMap,c=l.overallTask;if(c){var d,u=c.agentStubMap;u.each((function(e){o(i,e)&&(e.dirty(),d=!0)})),d&&c.dirty(),r.updatePayload(c,n);var m=r.getPerformArgs(c,i.block);u.each((function(e){e.perform(m)})),c.perform(m)&&(a=!0)}else p&&p.each((function(s,l){o(i,s)&&s.dirty();var p=r.getPerformArgs(s,i.block);p.skip=!e.performRawSeries&&t.isSeriesFiltered(s.context.model),r.updatePayload(s,n),s.perform(p)&&(a=!0)}))}})),this.unfinished=a||this.unfinished},e.prototype.performSeriesTasks=function(e){var t;e.eachSeries((function(e){t=e.dataTask.perform()||t})),this.unfinished=t||this.unfinished},e.prototype.plan=function(){this._pipelineMap.each((function(e){var t=e.tail;do{if(t.__block){e.blockIndex=t.__idxInPipeline;break}t=t.getUpstream()}while(t)}))},e.prototype.updatePayload=function(e,t){"remain"!==t&&(e.context.payload=t)},e.prototype._createSeriesStageTask=function(e,t,n,i){var a=this,r=t.seriesTaskMap,o=t.seriesTaskMap=fo(),s=e.seriesType,l=e.getTargetSeries;function p(t){var s=t.uid,l=o.set(s,r&&r.get(s)||sb({plan:Hw,reset:Ww,count:Yw}));l.context={model:t,ecModel:n,api:i,useClearVisual:e.isVisual&&!e.isLayout,plan:e.plan,reset:e.reset,scheduler:a},a._pipe(t,l)}e.createOnAllSeries?n.eachRawSeries(p):s?n.eachRawSeriesByType(s,p):l&&l(n,i).each(p)},e.prototype._createOverallStageTask=function(e,t,n,i){var a=this,r=t.overallTask=t.overallTask||sb({reset:Gw});r.context={ecModel:n,api:i,overallReset:e.overallReset,scheduler:a};var o=r.agentStubMap,s=r.agentStubMap=fo(),l=e.seriesType,p=e.getTargetSeries,c=!0,d=!1,u="";function m(e){var t=e.uid,n=s.set(t,o&&o.get(t)||(d=!0,sb({reset:zw,onDirty:jw})));n.context={model:e,overallProgress:c},n.agent=r,n.__block=c,a._pipe(e,n)}so(!e.createOnAllSeries,u),l?n.eachRawSeriesByType(l,m):p?p(n,i).each(m):(c=!1,Rr(n.getSeries(),m)),d&&r.dirty()},e.prototype._pipe=function(e,t){var n=e.uid,i=this._pipelineMap.get(n);!i.head&&(i.head=t),i.tail&&i.tail.pipe(t),i.tail=t,t.__idxInPipeline=i.count++,t.__pipeline=i},e.wrapStageHandler=function(e,t){return jr(e)&&(e={overallReset:e,seriesType:Xw(e)}),e.uid=hy("stageHandler"),t&&(e.visualType=t),e},e}();function Gw(e){e.overallReset(e.ecModel,e.api,e.payload)}function zw(e){return e.overallProgress&&Uw}function Uw(){this.agent.dirty(),this.getDownstream().dirty()}function jw(){this.agent&&this.agent.dirty()}function Hw(e){return e.plan?e.plan(e.model,e.ecModel,e.api,e.payload):null}function Ww(e){e.useClearVisual&&e.data.clearAllVisual();var t=e.resetDefines=dd(e.reset(e.model,e.ecModel,e.api,e.payload));return t.length>1?Br(t,(function(e,t){return Kw(t)})):$w}var $w=Kw(0);function Kw(e){return function(t,n){var i=n.data,a=n.resetDefines[e];if(a&&a.dataEach)for(var r=t.start;r0&&c===a.length-p.length){var d=a.slice(0,c);"data"!==d&&(t.mainType=d,t[p.toLowerCase()]=e,s=!0)}}o.hasOwnProperty(a)&&(n[a]=e,s=!0),s||(i[a]=e)}))}return{cptQuery:t,dataQuery:n,otherQuery:i}},e.prototype.filter=function(e,t){var n=this.eventInfo;if(!n)return!0;var i=n.targetEl,a=n.packedEvent,r=n.model,o=n.view;if(!r||!o)return!0;var s=t.cptQuery,l=t.dataQuery;return p(s,r,"mainType")&&p(s,r,"subType")&&p(s,r,"index","componentIndex")&&p(s,r,"name")&&p(s,r,"id")&&p(l,a,"name")&&p(l,a,"dataIndex")&&p(l,a,"dataType")&&(!o.filterForExposedEvent||o.filterForExposedEvent(e,t.otherQuery,i,a));function p(e,t,n,i){return null==e[n]||t[i||n]===e[n]}},e.prototype.afterTrigger=function(){this.eventInfo=null},e}(),pS=["symbol","symbolSize","symbolRotate","symbolOffset"],cS=pS.concat(["symbolKeepAspect"]),dS={createOnAllSeries:!0,performRawSeries:!0,reset:function(e,t){var n=e.getData();if(e.legendIcon&&n.setVisual("legendIcon",e.legendIcon),e.hasSymbolVisual){for(var i={},a={},r=!1,o=0;o=0&&AS(l)?l:.5,e.createRadialGradient(o,s,0,o,s,l)}(e,t,n):function(e,t,n){var i=null==t.x?0:t.x,a=null==t.x2?1:t.x2,r=null==t.y?0:t.y,o=null==t.y2?0:t.y2;return t.global||(i=i*n.width+n.x,a=a*n.width+n.x,r=r*n.height+n.y,o=o*n.height+n.y),i=AS(i)?i:0,a=AS(a)?a:1,r=AS(r)?r:0,o=AS(o)?o:0,e.createLinearGradient(i,r,a,o)}(e,t,n),a=t.colorStops,r=0;r0&&(t=i.lineDash,n=i.lineWidth,t&&"solid"!==t&&n>0?"dashed"===t?[4*n,2*n]:"dotted"===t?[n]:$r(t)?[t]:Ur(t)?t:null:null),r=i.lineDashOffset;if(a){var o=i.strokeNoScale&&e.getLineScale?e.getLineScale():1;o&&1!==o&&(a=Br(a,(function(e){return e/o})),r/=o)}return[a,r]}var LS=new Yu(!0);function VS(e){var t=e.stroke;return!(null==t||"none"===t||!(e.lineWidth>0))}function qS(e){return"string"==typeof e&&"none"!==e}function GS(e){var t=e.fill;return null!=t&&"none"!==t}function zS(e,t){if(null!=t.fillOpacity&&1!==t.fillOpacity){var n=e.globalAlpha;e.globalAlpha=t.fillOpacity*t.opacity,e.fill(),e.globalAlpha=n}else e.fill()}function US(e,t){if(null!=t.strokeOpacity&&1!==t.strokeOpacity){var n=e.globalAlpha;e.globalAlpha=t.strokeOpacity*t.opacity,e.stroke(),e.globalAlpha=n}else e.stroke()}function jS(e,t,n){var i=$d(t.image,t.__image,n);if(Yd(i)){var a=e.createPattern(i,t.repeat||"repeat");if("function"==typeof DOMMatrix&&a&&a.setTransform){var r=new DOMMatrix;r.translateSelf(t.x||0,t.y||0),r.rotateSelf(0,0,(t.rotation||0)*So),r.scaleSelf(t.scaleX||1,t.scaleY||1),a.setTransform(r)}return a}}var HS=["shadowBlur","shadowOffsetX","shadowOffsetY"],WS=[["lineCap","butt"],["lineJoin","miter"],["miterLimit",10]];function $S(e,t,n,i,a){var r=!1;if(!i&&t===(n=n||{}))return!1;if(i||t.opacity!==n.opacity){XS(e,a),r=!0;var o=Math.max(Math.min(t.opacity,1),0);e.globalAlpha=isNaN(o)?pu.opacity:o}(i||t.blend!==n.blend)&&(r||(XS(e,a),r=!0),e.globalCompositeOperation=t.blend||pu.blend);for(var s=0;s0&&e.unfinished);e.unfinished||this._zr.flush()}}},t.prototype.getDom=function(){return this._dom},t.prototype.getId=function(){return this.id},t.prototype.getZr=function(){return this._zr},t.prototype.isSSR=function(){return this._ssr},t.prototype.setOption=function(e,t,n){if(!this[mC])if(this._disposed)jC(this.id);else{var i,a,r;if(Kr(t)&&(n=t.lazyUpdate,i=t.silent,a=t.replaceMerge,r=t.transition,t=t.notMerge),this[mC]=!0,!this._model||t){var o=new px(this._api),s=this._theme,l=this._model=new ex;l.scheduler=this._scheduler,l.ssr=this._ssr,l.init(null,null,null,s,this._locale,o)}this._model.setOption(e,{replaceMerge:a},KC);var p={seriesTransition:r,optionChanged:!0};if(n)this[hC]={silent:i,updateParams:p},this[mC]=!1,this.getZr().wakeUp();else{try{wC(this),_C.update.call(this,null,p)}catch(e){throw this[hC]=null,this[mC]=!1,e}this._ssr||this._zr.flush(),this[hC]=null,this[mC]=!1,MC.call(this,i),kC.call(this,i)}}},t.prototype.setTheme=function(){od()},t.prototype.getModel=function(){return this._model},t.prototype.getOption=function(){return this._model&&this._model.getOption()},t.prototype.getWidth=function(){return this._zr.getWidth()},t.prototype.getHeight=function(){return this._zr.getHeight()},t.prototype.getDevicePixelRatio=function(){return this._zr.painter.dpr||rr.hasGlobalWindow&&window.devicePixelRatio||1},t.prototype.getRenderedCanvas=function(e){return this.renderToCanvas(e)},t.prototype.renderToCanvas=function(e){e=e||{};var t=this._zr.painter;return t.getRenderedCanvas({backgroundColor:e.backgroundColor||this._model.get("backgroundColor"),pixelRatio:e.pixelRatio||this.getDevicePixelRatio()})},t.prototype.renderToSVGString=function(e){e=e||{};var t=this._zr.painter;return t.renderToString({useViewBox:e.useViewBox})},t.prototype.getSvgDataURL=function(){if(rr.svgSupported){var e=this._zr;return Rr(e.storage.getDisplayList(),(function(e){e.stopAnimation(null,!0)})),e.painter.toDataURL()}},t.prototype.getDataURL=function(e){if(!this._disposed){var t=(e=e||{}).excludeComponents,n=this._model,i=[],a=this;Rr(t,(function(e){n.eachComponent({mainType:e},(function(e){var t=a._componentsMap[e.__viewId];t.group.ignore||(i.push(t),t.group.ignore=!0)}))}));var r="svg"===this._zr.painter.getType()?this.getSvgDataURL():this.renderToCanvas(e).toDataURL("image/"+(e&&e.type||"png"));return Rr(i,(function(e){e.group.ignore=!1})),r}jC(this.id)},t.prototype.getConnectedDataURL=function(e){if(!this._disposed){var t="svg"===e.type,n=this.group,i=Math.min,a=Math.max,r=1/0;if(JC[n]){var o=r,s=r,l=-1/0,p=-1/0,c=[],d=e&&e.pixelRatio||this.getDevicePixelRatio();Rr(QC,(function(r,d){if(r.group===n){var u=t?r.getZr().painter.getSvgDom().innerHTML:r.renderToCanvas(Ir(e)),m=r.getDom().getBoundingClientRect();o=i(m.left,o),s=i(m.top,s),l=a(m.right,l),p=a(m.bottom,p),c.push({dom:u,left:m.left,top:m.top})}}));var u=(l*=d)-(o*=d),m=(p*=d)-(s*=d),h=dr.createCanvas(),g=Ac(h,{renderer:t?"svg":"canvas"});if(g.resize({width:u,height:m}),t){var f="";return Rr(c,(function(e){var t=e.left-o,n=e.top-s;f+=''+e.dom+""})),g.painter.getSvgRoot().innerHTML=f,e.connectedBackgroundColor&&g.painter.setBackgroundColor(e.connectedBackgroundColor),g.refreshImmediately(),g.painter.toDataURL()}return e.connectedBackgroundColor&&g.add(new Em({shape:{x:0,y:0,width:u,height:m},style:{fill:e.connectedBackgroundColor}})),Rr(c,(function(e){var t=new bm({style:{x:e.left*d-o,y:e.top*d-s,image:e.dom}});g.add(t)})),g.refreshImmediately(),h.toDataURL("image/"+(e&&e.type||"png"))}return this.getDataURL(e)}jC(this.id)},t.prototype.convertToPixel=function(e,t){return TC(this,"convertToPixel",e,t)},t.prototype.convertFromPixel=function(e,t){return TC(this,"convertFromPixel",e,t)},t.prototype.containPixel=function(e,t){var n;if(!this._disposed)return Rr(Td(this._model,e),(function(e,i){i.indexOf("Models")>=0&&Rr(e,(function(e){var a=e.coordinateSystem;if(a&&a.containPoint)n=n||!!a.containPoint(t);else if("seriesModels"===i){var r=this._chartsMap[e.__viewId];r&&r.containPoint&&(n=n||r.containPoint(t,e))}else 0}),this)}),this),!!n;jC(this.id)},t.prototype.getVisual=function(e,t){var n=Td(this._model,e,{defaultMainType:"series"}),i=n.seriesModel;var a=i.getData(),r=n.hasOwnProperty("dataIndexInside")?n.dataIndexInside:n.hasOwnProperty("dataIndex")?a.indexOfRawIndex(n.dataIndex):null;return null!=r?mS(a,r,t):hS(a,t)},t.prototype.getViewOfComponentModel=function(e){return this._componentsMap[e.__viewId]},t.prototype.getViewOfSeriesModel=function(e){return this._chartsMap[e.__viewId]},t.prototype._initEvents=function(){var e,t,n,i=this;Rr(UC,(function(e){var t=function(t){var n,a=i.getModel(),r=t.target,o="globalout"===e;if(o?n={}:r&&vS(r,(function(e){var t=Um(e);if(t&&null!=t.dataIndex){var i=t.dataModel||a.getSeriesByIndex(t.seriesIndex);return n=i&&i.getDataParams(t.dataIndex,t.dataType,r)||{},!0}if(t.eventData)return n=kr({},t.eventData),!0}),!0),n){var s=n.componentType,l=n.componentIndex;"markLine"!==s&&"markPoint"!==s&&"markArea"!==s||(s="series",l=n.seriesIndex);var p=s&&null!=l&&a.getComponent(s,l),c=p&&i["series"===p.mainType?"_chartsMap":"_componentsMap"][p.__viewId];0,n.event=t,n.type=e,i._$eventProcessor.eventInfo={targetEl:r,packedEvent:n,model:p,view:c},i.trigger(e,n)}};t.zrEventfulCallAtLast=!0,i._zr.on(e,t,i)})),Rr(WC,(function(e,t){i._messageCenter.on(t,(function(e){this.trigger(t,e)}),i)})),Rr(["selectchanged"],(function(e){i._messageCenter.on(e,(function(t){this.trigger(e,t)}),i)})),e=this._messageCenter,t=this,n=this._api,e.on("selectchanged",(function(e){var i=n.getModel();e.isFromClick?(yS("map","selectchanged",t,i,e),yS("pie","selectchanged",t,i,e)):"select"===e.fromAction?(yS("map","selected",t,i,e),yS("pie","selected",t,i,e)):"unselect"===e.fromAction&&(yS("map","unselected",t,i,e),yS("pie","unselected",t,i,e))}))},t.prototype.isDisposed=function(){return this._disposed},t.prototype.clear=function(){this._disposed?jC(this.id):this.setOption({series:[]},!0)},t.prototype.dispose=function(){if(this._disposed)jC(this.id);else{this._disposed=!0,this.getDom()&&Pd(this.getDom(),t_,"");var e=this,t=e._api,n=e._model;Rr(e._componentsViews,(function(e){e.dispose(n,t)})),Rr(e._chartsViews,(function(e){e.dispose(n,t)})),e._zr.dispose(),e._dom=e._model=e._chartsMap=e._componentsMap=e._chartsViews=e._componentsViews=e._scheduler=e._api=e._zr=e._throttledZrFlush=e._theme=e._coordSysMgr=e._messageCenter=null,delete QC[e.id]}},t.prototype.resize=function(e){if(!this[mC])if(this._disposed)jC(this.id);else{this._zr.resize(e);var t=this._model;if(this._loadingFX&&this._loadingFX.resize(),t){var n=t.resetOption("media"),i=e&&e.silent;this[hC]&&(null==i&&(i=this[hC].silent),n=!0,this[hC]=null),this[mC]=!0;try{n&&wC(this),_C.update.call(this,{type:"resize",animation:kr({duration:0},e&&e.animation)})}catch(e){throw this[mC]=!1,e}this[mC]=!1,MC.call(this,i),kC.call(this,i)}}},t.prototype.showLoading=function(e,t){if(this._disposed)jC(this.id);else if(Kr(e)&&(t=e,e=""),e=e||"default",this.hideLoading(),ZC[e]){var n=ZC[e](this._api,t),i=this._zr;this._loadingFX=n,i.add(n)}},t.prototype.hideLoading=function(){this._disposed?jC(this.id):(this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null)},t.prototype.makeActionFromEvent=function(e){var t=kr({},e);return t.type=WC[e.type],t},t.prototype.dispatchAction=function(e,t){if(this._disposed)jC(this.id);else if(Kr(t)||(t={silent:!!t}),HC[e.type]&&this._model)if(this[mC])this._pendingActions.push(e);else{var n=t.silent;EC.call(this,e,n);var i=t.flush;i?this._zr.flush():!1!==i&&rr.browser.weChat&&this._throttledZrFlush(),MC.call(this,n),kC.call(this,n)}},t.prototype.updateLabelLayout=function(){sC.trigger("series:layoutlabels",this._model,this._api,{updatedSeries:[]})},t.prototype.appendData=function(e){if(this._disposed)jC(this.id);else{var t=e.seriesIndex,n=this.getModel().getSeriesByIndex(t);0,n.appendData(e),this._scheduler.unfinished=!0,this.getZr().wakeUp()}},t.internalField=function(){function e(e){e.clearColorPalette(),e.eachSeries((function(e){e.clearColorPalette()}))}function t(e){for(var t=[],n=e.currentStates,i=0;i0?{duration:r,delay:i.get("delay"),easing:i.get("easing")}:null;n.eachRendered((function(e){if(e.states&&e.states.emphasis){if(af(e))return;if(e instanceof gm&&function(e){var t=$m(e);t.normalFill=e.style.fill,t.normalStroke=e.style.stroke;var n=e.states.select||{};t.selectFill=n.style&&n.style.fill||null,t.selectStroke=n.style&&n.style.stroke||null}(e),e.__dirty){var n=e.prevStates;n&&e.useStates(n)}if(a){e.stateTransition=o;var i=e.getTextContent(),r=e.getTextGuideLine();i&&(i.stateTransition=o),r&&(r.stateTransition=o)}e.__dirty&&t(e)}}))}wC=function(e){var t=e._scheduler;t.restorePipelines(e._model),t.prepareStageTasks(),SC(e,!0),SC(e,!1),t.plan()},SC=function(e,t){for(var n=e._model,i=e._scheduler,a=t?e._componentsViews:e._chartsViews,r=t?e._componentsMap:e._chartsMap,o=e._zr,s=e._api,l=0;lt.get("hoverLayerThreshold")&&!rr.node&&!rr.worker&&t.eachSeries((function(t){if(!t.preventUsingHoverLayer){var n=e._chartsMap[t.__viewId];n.__alive&&n.eachRendered((function(e){e.states.emphasis&&(e.states.emphasis.hoverLayer=!0)}))}}))}(e,t),sC.trigger("series:afterupdate",t,i,s)},NC=function(e){e[gC]=!0,e.getZr().wakeUp()},LC=function(e){e[gC]&&(e.getZr().storage.traverse((function(e){af(e)||t(e)})),e[gC]=!1)},RC=function(e){return new(function(t){function n(){return null!==t&&t.apply(this,arguments)||this}return ze(n,t),n.prototype.getCoordinateSystems=function(){return e._coordSysMgr.getCoordinateSystems()},n.prototype.getComponentByElement=function(t){for(;t;){var n=t.__ecComponentInfo;if(null!=n)return e._model.getComponent(n.mainType,n.index);t=t.parent}},n.prototype.enterEmphasis=function(t,n){vh(t,n),NC(e)},n.prototype.leaveEmphasis=function(t,n){xh(t,n),NC(e)},n.prototype.enterBlur=function(t){bh(t),NC(e)},n.prototype.leaveBlur=function(t){wh(t),NC(e)},n.prototype.enterSelect=function(t){Sh(t),NC(e)},n.prototype.leaveSelect=function(t){Ch(t),NC(e)},n.prototype.getModel=function(){return e.getModel()},n.prototype.getViewOfComponentModel=function(t){return e.getViewOfComponentModel(t)},n.prototype.getViewOfSeriesModel=function(t){return e.getViewOfSeriesModel(t)},n}(rx))(e)},BC=function(e){function t(e,t){for(var n=0;n=0)){p_.push(n);var r=qw.wrapStageHandler(n,a);r.__prio=t,r.__raw=n,e.push(r)}}function d_(e,t){ZC[e]=t}var u_=function(e){var t=(e=Ir(e)).type,n="";t||sd(n);var i=t.split(":");2!==i.length&&sd(n);var a=!1;"echarts"===i[0]&&(t=i[1],a=!0),e.__isBuiltIn=a,Cb.set(t,e)};function m_(e){return null==e?0:e.length||1}function h_(e){return e}l_(cC,Rw),l_(dC,Nw),l_(dC,Lw),l_(cC,dS),l_(dC,uS),l_(7e3,(function(e,t){e.eachRawSeries((function(n){if(!e.isSeriesFiltered(n)){var i=n.getData();i.hasItemVisual()&&i.each((function(e){var n=i.getItemVisual(e,"decal");n&&(i.ensureUniqueItemVisual(e,"style").decal=iC(n,t))}));var a=i.getVisual("decal");if(a)i.getVisual("style").decal=iC(a,t)}}))})),a_(Dx),r_(900,(function(e){var t=fo();e.eachSeries((function(e){var n=e.get("stack");if(n){var i=t.get(n)||t.set(n,[]),a=e.getData(),r={stackResultDimension:a.getCalculationInfo("stackResultDimension"),stackedOverDimension:a.getCalculationInfo("stackedOverDimension"),stackedDimension:a.getCalculationInfo("stackedDimension"),stackedByDimension:a.getCalculationInfo("stackedByDimension"),isStackedByIndex:a.getCalculationInfo("isStackedByIndex"),data:a,seriesModel:e};if(!r.stackedDimension||!r.isStackedByIndex&&!r.stackedByDimension)return;i.length&&a.setCalculationInfo("stackedOnSeries",i[i.length-1].seriesModel),i.push(r)}})),t.each(Ox)})),d_("default",(function(e,t){Pr(t=t||{},{text:"loading",textColor:"#000",fontSize:12,fontWeight:"normal",fontStyle:"normal",fontFamily:"sans-serif",maskColor:"rgba(255, 255, 255, 0.8)",showSpinner:!0,color:"#5470c6",spinnerRadius:10,lineWidth:5,zlevel:0});var n=new Mc,i=new Em({style:{fill:t.maskColor},zlevel:t.zlevel,z:1e4});n.add(i);var a,r=new Pm({style:{text:t.text,fill:t.textColor,fontSize:t.fontSize,fontWeight:t.fontWeight,fontStyle:t.fontStyle,fontFamily:t.fontFamily},zlevel:t.zlevel,z:10001}),o=new Em({style:{fill:"none"},textContent:r,textConfig:{position:"right",distance:10},zlevel:t.zlevel,z:10001});return n.add(o),t.showSpinner&&((a=new qg({shape:{startAngle:-Vw/2,endAngle:-Vw/2+.1,r:t.spinnerRadius},style:{stroke:t.color,lineCap:"round",lineWidth:t.lineWidth},zlevel:t.zlevel,z:10001})).animateShape(!0).when(1e3,{endAngle:3*Vw/2}).start("circularInOut"),a.animateShape(!0).when(1e3,{startAngle:3*Vw/2}).delay(300).start("circularInOut"),n.add(a)),n.resize=function(){var n=r.getBoundingRect().width,s=t.showSpinner?t.spinnerRadius:0,l=(e.getWidth()-2*s-(t.showSpinner&&n?10:0)-n)/2-(t.showSpinner&&n?0:5+n/2)+(t.showSpinner?0:n/2)+(n?0:s),p=e.getHeight()/2;t.showSpinner&&a.setShape({cx:l,cy:p}),o.setShape({x:l-s,y:p-s,width:2*s,height:2*s}),i.setShape({x:0,y:0,width:e.getWidth(),height:e.getHeight()})},n.resize(),n})),s_({type:Qm,event:Qm,update:Qm},wo),s_({type:Jm,event:Jm,update:Jm},wo),s_({type:eh,event:eh,update:eh},wo),s_({type:th,event:th,update:th},wo),s_({type:nh,event:nh,update:nh},wo),i_("light",nS),i_("dark",sS);var g_=function(){function e(e,t,n,i,a,r){this._old=e,this._new=t,this._oldKeyGetter=n||h_,this._newKeyGetter=i||h_,this.context=a,this._diffModeMultiple="multiple"===r}return e.prototype.add=function(e){return this._add=e,this},e.prototype.update=function(e){return this._update=e,this},e.prototype.updateManyToOne=function(e){return this._updateManyToOne=e,this},e.prototype.updateOneToMany=function(e){return this._updateOneToMany=e,this},e.prototype.updateManyToMany=function(e){return this._updateManyToMany=e,this},e.prototype.remove=function(e){return this._remove=e,this},e.prototype.execute=function(){this[this._diffModeMultiple?"_executeMultiple":"_executeOneToOne"]()},e.prototype._executeOneToOne=function(){var e=this._old,t=this._new,n={},i=new Array(e.length),a=new Array(t.length);this._initIndexMap(e,null,i,"_oldKeyGetter"),this._initIndexMap(t,n,a,"_newKeyGetter");for(var r=0;r1){var p=s.shift();1===s.length&&(n[o]=s[0]),this._update&&this._update(p,r)}else 1===l?(n[o]=null,this._update&&this._update(s,r)):this._remove&&this._remove(r)}this._performRestAdd(a,n)},e.prototype._executeMultiple=function(){var e=this._old,t=this._new,n={},i={},a=[],r=[];this._initIndexMap(e,n,a,"_oldKeyGetter"),this._initIndexMap(t,i,r,"_newKeyGetter");for(var o=0;o1&&1===d)this._updateManyToOne&&this._updateManyToOne(p,l),i[s]=null;else if(1===c&&d>1)this._updateOneToMany&&this._updateOneToMany(p,l),i[s]=null;else if(1===c&&1===d)this._update&&this._update(p,l),i[s]=null;else if(c>1&&d>1)this._updateManyToMany&&this._updateManyToMany(p,l),i[s]=null;else if(c>1)for(var u=0;u1)for(var o=0;o30}var E_,M_,k_,P_,D_,O_,A_,F_=Kr,R_=Br,B_="undefined"==typeof Int32Array?Array:Int32Array,N_=["hasItemOption","_nameList","_idList","_invertedIndicesMap","_dimSummary","userOutput","_rawData","_dimValueGetter","_nameDimIdx","_idDimIdx","_nameRepeatCount"],L_=["_approximateExtent"],V_=function(){function e(e,t){var n;this.type="list",this._dimOmitted=!1,this._nameList=[],this._idList=[],this._visual={},this._layout={},this._itemVisuals=[],this._itemLayouts=[],this._graphicEls=[],this._approximateExtent={},this._calculationInfo={},this.hasItemOption=!1,this.TRANSFERABLE_METHODS=["cloneShallow","downSample","lttbDownSample","map"],this.CHANGABLE_METHODS=["filterSelf","selectRange"],this.DOWNSAMPLE_METHODS=["downSample","lttbDownSample"];var i=!1;C_(e)?(n=e.dimensions,this._dimOmitted=e.isDimensionOmitted(),this._schema=e):(i=!0,n=e),n=n||["x","y"];for(var a={},r=[],o={},s=!1,l={},p=0;p=t)){var n=this._store.getProvider();this._updateOrdinalMeta();var i=this._nameList,a=this._idList;if(n.getSource().sourceFormat===Ev&&!n.pure)for(var r=[],o=e;o0},e.prototype.ensureUniqueItemVisual=function(e,t){var n=this._itemVisuals,i=n[e];i||(i=n[e]={});var a=i[t];return null==a&&(Ur(a=this.getVisual(t))?a=a.slice():F_(a)&&(a=kr({},a)),i[t]=a),a},e.prototype.setItemVisual=function(e,t,n){var i=this._itemVisuals[e]||{};this._itemVisuals[e]=i,F_(t)?kr(i,t):i[t]=n},e.prototype.clearAllVisual=function(){this._visual={},this._itemVisuals=[]},e.prototype.setLayout=function(e,t){F_(e)?kr(this._layout,e):this._layout[e]=t},e.prototype.getLayout=function(e){return this._layout[e]},e.prototype.getItemLayout=function(e){return this._itemLayouts[e]},e.prototype.setItemLayout=function(e,t,n){this._itemLayouts[e]=n?kr(this._itemLayouts[e]||{},t):t},e.prototype.clearItemLayouts=function(){this._itemLayouts.length=0},e.prototype.setItemGraphicEl=function(e,t){var n=this.hostModel&&this.hostModel.seriesIndex;jm(n,this.dataType,e,t),this._graphicEls[e]=t},e.prototype.getItemGraphicEl=function(e){return this._graphicEls[e]},e.prototype.eachItemGraphicEl=function(e,t){Rr(this._graphicEls,(function(n,i){n&&e&&e.call(t,n,i)}))},e.prototype.cloneShallow=function(t){return t||(t=new e(this._schema?this._schema:R_(this.dimensions,this._getDimInfo,this),this.hostModel)),D_(t,this),t._store=this._store,t},e.prototype.wrapMethod=function(e,t){var n=this[e];jr(n)&&(this.__wrappedMethods=this.__wrappedMethods||[],this.__wrappedMethods.push(e),this[e]=function(){var e=n.apply(this,arguments);return t.apply(this,[e].concat(ro(arguments)))})},e.internalField=(E_=function(e){var t=e._invertedIndicesMap;Rr(t,(function(n,i){var a=e._dimInfos[i],r=a.ordinalMeta,o=a.stack,s=e._store;if(r||o){if(n=t[i]=o?new Array(s.count()):new B_(r.categories.length),r)for(var l=0;l1&&(s+="__ec__"+p),i[t]=s}})),e}();function q_(e,t){Vx(e)||(e=Gx(e));var n=(t=t||{}).coordDimensions||[],i=t.dimensionsDefine||e.dimensionsDefine||[],a=fo(),r=[],o=function(e,t,n,i){var a=Math.max(e.dimensionsDetectedCount||1,t.length,n.length,i||0);return Rr(t,(function(e){var t;Kr(e)&&(t=e.dimsDef)&&(a=Math.max(a,t.length))})),a}(e,n,i,t.dimensionsCount),s=t.canOmitUnusedDimensions&&I_(o),l=i===e.dimensionsDefine,p=l?T_(e):__(i),c=t.encodeDefine;!c&&t.encodeDefaulter&&(c=t.encodeDefaulter(e,o));for(var d=fo(c),u=new Pb(o),m=0;m0&&(i.name=a+(r-1)),r++,t.set(a,r)}}(r),new S_({source:e,dimensions:r,fullDimensionCount:o,dimensionOmitted:s})}function G_(e,t,n){if(n||t.hasKey(e)){for(var i=0;t.hasKey(e+i);)i++;e+=i}return t.set(e,!0),e}var z_=function(e){this.coordSysDims=[],this.axisMap=fo(),this.categoryAxisMap=fo(),this.coordSysName=e};var U_={cartesian2d:function(e,t,n,i){var a=e.getReferringComponents("xAxis",Ed).models[0],r=e.getReferringComponents("yAxis",Ed).models[0];t.coordSysDims=["x","y"],n.set("x",a),n.set("y",r),j_(a)&&(i.set("x",a),t.firstCategoryDimIndex=0),j_(r)&&(i.set("y",r),null==t.firstCategoryDimIndex&&(t.firstCategoryDimIndex=1))},singleAxis:function(e,t,n,i){var a=e.getReferringComponents("singleAxis",Ed).models[0];t.coordSysDims=["single"],n.set("single",a),j_(a)&&(i.set("single",a),t.firstCategoryDimIndex=0)},polar:function(e,t,n,i){var a=e.getReferringComponents("polar",Ed).models[0],r=a.findAxisModel("radiusAxis"),o=a.findAxisModel("angleAxis");t.coordSysDims=["radius","angle"],n.set("radius",r),n.set("angle",o),j_(r)&&(i.set("radius",r),t.firstCategoryDimIndex=0),j_(o)&&(i.set("angle",o),null==t.firstCategoryDimIndex&&(t.firstCategoryDimIndex=1))},geo:function(e,t,n,i){t.coordSysDims=["lng","lat"]},parallel:function(e,t,n,i){var a=e.ecModel,r=a.getComponent("parallel",e.get("parallelIndex")),o=t.coordSysDims=r.dimensions.slice();Rr(r.parallelAxisIndex,(function(e,r){var s=a.getComponent("parallelAxis",e),l=o[r];n.set(l,s),j_(s)&&(i.set(l,s),null==t.firstCategoryDimIndex&&(t.firstCategoryDimIndex=r))}))}};function j_(e){return"category"===e.get("type")}function H_(e,t,n){var i,a,r,o=(n=n||{}).byIndex,s=n.stackedCoordDimension;!function(e){return!C_(e.schema)}(t)?(a=t.schema,i=a.dimensions,r=t.store):i=t;var l,p,c,d,u=!(!e||!e.get("stack"));if(Rr(i,(function(e,t){Hr(e)&&(i[t]=e={name:e}),u&&!e.isExtraCoord&&(o||l||!e.ordinalMeta&&!e.stack||(l=e),p||"ordinal"===e.type||"time"===e.type||s&&s!==e.coordDim||(p=e))})),!p||o||l||(o=!0),p){c="__\0ecstackresult_"+e.id,d="__\0ecstackedover_"+e.id,l&&(l.createInvertedIndices=!0);var m=p.coordDim,h=p.type,g=0;Rr(i,(function(e){e.coordDim===m&&g++}));var f={name:c,coordDim:m,coordDimIndex:g,type:h,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length},y={name:d,coordDim:d,coordDimIndex:g+1,type:h,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length+1};a?(r&&(f.storeDimIndex=r.ensureCalculationDimension(d,h),y.storeDimIndex=r.ensureCalculationDimension(c,h)),a.appendCalculationDimension(f),a.appendCalculationDimension(y)):(i.push(f),i.push(y))}return{stackedDimension:p&&p.name,stackedByDimension:l&&l.name,isStackedByIndex:o,stackedOverDimension:d,stackResultDimension:c}}function W_(e,t){return!!t&&t===e.getCalculationInfo("stackedDimension")}function $_(e,t){return W_(e,t)?e.getCalculationInfo("stackResultDimension"):t}function K_(e,t,n){n=n||{};var i,a=t.getSourceManager(),r=!1;e?(r=!0,i=Gx(e)):r=(i=a.getSource()).sourceFormat===Ev;var o=function(e){var t=e.get("coordinateSystem"),n=new z_(t),i=U_[t];if(i)return i(e,n,n.axisMap,n.categoryAxisMap),n}(t),s=function(e,t){var n,i=e.get("coordinateSystem"),a=sx.get(i);return t&&t.coordSysDims&&(n=Br(t.coordSysDims,(function(e){var n={name:e},i=t.axisMap.get(e);if(i){var a=i.get("type");n.type=v_(a)}return n}))),n||(n=a&&(a.getDimensionsInfo?a.getDimensionsInfo():a.dimensions.slice())||["x","y"]),n}(t,o),l=n.useEncodeDefaulter,p=jr(l)?l:l?zr(Vv,s,t):null,c=q_(i,{coordDimensions:s,generateCoord:n.generateCoord,encodeDefine:t.getEncode(),encodeDefaulter:p,canOmitUnusedDimensions:!r}),d=function(e,t,n){var i,a;return n&&Rr(e,(function(e,r){var o=e.coordDim,s=n.categoryAxisMap.get(o);s&&(null==i&&(i=r),e.ordinalMeta=s.getOrdinalMeta(),t&&(e.createInvertedIndices=!0)),null!=e.otherDims.itemName&&(a=!0)})),a||null==i||(e[i].otherDims.itemName=0),i}(c.dimensions,n.createInvertedIndices,o),u=r?null:a.getSharedDataStore(c),m=H_(t,{schema:c,store:u}),h=new V_(c,t);h.setCalculationInfo(m);var g=null!=d&&function(e){if(e.sourceFormat===Ev){var t=function(e){var t=0;for(;tt[1]&&(t[1]=e[1])},e.prototype.unionExtentFromData=function(e,t){this.unionExtent(e.getApproximateExtent(t))},e.prototype.getExtent=function(){return this._extent.slice()},e.prototype.setExtent=function(e,t){var n=this._extent;isNaN(e)||(n[0]=e),isNaN(t)||(n[1]=t)},e.prototype.isInExtentRange=function(e){return this._extent[0]<=e&&this._extent[1]>=e},e.prototype.isBlank=function(){return this._isBlank},e.prototype.setBlank=function(e){this._isBlank=e},e}();Gd(Y_);var X_=0,Z_=function(){function e(e){this.categories=e.categories||[],this._needCollect=e.needCollect,this._deduplication=e.deduplication,this.uid=++X_}return e.createByAxisModel=function(t){var n=t.option,i=n.data,a=i&&Br(i,Q_);return new e({categories:a,needCollect:!a,deduplication:!1!==n.dedplication})},e.prototype.getOrdinal=function(e){return this._getOrCreateMap().get(e)},e.prototype.parseAndCollect=function(e){var t,n=this._needCollect;if(!Hr(e)&&!n)return e;if(n&&!this._deduplication)return t=this.categories.length,this.categories[t]=e,t;var i=this._getOrCreateMap();return null==(t=i.get(e))&&(n?(t=this.categories.length,this.categories[t]=e,i.set(e,t)):t=NaN),t},e.prototype._getOrCreateMap=function(){return this._map||(this._map=fo(this.categories))},e}();function Q_(e){return Kr(e)&&null!=e.value?e.value:e+""}function J_(e){return"interval"===e.type||"log"===e.type}function eT(e,t,n,i){var a={},r=e[1]-e[0],o=a.interval=Jc(r/t,!0);null!=n&&oi&&(o=a.interval=i);var s=a.intervalPrecision=nT(o);return function(e,t){!isFinite(e[0])&&(e[0]=t[0]),!isFinite(e[1])&&(e[1]=t[1]),iT(e,0,t),iT(e,1,t),e[0]>e[1]&&(e[0]=e[1])}(a.niceTickExtent=[qc(Math.ceil(e[0]/o)*o,s),qc(Math.floor(e[1]/o)*o,s)],e),a}function tT(e){var t=Math.pow(10,Qc(e)),n=e/t;return n?2===n?n=3:3===n?n=5:n*=2:n=1,qc(n*t)}function nT(e){return zc(e)+2}function iT(e,t,n){e[t]=Math.max(Math.min(e[t],n[1]),n[0])}function aT(e,t){return e>=t[0]&&e<=t[1]}function rT(e,t){return t[1]===t[0]?.5:(e-t[0])/(t[1]-t[0])}function oT(e,t){return e*(t[1]-t[0])+t[0]}var sT=function(e){function t(t){var n=e.call(this,t)||this;n.type="ordinal";var i=n.getSetting("ordinalMeta");return i||(i=new Z_({})),Ur(i)&&(i=new Z_({categories:Br(i,(function(e){return Kr(e)?e.value:e}))})),n._ordinalMeta=i,n._extent=n.getSetting("extent")||[0,i.categories.length-1],n}return ze(t,e),t.prototype.parse=function(e){return null==e?NaN:Hr(e)?this._ordinalMeta.getOrdinal(e):Math.round(e)},t.prototype.contain=function(e){return aT(e=this.parse(e),this._extent)&&null!=this._ordinalMeta.categories[e]},t.prototype.normalize=function(e){return rT(e=this._getTickNumber(this.parse(e)),this._extent)},t.prototype.scale=function(e){return e=Math.round(oT(e,this._extent)),this.getRawOrdinalNumber(e)},t.prototype.getTicks=function(){for(var e=[],t=this._extent,n=t[0];n<=t[1];)e.push({value:n}),n++;return e},t.prototype.getMinorTicks=function(e){},t.prototype.setSortInfo=function(e){if(null!=e){for(var t=e.ordinalNumbers,n=this._ordinalNumbersByTick=[],i=this._ticksByOrdinalNumber=[],a=0,r=this._ordinalMeta.categories.length,o=Math.min(r,t.length);a=0&&e=0&&e=e},t.prototype.getOrdinalMeta=function(){return this._ordinalMeta},t.prototype.calcNiceTicks=function(){},t.prototype.calcNiceExtent=function(){},t.type="ordinal",t}(Y_);Y_.registerClass(sT);var lT=qc,pT=function(e){function t(t){var n=e.call(this,t)||this;n.type="interval",n._interval=0,n._intervalPrecision=2;var i=n.getSetting("ticksGenerator");return jr(i)&&(n._ticksGenerator=i),n}return ze(t,e),t.prototype.parse=function(e){return e},t.prototype.contain=function(e){return aT(e,this._extent)},t.prototype.normalize=function(e){return rT(e,this._extent)},t.prototype.scale=function(e){return oT(e,this._extent)},t.prototype.setExtent=function(e,t){var n=this._extent;isNaN(e)||(n[0]=parseFloat(e)),isNaN(t)||(n[1]=parseFloat(t))},t.prototype.unionExtent=function(e){var t=this._extent;e[0]t[1]&&(t[1]=e[1]),this.setExtent(t[0],t[1])},t.prototype.getInterval=function(){return this._interval},t.prototype.setInterval=function(e){this._interval=e,this._niceExtent=this._extent.slice(),this._intervalPrecision=nT(e)},t.prototype.getTicks=function(e){var t,n=this._interval,i=this._extent,a=this._niceExtent,r=this._intervalPrecision,o=this._ticksGenerator;if(o)try{if(t=o(i,n,a,r))return t}catch(e){}if(t=[],!n)return t;i[0]1e4)return[];var l=t.length?t[t.length-1].value:a[1];return i[1]>l&&(e?t.push({value:lT(l+n,r)}):t.push({value:i[1]})),t},t.prototype.getMinorTicks=function(e){for(var t=this.getTicks(!0),n=[],i=this.getExtent(),a=1;ai[0]&&c0&&(r=null===r?s:Math.min(r,s))}n[i]=r}}return n}(e),n=[];return Rr(e,(function(e){var i,a=e.coordinateSystem.getBaseAxis(),r=a.getExtent();if("category"===a.type)i=a.getBandWidth();else if("value"===a.type||"time"===a.type){var o=a.dim+"_"+a.index,s=t[o],l=Math.abs(r[1]-r[0]),p=a.scale.getExtent(),c=Math.abs(p[1]-p[0]);i=s?l/c*s:l}else{var d=e.getData();i=Math.abs(r[1]-r[0])/d.count()}var u=Vc(e.get("barWidth"),i),m=Vc(e.get("barMaxWidth"),i),h=Vc(e.get("barMinWidth")||(ST(e)?.5:1),i),g=e.get("barGap"),f=e.get("barCategoryGap");n.push({bandWidth:i,barWidth:u,barMaxWidth:m,barMinWidth:h,barGap:g,barCategoryGap:f,axisKey:gT(a),stackId:hT(e)})})),vT(n)}function vT(e){var t={};Rr(e,(function(e,n){var i=e.axisKey,a=e.bandWidth,r=t[i]||{bandWidth:a,remainedWidth:a,autoWidthCount:0,categoryGap:null,gap:"20%",stacks:{}},o=r.stacks;t[i]=r;var s=e.stackId;o[s]||r.autoWidthCount++,o[s]=o[s]||{width:0,maxWidth:0};var l=e.barWidth;l&&!o[s].width&&(o[s].width=l,l=Math.min(r.remainedWidth,l),r.remainedWidth-=l);var p=e.barMaxWidth;p&&(o[s].maxWidth=p);var c=e.barMinWidth;c&&(o[s].minWidth=c);var d=e.barGap;null!=d&&(r.gap=d);var u=e.barCategoryGap;null!=u&&(r.categoryGap=u)}));var n={};return Rr(t,(function(e,t){n[t]={};var i=e.stacks,a=e.bandWidth,r=e.categoryGap;if(null==r){var o=qr(i).length;r=Math.max(35-4*o,15)+"%"}var s=Vc(r,a),l=Vc(e.gap,1),p=e.remainedWidth,c=e.autoWidthCount,d=(p-s)/(c+(c-1)*l);d=Math.max(d,0),Rr(i,(function(e){var t=e.maxWidth,n=e.minWidth;if(e.width){i=e.width;t&&(i=Math.min(i,t)),n&&(i=Math.max(i,n)),e.width=i,p-=i+l*i,c--}else{var i=d;t&&ti&&(i=n),i!==d&&(e.width=i,p-=i+l*i,c--)}})),d=(p-s)/(c+(c-1)*l),d=Math.max(d,0);var u,m=0;Rr(i,(function(e,t){e.width||(e.width=d),u=e,m+=e.width*(1+l)})),u&&(m-=u.width*l);var h=-m/2;Rr(i,(function(e,i){n[t][i]=n[t][i]||{bandWidth:a,offset:h,width:e.width},h+=e.width*(1+l)}))})),n}function xT(e,t){var n=fT(e,t),i=yT(n);Rr(n,(function(e){var t=e.getData(),n=e.coordinateSystem.getBaseAxis(),a=hT(e),r=i[gT(n)][a],o=r.offset,s=r.width;t.setLayout({bandWidth:r.bandWidth,offset:o,size:s})}))}function bT(e){return{seriesType:e,plan:gw(),reset:function(e){if(wT(e)){var t=e.getData(),n=e.coordinateSystem,i=n.getBaseAxis(),a=n.getOtherAxis(i),r=t.getDimensionIndex(t.mapDimension(a.dim)),o=t.getDimensionIndex(t.mapDimension(i.dim)),s=e.get("showBackground",!0),l=t.mapDimension(a.dim),p=t.getCalculationInfo("stackResultDimension"),c=W_(t,l)&&!!t.getCalculationInfo("stackedOnSeries"),d=a.isHorizontal(),u=function(e,t){return t.toGlobalCoord(t.dataToCoord("log"===t.type?1:0))}(0,a),m=ST(e),h=e.get("barMinHeight")||0,g=p&&t.getDimensionIndex(p),f=t.getLayout("size"),y=t.getLayout("offset");return{progress:function(e,t){for(var i,a=e.count,l=m&&uT(3*a),p=m&&s&&uT(3*a),v=m&&uT(a),x=n.master.getRect(),b=d?x.width:x.height,w=t.getStore(),S=0;null!=(i=e.next());){var C=w.get(c?g:r,i),_=w.get(o,i),T=u,I=void 0;c&&(I=+C-w.get(r,i));var E=void 0,M=void 0,k=void 0,P=void 0;if(d){var D=n.dataToPoint([C,_]);if(c)T=n.dataToPoint([I,_])[0];E=T,M=D[1]+y,k=D[0]-T,P=f,Math.abs(k)0)for(var s=0;s=0;--s)if(l[p]){r=l[p];break}r=r||o.none}if(Ur(r)){var c=null==e.level?0:e.level>=0?e.level:r.length+e.level;r=r[c=Math.min(c,r.length-1)]}}return Ny(new Date(e.value),r,a,i)}(e,t,n,this.getSetting("locale"),i)},t.prototype.getTicks=function(){var e=this._interval,t=this._extent,n=[];if(!e)return n;n.push({value:t[0],level:0});var i=this.getSetting("useUTC"),a=function(e,t,n,i){var a=1e4,r=Ay,o=0;function s(e,t,n,a,r,o,s){for(var l=new Date(t),p=t,c=l[a]();p1&&0===p&&r.unshift({value:r[0].value-u})}}for(p=0;p=i[0]&&y<=i[1]&&d++)}var v=(i[1]-i[0])/t;if(d>1.5*v&&u>v/1.5)break;if(p.push(g),d>v||e===r[m])break}c=[]}}0;var x=Lr(Br(p,(function(e){return Lr(e,(function(e){return e.value>=i[0]&&e.value<=i[1]&&!e.notAdd}))})),(function(e){return e.length>0})),b=[],w=x.length-1;for(m=0;mn&&(this._approxInterval=n);var r=_T.length,o=Math.min(function(e,t,n,i){for(;n>>1;e[a][1]16?16:e>7.5?7:e>3.5?4:e>1.5?2:1}function IT(e){return(e/=2592e6)>6?6:e>3?3:e>2?2:1}function ET(e){return(e/=Iy)>12?12:e>6?6:e>3.5?4:e>2?2:1}function MT(e,t){return(e/=t?Ty:_y)>30?30:e>20?20:e>15?15:e>10?10:e>5?5:e>2?2:1}function kT(e){return Jc(e,!0)}function PT(e,t,n){var i=new Date(e);switch(Ry(t)){case"year":case"month":i[Ky(n)](0);case"day":i[Yy(n)](1);case"hour":i[Xy(n)](0);case"minute":i[Zy(n)](0);case"second":i[Qy(n)](0),i[Jy(n)](0)}return i.getTime()}Y_.registerClass(CT);var DT=Y_.prototype,OT=pT.prototype,AT=qc,FT=Math.floor,RT=Math.ceil,BT=Math.pow,NT=Math.log,LT=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type="log",t.base=10,t._originalScale=new pT,t._interval=0,t}return ze(t,e),t.prototype.getTicks=function(e){var t=this._originalScale,n=this._extent,i=t.getExtent();return Br(OT.getTicks.call(this,e),(function(e){var t=e.value,a=qc(BT(this.base,t));return a=t===n[0]&&this._fixMin?qT(a,i[0]):a,{value:a=t===n[1]&&this._fixMax?qT(a,i[1]):a}}),this)},t.prototype.setExtent=function(e,t){var n=NT(this.base);e=NT(Math.max(0,e))/n,t=NT(Math.max(0,t))/n,OT.setExtent.call(this,e,t)},t.prototype.getExtent=function(){var e=this.base,t=DT.getExtent.call(this);t[0]=BT(e,t[0]),t[1]=BT(e,t[1]);var n=this._originalScale.getExtent();return this._fixMin&&(t[0]=qT(t[0],n[0])),this._fixMax&&(t[1]=qT(t[1],n[1])),t},t.prototype.unionExtent=function(e){this._originalScale.unionExtent(e);var t=this.base;e[0]=NT(e[0])/NT(t),e[1]=NT(e[1])/NT(t),DT.unionExtent.call(this,e)},t.prototype.unionExtentFromData=function(e,t){this.unionExtent(e.getApproximateExtent(t))},t.prototype.calcNiceTicks=function(e){e=e||10;var t=this._extent,n=t[1]-t[0];if(!(n===1/0||n<=0)){var i=Zc(n);for(e/n*i<=.5&&(i*=10);!isNaN(i)&&Math.abs(i)<1&&Math.abs(i)>0;)i*=10;var a=[qc(RT(t[0]/i)*i),qc(FT(t[1]/i)*i)];this._interval=i,this._niceExtent=a}},t.prototype.calcNiceExtent=function(e){OT.calcNiceExtent.call(this,e),this._fixMin=e.fixMin,this._fixMax=e.fixMax},t.prototype.parse=function(e){return e},t.prototype.contain=function(e){return aT(e=NT(e)/NT(this.base),this._extent)},t.prototype.normalize=function(e){return rT(e=NT(e)/NT(this.base),this._extent)},t.prototype.scale=function(e){return e=oT(e,this._extent),BT(this.base,e)},t.type="log",t}(Y_),VT=LT.prototype;function qT(e,t){return AT(e,zc(t))}VT.getMinorTicks=OT.getMinorTicks,VT.getLabel=OT.getLabel,Y_.registerClass(LT);var GT=function(){function e(e,t,n){this._prepareParams(e,t,n)}return e.prototype._prepareParams=function(e,t,n){n[1]0&&s>0&&!l&&(o=0),o<0&&s<0&&!p&&(s=0));var d=this._determinedMin,u=this._determinedMax;return null!=d&&(o=d,l=!0),null!=u&&(s=u,p=!0),{min:o,max:s,minFixed:l,maxFixed:p,isBlank:c}},e.prototype.modifyDataMinMax=function(e,t){this[UT[e]]=t},e.prototype.setDeterminedMinMax=function(e,t){var n=zT[e];this[n]=t},e.prototype.freeze=function(){this.frozen=!0},e}(),zT={min:"_determinedMin",max:"_determinedMax"},UT={min:"_dataMin",max:"_dataMax"};function jT(e,t,n){var i=e.rawExtentInfo;return i||(i=new GT(e,t,n),e.rawExtentInfo=i,i)}function HT(e,t){return null==t?null:to(t)?NaN:e.parse(t)}function WT(e,t){var n=e.type,i=jT(e,t,e.getExtent()).calculate();e.setBlank(i.isBlank);var a=i.min,r=i.max,o=t.ecModel;if(o&&"time"===n){var s=fT("bar",o),l=!1;if(Rr(s,(function(e){l=l||e.getBaseAxis()===t.axis})),l){var p=yT(s),c=function(e,t,n,i){var a=n.axis.getExtent(),r=a[1]-a[0],o=function(e,t,n){if(e&&t){var i=e[gT(t)];return null!=i&&null!=n?i[hT(n)]:i}}(i,n.axis);if(void 0===o)return{min:e,max:t};var s=1/0;Rr(o,(function(e){s=Math.min(e.offset,s)}));var l=-1/0;Rr(o,(function(e){l=Math.max(e.offset+e.width,l)})),s=Math.abs(s),l=Math.abs(l);var p=s+l,c=t-e,d=c/(1-(s+l)/r)-c;return t+=d*(l/p),e-=d*(s/p),{min:e,max:t}}(a,r,t,p);a=c.min,r=c.max}}return{extent:[a,r],fixMin:i.minFixed,fixMax:i.maxFixed}}function $T(e,t){var n=t,i=WT(e,n),a=i.extent,r=n.get("splitNumber");e instanceof LT&&(e.base=n.get("logBase"));var o=e.type,s=n.get("interval"),l="interval"===o||"time"===o;e.setExtent(a[0],a[1]),e.calcNiceExtent({splitNumber:r,fixMin:i.fixMin,fixMax:i.fixMax,minInterval:l?n.get("minInterval"):null,maxInterval:l?n.get("maxInterval"):null}),null!=s&&e.setInterval&&e.setInterval(s)}function KT(e,t){if(t=t||e.get("type"))switch(t){case"category":return new sT({ordinalMeta:e.getOrdinalMeta?e.getOrdinalMeta():e.getCategories(),extent:[1/0,-1/0]});case"time":return new CT({locale:e.ecModel.getLocaleModel(),useUTC:e.ecModel.get("useUTC")});case"value":return new pT({ticksGenerator:e.getTicksGenerator()});default:return new(Y_.getClass(t)||pT)}}function YT(e){var t,n,i=e.getLabelModel().get("formatter"),a="category"===e.type?e.scale.getExtent()[0]:null;return"time"===e.scale.type?(n=i,function(t,i){return e.scale.getFormattedLabel(t,i,n)}):Hr(i)?function(t){return function(n){var i=e.scale.getLabel(n);return t.replace("{value}",null!=i?i:"")}}(i):jr(i)?(t=i,function(n,i){return null!=a&&(i=n.value-a),t(XT(e,n),i,null!=n.level?{level:n.level}:null)}):function(t){return e.scale.getLabel(t)}}function XT(e,t){return"category"===e.type?e.scale.getLabel(t):t.value}function ZT(e,t){var n=t*Math.PI/180,i=e.width,a=e.height,r=i*Math.abs(Math.cos(n))+Math.abs(a*Math.sin(n)),o=i*Math.abs(Math.sin(n))+Math.abs(a*Math.cos(n));return new Ps(e.x,e.y,r,o)}function QT(e){var t=e.get("interval");return null==t?"auto":t}function JT(e){return"category"===e.type&&0===QT(e.getLabelModel())}function eI(e,t){var n={};return Rr(e.mapDimensionsAll(t),(function(t){n[$_(e,t)]=!0})),qr(n)}var tI=function(){function e(){}return e.prototype.getNeedCrossZero=function(){return!this.option.scale},e.prototype.getCoordSysModel=function(){},e}();var nI=[],iI={registerPreprocessor:a_,registerProcessor:r_,registerPostInit:function(e){o_("afterinit",e)},registerPostUpdate:function(e){o_("afterupdate",e)},registerUpdateLifecycle:o_,registerAction:s_,registerCoordinateSystem:function(e,t){sx.register(e,t)},registerLayout:function(e,t){c_(YC,e,t,1e3,"layout")},registerVisual:l_,registerTransform:u_,registerLoading:d_,registerMap:function(e,t,n){var i=pC("registerMap");i&&i(e,t,n)},registerImpl:function(e,t){lC[e]=t},PRIORITY:uC,ComponentModel:Sv,ComponentView:hw,SeriesModel:ow,ChartView:vw,registerComponentModel:function(e){Sv.registerClass(e)},registerComponentView:function(e){hw.registerClass(e)},registerSeriesModel:function(e){ow.registerClass(e)},registerChartView:function(e){vw.registerClass(e)},registerSubTypeDefaulter:function(e,t){Sv.registerSubTypeDefaulter(e,t)},registerPainter:function(e,t){Fc(e,t)}};function aI(e){Ur(e)?Rr(e,(function(e){aI(e)})):Dr(nI,e)>=0||(nI.push(e),jr(e)&&(e={install:e}),e.install(iI))}function rI(e,t){return Math.abs(e-t)<1e-8}function oI(e,t,n){var i=0,a=e[0];if(!a)return!1;for(var r=1;rn&&(e=a,n=o)}if(e)return function(e){for(var t=0,n=0,i=0,a=e.length,r=e[a-1][0],o=e[a-1][1],s=0;s>1^-(1&s),l=l>>1^-(1&l),a=s+=a,r=l+=r,i.push([s/n,l/n])}return i}function yI(e,t){return Br(Lr((e=function(e){if(!e.UTF8Encoding)return e;var t=e,n=t.UTF8Scale;return null==n&&(n=1024),Rr(t.features,(function(e){var t=e.geometry,i=t.encodeOffsets,a=t.coordinates;if(i)switch(t.type){case"LineString":t.coordinates=fI(a,i,n);break;case"Polygon":case"MultiLineString":gI(a,i,n);break;case"MultiPolygon":Rr(a,(function(e,t){return gI(e,i[t],n)}))}})),t.UTF8Encoding=!1,t}(e)).features,(function(e){return e.geometry&&e.properties&&e.geometry.coordinates.length>0})),(function(e){var n=e.properties,i=e.geometry,a=[];switch(i.type){case"Polygon":var r=i.coordinates;a.push(new dI(r[0],r.slice(1)));break;case"MultiPolygon":Rr(i.coordinates,(function(e){e[0]&&a.push(new dI(e[0],e.slice(1)))}));break;case"LineString":a.push(new uI([i.coordinates]));break;case"MultiLineString":a.push(new uI(i.coordinates))}var o=new mI(n[t||"name"],a,n.cp);return o.properties=n,o}))}var vI=Cd();function xI(e){return"category"===e.type?function(e){var t=e.getLabelModel(),n=wI(e,t);return!t.get("show")||e.scale.isBlank()?{labels:[],labelCategoryInterval:n.labelCategoryInterval}:n}(e):function(e){var t=e.scale.getTicks(),n=YT(e);return{labels:Br(t,(function(t,i){return{level:t.level,formattedLabel:n(t,i),rawLabel:e.scale.getLabel(t),tickValue:t.value}}))}}(e)}function bI(e,t){return"category"===e.type?function(e,t){var n,i,a=SI(e,"ticks"),r=QT(t),o=CI(a,r);if(o)return o;t.get("show")&&!e.scale.isBlank()||(n=[]);if(jr(r))n=II(e,r,!0);else if("auto"===r){var s=wI(e,e.getLabelModel());i=s.labelCategoryInterval,n=Br(s.labels,(function(e){return e.tickValue}))}else n=TI(e,i=r,!0);return _I(a,r,{ticks:n,tickCategoryInterval:i})}(e,t):{ticks:Br(e.scale.getTicks(),(function(e){return e.value}))}}function wI(e,t){var n,i,a=SI(e,"labels"),r=QT(t),o=CI(a,r);return o||(jr(r)?n=II(e,r):(i="auto"===r?function(e){var t=vI(e).autoInterval;return null!=t?t:vI(e).autoInterval=e.calculateCategoryInterval()}(e):r,n=TI(e,i)),_I(a,r,{labels:n,labelCategoryInterval:i}))}function SI(e,t){return vI(e)[t]||(vI(e)[t]=[])}function CI(e,t){for(var n=0;n1&&c/l>2&&(p=Math.round(Math.ceil(p/l)*l));var d=JT(e),u=o.get("showMinLabel")||d,m=o.get("showMaxLabel")||d;u&&p!==r[0]&&g(r[0]);for(var h=p;h<=r[1];h+=l)g(h);function g(e){var t={value:e};s.push(n?e:{formattedLabel:i(t),rawLabel:a.getLabel(t),tickValue:e})}return m&&h-l!==r[1]&&g(r[1]),s}function II(e,t,n){var i=e.scale,a=YT(e),r=[];return Rr(i.getTicks(),(function(e){var o=i.getLabel(e),s=e.value;t(e.value,o)&&r.push(n?s:{formattedLabel:a(e),rawLabel:o,tickValue:s})})),r}var EI=[0,1],MI=function(){function e(e,t,n){this.onBand=!1,this.inverse=!1,this.dim=e,this.scale=t,this._extent=n||[0,0]}return e.prototype.contain=function(e){var t=this._extent,n=Math.min(t[0],t[1]),i=Math.max(t[0],t[1]);return e>=n&&e<=i},e.prototype.containData=function(e){return this.scale.contain(e)},e.prototype.getExtent=function(){return this._extent.slice()},e.prototype.getPixelPrecision=function(e){return jc(e||this.scale.getExtent(),this._extent)},e.prototype.setExtent=function(e,t){var n=this._extent;n[0]=e,n[1]=t},e.prototype.dataToCoord=function(e,t){var n=this._extent,i=this.scale;return e=i.normalize(e),this.onBand&&"ordinal"===i.type&&kI(n=n.slice(),i.count()),Lc(e,EI,n,t)},e.prototype.coordToData=function(e,t){var n=this._extent,i=this.scale;this.onBand&&"ordinal"===i.type&&kI(n=n.slice(),i.count());var a=Lc(e,n,EI,t);return this.scale.scale(a)},e.prototype.pointToData=function(e,t){},e.prototype.getTicksCoords=function(e){var t=(e=e||{}).tickModel||this.getTickModel(),n=Br(bI(this,t).ticks,(function(e){return{coord:this.dataToCoord("ordinal"===this.scale.type?this.scale.getRawOrdinalNumber(e):e),tickValue:e}}),this);return function(e,t,n,i){var a=t.length;if(!e.onBand||n||!a)return;var r,o,s=e.getExtent();if(1===a)t[0].coord=s[0],r=t[1]={coord:s[1]};else{var l=t[a-1].tickValue-t[0].tickValue,p=(t[a-1].coord-t[0].coord)/l;Rr(t,(function(e){e.coord-=p/2})),o=1+e.scale.getExtent()[1]-t[a-1].tickValue,r={coord:t[a-1].coord+p*o},t.push(r)}var c=s[0]>s[1];d(t[0].coord,s[0])&&(i?t[0].coord=s[0]:t.shift());i&&d(s[0],t[0].coord)&&t.unshift({coord:s[0]});d(s[1],r.coord)&&(i?r.coord=s[1]:t.pop());i&&d(r.coord,s[1])&&t.push({coord:s[1]});function d(e,t){return e=qc(e),t=qc(t),c?e>t:e0&&e<100||(e=5),Br(this.scale.getMinorTicks(e),(function(e){return Br(e,(function(e){return{coord:this.dataToCoord(e),tickValue:e}}),this)}),this)},e.prototype.getViewLabels=function(){return xI(this).labels},e.prototype.getLabelModel=function(){return this.model.getModel("axisLabel")},e.prototype.getTickModel=function(){return this.model.getModel("axisTick")},e.prototype.getBandWidth=function(){if("time"===this.type){var e=this.model,t=e.get("bandWidthCalculator"),n=void 0;if(jr(t))try{if(n=t(e))return n}catch(e){}}var i=this._extent,a=this.scale.getExtent(),r=a[1]-a[0]+(this.onBand?1:0);0===r&&(r=1);var o=Math.abs(i[1]-i[0]);return Math.abs(o)/r},e.prototype.calculateCategoryInterval=function(){return function(e){var t=function(e){var t=e.getLabelModel();return{axisRotate:e.getRotate?e.getRotate():e.isHorizontal&&!e.isHorizontal()?90:0,labelRotate:t.get("rotate")||0,font:t.getFont()}}(e),n=YT(e),i=(t.axisRotate-t.labelRotate)/180*Math.PI,a=e.scale,r=a.getExtent(),o=a.count();if(r[1]-r[0]<1)return 0;var s=1;o>40&&(s=Math.max(1,Math.floor(o/40)));for(var l=r[0],p=e.dataToCoord(l+1)-e.dataToCoord(l),c=Math.abs(p*Math.cos(i)),d=Math.abs(p*Math.sin(i)),u=0,m=0;l<=r[1];l+=s){var h,g,f=uc(n({value:l}),t.font,"center","top");h=1.3*f.width,g=1.3*f.height,u=Math.max(u,h,7),m=Math.max(m,g,7)}var y=u/c,v=m/d;isNaN(y)&&(y=1/0),isNaN(v)&&(v=1/0);var x=Math.max(0,Math.floor(Math.min(y,v))),b=vI(e.model),w=e.getExtent(),S=b.lastAutoInterval,C=b.lastTickCount;return null!=S&&null!=C&&Math.abs(S-x)<=1&&Math.abs(C-o)<=1&&S>x&&b.axisExtent0===w[0]&&b.axisExtent1===w[1]?x=S:(b.lastTickCount=o,b.lastAutoInterval=x,b.axisExtent0=w[0],b.axisExtent1=w[1]),x}(this)},e}();function kI(e,t){var n=(e[1]-e[0])/t/2;e[0]+=n,e[1]-=n}var PI=2*Math.PI,DI=Yu.CMD,OI=["top","right","bottom","left"];function AI(e,t,n,i,a){var r=n.width,o=n.height;switch(e){case"top":i.set(n.x+r/2,n.y-t),a.set(0,-1);break;case"bottom":i.set(n.x+r/2,n.y+o+t),a.set(0,1);break;case"left":i.set(n.x-t,n.y+o/2),a.set(-1,0);break;case"right":i.set(n.x+r+t,n.y+o/2),a.set(1,0)}}function FI(e,t,n,i,a,r,o,s,l){o-=e,s-=t;var p=Math.sqrt(o*o+s*s),c=(o/=p)*n+e,d=(s/=p)*n+t;if(Math.abs(i-a)%PI<1e-4)return l[0]=c,l[1]=d,p-n;if(r){var u=i;i=em(a),a=em(u)}else i=em(i),a=em(a);i>a&&(a+=PI);var m=Math.atan2(s,o);if(m<0&&(m+=PI),m>=i&&m<=a||m+PI>=i&&m+PI<=a)return l[0]=c,l[1]=d,p-n;var h=n*Math.cos(i)+e,g=n*Math.sin(i)+t,f=n*Math.cos(a)+e,y=n*Math.sin(a)+t,v=(h-o)*(h-o)+(g-s)*(g-s),x=(f-o)*(f-o)+(y-s)*(y-s);return v0){t=t/180*Math.PI,qI.fromArray(e[0]),GI.fromArray(e[1]),zI.fromArray(e[2]),ws.sub(UI,qI,GI),ws.sub(jI,zI,GI);var n=UI.len(),i=jI.len();if(!(n<.001||i<.001)){UI.scale(1/n),jI.scale(1/i);var a=UI.dot(jI);if(Math.cos(t)1&&ws.copy($I,zI),$I.toArray(e[1])}}}}function YI(e,t,n){if(n<=180&&n>0){n=n/180*Math.PI,qI.fromArray(e[0]),GI.fromArray(e[1]),zI.fromArray(e[2]),ws.sub(UI,GI,qI),ws.sub(jI,zI,GI);var i=UI.len(),a=jI.len();if(!(i<.001||a<.001))if(UI.scale(1/i),jI.scale(1/a),UI.dot(t)=o)ws.copy($I,zI);else{$I.scaleAndAdd(jI,r/Math.tan(Math.PI/2-s));var l=zI.x!==GI.x?($I.x-GI.x)/(zI.x-GI.x):($I.y-GI.y)/(zI.y-GI.y);if(isNaN(l))return;l<0?ws.copy($I,GI):l>1&&ws.copy($I,zI)}$I.toArray(e[1])}}}function XI(e,t,n,i){var a="normal"===n,r=a?e:e.ensureState(n);r.ignore=t;var o=i.get("smooth");o&&!0===o&&(o=.3),r.shape=r.shape||{},o>0&&(r.shape.smooth=o);var s=i.getModel("lineStyle").getLineStyle();a?e.useStyle(s):r.style=s}function ZI(e,t){var n=t.smooth,i=t.points;if(i)if(e.moveTo(i[0][0],i[0][1]),n>0&&i.length>=3){var a=Fo(i[0],i[1]),r=Fo(i[1],i[2]);if(!a||!r)return e.lineTo(i[1][0],i[1][1]),void e.lineTo(i[2][0],i[2][1]);var o=Math.min(a,r)*n,s=No([],i[1],i[0],o/a),l=No([],i[1],i[2],o/r),p=No([],s,l,.5);e.bezierCurveTo(s[0],s[1],s[0],s[1],p[0],p[1]),e.bezierCurveTo(l[0],l[1],l[0],l[1],i[2][0],i[2][1])}else for(var c=1;c0&&r&&S(-d/o,0,o);var f,y,v=e[0],x=e[o-1];return b(),f<0&&C(-f,.8),y<0&&C(y,.8),b(),w(f,y,1),w(y,f,-1),b(),f<0&&_(-f),y<0&&_(y),p}function b(){f=v.rect[t]-i,y=a-x.rect[t]-x.rect[n]}function w(e,t,n){if(e<0){var i=Math.min(t,-e);if(i>0){S(i*n,0,o);var a=i+e;a<0&&C(-a*n,1)}else C(-e*n,1)}}function S(n,i,a){0!==n&&(p=!0);for(var r=i;r0)for(l=0;l0;l--){S(-(r[l-1]*d),l,o)}}}function _(e){var t=e<0?-1:1;e=Math.abs(e);for(var n=Math.ceil(e/(o-1)),i=0;i0?S(n,0,i+1):S(-n,o-i-1,o),(e-=n)<=0)return}}function nE(e,t,n,i){return tE(e,"y","height",t,n,i)}function iE(e){var t=[];e.sort((function(e,t){return t.priority-e.priority}));var n=new Ps(0,0,0,0);function i(e){if(!e.ignore){var t=e.ensureState("emphasis");null==t.ignore&&(t.ignore=!1)}e.ignore=!0}for(var a=0;a=0&&n.attr(m.oldLayoutSelect),Dr(p,"emphasis")>=0&&n.attr(m.oldLayoutEmphasis)),tf(n,s,t,o)}else if(n.attr(s),!Zf(n).valueAnimation){var c=io(n.style.opacity,1);n.style.opacity=0,nf(n,{style:{opacity:c}},t,o)}if(m.oldLayout=s,n.states.select){var d=m.oldLayoutSelect={};cE(d,s,dE),cE(d,n.states.select,dE)}if(n.states.emphasis){var u=m.oldLayoutEmphasis={};cE(u,s,dE),cE(u,n.states.emphasis,dE)}Jf(n,o,l,t,t)}if(i&&!i.ignore&&!i.invisible){a=(m=pE(i)).oldLayout;var m,h={points:i.shape.points};a?(i.attr({shape:a}),tf(i,{shape:h},t)):(i.setShape(h),i.style.strokePercent=0,nf(i,{style:{strokePercent:1}},t)),m.oldLayout=h}},e}(),mE=Cd();function hE(e){e.registerUpdateLifecycle("series:beforeupdate",(function(e,t,n){var i=mE(t).labelManager;i||(i=mE(t).labelManager=new uE),i.clearLabels()})),e.registerUpdateLifecycle("series:layoutlabels",(function(e,t,n){var i=mE(t).labelManager;n.updatedSeries.forEach((function(e){i.addLabelsOfSeries(t.getViewOfSeriesModel(e))})),i.updateLayoutConfig(t),i.layout(t),i.processLabelsOverall()}))}aI(hE);var gE=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n}return ze(t,e),t.prototype.getInitialData=function(e){return K_(null,this,{useEncodeDefaulter:!0})},t.prototype.getLegendIcon=function(e){var t=new Mc,n=PS("line",0,e.itemHeight/2,e.itemWidth,0,e.lineStyle.stroke,!1);t.add(n),n.setStyle(e.lineStyle);var i=this.getData().getVisual("symbol"),a=this.getData().getVisual("symbolRotate"),r="none"===i?"circle":i,o=.8*e.itemHeight,s=PS(r,(e.itemWidth-o)/2,(e.itemHeight-o)/2,o,o,e.itemStyle.fill);t.add(s),s.setStyle(e.itemStyle);var l="inherit"===e.iconRotate?a:e.iconRotate||0;return s.rotation=l*Math.PI/180,s.setOrigin([e.itemWidth/2,e.itemHeight/2]),r.indexOf("empty")>-1&&(s.style.stroke=s.style.fill,s.style.fill="#fff",s.style.lineWidth=2),t},t.type="series.line",t.dependencies=["grid","polar"],t.defaultOption={z:3,coordinateSystem:"cartesian2d",legendHoverLink:!0,clip:!0,label:{position:"top"},endLabel:{show:!1,valueAnimation:!0,distance:8},lineStyle:{width:2,type:"solid"},emphasis:{scale:!0},step:!1,smooth:!1,smoothMonotone:null,symbol:"emptyCircle",symbolSize:4,symbolRotate:null,showSymbol:!0,showAllSymbol:"auto",connectNulls:!1,sampling:"none",animationEasing:"linear",progressive:0,hoverLayerThreshold:1/0,universalTransition:{divideShape:"clone"},triggerLineEvent:!1},t}(ow);function fE(e,t){var n=e.mapDimensionsAll("defaultedLabel"),i=n.length;if(1===i){var a=ib(e,t,n[0]);return null!=a?a+"":null}if(i){for(var r=[],o=0;o=0&&i.push(t[r])}return i.join(" ")}var vE=function(e){function t(t,n,i,a){var r=e.call(this)||this;return r.updateData(t,n,i,a),r}return ze(t,e),t.prototype._createSymbol=function(e,t,n,i,a){this.removeAll();var r=PS(e,-1,-1,2,2,null,a);r.attr({z2:100,culling:!0,scaleX:i[0]/2,scaleY:i[1]/2}),r.drift=xE,this._symbolType=e,this.add(r)},t.prototype.stopSymbolAnimation=function(e){this.childAt(0).stopAnimation(null,e)},t.prototype.getSymbolType=function(){return this._symbolType},t.prototype.getSymbolPath=function(){return this.childAt(0)},t.prototype.highlight=function(){vh(this.childAt(0))},t.prototype.downplay=function(){xh(this.childAt(0))},t.prototype.setZ=function(e,t){var n=this.childAt(0);n.zlevel=e,n.z=t},t.prototype.setDraggable=function(e,t){var n=this.childAt(0);n.draggable=e,n.cursor=!t&&e?"move":n.cursor},t.prototype.updateData=function(e,n,i,a){this.silent=!1;var r=e.getItemVisual(n,"symbol")||"circle",o=e.hostModel,s=t.getSymbolSize(e,n),l=r!==this._symbolType,p=a&&a.disableAnimation;if(l){var c=e.getItemVisual(n,"symbolKeepAspect");this._createSymbol(r,e,n,s,c)}else{(u=this.childAt(0)).silent=!1;var d={scaleX:s[0]/2,scaleY:s[1]/2};p?u.attr(d):tf(u,d,o,n),lf(u)}if(this._updateCommon(e,n,s,i,a),l){var u=this.childAt(0);if(!p){d={scaleX:this._sizeX,scaleY:this._sizeY,style:{opacity:u.style.opacity}};u.scaleX=u.scaleY=0,u.style.opacity=0,nf(u,d,o,n)}}p&&this.childAt(0).stopAnimation("leave")},t.prototype._updateCommon=function(e,t,n,i,a){var r,o,s,l,p,c,d,u,m,h=this.childAt(0),g=e.hostModel;if(i&&(r=i.emphasisItemStyle,o=i.blurItemStyle,s=i.selectItemStyle,l=i.focus,p=i.blurScope,d=i.labelStatesModels,u=i.hoverScale,m=i.cursorStyle,c=i.emphasisDisabled),!i||e.hasItemOption){var f=i&&i.itemModel?i.itemModel:e.getItemModel(t),y=f.getModel("emphasis");r=y.getModel("itemStyle").getItemStyle(),s=f.getModel(["select","itemStyle"]).getItemStyle(),o=f.getModel(["blur","itemStyle"]).getItemStyle(),l=y.get("focus"),p=y.get("blurScope"),c=y.get("disabled"),d=Uf(f),u=y.getShallow("scale"),m=f.getShallow("cursor")}var v=e.getItemVisual(t,"symbolRotate");h.attr("rotation",(v||0)*Math.PI/180||0);var x=OS(e.getItemVisual(t,"symbolOffset"),n);x&&(h.x=x[0],h.y=x[1]),m&&h.attr("cursor",m);var b=e.getItemVisual(t,"style"),w=b.fill;if(h instanceof bm){var S=h.style;h.useStyle(kr({image:S.image,x:S.x,y:S.y,width:S.width,height:S.height},b))}else h.__isEmptyBrush?h.useStyle(kr({},b)):h.useStyle(b),h.style.decal=null,h.setColor(w,a&&a.symbolInnerColor),h.style.strokeNoScale=!0;var C=e.getItemVisual(t,"liftZ"),_=this._z2;null!=C?null==_&&(this._z2=h.z2,h.z2+=C):null!=_&&(h.z2=_,this._z2=null);var T=a&&a.useNameLabel;zf(h,d,{labelFetcher:g,labelDataIndex:t,defaultText:function(t){return T?e.getName(t):fE(e,t)},inheritColor:w,defaultOpacity:b.opacity}),this._sizeX=n[0]/2,this._sizeY=n[1]/2;var I=h.ensureState("emphasis");I.style=r,h.ensureState("select").style=s,h.ensureState("blur").style=o;var E=null==u||!0===u?Math.max(1.1,3/this._sizeY):isFinite(u)&&u>0?+u:1;I.scaleX=this._sizeX*E,I.scaleY=this._sizeY*E,this.setSymbolScale(1),Oh(this,l,p,c)},t.prototype.setSymbolScale=function(e){this.scaleX=this.scaleY=e},t.prototype.fadeOut=function(e,t,n){var i=this.childAt(0),a=Um(this).dataIndex,r=n&&n.animation;if(this.silent=i.silent=!0,n&&n.fadeLabel){var o=i.getTextContent();o&&rf(o,{style:{opacity:0}},t,{dataIndex:a,removeOpt:r,cb:function(){i.removeTextContent()}})}else i.removeTextContent();rf(i,{style:{opacity:0},scaleX:0,scaleY:0},t,{dataIndex:a,cb:e,removeOpt:r})},t.getSymbolSize=function(e,t){return DS(e.getItemVisual(t,"symbolSize"))},t}(Mc);function xE(e,t){this.parent.drift(e,t)}function bE(e,t,n,i){return t&&!isNaN(t[0])&&!isNaN(t[1])&&!(i.isIgnore&&i.isIgnore(n))&&!(i.clipShape&&!i.clipShape.contain(t[0],t[1]))&&"none"!==e.getItemVisual(n,"symbol")}function wE(e){return null==e||Kr(e)||(e={isIgnore:e}),e||{}}function SE(e){var t=e.hostModel,n=t.getModel("emphasis");return{emphasisItemStyle:n.getModel("itemStyle").getItemStyle(),blurItemStyle:t.getModel(["blur","itemStyle"]).getItemStyle(),selectItemStyle:t.getModel(["select","itemStyle"]).getItemStyle(),focus:n.get("focus"),blurScope:n.get("blurScope"),emphasisDisabled:n.get("disabled"),hoverScale:n.get("scale"),labelStatesModels:Uf(t),cursorStyle:t.get("cursor")}}var CE=function(){function e(e){this.group=new Mc,this._SymbolCtor=e||vE}return e.prototype.updateData=function(e,t){this._progressiveEls=null,t=wE(t);var n=this.group,i=e.hostModel,a=this._data,r=this._SymbolCtor,o=t.disableAnimation,s=SE(e),l={disableAnimation:o},p=t.getSymbolPoint||function(t){return e.getItemLayout(t)};a||n.removeAll(),e.diff(a).add((function(i){var a=p(i);if(bE(e,a,i,t)){var o=new r(e,i,s,l);o.setPosition(a),e.setItemGraphicEl(i,o),n.add(o)}})).update((function(c,d){var u=a.getItemGraphicEl(d),m=p(c);if(bE(e,m,c,t)){var h=e.getItemVisual(c,"symbol")||"circle",g=u&&u.getSymbolType&&u.getSymbolType();if(!u||g&&g!==h)n.remove(u),(u=new r(e,c,s,l)).setPosition(m);else{u.updateData(e,c,s,l);var f={x:m[0],y:m[1]};o?u.attr(f):tf(u,f,i)}n.add(u),e.setItemGraphicEl(c,u)}else n.remove(u)})).remove((function(e){var t=a.getItemGraphicEl(e);t&&t.fadeOut((function(){n.remove(t)}),i)})).execute(),this._getSymbolPoint=p,this._data=e},e.prototype.updateLayout=function(){var e=this,t=this._data;t&&t.eachItemGraphicEl((function(t,n){var i=e._getSymbolPoint(n);t.setPosition(i),t.markRedraw()}))},e.prototype.incrementalPrepareUpdate=function(e){this._seriesScope=SE(e),this._data=null,this.group.removeAll()},e.prototype.incrementalUpdate=function(e,t,n){function i(e){e.isGroup||(e.incremental=!0,e.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[],n=wE(n);for(var a=e.start;a0?n=i[0]:i[1]<0&&(n=i[1]);return n}(a,n),o=i.dim,s=a.dim,l=t.mapDimension(s),p=t.mapDimension(o),c="x"===s||"radius"===s?1:0,d=Br(e.dimensions,(function(e){return t.mapDimension(e)})),u=!1,m=t.getCalculationInfo("stackResultDimension");return W_(t,d[0])&&(u=!0,d[0]=m),W_(t,d[1])&&(u=!0,d[1]=m),{dataDimsForPoint:d,valueStart:r,valueAxisDim:s,baseAxisDim:o,stacked:!!u,valueDim:l,baseDim:p,baseDataOffset:c,stackedOverDimension:t.getCalculationInfo("stackedOverDimension")}}function TE(e,t,n,i){var a=NaN;e.stacked&&(a=n.get(n.getCalculationInfo("stackedOverDimension"),i)),isNaN(a)&&(a=e.valueStart);var r=e.baseDataOffset,o=[];return o[r]=n.get(e.baseDim,i),o[1-r]=a,t.dataToPoint(o)}var IE=Math.min,EE=Math.max;function ME(e,t){return isNaN(e)||isNaN(t)}function kE(e,t,n,i,a,r,o,s,l){for(var p,c,d,u,m,h,g=n,f=0;f=a||g<0)break;if(ME(y,v)){if(l){g+=r;continue}break}if(g===n)e[r>0?"moveTo":"lineTo"](y,v),d=y,u=v;else{var x=y-p,b=v-c;if(x*x+b*b<.5){g+=r;continue}if(o>0){for(var w=g+r,S=t[2*w],C=t[2*w+1];S===y&&C===v&&f=i||ME(S,C))m=y,h=v;else{I=S-p,E=C-c;var P=y-p,D=S-y,O=v-c,A=C-v,F=void 0,R=void 0;if("x"===s){var B=I>0?1:-1;m=y-B*(F=Math.abs(P))*o,h=v,M=y+B*(R=Math.abs(D))*o,k=v}else if("y"===s){var N=E>0?1:-1;m=y,h=v-N*(F=Math.abs(O))*o,M=y,k=v+N*(R=Math.abs(A))*o}else F=Math.sqrt(P*P+O*O),m=y-I*o*(1-(T=(R=Math.sqrt(D*D+A*A))/(R+F))),h=v-E*o*(1-T),k=v+E*o*T,M=IE(M=y+I*o*T,EE(S,y)),k=IE(k,EE(C,v)),M=EE(M,IE(S,y)),h=v-(E=(k=EE(k,IE(C,v)))-v)*F/R,m=IE(m=y-(I=M-y)*F/R,EE(p,y)),h=IE(h,EE(c,v)),M=y+(I=y-(m=EE(m,IE(p,y))))*R/F,k=v+(E=v-(h=EE(h,IE(c,v))))*R/F}e.bezierCurveTo(d,u,m,h,y,v),d=M,u=k}else e.lineTo(y,v)}p=y,c=v,g+=r}return f}var PE=function(){this.smooth=0,this.smoothConstraint=!0},DE=function(e){function t(t){var n=e.call(this,t)||this;return n.type="ec-polyline",n}return ze(t,e),t.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},t.prototype.getDefaultShape=function(){return new PE},t.prototype.buildPath=function(e,t){var n=t.points,i=0,a=n.length/2;if(t.connectNulls){for(;a>0&&ME(n[2*a-2],n[2*a-1]);a--);for(;i=0){var f=o?(c-i)*g+i:(p-n)*g+n;return o?[e,f]:[f,e]}n=p,i=c;break;case r.C:p=a[l++],c=a[l++],d=a[l++],u=a[l++],m=a[l++],h=a[l++];var y=o?ul(n,p,d,m,e,s):ul(i,c,u,h,e,s);if(y>0)for(var v=0;v=0){f=o?cl(i,c,u,h,x):cl(n,p,d,m,x);return o?[e,f]:[f,e]}}n=m,i=h}}},t}(gm),OE=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t}(PE),AE=function(e){function t(t){var n=e.call(this,t)||this;return n.type="ec-polygon",n}return ze(t,e),t.prototype.getDefaultShape=function(){return new OE},t.prototype.buildPath=function(e,t){var n=t.points,i=t.stackedOnPoints,a=0,r=n.length/2,o=t.smoothMonotone;if(t.connectNulls){for(;r>0&&ME(n[2*r-2],n[2*r-1]);r--);for(;a=0;o--){var s=e.getDimensionInfo(i[o].dimension);if("x"===(a=s&&s.coordDim)||"y"===a){r=i[o];break}}if(r){var l=t.getAxis(a),p=Br(r.stops,(function(e){return{coord:l.toGlobalCoord(l.dataToCoord(e.value)),color:e.color}})),c=p.length,d=r.outerColors.slice();c&&p[0].coord>p[c-1].coord&&(p.reverse(),d.reverse());var u=function(e,t){var n,i,a=[],r=e.length;function o(e,t,n){var i=e.coord;return{coord:n,color:Hl((n-i)/(t.coord-i),[e.color,t.color])}}for(var s=0;st){i?a.push(o(i,l,t)):n&&a.push(o(n,l,0),o(n,l,t));break}n&&(a.push(o(n,l,0)),n=null),a.push(l),i=l}}return a}(p,"x"===a?n.getWidth():n.getHeight()),m=u.length;if(!m&&c)return p[0].coord<0?d[1]?d[1]:p[c-1].color:d[0]?d[0]:p[0].color;var h=u[0].coord-10,g=u[m-1].coord+10,f=g-h;if(f<.001)return"transparent";Rr(u,(function(e){e.offset=(e.coord-h)/f})),u.push({offset:m?u[m-1].offset:.5,color:d[1]||"transparent"}),u.unshift({offset:m?u[0].offset:.5,color:d[0]||"transparent"});var y=new Ug(0,0,0,0,u,!0);return y[a]=h,y[a+"2"]=g,y}}}function jE(e,t,n){var i=e.get("showAllSymbol"),a="auto"===i;if(!i||a){var r=n.getAxesByScale("ordinal")[0];if(r&&(!a||!function(e,t){var n=e.getExtent(),i=Math.abs(n[1]-n[0])/e.scale.count();isNaN(i)&&(i=0);for(var a=t.count(),r=Math.max(1,Math.round(a/5)),o=0;oi)return!1;return!0}(r,t))){var o=t.mapDimension(r.dim),s={};return Rr(r.getViewLabels(),(function(e){var t=r.scale.getRawOrdinalNumber(e.tickValue);s[t]=1})),function(e){return!s.hasOwnProperty(t.get(o,e))}}}}function HE(e,t){return[e[2*t],e[2*t+1]]}function WE(e){if(e.get(["endLabel","show"]))return!0;for(var t=0;t0&&"bolder"===e.get(["emphasis","lineStyle","width"]))&&(m.getState("emphasis").style.lineWidth=+m.style.lineWidth+1);Um(m).seriesIndex=e.seriesIndex,Oh(m,P,D,O);var A=GE(e.get("smooth")),F=e.get("smoothMonotone");if(m.setShape({smooth:A,smoothMonotone:F,connectNulls:S}),h){var R=o.getCalculationInfo("stackedOnSeries"),B=0;h.useStyle(Pr(l.getAreaStyle(),{fill:E,opacity:.7,lineJoin:"bevel",decal:o.getVisual("style").decal})),R&&(B=GE(R.get("smooth"))),h.setShape({smooth:A,stackedOnSmooth:B,smoothMonotone:F,connectNulls:S}),Bh(h,e,"areaStyle"),Um(h).seriesIndex=e.seriesIndex,Oh(h,P,D,O)}var N=function(e){i._changePolyState(e)};o.eachItemGraphicEl((function(e){e&&(e.onHoverStateChange=N)})),this._polyline.onHoverStateChange=N,this._data=o,this._coordSys=a,this._stackedOnPoints=b,this._points=p,this._step=I,this._valueOrigin=v,e.get("triggerLineEvent")&&(this.packEventData(e,m),h&&this.packEventData(e,h))},t.prototype.packEventData=function(e,t){Um(t).eventData={componentType:"series",componentSubType:"line",componentIndex:e.componentIndex,seriesIndex:e.seriesIndex,seriesName:e.name,seriesType:"line"}},t.prototype.highlight=function(e,t,n,i){var a=e.getData(),r=Sd(a,i);if(this._changePolyState("emphasis"),!(r instanceof Array)&&null!=r&&r>=0){var o=a.getLayout("points"),s=a.getItemGraphicEl(r);if(!s){var l=o[2*r],p=o[2*r+1];if(isNaN(l)||isNaN(p))return;if(this._clipShapeForSymbol&&!this._clipShapeForSymbol.contain(l,p))return;var c=e.get("zlevel")||0,d=e.get("z")||0;(s=new vE(a,r)).x=l,s.y=p,s.setZ(c,d);var u=s.getSymbolPath().getTextContent();u&&(u.zlevel=c,u.z=d,u.z2=this._polyline.z2+1),s.__temp=!0,a.setItemGraphicEl(r,s),s.stopSymbolAnimation(!0),this.group.add(s)}s.highlight()}else vw.prototype.highlight.call(this,e,t,n,i)},t.prototype.downplay=function(e,t,n,i){var a=e.getData(),r=Sd(a,i);if(this._changePolyState("normal"),null!=r&&r>=0){var o=a.getItemGraphicEl(r);o&&(o.__temp?(a.setItemGraphicEl(r,null),this.group.remove(o)):o.downplay())}else vw.prototype.downplay.call(this,e,t,n,i)},t.prototype._changePolyState=function(e){var t=this._polygon;mh(this._polyline,e),t&&mh(t,e)},t.prototype._newPolyline=function(e){var t=this._polyline;return t&&this._lineGroup.remove(t),t=new DE({shape:{points:e},segmentIgnoreThreshold:2,z2:10}),this._lineGroup.add(t),this._polyline=t,t},t.prototype._newPolygon=function(e,t){var n=this._polygon;return n&&this._lineGroup.remove(n),n=new AE({shape:{points:e,stackedOnPoints:t},segmentIgnoreThreshold:2}),this._lineGroup.add(n),this._polygon=n,n},t.prototype._initSymbolLabelAnimation=function(e,t,n){var i,a,r=t.getBaseAxis(),o=r.inverse;"cartesian2d"===t.type?(i=r.isHorizontal(),a=!1):"polar"===t.type&&(i="angle"===r.dim,a=!0);var s=e.hostModel,l=s.get("animationDuration");jr(l)&&(l=l(null));var p=s.get("animationDelay")||0,c=jr(p)?p(null):p;e.eachItemGraphicEl((function(e,r){var s=e;if(s){var d=[e.x,e.y],u=void 0,m=void 0,h=void 0;if(n)if(a){var g=n,f=t.pointToCoord(d);i?(u=g.startAngle,m=g.endAngle,h=-f[1]/180*Math.PI):(u=g.r0,m=g.r,h=f[0])}else{var y=n;i?(u=y.x,m=y.x+y.width,h=e.x):(u=y.y+y.height,m=y.y,h=e.y)}var v=m===u?0:(h-u)/(m-u);o&&(v=1-v);var x=jr(p)?p(r):l*v+c,b=s.getSymbolPath(),w=b.getTextContent();s.attr({scaleX:0,scaleY:0}),s.animateTo({scaleX:1,scaleY:1},{duration:200,setToFinal:!0,delay:x}),w&&w.animateFrom({style:{opacity:0}},{duration:300,delay:x}),b.disableLabelAnimation=!0}}))},t.prototype._initOrUpdateEndLabel=function(e,t,n){var i=e.getModel("endLabel");if(WE(e)){var a=e.getData(),r=this._polyline,o=a.getLayout("points");if(!o)return r.removeTextContent(),void(this._endLabel=null);var s=this._endLabel;s||((s=this._endLabel=new Pm({z2:200})).ignoreClip=!0,r.setTextContent(this._endLabel),r.disableLabelAnimation=!0);var l=function(e){for(var t,n,i=e.length/2;i>0&&(t=e[2*i-2],n=e[2*i-1],isNaN(t)||isNaN(n));i--);return i-1}(o);l>=0&&(zf(r,Uf(e,"endLabel"),{inheritColor:n,labelFetcher:e,labelDataIndex:l,defaultText:function(e,t,n){return null!=n?yE(a,n):fE(a,e)},enableTextSetter:!0},function(e,t){var n=t.getBaseAxis(),i=n.isHorizontal(),a=n.inverse,r=i?a?"right":"left":"center",o=i?"middle":a?"top":"bottom";return{normal:{align:e.get("align")||r,verticalAlign:e.get("verticalAlign")||o}}}(i,t)),r.textConfig.position=null)}else this._endLabel&&(this._polyline.removeTextContent(),this._endLabel=null)},t.prototype._endLabelOnDuring=function(e,t,n,i,a,r,o){var s=this._endLabel,l=this._polyline;if(s){e<1&&null==i.originalX&&(i.originalX=s.x,i.originalY=s.y);var p=n.getLayout("points"),c=n.hostModel,d=c.get("connectNulls"),u=r.get("precision"),m=r.get("distance")||0,h=o.getBaseAxis(),g=h.isHorizontal(),f=h.inverse,y=t.shape,v=f?g?y.x:y.y+y.height:g?y.x+y.width:y.y,x=(g?m:0)*(f?-1:1),b=(g?0:-m)*(f?-1:1),w=g?"x":"y",S=function(e,t,n){for(var i,a,r=e.length/2,o="x"===n?0:1,s=0,l=-1,p=0;p=t||i>=t&&a<=t){l=p;break}s=p,i=a}else i=a;return{range:[s,l],t:(t-i)/(a-i)}}(p,v,w),C=S.range,_=C[1]-C[0],T=void 0;if(_>=1){if(_>1&&!d){var I=HE(p,C[0]);s.attr({x:I[0]+x,y:I[1]+b}),a&&(T=c.getRawValue(C[0]))}else{(I=l.getPointOn(v,w))&&s.attr({x:I[0]+x,y:I[1]+b});var E=c.getRawValue(C[0]),M=c.getRawValue(C[1]);a&&(T=Od(n,u,E,M,S.t))}i.lastFrameIndex=C[0]}else{var k=1===e||i.lastFrameIndex>0?C[0]:0;I=HE(p,k);a&&(T=c.getRawValue(k)),s.attr({x:I[0]+x,y:I[1]+b})}if(a){var P=Zf(s);"function"==typeof P.setLabelText&&P.setLabelText(T)}}},t.prototype._doUpdateAnimation=function(e,t,n,i,a,r,o){var s=this._polyline,l=this._polygon,p=e.hostModel,c=function(e,t,n,i,a,r,o){for(var s=function(e,t){var n=[];return t.diff(e).add((function(e){n.push({cmd:"+",idx:e})})).update((function(e,t){n.push({cmd:"=",idx:t,idx1:e})})).remove((function(e){n.push({cmd:"-",idx:e})})).execute(),n}(e,t),l=[],p=[],c=[],d=[],u=[],m=[],h=[],g=_E(a,t,o),f=e.getLayout("points")||[],y=t.getLayout("points")||[],v=0;v3e3||l&&qE(u,h)>3e3)return s.stopAnimation(),s.setShape({points:m}),void(l&&(l.stopAnimation(),l.setShape({points:m,stackedOnPoints:h})));s.shape.__points=c.current,s.shape.points=d;var g={shape:{points:m}};c.current!==d&&(g.shape.__points=c.next),s.stopAnimation(),tf(s,g,p),l&&(l.setShape({points:d,stackedOnPoints:u}),l.stopAnimation(),tf(l,{shape:{stackedOnPoints:h}},p),s.shape.points!==l.shape.points&&(l.shape.points=s.shape.points));for(var f=[],y=c.status,v=0;vt&&(t=e[n]);return isFinite(t)?t:NaN},min:function(e){for(var t=1/0,n=0;nt&&(t=r,n=a)}return isFinite(n)?n:NaN},nearest:function(e){return e[0]}},ZE=function(e){return Math.round(e.length/2)};function QE(e){return{seriesType:e,reset:function(e,t,n){var i=e.getData(),a=e.get("sampling"),r=e.coordinateSystem,o=i.count();if(o>10&&"cartesian2d"===r.type&&a){var s=r.getBaseAxis(),l=r.getOtherAxis(s),p=s.getExtent(),c=n.getDevicePixelRatio(),d=Math.abs(p[1]-p[0])*(c||1),u=Math.round(o/d);if(isFinite(u)&&u>1){"lttb"===a&&e.setData(i.lttbDownSample(i.mapDimension(l.dim),1/u));var m=void 0;Hr(a)?m=XE[a]:jr(a)&&(m=a),m&&e.setData(i.downSample(i.mapDimension(l.dim),1/u,m,ZE))}}}}}function JE(e){e.registerChartView(KE),e.registerSeriesModel(gE),e.registerLayout(YE("line",!0)),e.registerVisual({seriesType:"line",reset:function(e){var t=e.getData(),n=e.getModel("lineStyle").getLineStyle();n&&!n.stroke&&(n.stroke=t.getVisual("style").fill),t.setVisual("legendLineStyle",n)}}),e.registerProcessor(e.PRIORITY.PROCESSOR.STATISTIC,QE("line"))}var eM=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.getInitialData=function(e,t){return K_(null,this,{useEncodeDefaulter:!0})},t.prototype.getMarkerPosition=function(e,t,n){var i=this.coordinateSystem;if(i&&i.clampData){var a=i.clampData(e),r=i.dataToPoint(a);if(n)Rr(i.getAxes(),(function(e,n){if("category"===e.type&&null!=t){var i=e.getTicksCoords(),o=e.getTickModel().get("alignWithLabel"),s=a[n],l="x1"===t[n]||"y1"===t[n];if(l&&!o&&(s+=1),i.length<2)return;if(2===i.length)return void(r[n]=e.toGlobalCoord(e.getExtent()[l?1:0]));for(var p=void 0,c=void 0,d=1,u=0;us){c=(m+p)/2;break}1===u&&(d=h-i[0].tickValue)}null==c&&(p?p&&(c=i[i.length-1].coord):c=i[0].coord),r[n]=e.toGlobalCoord(c)}}));else{var o=this.getData(),s=o.getLayout("offset"),l=o.getLayout("size"),p=i.getBaseAxis().isHorizontal()?0:1;r[p]+=s+l/2}return r}return[NaN,NaN]},t.type="series.__base_bar__",t.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,barMinHeight:0,barMinAngle:0,large:!1,largeThreshold:400,progressive:3e3,progressiveChunkMode:"mod"},t}(ow);ow.registerClass(eM);var tM=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.getInitialData=function(){return K_(null,this,{useEncodeDefaulter:!0,createInvertedIndices:!!this.get("realtimeSort",!0)||null})},t.prototype.getProgressive=function(){return!!this.get("large")&&this.get("progressive")},t.prototype.getProgressiveThreshold=function(){var e=this.get("progressiveThreshold"),t=this.get("largeThreshold");return t>e&&(e=t),e},t.prototype.brushSelector=function(e,t,n){return n.rect(t.getItemLayout(e))},t.type="series.bar",t.dependencies=["grid","polar"],t.defaultOption=gy(eM.defaultOption,{clip:!0,roundCap:!1,showBackground:!1,backgroundStyle:{color:"rgba(180, 180, 180, 0.2)",borderColor:null,borderWidth:0,borderType:"solid",borderRadius:0,shadowBlur:0,shadowColor:null,shadowOffsetX:0,shadowOffsetY:0,opacity:1},select:{itemStyle:{borderColor:"#212121"}},realtimeSort:!1}),t}(eM),nM=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0},iM=function(e){function t(t){var n=e.call(this,t)||this;return n.type="sausage",n}return ze(t,e),t.prototype.getDefaultShape=function(){return new nM},t.prototype.buildPath=function(e,t){var n=t.cx,i=t.cy,a=Math.max(t.r0||0,0),r=Math.max(t.r,0),o=.5*(r-a),s=a+o,l=t.startAngle,p=t.endAngle,c=t.clockwise,d=2*Math.PI,u=c?p-lr)return!0;r=p}return!1},t.prototype._isOrderDifferentInView=function(e,t){for(var n=t.scale,i=n.getExtent(),a=Math.max(0,i[0]),r=Math.min(i[1],n.getOrdinalMeta().categories.length-1);a<=r;++a)if(e.ordinalNumbers[a]!==n.getRawOrdinalNumber(a))return!0},t.prototype._updateSortWithinSameData=function(e,t,n,i){if(this._isOrderChangedWithinSameData(e,t,n)){var a=this._dataSort(e,n,t);this._isOrderDifferentInView(a,n)&&(this._removeOnRenderedListener(i),i.dispatchAction({type:"changeAxisOrder",componentType:n.dim+"Axis",axisId:n.index,sortInfo:a}))}},t.prototype._dispatchInitSort=function(e,t,n){var i=t.baseAxis,a=this._dataSort(e,i,(function(n){return e.get(e.mapDimension(t.otherAxis.dim),n)}));n.dispatchAction({type:"changeAxisOrder",componentType:i.dim+"Axis",isInitSort:!0,axisId:i.index,sortInfo:a})},t.prototype.remove=function(e,t){this._clear(this._model),this._removeOnRenderedListener(t)},t.prototype.dispose=function(e,t){this._removeOnRenderedListener(t)},t.prototype._removeOnRenderedListener=function(e){this._onRendered&&(e.getZr().off("rendered",this._onRendered),this._onRendered=null)},t.prototype._clear=function(e){var t=this.group,n=this._data;e&&e.isAnimationEnabled()&&n&&!this._isLargeDraw?(this._removeBackground(),this._backgroundEls=[],n.eachItemGraphicEl((function(t){sf(t,e,Um(t).dataIndex)}))):t.removeAll(),this._data=null,this._isFirstFrame=!0},t.prototype._removeBackground=function(){this.group.remove(this._backgroundGroup),this._backgroundGroup=null},t.type="bar",t}(vw),cM={cartesian2d:function(e,t){var n=t.width<0?-1:1,i=t.height<0?-1:1;n<0&&(t.x+=t.width,t.width=-t.width),i<0&&(t.y+=t.height,t.height=-t.height);var a=e.x+e.width,r=e.y+e.height,o=sM(t.x,e.x),s=lM(t.x+t.width,a),l=sM(t.y,e.y),p=lM(t.y+t.height,r),c=sa?s:o,t.y=d&&l>r?p:l,t.width=c?0:s-o,t.height=d?0:p-l,n<0&&(t.x+=t.width,t.width=-t.width),i<0&&(t.y+=t.height,t.height=-t.height),c||d},polar:function(e,t){var n=t.r0<=t.r?1:-1;if(n<0){var i=t.r;t.r=t.r0,t.r0=i}var a=lM(t.r,e.r),r=sM(t.r0,e.r0);t.r=a,t.r0=r;var o=a-r<0;if(n<0){i=t.r;t.r=t.r0,t.r0=i}return o}},dM={cartesian2d:function(e,t,n,i,a,r,o,s,l){var p=new Em({shape:kr({},i),z2:1});(p.__dataIndex=n,p.name="item",r)&&(p.shape[a?"height":"width"]=0);return p},polar:function(e,t,n,i,a,r,o,s,l){var p=!a&&l?iM:_g,c=new p({shape:i,z2:1});c.name="item";var d,u,m=vM(a);if(c.calculateTextPosition=(d=m,u=({isRoundCap:p===iM}||{}).isRoundCap,function(e,t,n){var i=t.position;if(!i||i instanceof Array)return yc(e,t,n);var a=d(i),r=null!=t.distance?t.distance:5,o=this.shape,s=o.cx,l=o.cy,p=o.r,c=o.r0,m=(p+c)/2,h=o.startAngle,g=o.endAngle,f=(h+g)/2,y=u?Math.abs(p-c)/2:0,v=Math.cos,x=Math.sin,b=s+p*v(h),w=l+p*x(h),S="left",C="top";switch(a){case"startArc":b=s+(c-r)*v(f),w=l+(c-r)*x(f),S="center",C="top";break;case"insideStartArc":b=s+(c+r)*v(f),w=l+(c+r)*x(f),S="center",C="bottom";break;case"startAngle":b=s+m*v(h)+aM(h,r+y,!1),w=l+m*x(h)+rM(h,r+y,!1),S="right",C="middle";break;case"insideStartAngle":b=s+m*v(h)+aM(h,-r+y,!1),w=l+m*x(h)+rM(h,-r+y,!1),S="left",C="middle";break;case"middle":b=s+m*v(f),w=l+m*x(f),S="center",C="middle";break;case"endArc":b=s+(p+r)*v(f),w=l+(p+r)*x(f),S="center",C="bottom";break;case"insideEndArc":b=s+(p-r)*v(f),w=l+(p-r)*x(f),S="center",C="top";break;case"endAngle":b=s+m*v(g)+aM(g,r+y,!0),w=l+m*x(g)+rM(g,r+y,!0),S="left",C="middle";break;case"insideEndAngle":b=s+m*v(g)+aM(g,-r+y,!0),w=l+m*x(g)+rM(g,-r+y,!0),S="right",C="middle";break;default:return yc(e,t,n)}return(e=e||{}).x=b,e.y=w,e.align=S,e.verticalAlign=C,e}),r){var h=a?"r":"endAngle",g={};c.shape[h]=a?i.r0:i.startAngle,g[h]=i[h],(s?tf:nf)(c,{shape:g},r)}return c}};function uM(e,t,n,i,a,r,o,s){var l,p;r?(p={x:i.x,width:i.width},l={y:i.y,height:i.height}):(p={y:i.y,height:i.height},l={x:i.x,width:i.width}),s||(o?tf:nf)(n,{shape:l},t,a,null),(o?tf:nf)(n,{shape:p},t?e.baseAxis.model:null,a)}function mM(e,t){for(var n=0;n0?1:-1,o=i.height>0?1:-1;return{x:i.x+r*a/2,y:i.y+o*a/2,width:i.width-r*a,height:i.height-o*a}},polar:function(e,t,n){var i=e.getItemLayout(t);return{cx:i.cx,cy:i.cy,r0:i.r0,r:i.r,startAngle:i.startAngle,endAngle:i.endAngle,clockwise:i.clockwise}}};function vM(e){return function(e){var t=e?"Arc":"Angle";return function(e){switch(e){case"start":case"insideStart":case"end":case"insideEnd":return e+t;default:return e}}}(e)}function xM(e,t,n,i,a,r,o,s){var l=t.getItemVisual(n,"style");if(s){if(!r.get("roundCap")){var p=e.shape;kr(p,oM(i.getModel("itemStyle"),p,!0)),e.setShape(p)}}else{var c=i.get(["itemStyle","borderRadius"])||0;e.setShape("r",c)}e.useStyle(l);var d=i.getShallow("cursor");d&&e.attr("cursor",d);var u=s?o?a.r>=a.r0?"endArc":"startArc":a.endAngle>=a.startAngle?"endAngle":"startAngle":o?a.height>=0?"bottom":"top":a.width>=0?"right":"left",m=Uf(i);zf(e,m,{labelFetcher:r,labelDataIndex:n,defaultText:fE(r.getData(),n),inheritColor:l.fill,defaultOpacity:l.opacity,defaultOutsidePosition:u});var h=e.getTextContent();if(s&&h){var g=i.get(["label","position"]);e.textConfig.inside="middle"===g||null,function(e,t,n,i){if($r(i))e.setTextConfig({rotation:i});else if(Ur(t))e.setTextConfig({rotation:0});else{var a,r=e.shape,o=r.clockwise?r.startAngle:r.endAngle,s=r.clockwise?r.endAngle:r.startAngle,l=(o+s)/2,p=n(t);switch(p){case"startArc":case"insideStartArc":case"middle":case"insideEndArc":case"endArc":a=l;break;case"startAngle":case"insideStartAngle":a=o;break;case"endAngle":case"insideEndAngle":a=s;break;default:return void e.setTextConfig({rotation:0})}var c=1.5*Math.PI-a;"middle"===p&&c>Math.PI/2&&c<1.5*Math.PI&&(c-=Math.PI),e.setTextConfig({rotation:c})}}(e,"outside"===g?u:g,vM(o),i.get(["label","rotate"]))}Qf(h,m,r.getRawValue(n),(function(e){return yE(t,e)}));var f=i.getModel(["emphasis"]);Oh(e,f.get("focus"),f.get("blurScope"),f.get("disabled")),Bh(e,i),function(e){return null!=e.startAngle&&null!=e.endAngle&&e.startAngle===e.endAngle}(a)&&(e.style.fill="none",e.style.stroke="none",Rr(e.states,(function(e){e.style&&(e.style.fill=e.style.stroke="none")})))}var bM=function(){},wM=function(e){function t(t){var n=e.call(this,t)||this;return n.type="largeBar",n}return ze(t,e),t.prototype.getDefaultShape=function(){return new bM},t.prototype.buildPath=function(e,t){for(var n=t.points,i=this.baseDimIdx,a=1-this.baseDimIdx,r=[],o=[],s=this.barWidth,l=0;l=s[0]&&t<=s[0]+l[0]&&n>=s[1]&&n<=s[1]+l[1])return o[c]}return-1}(this,e.offsetX,e.offsetY);Um(this).dataIndex=t>=0?t:null}),30,!1);function _M(e,t,n){if(NE(n,"cartesian2d")){var i=t,a=n.getArea();return{x:e?i.x:a.x,y:e?a.y:i.y,width:e?i.width:a.width,height:e?a.height:i.height}}var r=t;return{cx:(a=n.getArea()).cx,cy:a.cy,r0:e?a.r0:r.r0,r:e?a.r:r.r,startAngle:e?r.startAngle:0,endAngle:e?r.endAngle:2*Math.PI}}function TM(e){e.registerChartView(pM),e.registerSeriesModel(tM),e.registerLayout(e.PRIORITY.VISUAL.LAYOUT,zr(xT,"bar")),e.registerLayout(e.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT,bT("bar")),e.registerProcessor(e.PRIORITY.PROCESSOR.STATISTIC,QE("bar")),e.registerAction({type:"changeAxisOrder",event:"changeAxisOrder",update:"update"},(function(e,t){var n=e.componentType||"series";t.eachComponent({mainType:n,query:e},(function(t){e.sortInfo&&t.axis.setCategorySortInfo(e.sortInfo)}))}))}var IM=2*Math.PI,EM=Math.PI/180;function MM(e,t){return gv(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()})}function kM(e,t){var n=MM(e,t),i=e.get("center"),a=e.get("radius");Ur(a)||(a=[0,a]);var r,o,s=Vc(n.width,t.getWidth()),l=Vc(n.height,t.getHeight()),p=Math.min(s,l),c=Vc(a[0],p/2),d=Vc(a[1],p/2),u=e.coordinateSystem;if(u){var m=u.dataToPoint(i);r=m[0]||0,o=m[1]||0}else Ur(i)||(i=[i,i]),r=Vc(i[0],s)+n.x,o=Vc(i[1],l)+n.y;return{cx:r,cy:o,r0:c,r:d}}function PM(e,t,n){t.eachSeriesByType(e,(function(e){var t=e.getData(),i=t.mapDimension("value"),a=MM(e,n),r=kM(e,n),o=r.cx,s=r.cy,l=r.r,p=r.r0,c=-e.get("startAngle")*EM,d=e.get("endAngle"),u=e.get("padAngle")*EM;d="auto"===d?c-IM:-d*EM;var m=e.get("minAngle")*EM+u,h=0;t.each(i,(function(e){!isNaN(e)&&h++}));var g=t.getSum(i),f=Math.PI/(g||h)*2,y=e.get("clockwise"),v=e.get("roseType"),x=e.get("stillShowZeroSum"),b=t.getDataExtent(i);b[0]=0;var w=y?1:-1,S=[c,d],C=w*u/2;Ku(S,!y),c=S[0],d=S[1];var _=Math.abs(d-c),T=_,I=0,E=c;if(t.setLayout({viewRect:a,r:l}),t.each(i,(function(e,n){var i;if(isNaN(e))t.setItemLayout(n,{angle:NaN,startAngle:NaN,endAngle:NaN,clockwise:y,cx:o,cy:s,r0:p,r:v?NaN:l});else{(i="area"!==v?0===g&&x?f:e*f:_/h)i?c=r=E+w*i/2:(r=E+C,c=a-C),t.setItemLayout(n,{angle:i,startAngle:r,endAngle:c,clockwise:y,cx:o,cy:s,r0:p,r:v?Lc(e,b,[p,l]):l}),E=a}})),Tn?o:r,c=Math.abs(l.label.y-n);if(c>=p.maxY){var d=l.label.x-t-l.len2*a,u=i+l.len,h=Math.abs(d)e.unconstrainedWidth?null:m:null;i.setStyle("width",h)}var g=i.getBoundingRect();r.width=g.width;var f=(i.style.margin||0)+2.1;r.height=g.height+f,r.y-=(r.height-d)/2}}}function RM(e){return"center"===e.position}function BM(e){var t,n,i=e.getData(),a=[],r=!1,o=(e.get("minShowLabelAngle")||0)*OM,s=i.getLayout("viewRect"),l=i.getLayout("r"),p=s.width,c=s.x,d=s.y,u=s.height;function m(e){e.ignore=!0}i.each((function(e){var s=i.getItemGraphicEl(e),d=s.shape,u=s.getTextContent(),h=s.getTextGuideLine(),g=i.getItemModel(e),f=g.getModel("label"),y=f.get("position")||g.get(["emphasis","label","position"]),v=f.get("distanceToLabelLine"),x=f.get("alignTo"),b=Vc(f.get("edgeDistance"),p),w=f.get("bleedMargin"),S=g.getModel("labelLine"),C=S.get("length");C=Vc(C,p);var _=S.get("length2");if(_=Vc(_,p),Math.abs(d.endAngle-d.startAngle)0?"right":"left":P>0?"left":"right"}var L=Math.PI,V=0,q=f.get("rotate");if($r(q))V=q*(L/180);else if("center"===y)V=0;else if("radial"===q||!0===q){V=P<0?-k+L:-k}else if("tangential"===q&&"outside"!==y&&"outer"!==y){var G=Math.atan2(P,D);G<0&&(G=2*L+G),D>0&&(G=L+G),V=G-L}if(r=!!V,u.x=T,u.y=I,u.rotation=V,u.setStyle({verticalAlign:"middle"}),O){u.setStyle({align:M});var z=u.states.select;z&&(z.x+=u.x,z.y+=u.y)}else{var U=u.getBoundingRect().clone();U.applyTransform(u.getComputedTransform());var j=(u.style.margin||0)+2.1;U.y-=j/2,U.height+=j,a.push({label:u,labelLine:h,position:y,len:C,len2:_,minTurnAngle:S.get("minTurnAngle"),maxSurfaceAngle:S.get("maxSurfaceAngle"),surfaceNormal:new ws(P,D),linePoints:E,textAlign:M,labelDistance:v,labelAlignTo:x,edgeDistance:b,bleedMargin:w,rect:U,unconstrainedWidth:U.width,labelStyleWidth:u.style.width})}s.setTextConfig({inside:O})}})),!r&&e.get("avoidLabelOverlap")&&function(e,t,n,i,a,r,o,s){for(var l=[],p=[],c=Number.MAX_VALUE,d=-Number.MAX_VALUE,u=0;u0){for(var l=r.getItemLayout(0),p=1;isNaN(l&&l.startAngle)&&p=n.r0}},t.type="pie",t}(vw);function VM(e,t,n){t=Ur(t)&&{coordDimensions:t}||kr({encodeDefine:e.getEncode()},t);var i=e.getSource(),a=q_(i,t).dimensions,r=new V_(a,e);return r.initData(i,n),r}var qM=function(){function e(e,t){this._getDataWithEncodedVisual=e,this._getRawData=t}return e.prototype.getAllNames=function(){var e=this._getRawData();return e.mapArray(e.getName)},e.prototype.containName=function(e){return this._getRawData().indexOfName(e)>=0},e.prototype.indexOfName=function(e){return this._getDataWithEncodedVisual().indexOfName(e)},e.prototype.getItemVisual=function(e,t){return this._getDataWithEncodedVisual().getItemVisual(e,t)},e}(),GM=Cd(),zM=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.init=function(t){e.prototype.init.apply(this,arguments),this.legendVisualProvider=new qM(Gr(this.getData,this),Gr(this.getRawData,this)),this._defaultLabelLine(t)},t.prototype.mergeOption=function(){e.prototype.mergeOption.apply(this,arguments)},t.prototype.getInitialData=function(){return VM(this,{coordDimensions:["value"],encodeDefaulter:zr(qv,this)})},t.prototype.getDataParams=function(t){var n=this.getData(),i=GM(n),a=i.seats;if(!a){var r=[];n.each(n.mapDimension("value"),(function(e){r.push(e)})),a=i.seats=Hc(r,n.hostModel.get("percentPrecision"))}var o=e.prototype.getDataParams.call(this,t);return o.percent=a[t]||0,o.$vars.push("percent"),o},t.prototype._defaultLabelLine=function(e){ud(e,"labelLine",["show"]);var t=e.labelLine,n=e.emphasis.labelLine;t.show=t.show&&e.label.show,n.show=n.show&&e.emphasis.label.show},t.type="series.pie",t.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,endAngle:"auto",padAngle:0,minAngle:0,minShowLabelAngle:0,selectedOffset:10,percentPrecision:2,stillShowZeroSum:!0,left:0,top:0,right:0,bottom:0,width:null,height:null,label:{rotate:0,show:!0,overflow:"truncate",position:"outer",alignTo:"none",edgeDistance:"25%",bleedMargin:10,distanceToLabelLine:5},labelLine:{show:!0,length:15,length2:15,smooth:!1,minTurnAngle:90,maxSurfaceAngle:90,lineStyle:{width:1,type:"solid"}},itemStyle:{borderWidth:1,borderJoin:"round"},showEmptyCircle:!0,emptyCircleStyle:{color:"lightgray",opacity:1},labelLayout:{hideOverlap:!0},emphasis:{scale:!0,scaleSize:5},avoidLabelOverlap:!0,animationType:"expansion",animationDuration:1e3,animationTypeUpdate:"transition",animationEasingUpdate:"cubicInOut",animationDurationUpdate:500,animationEasing:"cubicInOut"},t}(ow);function UM(e){e.registerChartView(LM),e.registerSeriesModel(zM),fS("pie",e.registerAction),e.registerLayout(zr(PM,"pie")),e.registerProcessor(DM("pie")),e.registerProcessor(function(e){return{seriesType:e,reset:function(e,t){var n=e.getData();n.filterSelf((function(e){var t=n.mapDimension("value"),i=n.get(t,e);return!($r(i)&&!isNaN(i)&&i<0)}))}}}("pie"))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n}ze(t,e),t.prototype.getInitialData=function(e,t){return K_(null,this,{useEncodeDefaulter:!0})},t.prototype.getProgressive=function(){var e=this.option.progressive;return null==e?this.option.large?5e3:this.get("progressive"):e},t.prototype.getProgressiveThreshold=function(){var e=this.option.progressiveThreshold;return null==e?this.option.large?1e4:this.get("progressiveThreshold"):e},t.prototype.brushSelector=function(e,t,n){return n.point(t.getItemLayout(e))},t.prototype.getZLevelKey=function(){return this.getData().count()>this.getProgressiveThreshold()?this.id:""},t.type="series.scatter",t.dependencies=["grid","polar","geo","singleAxis","calendar"],t.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,symbolSize:10,large:!1,largeThreshold:2e3,itemStyle:{opacity:.8},emphasis:{scale:!0},clip:!0,select:{itemStyle:{borderColor:"#212121"}},universalTransition:{divideShape:"clone"}}}(ow);var jM=function(){},HM=function(e){function t(t){var n=e.call(this,t)||this;return n._off=0,n.hoverDataIdx=-1,n}return ze(t,e),t.prototype.getDefaultShape=function(){return new jM},t.prototype.reset=function(){this.notClear=!1,this._off=0},t.prototype.buildPath=function(e,t){var n,i=t.points,a=t.size,r=this.symbolProxy,o=r.shape,s=e.getContext?e.getContext():e,l=s&&a[0]<4,p=this.softClipShape;if(l)this._ctx=s;else{for(this._ctx=null,n=this._off;n=0;s--){var l=2*s,p=i[l]-r/2,c=i[l+1]-o/2;if(e>=p&&t>=c&&e<=p+r&&t<=c+o)return s}return-1},t.prototype.contain=function(e,t){var n=this.transformCoordToLocal(e,t),i=this.getBoundingRect();return e=n[0],t=n[1],i.contain(e,t)?(this.hoverDataIdx=this.findDataIndex(e,t))>=0:(this.hoverDataIdx=-1,!1)},t.prototype.getBoundingRect=function(){var e=this._rect;if(!e){for(var t=this.shape,n=t.points,i=t.size,a=i[0],r=i[1],o=1/0,s=1/0,l=-1/0,p=-1/0,c=0;c=0&&(l.dataIndex=n+(e.startIndex||0))}))},e.prototype.remove=function(){this._clear()},e.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},e}(),$M=(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.render=function(e,t,n){var i=e.getData();this._updateSymbolDraw(i,e).updateData(i,{clipShape:this._getClipShape(e)}),this._finished=!0},t.prototype.incrementalPrepareRender=function(e,t,n){var i=e.getData();this._updateSymbolDraw(i,e).incrementalPrepareUpdate(i),this._finished=!1},t.prototype.incrementalRender=function(e,t,n){this._symbolDraw.incrementalUpdate(e,t.getData(),{clipShape:this._getClipShape(t)}),this._finished=e.end===t.getData().count()},t.prototype.updateTransform=function(e,t,n){var i=e.getData();if(this.group.dirty(),!this._finished||i.count()>1e4)return{update:!0};var a=YE("").reset(e,t,n);a.progress&&a.progress({start:0,end:i.count(),count:i.count()},i),this._symbolDraw.updateLayout(i)},t.prototype.eachRendered=function(e){this._symbolDraw&&this._symbolDraw.eachRendered(e)},t.prototype._getClipShape=function(e){if(e.get("clip",!0)){var t=e.coordinateSystem;return t&&t.getArea&&t.getArea(.1)}},t.prototype._updateSymbolDraw=function(e,t){var n=this._symbolDraw,i=t.pipelineContext.large;return n&&i===this._isLargeDraw||(n&&n.remove(),n=this._symbolDraw=i?new WM:new CE,this._isLargeDraw=i,this.group.removeAll()),this.group.add(n.group),n},t.prototype.remove=function(e,t){this._symbolDraw&&this._symbolDraw.remove(!0),this._symbolDraw=null},t.prototype.dispose=function(){},t.type="scatter"}(vw),function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.type="grid",t.dependencies=["xAxis","yAxis"],t.layoutMode="box",t.defaultOption={show:!1,z:0,left:"10%",top:60,right:"10%",bottom:70,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"},t}(Sv)),KM=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.getCoordSysModel=function(){return this.getReferringComponents("grid",Ed).models[0]},t.type="cartesian2dAxis",t}(Sv);Ar(KM,tI);var YM={show:!0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#6E7079",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#E0E6F1"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.2)","rgba(210,219,238,0.2)"]}}},XM=Er({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},YM),ZM=Er({boundaryGap:[0,0],axisLine:{show:"auto"},axisTick:{show:"auto"},splitNumber:5,minorTick:{show:!1,splitNumber:5,length:3,lineStyle:{}},minorSplitLine:{show:!1,lineStyle:{color:"#F4F7FD",width:1}}},YM),QM={category:XM,value:ZM,time:Er({splitNumber:6,axisLabel:{showMinLabel:!1,showMaxLabel:!1,rich:{primary:{fontWeight:"bold"}}},splitLine:{show:!1}},ZM),log:Pr({logBase:10},ZM)},JM={value:1,category:1,time:1,log:1};function ek(e,t,n,i){Rr(JM,(function(a,r){var o=Er(Er({},QM[r],!0),i,!0),s=function(e){function n(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t+"Axis."+r,n}return ze(n,e),n.prototype.mergeDefaultAndTheme=function(e,t){var n=yv(this),i=n?xv(e):{};Er(e,t.getTheme().get(r+"Axis")),Er(e,this.getDefaultOption()),e.type=tk(e),n&&vv(e,i,n)},n.prototype.optionUpdated=function(){"category"===this.option.type&&(this.__ordinalMeta=Z_.createByAxisModel(this))},n.prototype.getCategories=function(e){var t=this.option;if("category"===t.type)return e?t.data:this.__ordinalMeta.categories},n.prototype.getOrdinalMeta=function(){return this.__ordinalMeta},n.prototype.getTicksGenerator=function(){var e=this.option;if("value"===e.type)return e.ticksGenerator},n.type=t+"Axis."+r,n.defaultOption=o,n}(n);e.registerComponentModel(s)})),e.registerSubTypeDefaulter(t+"Axis",tk)}function tk(e){return e.type||(e.data?"category":"value")}var nk=function(){function e(e){this.type="cartesian",this._dimList=[],this._axes={},this.name=e||""}return e.prototype.getAxis=function(e){return this._axes[e]},e.prototype.getAxes=function(){return Br(this._dimList,(function(e){return this._axes[e]}),this)},e.prototype.getAxesByScale=function(e){return e=e.toLowerCase(),Lr(this.getAxes(),(function(t){return t.scale.type===e}))},e.prototype.addAxis=function(e){var t=e.dim;this._axes[t]=e,this._dimList.push(t)},e}(),ik=["x","y"];function ak(e){return"interval"===e.type||"time"===e.type}var rk=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type="cartesian2d",t.dimensions=ik,t}return ze(t,e),t.prototype.calcAffineTransform=function(){this._transform=this._invTransform=null;var e=this.getAxis("x").scale,t=this.getAxis("y").scale;if(ak(e)&&ak(t)){var n=e.getExtent(),i=t.getExtent(),a=this.dataToPoint([n[0],i[0]]),r=this.dataToPoint([n[1],i[1]]),o=n[1]-n[0],s=i[1]-i[0];if(o&&s){var l=(r[0]-a[0])/o,p=(r[1]-a[1])/s,c=a[0]-n[0]*l,d=a[1]-i[0]*p,u=this._transform=[l,0,0,p,c,d];this._invTransform=xs([],u)}}},t.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAxis("x")},t.prototype.containPoint=function(e){var t=this.getAxis("x"),n=this.getAxis("y");return t.contain(t.toLocalCoord(e[0]))&&n.contain(n.toLocalCoord(e[1]))},t.prototype.containData=function(e){return this.getAxis("x").containData(e[0])&&this.getAxis("y").containData(e[1])},t.prototype.containZone=function(e,t){var n=this.dataToPoint(e),i=this.dataToPoint(t),a=this.getArea(),r=new Ps(n[0],n[1],i[0]-n[0],i[1]-n[1]);return a.intersect(r)},t.prototype.dataToPoint=function(e,t,n){n=n||[];var i=e[0],a=e[1];if(this._transform&&null!=i&&isFinite(i)&&null!=a&&isFinite(a))return Lo(n,e,this._transform);var r=this.getAxis("x"),o=this.getAxis("y");return n[0]=r.toGlobalCoord(r.dataToCoord(i,t)),n[1]=o.toGlobalCoord(o.dataToCoord(a,t)),n},t.prototype.clampData=function(e,t){var n=this.getAxis("x").scale,i=this.getAxis("y").scale,a=n.getExtent(),r=i.getExtent(),o=n.parse(e[0]),s=i.parse(e[1]);return(t=t||[])[0]=Math.min(Math.max(Math.min(a[0],a[1]),o),Math.max(a[0],a[1])),t[1]=Math.min(Math.max(Math.min(r[0],r[1]),s),Math.max(r[0],r[1])),t},t.prototype.pointToData=function(e,t){var n=[];if(this._invTransform)return Lo(n,e,this._invTransform);var i=this.getAxis("x"),a=this.getAxis("y");return n[0]=i.coordToData(i.toLocalCoord(e[0]),t),n[1]=a.coordToData(a.toLocalCoord(e[1]),t),n},t.prototype.getOtherAxis=function(e){return this.getAxis("x"===e.dim?"y":"x")},t.prototype.getArea=function(e){e=e||0;var t=this.getAxis("x").getGlobalExtent(),n=this.getAxis("y").getGlobalExtent(),i=Math.min(t[0],t[1])-e,a=Math.min(n[0],n[1])-e,r=Math.max(t[0],t[1])-i+e,o=Math.max(n[0],n[1])-a+e;return new Ps(i,a,r,o)},t}(nk),ok=function(e){function t(t,n,i,a,r){var o=e.call(this,t,n,i)||this;return o.index=0,o.type=a||"value",o.position=r||"bottom",o}return ze(t,e),t.prototype.isHorizontal=function(){var e=this.position;return"top"===e||"bottom"===e},t.prototype.getGlobalExtent=function(e){var t=this.getExtent();return t[0]=this.toGlobalCoord(t[0]),t[1]=this.toGlobalCoord(t[1]),e&&t[0]>t[1]&&t.reverse(),t},t.prototype.pointToData=function(e,t){return this.coordToData(this.toLocalCoord(e["x"===this.dim?0:1]),t)},t.prototype.setCategorySortInfo=function(e){if("category"!==this.type)return!1;this.model.option.categorySortInfo=e,this.scale.setSortInfo(e)},t}(MI);function sk(e,t,n){n=n||{};var i=e.coordinateSystem,a=t.axis,r={},o=a.getAxesOnZeroOf()[0],s=a.position,l=o?"onZero":s,p=a.dim,c=i.getRect(),d=[c.x,c.x+c.width,c.y,c.y+c.height],u={left:0,right:1,top:0,bottom:1,onZero:2},m=t.get("offset")||0,h="x"===p?[d[2]-m,d[3]+m]:[d[0]-m,d[1]+m];if(o){var g=o.toGlobalCoord(o.dataToCoord(0));h[u.onZero]=Math.max(Math.min(g,h[1]),h[0])}r.position=["y"===p?h[u[l]]:d[0],"x"===p?h[u[l]]:d[3]],r.rotation=Math.PI/2*("x"===p?0:1);r.labelDirection=r.tickDirection=r.nameDirection={top:-1,bottom:1,left:-1,right:1}[s],r.labelOffset=o?h[u[s]]-h[u.onZero]:0,t.get(["axisTick","inside"])&&(r.tickDirection=-r.tickDirection),no(n.labelInside,t.get(["axisLabel","inside"]))&&(r.labelDirection=-r.labelDirection);var f=t.get(["axisLabel","rotate"]);return r.labelRotate="top"===l?-f:f,r.z2=1,r}function lk(e){return"cartesian2d"===e.get("coordinateSystem")}function pk(e){var t={xAxisModel:null,yAxisModel:null};return Rr(t,(function(n,i){var a=i.replace(/Model$/,""),r=e.getReferringComponents(a,Ed).models[0];t[i]=r})),t}var ck=Math.log;function dk(e,t,n){var i=pT.prototype,a=i.getTicks.call(n),r=i.getTicks.call(n,!0),o=a.length-1,s=i.getInterval.call(n),l=WT(e,t),p=l.extent,c=l.fixMin,d=l.fixMax;if("log"===e.type){var u=ck(e.base);p=[ck(p[0])/u,ck(p[1])/u]}e.setExtent(p[0],p[1]),e.calcNiceExtent({splitNumber:o,fixMin:c,fixMax:d});var m=i.getExtent.call(e);c&&(p[0]=m[0]),d&&(p[1]=m[1]);var h=i.getInterval.call(e),g=p[0],f=p[1];if(c&&d)h=(f-g)/o;else if(c)for(f=p[0]+h*o;fp[0]&&isFinite(g)&&isFinite(p[0]);)h=tT(h),g=p[1]-h*o;else{e.getTicks().length-1>o&&(h=tT(h));var y=h*o;(g=qc((f=Math.ceil(p[1]/h)*h)-y))<0&&p[0]>=0?(g=0,f=qc(y)):f>0&&p[1]<=0&&(f=0,g=-qc(y))}var v=(a[0].value-r[0].value)/s,x=(a[o].value-r[o].value)/s;i.setExtent.call(e,g+h*v,f+h*x),i.setInterval.call(e,h),(v||x)&&i.setNiceExtent.call(e,g+h,f-h)}var uk=function(){function e(e,t,n){this.type="grid",this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this.axisPointerEnabled=!0,this.dimensions=ik,this._initCartesian(e,t,n),this.model=e}return e.prototype.getRect=function(){return this._rect},e.prototype.update=function(e,t){var n=this._axesMap;function i(e){var t,n=qr(e),i=n.length;if(i){for(var a=[],r=i-1;r>=0;r--){var o=(l=e[+n[r]]).model,s=l.scale;J_(s)&&o.get("alignTicks")&&null==o.get("interval")&&null==o.getTicksGenerator()?a.push(l):($T(s,o),J_(s)&&!s.isBlank()&&(t=l))}if(a.length){for(;!t&&a.length;){var l;$T((l=a.pop()).scale,l.model),l.scale.isBlank()||(t=l)}a.length&&t&&Rr(a,(function(e){dk(e.scale,e.model,t.scale)}))}}}this._updateScale(e,this.model),i(n.x),i(n.y);var a={};Rr(n.x,(function(e){hk(n,"y",e,a)})),Rr(n.y,(function(e){hk(n,"x",e,a)})),this.resize(this.model,t)},e.prototype.resize=function(e,t,n){var i=e.getBoxLayoutParams(),a=!n&&e.get("containLabel"),r=gv(i,{width:t.getWidth(),height:t.getHeight()});this._rect=r;var o=this._axesList;function s(){Rr(o,(function(e){var t=e.isHorizontal(),n=t?[0,r.width]:[0,r.height],i=e.inverse?1:0;e.setExtent(n[i],n[1-i]),function(e,t){var n=e.getExtent(),i=n[0]+n[1];e.toGlobalCoord="x"===e.dim?function(e){return e+t}:function(e){return i-e+t},e.toLocalCoord="x"===e.dim?function(e){return e-t}:function(e){return i-e+t}}(e,t?r.x:r.y)}))}s(),a&&(Rr(o,(function(e){if(!e.model.get(["axisLabel","inside"])){var t=function(e){var t=e.model,n=e.scale;if(t.get(["axisLabel","show"])&&!n.isBlank()){var i,a,r=n.getExtent();a=n instanceof sT?n.count():(i=n.getTicks()).length;var o,s=e.getLabelModel(),l=YT(e),p=1;a>40&&(p=Math.ceil(a/40));for(var c=0;c0&&i>0||n<0&&i<0)}(e)}var fk=Math.PI,yk=function(){function e(e,t){this.group=new Mc,this.opt=t,this.axisModel=e,Pr(t,{labelOffset:0,nameDirection:1,tickDirection:1,labelDirection:1,silent:!0,handleAutoShown:function(){return!0}});var n=new Mc({x:t.position[0],y:t.position[1],rotation:t.rotation});n.updateTransform(),this._transformGroup=n}return e.prototype.hasBuilder=function(e){return!!vk[e]},e.prototype.add=function(e){vk[e](this.opt,this.axisModel,this.group,this._transformGroup)},e.prototype.getGroup=function(){return this.group},e.innerTextLayout=function(e,t,n){var i,a,r=$c(t-e);return Kc(r)?(a=n>0?"top":"bottom",i="center"):Kc(r-fk)?(a=n>0?"bottom":"top",i="center"):(a="middle",i=r>0&&r0?"right":"left":n>0?"left":"right"),{rotation:r,textAlign:i,textVerticalAlign:a}},e.makeAxisEventDataBase=function(e){var t={componentType:e.mainType,componentIndex:e.componentIndex};return t[e.mainType+"Index"]=e.componentIndex,t},e.isLabelSilent=function(e){var t=e.get("tooltip");return e.get("silent")||!(e.get("triggerEvent")||t&&t.show)},e}(),vk={axisLine:function(e,t,n,i){var a=t.get(["axisLine","show"]);if("auto"===a&&e.handleAutoShown&&(a=e.handleAutoShown("axisLine")),a){var r=t.axis.getExtent(),o=i.transform,s=[r[0],0],l=[r[1],0],p=s[0]>l[0];o&&(Lo(s,s,o),Lo(l,l,o));var c=kr({lineCap:"round"},t.getModel(["axisLine","lineStyle"]).getLineStyle()),d=new Fg({shape:{x1:s[0],y1:s[1],x2:l[0],y2:l[1]},style:c,strokeContainThreshold:e.strokeContainThreshold||5,silent:!0,z2:1});Sf(d.shape,d.style.lineWidth),d.anid="line",n.add(d);var u=t.get(["axisLine","symbol"]);if(null!=u){var m=t.get(["axisLine","symbolSize"]);Hr(u)&&(u=[u,u]),(Hr(m)||$r(m))&&(m=[m,m]);var h=OS(t.get(["axisLine","symbolOffset"])||0,m),g=m[0],f=m[1];Rr([{rotate:e.rotation+Math.PI/2,offset:h[0],r:0},{rotate:e.rotation-Math.PI/2,offset:h[1],r:Math.sqrt((s[0]-l[0])*(s[0]-l[0])+(s[1]-l[1])*(s[1]-l[1]))}],(function(t,i){if("none"!==u[i]&&null!=u[i]){var a=PS(u[i],-g/2,-f/2,g,f,c.stroke,!0),r=t.r+t.offset,o=p?l:s;a.attr({rotation:t.rotate,x:o[0]+r*Math.cos(e.rotation),y:o[1]-r*Math.sin(e.rotation),silent:!0,z2:11}),n.add(a)}}))}}},axisTickLabel:function(e,t,n,i){var a=function(e,t,n,i){var a=n.axis,r=n.getModel("axisTick"),o=r.get("show");"auto"===o&&i.handleAutoShown&&(o=i.handleAutoShown("axisTick"));if(!o||a.scale.isBlank())return;for(var s=r.getModel("lineStyle"),l=i.tickDirection*r.get("length"),p=Sk(a.getTicksCoords(),t.transform,l,Pr(s.getLineStyle(),{stroke:n.get(["axisLine","lineStyle","color"])}),"ticks"),c=0;cd[1]?-1:1,m=["start"===s?d[0]-u*c:"end"===s?d[1]+u*c:(d[0]+d[1])/2,wk(s)?e.labelOffset+l*c:0],h=t.get("nameRotate");null!=h&&(h=h*fk/180),wk(s)?r=yk.innerTextLayout(e.rotation,null!=h?h:e.rotation,l):(r=function(e,t,n,i){var a,r,o=$c(n-e),s=i[0]>i[1],l="start"===t&&!s||"start"!==t&&s;Kc(o-fk/2)?(r=l?"bottom":"top",a="center"):Kc(o-1.5*fk)?(r=l?"top":"bottom",a="center"):(r="middle",a=o<1.5*fk&&o>fk/2?l?"left":"right":l?"right":"left");return{rotation:o,textAlign:a,textVerticalAlign:r}}(e.rotation,s,h||0,d),null!=(o=e.axisNameAvailableWidth)&&(o=Math.abs(o/Math.sin(r.rotation)),!isFinite(o)&&(o=null)));var g=p.getFont(),f=t.get("nameTruncate",!0)||{},y=f.ellipsis,v=no(e.nameTruncateMaxWidth,f.maxWidth,o),x=new Pm({x:m[0],y:m[1],rotation:r.rotation,silent:yk.isLabelSilent(t),style:jf(p,{text:a,font:g,overflow:"truncate",width:v,ellipsis:y,fill:p.getTextColor()||t.get(["axisLine","lineStyle","color"]),align:p.get("align")||r.textAlign,verticalAlign:p.get("verticalAlign")||r.textVerticalAlign}),z2:1});if(Rf({el:x,componentModel:t,itemName:a}),x.__fullText=a,x.anid="name",t.get("triggerEvent")){var b=yk.makeAxisEventDataBase(t);b.targetType="axisName",b.name=a,Um(x).eventData=b}i.add(x),x.updateTransform(),n.add(x),x.decomposeTransform()}}};function xk(e){e&&(e.ignore=!0)}function bk(e,t){var n=e&&e.getBoundingRect().clone(),i=t&&t.getBoundingRect().clone();if(n&&i){var a=ms([]);return ys(a,a,-e.rotation),n.applyTransform(gs([],a,e.getLocalTransform())),i.applyTransform(gs([],a,t.getLocalTransform())),n.intersect(i)}}function wk(e){return"middle"===e||"center"===e}function Sk(e,t,n,i,a){for(var r=[],o=[],s=[],l=0;l=0||e===t}function Tk(e){var t=(e.ecModel.getComponent("axisPointer")||{}).coordSysAxesInfo;return t&&t.axesInfo[Ek(e)]}function Ik(e){return!!e.get(["handle","show"])}function Ek(e){return e.type+"||"+e.id}var Mk={},kk=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.render=function(t,n,i,a){this.axisPointerClass&&function(e){var t=Tk(e);if(t){var n=t.axisPointerModel,i=t.axis.scale,a=n.option,r=n.get("status"),o=n.get("value");null!=o&&(o=i.parse(o));var s=Ik(n);null==r&&(a.status=s?"show":"hide");var l=i.getExtent().slice();l[0]>l[1]&&l.reverse(),(null==o||o>l[1])&&(o=l[1]),o0&&!d.min?d.min=0:null!=d.min&&d.min<0&&!d.max&&(d.max=0);var u=o;null!=d.color&&(u=Pr({color:d.color},o));var m=Er(Ir(d),{boundaryGap:e,splitNumber:t,scale:n,axisLine:i,axisTick:a,axisLabel:r,name:d.text,showName:s,nameLocation:"end",nameGap:p,nameTextStyle:u,triggerEvent:c},!1);if(Hr(l)){var h=m.name;m.name=l.replace("{value}",null!=h?h:"")}else jr(l)&&(m.name=l(m.name,m));var g=new uy(m,null,this.ecModel);return Ar(g,tI.prototype),g.mainType="radar",g.componentIndex=this.componentIndex,g}),this);this._indicatorModels=d},t.prototype.getIndicatorModels=function(){return this._indicatorModels},t.type="radar",t.defaultOption={z:0,center:["50%","50%"],radius:"75%",startAngle:90,axisName:{show:!0},boundaryGap:[0,0],splitNumber:5,axisNameGap:15,scale:!1,shape:"polygon",axisLine:Er({lineStyle:{color:"#bbb"}},Kk.axisLine),axisLabel:Yk(Kk.axisLabel,!1),axisTick:Yk(Kk.axisTick,!1),splitLine:Yk(Kk.splitLine,!0),splitArea:Yk(Kk.splitArea,!0),indicator:[]},t}(Sv),Zk=["axisLine","axisTickLabel","axisName"],Qk=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.render=function(e,t,n){this.group.removeAll(),this._buildAxes(e),this._buildSplitLineAndArea(e)},t.prototype._buildAxes=function(e){var t=e.coordinateSystem;Rr(Br(t.getIndicatorAxes(),(function(e){var n=e.model.get("showName")?e.name:"";return new yk(e.model,{axisName:n,position:[t.cx,t.cy],rotation:e.angle,labelDirection:-1,tickDirection:-1,nameDirection:1})})),(function(e){Rr(Zk,e.add,e),this.group.add(e.getGroup())}),this)},t.prototype._buildSplitLineAndArea=function(e){var t=e.coordinateSystem,n=t.getIndicatorAxes();if(n.length){var i=e.get("shape"),a=e.getModel("splitLine"),r=e.getModel("splitArea"),o=a.getModel("lineStyle"),s=r.getModel("areaStyle"),l=a.get("show"),p=r.get("show"),c=o.get("color"),d=s.get("color"),u=Ur(c)?c:[c],m=Ur(d)?d:[d],h=[],g=[];if("circle"===i)for(var f=n[0].getTicksCoords(),y=t.cx,v=t.cy,x=0;x3?1.4:a>1?1.2:1.1;sP(this,"zoom","zoomOnMouseWheel",e,{scale:i>0?s:1/s,originX:r,originY:o,isAvailableBehavior:null})}if(n){var l=Math.abs(i);sP(this,"scrollMove","moveOnMouseWheel",e,{scrollDelta:(i>0?1:-1)*(l>3?.4:l>1?.15:.05),originX:r,originY:o,isAvailableBehavior:null})}}},t.prototype._pinchHandler=function(e){aP(this._zr,"globalPan")||sP(this,"zoom",null,e,{scale:e.pinchScale>1?1.1:1/1.1,originX:e.pinchX,originY:e.pinchY,isAvailableBehavior:null})},t}(Uo);function sP(e,t,n,i,a){e.pointerChecker&&e.pointerChecker(i,a.originX,a.originY)&&(ls(i.event),lP(e,t,n,i,a))}function lP(e,t,n,i,a){a.isAvailableBehavior=Gr(pP,null,n,i),e.trigger(t,a)}function pP(e,t,n){var i=n[e];return!e||i&&(!Hr(i)||t.event[i+"Key"])}function cP(e,t,n){var i=e.target;i.x+=t,i.y+=n,i.dirty()}function dP(e,t,n,i){var a=e.target,r=e.zoomLimit,o=e.zoom=e.zoom||1;if(o*=t,r){var s=r.min||0,l=r.max||1/0;o=Math.max(Math.min(l,o),s)}var p=o/e.zoom;e.zoom=o,a.x-=(n-a.x)*(p-1),a.y-=(i-a.y)*(p-1),a.scaleX*=p,a.scaleY*=p,a.dirty()}var uP,mP={axisPointer:1,tooltip:1,brush:1};function hP(e,t,n){var i=t.getComponentByElement(e.topTarget),a=i&&i.coordinateSystem;return i&&i!==n&&!mP.hasOwnProperty(i.mainType)&&a&&a.model!==n}function gP(e){Hr(e)&&(e=(new DOMParser).parseFromString(e,"text/xml"));var t=e;for(9===t.nodeType&&(t=t.firstChild);"svg"!==t.nodeName.toLowerCase()||1!==t.nodeType;)t=t.nextSibling;return t}var fP={fill:"fill",stroke:"stroke","stroke-width":"lineWidth",opacity:"opacity","fill-opacity":"fillOpacity","stroke-opacity":"strokeOpacity","stroke-dasharray":"lineDash","stroke-dashoffset":"lineDashOffset","stroke-linecap":"lineCap","stroke-linejoin":"lineJoin","stroke-miterlimit":"miterLimit","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","text-anchor":"textAlign",visibility:"visibility",display:"display"},yP=qr(fP),vP={"alignment-baseline":"textBaseline","stop-color":"stopColor"},xP=qr(vP),bP=function(){function e(){this._defs={},this._root=null}return e.prototype.parse=function(e,t){t=t||{};var n=gP(e);this._defsUsePending=[];var i=new Mc;this._root=i;var a=[],r=n.getAttribute("viewBox")||"",o=parseFloat(n.getAttribute("width")||t.width),s=parseFloat(n.getAttribute("height")||t.height);isNaN(o)&&(o=null),isNaN(s)&&(s=null),IP(n,i,null,!0,!1);for(var l,p,c=n.firstChild;c;)this._parseNode(c,i,a,null,!1,!1),c=c.nextSibling;if(function(e,t){for(var n=0;n=4&&(l={x:parseFloat(d[0]||0),y:parseFloat(d[1]||0),width:parseFloat(d[2]),height:parseFloat(d[3])})}if(l&&null!=o&&null!=s&&(p=RP(l,{x:0,y:0,width:o,height:s}),!t.ignoreViewBox)){var u=i;(i=new Mc).add(u),u.scaleX=u.scaleY=p.scale,u.x=p.x,u.y=p.y}return t.ignoreRootClip||null==o||null==s||i.setClipPath(new Em({shape:{x:0,y:0,width:o,height:s}})),{root:i,width:o,height:s,viewBoxRect:l,viewBoxTransform:p,named:a}},e.prototype._parseNode=function(e,t,n,i,a,r){var o,s=e.nodeName.toLowerCase(),l=i;if("defs"===s&&(a=!0),"text"===s&&(r=!0),"defs"===s||"switch"===s)o=t;else{if(!a){var p=uP[s];if(p&&bo(uP,s)){o=p.call(this,e,t);var c=e.getAttribute("name");if(c){var d={name:c,namedFrom:null,svgNodeTagLower:s,el:o};n.push(d),"g"===s&&(l=d)}else i&&n.push({name:i.name,namedFrom:i,svgNodeTagLower:s,el:o});t.add(o)}}var u=wP[s];if(u&&bo(wP,s)){var m=u.call(this,e),h=e.getAttribute("id");h&&(this._defs[h]=m)}}if(o&&o.isGroup)for(var g=e.firstChild;g;)1===g.nodeType?this._parseNode(g,o,n,l,a,r):3===g.nodeType&&r&&this._parseText(g,o),g=g.nextSibling},e.prototype._parseText=function(e,t){var n=new ym({style:{text:e.textContent},silent:!0,x:this._textX||0,y:this._textY||0});_P(t,n),IP(e,n,this._defsUsePending,!1,!1),function(e,t){var n=t.__selfStyle;if(n){var i=n.textBaseline,a=i;i&&"auto"!==i?"baseline"===i?a="alphabetic":"before-edge"===i||"text-before-edge"===i?a="top":"after-edge"===i||"text-after-edge"===i?a="bottom":"central"!==i&&"mathematical"!==i||(a="middle"):a="alphabetic",e.style.textBaseline=a}var r=t.__inheritedStyle;if(r){var o=r.textAlign,s=o;o&&("middle"===o&&(s="center"),e.style.textAlign=s)}}(n,t);var i=n.style,a=i.fontSize;a&&a<9&&(i.fontSize=9,n.scaleX*=a/9,n.scaleY*=a/9);var r=(i.fontSize||i.fontFamily)&&[i.fontStyle,i.fontWeight,(i.fontSize||12)+"px",i.fontFamily||"sans-serif"].join(" ");i.font=r;var o=n.getBoundingRect();return this._textX+=o.width,t.add(n),n},e.internalField=void(uP={g:function(e,t){var n=new Mc;return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n},rect:function(e,t){var n=new Em;return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n.setShape({x:parseFloat(e.getAttribute("x")||"0"),y:parseFloat(e.getAttribute("y")||"0"),width:parseFloat(e.getAttribute("width")||"0"),height:parseFloat(e.getAttribute("height")||"0")}),n.silent=!0,n},circle:function(e,t){var n=new sg;return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(e.getAttribute("cx")||"0"),cy:parseFloat(e.getAttribute("cy")||"0"),r:parseFloat(e.getAttribute("r")||"0")}),n.silent=!0,n},line:function(e,t){var n=new Fg;return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n.setShape({x1:parseFloat(e.getAttribute("x1")||"0"),y1:parseFloat(e.getAttribute("y1")||"0"),x2:parseFloat(e.getAttribute("x2")||"0"),y2:parseFloat(e.getAttribute("y2")||"0")}),n.silent=!0,n},ellipse:function(e,t){var n=new pg;return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(e.getAttribute("cx")||"0"),cy:parseFloat(e.getAttribute("cy")||"0"),rx:parseFloat(e.getAttribute("rx")||"0"),ry:parseFloat(e.getAttribute("ry")||"0")}),n.silent=!0,n},polygon:function(e,t){var n,i=e.getAttribute("points");i&&(n=TP(i));var a=new kg({shape:{points:n||[]},silent:!0});return _P(t,a),IP(e,a,this._defsUsePending,!1,!1),a},polyline:function(e,t){var n,i=e.getAttribute("points");i&&(n=TP(i));var a=new Dg({shape:{points:n||[]},silent:!0});return _P(t,a),IP(e,a,this._defsUsePending,!1,!1),a},image:function(e,t){var n=new bm;return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n.setStyle({image:e.getAttribute("xlink:href")||e.getAttribute("href"),x:+e.getAttribute("x"),y:+e.getAttribute("y"),width:+e.getAttribute("width"),height:+e.getAttribute("height")}),n.silent=!0,n},text:function(e,t){var n=e.getAttribute("x")||"0",i=e.getAttribute("y")||"0",a=e.getAttribute("dx")||"0",r=e.getAttribute("dy")||"0";this._textX=parseFloat(n)+parseFloat(a),this._textY=parseFloat(i)+parseFloat(r);var o=new Mc;return _P(t,o),IP(e,o,this._defsUsePending,!1,!0),o},tspan:function(e,t){var n=e.getAttribute("x"),i=e.getAttribute("y");null!=n&&(this._textX=parseFloat(n)),null!=i&&(this._textY=parseFloat(i));var a=e.getAttribute("dx")||"0",r=e.getAttribute("dy")||"0",o=new Mc;return _P(t,o),IP(e,o,this._defsUsePending,!1,!0),this._textX+=parseFloat(a),this._textY+=parseFloat(r),o},path:function(e,t){var n=rg(e.getAttribute("d")||"");return _P(t,n),IP(e,n,this._defsUsePending,!1,!1),n.silent=!0,n}}),e}(),wP={lineargradient:function(e){var t=parseInt(e.getAttribute("x1")||"0",10),n=parseInt(e.getAttribute("y1")||"0",10),i=parseInt(e.getAttribute("x2")||"10",10),a=parseInt(e.getAttribute("y2")||"0",10),r=new Ug(t,n,i,a);return SP(e,r),CP(e,r),r},radialgradient:function(e){var t=parseInt(e.getAttribute("cx")||"0",10),n=parseInt(e.getAttribute("cy")||"0",10),i=parseInt(e.getAttribute("r")||"0",10),a=new jg(t,n,i);return SP(e,a),CP(e,a),a}};function SP(e,t){"userSpaceOnUse"===e.getAttribute("gradientUnits")&&(t.global=!0)}function CP(e,t){for(var n=e.firstChild;n;){if(1===n.nodeType&&"stop"===n.nodeName.toLocaleLowerCase()){var i=n.getAttribute("offset"),a=void 0;a=i&&i.indexOf("%")>0?parseInt(i,10)/100:i?parseFloat(i):0;var r={};FP(n,r,r);var o=r.stopColor||n.getAttribute("stop-color")||"#000000";t.colorStops.push({offset:a,color:o})}n=n.nextSibling}}function _P(e,t){e&&e.__inheritedStyle&&(t.__inheritedStyle||(t.__inheritedStyle={}),Pr(t.__inheritedStyle,e.__inheritedStyle))}function TP(e){for(var t=PP(e),n=[],i=0;i0;r-=2){var o=i[r],s=i[r-1],l=PP(o);switch(a=a||[1,0,0,1,0,0],s){case"translate":fs(a,a,[parseFloat(l[0]),parseFloat(l[1]||"0")]);break;case"scale":vs(a,a,[parseFloat(l[0]),parseFloat(l[1]||l[0])]);break;case"rotate":ys(a,a,-parseFloat(l[0])*OP,[parseFloat(l[1]||"0"),parseFloat(l[2]||"0")]);break;case"skewX":gs(a,[1,0,Math.tan(parseFloat(l[0])*OP),1,0,0],a);break;case"skewY":gs(a,[1,Math.tan(parseFloat(l[0])*OP),0,1,0,0],a);break;case"matrix":a[0]=parseFloat(l[0]),a[1]=parseFloat(l[1]),a[2]=parseFloat(l[2]),a[3]=parseFloat(l[3]),a[4]=parseFloat(l[4]),a[5]=parseFloat(l[5])}}t.setLocalTransform(a)}}(e,t),FP(e,o,s),i||function(e,t,n){for(var i=0;i0,h={api:n,geo:s,mapOrGeoModel:e,data:o,isVisualEncodedByVisualMap:m,isGeo:r,transformInfoRaw:d};"geoJSON"===s.resourceType?this._buildGeoJSON(h):"geoSVG"===s.resourceType&&this._buildSVG(h),this._updateController(e,t,n),this._updateMapSelectHandler(e,l,n,i)},e.prototype._buildGeoJSON=function(e){var t=this._regionsGroupByName=fo(),n=fo(),i=this._regionsGroup,a=e.transformInfoRaw,r=e.mapOrGeoModel,o=e.data,s=e.geo.projection,l=s&&s.stream;function p(e,t){return t&&(e=t(e)),e&&[e[0]*a.scaleX+a.x,e[1]*a.scaleY+a.y]}function c(e){for(var t=[],n=!l&&s&&s.project,i=0;i=0)&&(u=a);var m=o?{normal:{align:"center",verticalAlign:"middle"}}:null;zf(t,Uf(i),{labelFetcher:u,labelDataIndex:d,defaultText:n},m);var h=t.getTextContent();if(h&&(QP(h).ignore=h.ignore,t.textConfig&&o)){var g=t.getBoundingRect().clone();t.textConfig.layoutRect=g,t.textConfig.position=[(o[0]-g.x)/g.width*100+"%",(o[1]-g.y)/g.height*100+"%"]}t.disableLabelAnimation=!0}else t.removeTextContent(),t.removeTextConfig(),t.disableLabelAnimation=null}function aD(e,t,n,i,a,r){e.data?e.data.setItemGraphicEl(r,t):Um(t).eventData={componentType:"geo",componentIndex:a.componentIndex,geoIndex:a.componentIndex,name:n,region:i&&i.option||{}}}function rD(e,t,n,i,a){e.data||Rf({el:t,componentModel:a,itemName:n,itemTooltipOption:i.get("tooltip")})}function oD(e,t,n,i,a){t.highDownSilentOnTouch=!!a.get("selectedMode");var r=i.getModel("emphasis"),o=r.get("focus");return Oh(t,o,r.get("blurScope"),r.get("disabled")),e.isGeo&&function(e,t,n){var i=Um(e);i.componentMainType=t.mainType,i.componentIndex=t.componentIndex,i.componentHighDownName=n}(t,a,n),o}function sD(e,t,n){var i,a=[];function r(){i=[]}function o(){i.length&&(a.push(i),i=[])}var s=t({polygonStart:r,polygonEnd:o,lineStart:r,lineEnd:o,point:function(e,t){isFinite(e)&&isFinite(t)&&i.push([e,t])},sphere:function(){}});return!n&&s.polygonStart(),Rr(e,(function(e){s.lineStart();for(var t=0;t-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2),n},t.type="series.map",t.dependencies=["geo"],t.layoutMode="box",t.defaultOption={z:2,coordinateSystem:"geo",map:"",left:"center",top:"center",aspectScale:null,showLegendSymbol:!0,boundingCoords:null,center:null,zoom:1,scaleLimit:null,selectedMode:!0,label:{show:!1,color:"#000"},itemStyle:{borderWidth:.5,borderColor:"#444",areaColor:"#eee"},emphasis:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{areaColor:"rgba(255,215,0,0.8)"}},select:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{color:"rgba(255,215,0,0.8)"}},nameProperty:"name"}}(ow);var lD=Lo,pD=function(e){function t(t){var n=e.call(this)||this;return n.type="view",n.dimensions=["x","y"],n._roamTransformable=new oc,n._rawTransformable=new oc,n.name=t,n}return ze(t,e),t.prototype.setBoundingRect=function(e,t,n,i){return this._rect=new Ps(e,t,n,i),this._rect},t.prototype.getBoundingRect=function(){return this._rect},t.prototype.setViewRect=function(e,t,n,i){this._transformTo(e,t,n,i),this._viewRect=new Ps(e,t,n,i)},t.prototype._transformTo=function(e,t,n,i){var a=this.getBoundingRect(),r=this._rawTransformable;r.transform=a.calculateTransform(new Ps(e,t,n,i));var o=r.parent;r.parent=null,r.decomposeTransform(),r.parent=o,this._updateTransform()},t.prototype.setCenter=function(e,t){e&&(this._center=[Vc(e[0],t.getWidth()),Vc(e[1],t.getHeight())],this._updateCenterAndZoom())},t.prototype.setZoom=function(e){e=e||1;var t=this.zoomLimit;t&&(null!=t.max&&(e=Math.min(t.max,e)),null!=t.min&&(e=Math.max(t.min,e))),this._zoom=e,this._updateCenterAndZoom()},t.prototype.getDefaultCenter=function(){var e=this.getBoundingRect();return[e.x+e.width/2,e.y+e.height/2]},t.prototype.getCenter=function(){return this._center||this.getDefaultCenter()},t.prototype.getZoom=function(){return this._zoom||1},t.prototype.getRoamTransform=function(){return this._roamTransformable.getLocalTransform()},t.prototype._updateCenterAndZoom=function(){var e=this._rawTransformable.getLocalTransform(),t=this._roamTransformable,n=this.getDefaultCenter(),i=this.getCenter(),a=this.getZoom();i=Lo([],i,e),n=Lo([],n,e),t.originX=i[0],t.originY=i[1],t.x=n[0]-i[0],t.y=n[1]-i[1],t.scaleX=t.scaleY=a,this._updateTransform()},t.prototype._updateTransform=function(){var e=this._roamTransformable,t=this._rawTransformable;t.parent=e,e.updateTransform(),t.updateTransform(),hs(this.transform||(this.transform=[]),t.transform||[1,0,0,1,0,0]),this._rawTransform=t.getLocalTransform(),this.invTransform=this.invTransform||[],xs(this.invTransform,this.transform),this.decomposeTransform()},t.prototype.getTransformInfo=function(){var e=this._rawTransformable,t=this._roamTransformable,n=new oc;return n.transform=t.transform,n.decomposeTransform(),{roam:{x:n.x,y:n.y,scaleX:n.scaleX,scaleY:n.scaleY},raw:{x:e.x,y:e.y,scaleX:e.scaleX,scaleY:e.scaleY}}},t.prototype.getViewRect=function(){return this._viewRect},t.prototype.getViewRectAfterRoam=function(){var e=this.getBoundingRect().clone();return e.applyTransform(this.transform),e},t.prototype.dataToPoint=function(e,t,n){var i=t?this._rawTransform:this.transform;return n=n||[],i?lD(n,e,i):_o(n,e)},t.prototype.pointToData=function(e){var t=this.invTransform;return t?lD([],e,t):[e[0],e[1]]},t.prototype.convertToPixel=function(e,t,n){var i=cD(t);return i===this?i.dataToPoint(n):null},t.prototype.convertFromPixel=function(e,t,n){var i=cD(t);return i===this?i.pointToData(n):null},t.prototype.containPoint=function(e){return this.getViewRectAfterRoam().contain(e[0],e[1])},t.dimensions=["x","y"],t}(oc);function cD(e){var t=e.seriesModel;return t?t.coordinateSystem:null}var dD={geoJSON:{aspectScale:.75,invertLongitute:!0},geoSVG:{aspectScale:1,invertLongitute:!1}},uD=["lng","lat"],mD=function(e){function t(t,n,i){var a=e.call(this,t)||this;a.dimensions=uD,a.type="geo",a._nameCoordMap=fo(),a.map=n;var r,o=i.projection,s=$P(n,i.nameMap,i.nameProperty),l=WP(n),p=(a.resourceType=l?l.type:null,a.regions=s.regions),c=dD[l.type];if(a._regionsMap=s.regionsMap,a.regions=s.regions,a.projection=o,o)for(var d=0;d1?(m.width=u,m.height=u/x):(m.height=u,m.width=u*x),m.y=d[1]-m.height/2,m.x=d[0]-m.width/2;else{var w=e.getBoxLayoutParams();w.aspect=x,m=gv(w,{width:y,height:v})}this.setViewRect(m.x,m.y,m.width,m.height),this.setCenter(e.get("center"),t),this.setZoom(e.get("zoom"))}Ar(mD,pD);var fD=function(){function e(){this.dimensions=uD}return e.prototype.create=function(e,t){var n=[];function i(e){return{nameProperty:e.get("nameProperty"),aspectScale:e.get("aspectScale"),projection:e.get("projection")}}e.eachComponent("geo",(function(e,a){var r=e.get("map"),o=new mD(r+a,r,kr({nameMap:e.get("nameMap")},i(e)));o.zoomLimit=e.get("scaleLimit"),n.push(o),e.coordinateSystem=o,o.model=e,o.resize=gD,o.resize(e,t)})),e.eachSeries((function(e){if("geo"===e.get("coordinateSystem")){var t=e.get("geoIndex")||0;e.coordinateSystem=n[t]}}));var a={};return e.eachSeriesByType("map",(function(e){if(!e.getHostGeoModel()){var t=e.getMapType();a[t]=a[t]||[],a[t].push(e)}})),Rr(a,(function(e,a){var r=Br(e,(function(e){return e.get("nameMap")})),o=new mD(a,a,kr({nameMap:Mr(r)},i(e[0])));o.zoomLimit=no.apply(null,Br(e,(function(e){return e.get("scaleLimit")}))),n.push(o),o.resize=gD,o.resize(e[0],t),Rr(e,(function(e){e.coordinateSystem=o,function(e,t){Rr(t.get("geoCoord"),(function(t,n){e.addGeoCoord(n,t)}))}(o,e)}))})),n},e.prototype.getFilledRegions=function(e,t,n,i){for(var a=(e||[]).slice(),r=fo(),o=0;ov.x)||(b-=Math.PI);var C=w?"left":"right",_=s.getModel("label"),T=_.get("rotate"),I=T*(Math.PI/180),E=f.getTextContent();E&&(f.setTextConfig({position:_.get("position")||C,rotation:null==T?-b:I,origin:"center"}),E.setStyle("verticalAlign","middle"))}var M=s.get(["emphasis","focus"]),k="relative"===M?yo(o.getAncestorsIndices(),o.getDescendantIndices()):"ancestor"===M?o.getAncestorsIndices():"descendant"===M?o.getDescendantIndices():null;k&&(Um(n).focus=k),function(e,t,n,i,a,r,o,s){var l=t.getModel(),p=e.get("edgeShape"),c=e.get("layout"),d=e.getOrient(),u=e.get(["lineStyle","curveness"]),m=e.get("edgeForkPosition"),h=l.getModel("lineStyle").getLineStyle(),g=i.__edge;if("curve"===p)t.parentNode&&t.parentNode!==n&&(g||(g=i.__edge=new Lg({shape:ID(c,d,u,a,a)})),tf(g,{shape:ID(c,d,u,r,o)},e));else if("polyline"===p)if("orthogonal"===c){if(t!==n&&t.children&&0!==t.children.length&&!0===t.isExpand){for(var f=t.children,y=[],v=0;vt&&(t=i.height)}this.height=t+1},e.prototype.getNodeById=function(e){if(this.getId()===e)return this;for(var t=0,n=this.children,i=n.length;t=0&&this.hostTree.data.setItemLayout(this.dataIndex,e,t)},e.prototype.getLayout=function(){return this.hostTree.data.getItemLayout(this.dataIndex)},e.prototype.getModel=function(e){if(!(this.dataIndex<0))return this.hostTree.data.getItemModel(this.dataIndex).getModel(e)},e.prototype.getLevelModel=function(){return(this.hostTree.levelModels||[])[this.depth]},e.prototype.setVisual=function(e,t){this.dataIndex>=0&&this.hostTree.data.setItemVisual(this.dataIndex,e,t)},e.prototype.getVisual=function(e){return this.hostTree.data.getItemVisual(this.dataIndex,e)},e.prototype.getRawIndex=function(){return this.hostTree.data.getRawIndex(this.dataIndex)},e.prototype.getId=function(){return this.hostTree.data.getId(this.dataIndex)},e.prototype.getChildIndex=function(){if(this.parentNode){for(var e=this.parentNode.children,t=0;t=0){var i=n.getData().tree.root,a=e.targetNode;if(Hr(a)&&(a=i.getNodeById(a)),a&&i.contains(a))return{node:a};var r=e.targetNodeId;if(null!=r&&(a=i.getNodeById(r)))return{node:a}}}function VD(e){for(var t=[];e;)(e=e.parentNode)&&t.push(e);return t.reverse()}function qD(e,t){return Dr(VD(e),t)>=0}function GD(e,t){for(var n=[];e;){var i=e.dataIndex;n.push({name:e.name,dataIndex:i,value:t.getRawValue(i)}),e=e.parentNode}return n.reverse(),n}!function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.hasSymbolVisual=!0,t.ignoreStyleOnData=!0,t}ze(t,e),t.prototype.getInitialData=function(e){var t={name:e.name,children:e.data},n=e.leaves||{},i=new uy(n,this,this.ecModel),a=ND.createTree(t,this,(function(e){e.wrapMethod("getItemModel",(function(e,t){var n=a.getNodeByDataIndex(t);return n&&n.children.length&&n.isExpand||(e.parentModel=i),e}))}));var r=0;a.eachNode("preorder",(function(e){e.depth>r&&(r=e.depth)}));var o=e.expandAndCollapse&&e.initialTreeDepth>=0?e.initialTreeDepth:r;return a.root.eachNode("preorder",(function(e){var t=e.hostTree.data.getRawDataItem(e.dataIndex);e.isExpand=t&&null!=t.collapsed?!t.collapsed:e.depth<=o})),a.data},t.prototype.getOrient=function(){var e=this.get("orient");return"horizontal"===e?e="LR":"vertical"===e&&(e="TB"),e},t.prototype.setZoom=function(e){this.option.zoom=e},t.prototype.setCenter=function(e){this.option.center=e},t.prototype.formatTooltip=function(e,t,n){for(var i=this.getData().tree,a=i.root.children[0],r=i.getNodeByDataIndex(e),o=r.getValue(),s=r.name;r&&r!==a;)s=r.parentNode.name+"."+s,r=r.parentNode;return jb("nameValue",{name:s,value:o,noValue:isNaN(o)||null==o})},t.prototype.getDataParams=function(t){var n=e.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return n.treeAncestors=GD(i,this),n.collapsed=!i.isExpand,n},t.type="series.tree",t.layoutMode="box",t.defaultOption={z:2,coordinateSystem:"view",left:"12%",top:"12%",right:"12%",bottom:"12%",layout:"orthogonal",edgeShape:"curve",edgeForkPosition:"50%",roam:!1,nodeScaleRatio:.4,center:null,zoom:1,orient:"LR",symbol:"emptyCircle",symbolSize:7,expandAndCollapse:!0,initialTreeDepth:2,lineStyle:{color:"#ccc",width:1.5,curveness:.5},itemStyle:{color:"lightsteelblue",borderWidth:1.5},label:{show:!0},animationEasing:"linear",animationDuration:700,animationDurationUpdate:500}}(ow);function zD(e){var t=e.getData().tree,n={};t.eachNode((function(t){for(var i=t;i&&i.depth>1;)i=i.parentNode;var a=Zv(e.ecModel,i.name||i.dataIndex+"",n);t.setVisual("decal",a)}))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.preventUsingHoverLayer=!0,n}ze(t,e),t.prototype.getInitialData=function(e,t){var n={name:e.name,children:e.data};UD(n);var i=e.levels||[],a=this.designatedVisualItemStyle={},r=new uy({itemStyle:a},this,t);i=e.levels=function(e,t){var n,i,a=dd(t.get("color")),r=dd(t.get(["aria","decal","decals"]));if(!a)return;e=e||[],Rr(e,(function(e){var t=new uy(e),a=t.get("color"),r=t.get("decal");(t.get(["itemStyle","color"])||a&&"none"!==a)&&(n=!0),(t.get(["itemStyle","decal"])||r&&"none"!==r)&&(i=!0)}));var o=e[0]||(e[0]={});n||(o.color=a.slice());!i&&r&&(o.decal=r.slice());return e}(i,t);var o=Br(i||[],(function(e){return new uy(e,r,t)}),this),s=ND.createTree(n,this,(function(e){e.wrapMethod("getItemModel",(function(e,t){var n=s.getNodeByDataIndex(t),i=n?o[n.depth]:null;return e.parentModel=i||r,e}))}));return s.data},t.prototype.optionUpdated=function(){this.resetViewRoot()},t.prototype.formatTooltip=function(e,t,n){var i=this.getData(),a=this.getRawValue(e);return jb("nameValue",{name:i.getName(e),value:a})},t.prototype.getDataParams=function(t){var n=e.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return n.treeAncestors=GD(i,this),n.treePathInfo=n.treeAncestors,n},t.prototype.setLayoutInfo=function(e){this.layoutInfo=this.layoutInfo||{},kr(this.layoutInfo,e)},t.prototype.mapIdToIndex=function(e){var t=this._idIndexMap;t||(t=this._idIndexMap=fo(),this._idIndexMapCount=0);var n=t.get(e);return null==n&&t.set(e,n=this._idIndexMapCount++),n},t.prototype.getViewRoot=function(){return this._viewRoot},t.prototype.resetViewRoot=function(e){e?this._viewRoot=e:e=this._viewRoot;var t=this.getRawData().tree.root;e&&(e===t||t.contains(e))||(this._viewRoot=t)},t.prototype.enableAriaDecal=function(){zD(this)},t.type="series.treemap",t.layoutMode="box",t.defaultOption={progressive:0,left:"center",top:"middle",width:"80%",height:"80%",sort:!0,clipWindow:"origin",squareRatio:.5*(1+Math.sqrt(5)),leafDepth:null,drillDownIcon:"▶",zoomToNodeRatio:.1024,roam:!0,nodeClick:"zoomToNode",animation:!0,animationDurationUpdate:900,animationEasing:"quinticInOut",breadcrumb:{show:!0,height:22,left:"center",top:"bottom",emptyItemWidth:25,itemStyle:{color:"rgba(0,0,0,0.7)",textStyle:{color:"#fff"}},emphasis:{itemStyle:{color:"rgba(0,0,0,0.9)"}}},label:{show:!0,distance:0,padding:5,position:"inside",color:"#fff",overflow:"truncate"},upperLabel:{show:!1,position:[0,"50%"],height:20,overflow:"truncate",verticalAlign:"middle"},itemStyle:{color:null,colorAlpha:null,colorSaturation:null,borderWidth:0,gapWidth:0,borderColor:"#fff",borderColorSaturation:null},emphasis:{upperLabel:{show:!0,position:[0,"50%"],overflow:"truncate",verticalAlign:"middle"}},visualDimension:0,visualMin:null,visualMax:null,color:[],colorAlpha:null,colorSaturation:null,colorMappingBy:"index",visibleMin:10,childrenVisibleMin:null,levels:[]}}(ow);function UD(e){var t=0;Rr(e.children,(function(e){UD(e);var n=e.value;Ur(n)&&(n=n[0]),t+=n}));var n=e.value;Ur(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=t),n<0&&(n=0),Ur(e.value)?e.value[0]=n:e.value=n}var jD=function(){function e(e){this.group=new Mc,e.add(this.group)}return e.prototype.render=function(e,t,n,i){var a=e.getModel("breadcrumb"),r=this.group;if(r.removeAll(),a.get("show")&&n){var o=a.getModel("itemStyle"),s=a.getModel("emphasis"),l=o.getModel("textStyle"),p=s.getModel(["itemStyle","textStyle"]),c={pos:{left:a.get("left"),right:a.get("right"),top:a.get("top"),bottom:a.get("bottom")},box:{width:t.getWidth(),height:t.getHeight()},emptyItemWidth:a.get("emptyItemWidth"),totalWidth:0,renderList:[]};this._prepare(n,c,l),this._renderContent(e,c,o,s,l,p,i),fv(r,c.pos,c.box)}},e.prototype._prepare=function(e,t,n){for(var i=e;i;i=i.parentNode){var a=xd(i.getModel().get("name"),""),r=n.getTextRect(a),o=Math.max(r.width+16,t.emptyItemWidth);t.totalWidth+=o+8,t.renderList.push({node:i,text:a,width:o})}},e.prototype._renderContent=function(e,t,n,i,a,r,o){for(var s,l,p,c,d,u,m,h,g,f=0,y=t.emptyItemWidth,v=e.get(["breadcrumb","height"]),x=(s=t.pos,l=t.box,c=l.width,d=l.height,u=Vc(s.left,c),m=Vc(s.top,d),h=Vc(s.right,c),g=Vc(s.bottom,d),(isNaN(u)||isNaN(parseFloat(s.left)))&&(u=0),(isNaN(h)||isNaN(parseFloat(s.right)))&&(h=c),(isNaN(m)||isNaN(parseFloat(s.top)))&&(m=0),(isNaN(g)||isNaN(parseFloat(s.bottom)))&&(g=d),p=nv(p||0),{width:Math.max(h-u-p[1]-p[3],0),height:Math.max(g-m-p[0]-p[2],0)}),b=t.totalWidth,w=t.renderList,S=i.getModel("itemStyle").getItemStyle(),C=w.length-1;C>=0;C--){var _=w[C],T=_.node,I=_.width,E=_.text;b>x.width&&(b-=I-y,I=y,E=null);var M=new kg({shape:{points:HD(f,0,I,v,C===w.length-1,0===C)},style:Pr(n.getItemStyle(),{lineJoin:"bevel"}),textContent:new Pm({style:jf(a,{text:E})}),textConfig:{position:"inside"},z2:1e5,onclick:zr(o,T)});M.disableLabelAnimation=!0,M.getTextContent().ensureState("emphasis").style=jf(r,{text:E}),M.ensureState("emphasis").style=S,Oh(M,i.get("focus"),i.get("blurScope"),i.get("disabled")),this.group.add(M),WD(M,e,T),f+=I+8}},e.prototype.remove=function(){this.group.removeAll()},e}();function HD(e,t,n,i,a,r){var o=[[a?e:e-5,t],[e+n,t],[e+n,t+i],[a?e:e-5,t+i]];return!r&&o.splice(2,0,[e+n+5,t+i/2]),!a&&o.push([e,t+i/2]),o}function WD(e,t,n){Um(e).eventData={componentType:"series",componentSubType:"treemap",componentIndex:t.componentIndex,seriesIndex:t.seriesIndex,seriesName:t.name,seriesType:"treemap",selfType:"breadcrumb",nodeData:{dataIndex:n&&n.dataIndex,name:n&&n.name},treePathInfo:n&&GD(n,t)}}var $D=function(){function e(){this._storage=[],this._elExistsMap={}}return e.prototype.add=function(e,t,n,i,a){return!this._elExistsMap[e.id]&&(this._elExistsMap[e.id]=!0,this._storage.push({el:e,target:t,duration:n,delay:i,easing:a}),!0)},e.prototype.finished=function(e){return this._finishedCallback=e,this},e.prototype.start=function(){for(var e=this,t=this._storage.length,n=function(){--t<=0&&(e._storage.length=0,e._elExistsMap={},e._finishedCallback&&e._finishedCallback())},i=0,a=this._storage.length;i3||Math.abs(e.dy)>3)){var t=this.seriesModel.getData().tree.root;if(!t)return;var n=t.getLayout();if(!n)return;this.api.dispatchAction({type:"treemapMove",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:n.x+e.dx,y:n.y+e.dy,width:n.width,height:n.height}})}},t.prototype._onZoom=function(e){var t=e.originX,n=e.originY;if("animating"!==this._state){var i=this.seriesModel.getData().tree.root;if(!i)return;var a=i.getLayout();if(!a)return;var r=new Ps(a.x,a.y,a.width,a.height),o=this.seriesModel.layoutInfo,s=[1,0,0,1,0,0];fs(s,s,[-(t-=o.x),-(n-=o.y)]),vs(s,s,[e.scale,e.scale]),fs(s,s,[t,n]),r.applyTransform(s),this.api.dispatchAction({type:"treemapRender",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:r.x,y:r.y,width:r.width,height:r.height}})}},t.prototype._initEvents=function(e){var t=this;e.on("click",(function(e){if("ready"===t._state){var n=t.seriesModel.get("nodeClick",!0);if(n){var i=t.findTarget(e.offsetX,e.offsetY);if(i){var a=i.node;if(a.getLayout().isLeafRoot)t._rootToNode(i);else if("zoomToNode"===n)t._zoomToNode(i);else if("link"===n){var r=a.hostTree.data.getItemModel(a.dataIndex),o=r.get("link",!0),s=r.get("target",!0)||"blank";o&&pv(o,s)}}}}}),this)},t.prototype._renderBreadcrumb=function(e,t,n){var i=this;n||(n=null!=e.get("leafDepth",!0)?{node:e.getViewRoot()}:this.findTarget(t.getWidth()/2,t.getHeight()/2))||(n={node:e.getData().tree.root}),(this._breadcrumb||(this._breadcrumb=new jD(this.group))).render(e,t,n.node,(function(t){"animating"!==i._state&&(qD(e.getViewRoot(),t)?i._rootToNode({node:t}):i._zoomToNode({node:t}))}))},t.prototype.remove=function(){this._clearController(),this._containerGroup&&this._containerGroup.removeAll(),this._storage={nodeGroup:[],background:[],content:[]},this._state="ready",this._breadcrumb&&this._breadcrumb.remove()},t.prototype.dispose=function(){this._clearController()},t.prototype._zoomToNode=function(e){this.api.dispatchAction({type:"treemapZoomToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:e.node})},t.prototype._rootToNode=function(e){this.api.dispatchAction({type:"treemapRootToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:e.node})},t.prototype.findTarget=function(e,t){var n;return this.seriesModel.getViewRoot().eachNode({attr:"viewChildren",order:"preorder"},(function(i){var a=this._storage.background[i.getRawIndex()];if(a){var r=a.transformCoordToLocal(e,t),o=a.shape;if(!(o.x<=r[0]&&r[0]<=o.x+o.width&&o.y<=r[1]&&r[1]<=o.y+o.height))return!1;n={node:i,offsetX:r[0],offsetY:r[1]}}}),this),n},t.type="treemap"}(vw);var tO=Rr,nO=Kr,iO=-1,aO=function(){function e(t){var n=t.mappingMethod,i=t.type,a=this.option=Ir(t);this.type=i,this.mappingMethod=n,this._normalizeData=hO[n];var r=e.visualHandlers[i];this.applyVisual=r.applyVisual,this.getColorMapper=r.getColorMapper,this._normalizedToVisual=r._normalizedToVisual[n],"piecewise"===n?(rO(a),function(e){var t=e.pieceList;e.hasSpecialVisual=!1,Rr(t,(function(t,n){t.originIndex=n,null!=t.visual&&(e.hasSpecialVisual=!0)}))}(a)):"category"===n?a.categories?function(e){var t=e.categories,n=e.categoryMap={},i=e.visual;if(tO(t,(function(e,t){n[e]=t})),!Ur(i)){var a=[];Kr(i)?tO(i,(function(e,t){var i=n[t];a[null!=i?i:iO]=e})):a[-1]=i,i=mO(e,a)}for(var r=t.length-1;r>=0;r--)null==i[r]&&(delete n[t[r]],t.pop())}(a):rO(a,!0):(so("linear"!==n||a.dataExtent),rO(a))}return e.prototype.mapValueToVisual=function(e){var t=this._normalizeData(e);return this._normalizedToVisual(t,e)},e.prototype.getNormalizer=function(){return Gr(this._normalizeData,this)},e.listVisualTypes=function(){return qr(e.visualHandlers)},e.isValidType=function(t){return e.visualHandlers.hasOwnProperty(t)},e.eachVisual=function(e,t,n){Kr(e)?Rr(e,t,n):t.call(n,e)},e.mapVisual=function(t,n,i){var a,r=Ur(t)?[]:Kr(t)?{}:(a=!0,null);return e.eachVisual(t,(function(e,t){var o=n.call(i,e,t);a?r=o:r[t]=o})),r},e.retrieveVisuals=function(t){var n,i={};return t&&tO(e.visualHandlers,(function(e,a){t.hasOwnProperty(a)&&(i[a]=t[a],n=!0)})),n?i:null},e.prepareVisualTypes=function(e){if(Ur(e))e=e.slice();else{if(!nO(e))return[];var t=[];tO(e,(function(e,n){t.push(n)})),e=t}return e.sort((function(e,t){return"color"===t&&"color"!==e&&0===e.indexOf("color")?1:-1})),e},e.dependsOn=function(e,t){return"color"===t?!(!e||0!==e.indexOf(t)):e===t},e.findPieceIndex=function(e,t,n){for(var i,a=1/0,r=0,o=t.length;ri&&(i=t);var r=i%2?i+2:i+3;a=[];for(var o=0;o0&&(v[0]=-v[0],v[1]=-v[1]);var b=y[0]<0?-1:1;if("start"!==i.__position&&"end"!==i.__position){var w=-Math.atan2(y[1],y[0]);p[0].8?"left":c[0]<-.8?"right":"center",u=c[1]>.8?"top":c[1]<-.8?"bottom":"middle";break;case"start":i.x=-c[0]*h+l[0],i.y=-c[1]*g+l[1],d=c[0]>.8?"right":c[0]<-.8?"left":"center",u=c[1]>.8?"bottom":c[1]<-.8?"top":"middle";break;case"insideStartTop":case"insideStart":case"insideStartBottom":i.x=h*b+l[0],i.y=l[1]+S,d=y[0]<0?"right":"left",i.originX=-h*b,i.originY=-S;break;case"insideMiddleTop":case"insideMiddle":case"insideMiddleBottom":case"middle":i.x=x[0],i.y=x[1]+S,d="center",i.originY=-S;break;case"insideEndTop":case"insideEnd":case"insideEndBottom":i.x=-h*b+p[0],i.y=p[1]+S,d=y[0]>=0?"right":"left",i.originX=h*b,i.originY=-S}i.scaleX=i.scaleY=a,i.setStyle({verticalAlign:i.__verticalAlign||u,align:i.__align||d})}}}function C(e,t){var n=e.__specifiedRotation;if(null==n){var i=o.tangentAt(t);e.attr("rotation",(1===t?-1:1)*Math.PI/2-Math.atan2(i[1],i[0]))}else e.attr("rotation",n)}},t}(Mc),zO=function(){function e(e){this.group=new Mc,this._LineCtor=e||GO}return e.prototype.updateData=function(e){var t=this;this._progressiveEls=null;var n=this,i=n.group,a=n._lineData;n._lineData=e,a||i.removeAll();var r=UO(e);e.diff(a).add((function(n){t._doAdd(e,n,r)})).update((function(n,i){t._doUpdate(a,e,i,n,r)})).remove((function(e){i.remove(a.getItemGraphicEl(e))})).execute()},e.prototype.updateLayout=function(){var e=this._lineData;e&&e.eachItemGraphicEl((function(t,n){t.updateLayout(e,n)}),this)},e.prototype.incrementalPrepareUpdate=function(e){this._seriesScope=UO(e),this._lineData=null,this.group.removeAll()},e.prototype.incrementalUpdate=function(e,t){function n(e){e.isGroup||function(e){return e.animators&&e.animators.length>0}(e)||(e.incremental=!0,e.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[];for(var i=e.start;i=0?i+=p:i-=p:h>=0?i-=p:i+=p}return i}function JO(e,t){var n=[],i=bl,a=[[],[],[]],r=[[],[]],o=[];t/=2,e.eachEdge((function(e,s){var l=e.getLayout(),p=e.getVisual("fromSymbol"),c=e.getVisual("toSymbol");l.__original||(l.__original=[To(l[0]),To(l[1])],l[2]&&l.__original.push(To(l[2])));var d=l.__original;if(null!=l[2]){if(_o(a[0],d[0]),_o(a[1],d[2]),_o(a[2],d[1]),p&&"none"!==p){var u=TO(e.node1),m=QO(a,d[0],u*t);i(a[0][0],a[1][0],a[2][0],m,n),a[0][0]=n[3],a[1][0]=n[4],i(a[0][1],a[1][1],a[2][1],m,n),a[0][1]=n[3],a[1][1]=n[4]}if(c&&"none"!==c){u=TO(e.node2),m=QO(a,d[1],u*t);i(a[0][0],a[1][0],a[2][0],m,n),a[1][0]=n[1],a[2][0]=n[2],i(a[0][1],a[1][1],a[2][1],m,n),a[1][1]=n[1],a[2][1]=n[2]}_o(l[0],a[0]),_o(l[1],a[2]),_o(l[2],a[1])}else{if(_o(r[0],d[0]),_o(r[1],d[1]),Mo(o,r[1],r[0]),Oo(o,o),p&&"none"!==p){u=TO(e.node1);Eo(r[0],r[0],o,u*t)}if(c&&"none"!==c){u=TO(e.node2);Eo(r[1],r[1],o,-u*t)}_o(l[0],r[0]),_o(l[1],r[1])}}))}function eA(e){return"view"===e.type}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.init=function(e,t){var n=new CE,i=new zO,a=this.group;this._controller=new oP(t.getZr()),this._controllerHost={target:a},a.add(n.group),a.add(i.group),this._symbolDraw=n,this._lineDraw=i,this._firstRender=!0},t.prototype.render=function(e,t,n){var i=this,a=e.coordinateSystem;this._model=e;var r=this._symbolDraw,o=this._lineDraw,s=this.group;if(eA(a)){var l={x:a.x,y:a.y,scaleX:a.scaleX,scaleY:a.scaleY};this._firstRender?s.attr(l):tf(s,l,e)}JO(e.getGraph(),_O(e));var p=e.getData();r.updateData(p);var c=e.getEdgeData();o.updateData(c),this._updateNodeAndLinkScale(),this._updateController(e,t,n),clearTimeout(this._layoutTimeout);var d=e.forceLayout,u=e.get(["force","layoutAnimation"]);d&&this._startForceLayoutIteration(d,u);var m=e.get("layout");p.graph.eachNode((function(t){var n=t.dataIndex,a=t.getGraphicEl(),r=t.getModel();if(a){a.off("drag").off("dragend");var o=r.get("draggable");o&&a.on("drag",(function(r){switch(m){case"force":d.warmUp(),!i._layouting&&i._startForceLayoutIteration(d,u),d.setFixed(n),p.setItemLayout(n,[a.x,a.y]);break;case"circular":p.setItemLayout(n,[a.x,a.y]),t.setLayout({fixed:!0},!0),MO(e,"symbolSize",t,[r.offsetX,r.offsetY]),i.updateLayout(e);break;default:p.setItemLayout(n,[a.x,a.y]),CO(e.getGraph(),e),i.updateLayout(e)}})).on("dragend",(function(){d&&d.setUnfixed(n)})),a.setDraggable(o,!!r.get("cursor")),"adjacency"===r.get(["emphasis","focus"])&&(Um(a).focus=t.getAdjacentDataIndices())}})),p.graph.eachEdge((function(e){var t=e.getGraphicEl(),n=e.getModel().get(["emphasis","focus"]);t&&"adjacency"===n&&(Um(t).focus={edge:[e.dataIndex],node:[e.node1.dataIndex,e.node2.dataIndex]})}));var h="circular"===e.get("layout")&&e.get(["circular","rotateLabel"]),g=p.getLayout("cx"),f=p.getLayout("cy");p.graph.eachNode((function(e){PO(e,h,g,f)})),this._firstRender=!1},t.prototype.dispose=function(){this.remove(),this._controller&&this._controller.dispose(),this._controllerHost=null},t.prototype._startForceLayoutIteration=function(e,t){var n=this;!function i(){e.step((function(e){n.updateLayout(n._model),(n._layouting=!e)&&(t?n._layoutTimeout=setTimeout(i,16):i())}))}()},t.prototype._updateController=function(e,t,n){var i=this,a=this._controller,r=this._controllerHost,o=this.group;a.setPointerChecker((function(t,i,a){var r=o.getBoundingRect();return r.applyTransform(o.transform),r.contain(i,a)&&!hP(t,n,e)})),eA(e.coordinateSystem)?(a.enable(e.get("roam")),r.zoomLimit=e.get("scaleLimit"),r.zoom=e.coordinateSystem.getZoom(),a.off("pan").off("zoom").on("pan",(function(t){cP(r,t.dx,t.dy),n.dispatchAction({seriesId:e.id,type:"graphRoam",dx:t.dx,dy:t.dy})})).on("zoom",(function(t){dP(r,t.scale,t.originX,t.originY),n.dispatchAction({seriesId:e.id,type:"graphRoam",zoom:t.scale,originX:t.originX,originY:t.originY}),i._updateNodeAndLinkScale(),JO(e.getGraph(),_O(e)),i._lineDraw.updateLayout(),n.updateLabelLayout()}))):a.disable()},t.prototype._updateNodeAndLinkScale=function(){var e=this._model,t=e.getData(),n=_O(e);t.eachItemGraphicEl((function(e,t){e&&e.setSymbolScale(n)}))},t.prototype.updateLayout=function(e){JO(e.getGraph(),_O(e)),this._symbolDraw.updateLayout(),this._lineDraw.updateLayout()},t.prototype.remove=function(){clearTimeout(this._layoutTimeout),this._layouting=!1,this._layoutTimeout=null,this._symbolDraw&&this._symbolDraw.remove(),this._lineDraw&&this._lineDraw.remove()},t.type="graph"}(vw);function tA(e){return"_EC_"+e}var nA=function(){function e(e){this.type="graph",this.nodes=[],this.edges=[],this._nodesMap={},this._edgesMap={},this._directed=e||!1}return e.prototype.isDirected=function(){return this._directed},e.prototype.addNode=function(e,t){e=null==e?""+t:""+e;var n=this._nodesMap;if(!n[tA(e)]){var i=new iA(e,t);return i.hostGraph=this,this.nodes.push(i),n[tA(e)]=i,i}},e.prototype.getNodeByIndex=function(e){var t=this.data.getRawIndex(e);return this.nodes[t]},e.prototype.getNodeById=function(e){return this._nodesMap[tA(e)]},e.prototype.addEdge=function(e,t,n){var i=this._nodesMap,a=this._edgesMap;if($r(e)&&(e=this.nodes[e]),$r(t)&&(t=this.nodes[t]),e instanceof iA||(e=i[tA(e)]),t instanceof iA||(t=i[tA(t)]),e&&t){var r=e.id+"-"+t.id,o=new aA(e,t,n);return o.hostGraph=this,this._directed&&(e.outEdges.push(o),t.inEdges.push(o)),e.edges.push(o),e!==t&&t.edges.push(o),this.edges.push(o),a[r]=o,o}},e.prototype.getEdgeByIndex=function(e){var t=this.edgeData.getRawIndex(e);return this.edges[t]},e.prototype.getEdge=function(e,t){e instanceof iA&&(e=e.id),t instanceof iA&&(t=t.id);var n=this._edgesMap;return this._directed?n[e+"-"+t]:n[e+"-"+t]||n[t+"-"+e]},e.prototype.eachNode=function(e,t){for(var n=this.nodes,i=n.length,a=0;a=0&&e.call(t,n[a],a)},e.prototype.eachEdge=function(e,t){for(var n=this.edges,i=n.length,a=0;a=0&&n[a].node1.dataIndex>=0&&n[a].node2.dataIndex>=0&&e.call(t,n[a],a)},e.prototype.breadthFirstTraverse=function(e,t,n,i){if(t instanceof iA||(t=this._nodesMap[tA(t)]),t){for(var a="out"===n?"outEdges":"in"===n?"inEdges":"edges",r=0;r=0&&n.node2.dataIndex>=0}));for(a=0,r=i.length;a=0&&this[e][t].setItemVisual(this.dataIndex,n,i)},getVisual:function(n){return this[e][t].getItemVisual(this.dataIndex,n)},setLayout:function(n,i){this.dataIndex>=0&&this[e][t].setItemLayout(this.dataIndex,n,i)},getLayout:function(){return this[e][t].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[e][t].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[e][t].getRawIndex(this.dataIndex)}}}function oA(e,t,n,i,a){for(var r=new nA(i),o=0;o "+u)),p++)}var m,h=n.get("coordinateSystem");if("cartesian2d"===h||"polar"===h)m=K_(e,n);else{var g=sx.get(h),f=g&&g.dimensions||[];Dr(f,"value")<0&&f.concat(["value"]);var y=q_(e,{coordDimensions:f,encodeDefine:n.getEncode()}).dimensions;(m=new V_(y,n)).initData(e)}var v=new V_(["value"],n);return v.initData(l,s),a&&a(m,v),MD({mainData:m,struct:r,structAttr:"graph",datas:{node:m,edge:v},datasAttr:{node:"data",edge:"edgeData"}}),r.update(),r}Ar(iA,rA("hostGraph","data")),Ar(aA,rA("hostGraph","edgeData"));!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n}ze(t,e),t.prototype.init=function(t){e.prototype.init.apply(this,arguments);var n=this;function i(){return n._categoriesData}this.legendVisualProvider=new qM(i,i),this.fillDataTextStyle(t.edges||t.links),this._updateCategoriesData()},t.prototype.mergeOption=function(t){e.prototype.mergeOption.apply(this,arguments),this.fillDataTextStyle(t.edges||t.links),this._updateCategoriesData()},t.prototype.mergeDefaultAndTheme=function(t){e.prototype.mergeDefaultAndTheme.apply(this,arguments),ud(t,"edgeLabel",["show"])},t.prototype.getInitialData=function(e,t){var n,i=e.edges||e.links||[],a=e.data||e.nodes||[],r=this;if(a&&i){yO(n=this)&&(n.__curvenessList=[],n.__edgeMap={},vO(n));var o=oA(a,i,this,!0,(function(e,t){e.wrapMethod("getItemModel",(function(e){var t=r._categoriesModels[e.getShallow("category")];return t&&(t.parentModel=e.parentModel,e.parentModel=t),e}));var n=uy.prototype.getModel;function i(e,t){var i=n.call(this,e,t);return i.resolveParentPath=a,i}function a(e){if(e&&("label"===e[0]||"label"===e[1])){var t=e.slice();return"label"===e[0]?t[0]="edgeLabel":"label"===e[1]&&(t[1]="edgeLabel"),t}return e}t.wrapMethod("getItemModel",(function(e){return e.resolveParentPath=a,e.getModel=i,e}))}));return Rr(o.edges,(function(e){!function(e,t,n,i){if(yO(n)){var a=xO(e,t,n),r=n.__edgeMap,o=r[bO(a)];r[a]&&!o?r[a].isForward=!0:o&&r[a]&&(o.isForward=!0,r[a].isForward=!1),r[a]=r[a]||[],r[a].push(i)}}(e.node1,e.node2,this,e.dataIndex)}),this),o.data}},t.prototype.getGraph=function(){return this.getData().graph},t.prototype.getEdgeData=function(){return this.getGraph().edgeData},t.prototype.getCategoriesData=function(){return this._categoriesData},t.prototype.formatTooltip=function(e,t,n){if("edge"===n){var i=this.getData(),a=this.getDataParams(e,n),r=i.graph.getEdgeByIndex(e),o=i.getName(r.node1.dataIndex),s=i.getName(r.node2.dataIndex),l=[];return null!=o&&l.push(o),null!=s&&l.push(s),jb("nameValue",{name:l.join(" > "),value:a.value,noValue:null==a.value})}return nw({series:this,dataIndex:e,multipleSeries:t})},t.prototype._updateCategoriesData=function(){var e=Br(this.option.categories||[],(function(e){return null!=e.value?e:kr({value:0},e)})),t=new V_(["value"],this);t.initData(e),this._categoriesData=t,this._categoriesModels=t.mapArray((function(e){return t.getItemModel(e)}))},t.prototype.setZoom=function(e){this.option.zoom=e},t.prototype.setCenter=function(e){this.option.center=e},t.prototype.isAnimationEnabled=function(){return e.prototype.isAnimationEnabled.call(this)&&!("force"===this.get("layout")&&this.get(["force","layoutAnimation"]))},t.type="series.graph",t.dependencies=["grid","polar","geo","singleAxis","calendar"],t.defaultOption={z:2,coordinateSystem:"view",legendHoverLink:!0,layout:null,circular:{rotateLabel:!1},force:{initLayout:null,repulsion:[0,50],gravity:.1,friction:.6,edgeLength:30,layoutAnimation:!0},left:"center",top:"center",symbol:"circle",symbolSize:10,edgeSymbol:["none","none"],edgeSymbolSize:10,edgeLabel:{position:"middle",distance:5},draggable:!1,roam:!1,center:null,zoom:1,nodeScaleRatio:.6,label:{show:!1,formatter:"{b}"},itemStyle:{},lineStyle:{color:"#aaa",width:1,opacity:.5},emphasis:{scale:!0,label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}}}(ow);var sA=function(){this.angle=0,this.width=10,this.r=10,this.x=0,this.y=0},lA=function(e){function t(t){var n=e.call(this,t)||this;return n.type="pointer",n}return ze(t,e),t.prototype.getDefaultShape=function(){return new sA},t.prototype.buildPath=function(e,t){var n=Math.cos,i=Math.sin,a=t.r,r=t.width,o=t.angle,s=t.x-n(o)*r*(r>=a/3?1:2),l=t.y-i(o)*r*(r>=a/3?1:2);o=t.angle-Math.PI/2,e.moveTo(s,l),e.lineTo(t.x+n(o)*r,t.y+i(o)*r),e.lineTo(t.x+n(t.angle)*a,t.y+i(t.angle)*a),e.lineTo(t.x-n(o)*r,t.y-i(o)*r),e.lineTo(s,l)},t}(gm);function pA(e,t){var n=null==e?"":e+"";return t&&(Hr(t)?n=t.replace("{value}",n):jr(t)&&(n=t(e))),n}(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.render=function(e,t,n){this.group.removeAll();var i=e.get(["axisLine","lineStyle","color"]),a=function(e,t){var n=e.get("center"),i=t.getWidth(),a=t.getHeight(),r=Math.min(i,a);return{cx:Vc(n[0],t.getWidth()),cy:Vc(n[1],t.getHeight()),r:Vc(e.get("radius"),r/2)}}(e,n);this._renderMain(e,t,n,i,a),this._data=e.getData()},t.prototype.dispose=function(){},t.prototype._renderMain=function(e,t,n,i,a){var r=this.group,o=e.get("clockwise"),s=-e.get("startAngle")/180*Math.PI,l=-e.get("endAngle")/180*Math.PI,p=e.getModel("axisLine"),c=p.get("roundCap")?iM:_g,d=p.get("show"),u=p.getModel("lineStyle"),m=u.get("width"),h=[s,l];Ku(h,!o);for(var g=(l=h[1])-(s=h[0]),f=s,y=[],v=0;d&&v=e&&(0===t?0:i[t-1][0])Math.PI/2&&(L+=Math.PI):"tangential"===N?L=-_-Math.PI/2:$r(N)&&(L=N*Math.PI/180),0===L?d.add(new Pm({style:jf(x,{text:A,x:R,y:B,verticalAlign:c<-.8?"top":c>.8?"bottom":"middle",align:p<-.4?"left":p>.4?"right":"center"},{inheritColor:F}),silent:!0})):d.add(new Pm({style:jf(x,{text:A,x:R,y:B,verticalAlign:"middle",align:"center"},{inheritColor:F}),silent:!0,originX:R,originY:B,rotation:L}))}if(v.get("show")&&P!==b){O=(O=v.get("distance"))?O+l:l;for(var V=0;V<=w;V++){p=Math.cos(_),c=Math.sin(_);var q=new Fg({shape:{x1:p*(h-O)+u,y1:c*(h-O)+m,x2:p*(h-C-O)+u,y2:c*(h-C-O)+m},silent:!0,style:M});"auto"===M.stroke&&q.setStyle({stroke:i((P+V/w)/b)}),d.add(q),_+=I}_-=I}else _+=T}},t.prototype._renderPointer=function(e,t,n,i,a,r,o,s,l){var p=this.group,c=this._data,d=this._progressEls,u=[],m=e.get(["pointer","show"]),h=e.getModel("progress"),g=h.get("show"),f=e.getData(),y=f.mapDimension("value"),v=+e.get("min"),x=+e.get("max"),b=[v,x],w=[r,o];function S(t,n){var i,r=f.getItemModel(t).getModel("pointer"),o=Vc(r.get("width"),a.r),s=Vc(r.get("length"),a.r),l=e.get(["pointer","icon"]),p=r.get("offsetCenter"),c=Vc(p[0],a.r),d=Vc(p[1],a.r),u=r.get("keepAspect");return(i=l?PS(l,c-o/2,d-s,o,s,null,u):new lA({shape:{angle:-Math.PI/2,width:o,r:s,x:c,y:d}})).rotation=-(n+Math.PI/2),i.x=a.cx,i.y=a.cy,i}function C(e,t){var n=h.get("roundCap")?iM:_g,i=h.get("overlap"),o=i?h.get("width"):l/f.count(),p=i?a.r-o:a.r-(e+1)*o,c=i?a.r:a.r-e*o,d=new n({shape:{startAngle:r,endAngle:t,cx:a.cx,cy:a.cy,clockwise:s,r0:p,r:c}});return i&&(d.z2=x-f.get(y,e)%x),d}(g||m)&&(f.diff(c).add((function(t){var n=f.get(y,t);if(m){var i=S(t,r);nf(i,{rotation:-((isNaN(+n)?w[0]:Lc(n,b,w,!0))+Math.PI/2)},e),p.add(i),f.setItemGraphicEl(t,i)}if(g){var a=C(t,r),o=h.get("clip");nf(a,{shape:{endAngle:Lc(n,b,w,o)}},e),p.add(a),jm(e.seriesIndex,f.dataType,t,a),u[t]=a}})).update((function(t,n){var i=f.get(y,t);if(m){var a=c.getItemGraphicEl(n),o=a?a.rotation:r,s=S(t,o);s.rotation=o,tf(s,{rotation:-((isNaN(+i)?w[0]:Lc(i,b,w,!0))+Math.PI/2)},e),p.add(s),f.setItemGraphicEl(t,s)}if(g){var l=d[n],v=C(t,l?l.shape.endAngle:r),x=h.get("clip");tf(v,{shape:{endAngle:Lc(i,b,w,x)}},e),p.add(v),jm(e.seriesIndex,f.dataType,t,v),u[t]=v}})).execute(),f.each((function(e){var t=f.getItemModel(e),n=t.getModel("emphasis"),a=n.get("focus"),r=n.get("blurScope"),o=n.get("disabled");if(m){var s=f.getItemGraphicEl(e),l=f.getItemVisual(e,"style"),p=l.fill;if(s instanceof bm){var c=s.style;s.useStyle(kr({image:c.image,x:c.x,y:c.y,width:c.width,height:c.height},l))}else s.useStyle(l),"pointer"!==s.type&&s.setColor(p);s.setStyle(t.getModel(["pointer","itemStyle"]).getItemStyle()),"auto"===s.style.fill&&s.setStyle("fill",i(Lc(f.get(y,e),b,[0,1],!0))),s.z2EmphasisLift=0,Bh(s,t),Oh(s,a,r,o)}if(g){var d=u[e];d.useStyle(f.getItemVisual(e,"style")),d.setStyle(t.getModel(["progress","itemStyle"]).getItemStyle()),d.z2EmphasisLift=0,Bh(d,t),Oh(d,a,r,o)}})),this._progressEls=u)},t.prototype._renderAnchor=function(e,t){var n=e.getModel("anchor");if(n.get("show")){var i=n.get("size"),a=n.get("icon"),r=n.get("offsetCenter"),o=n.get("keepAspect"),s=PS(a,t.cx-i/2+Vc(r[0],t.r),t.cy-i/2+Vc(r[1],t.r),i,i,null,o);s.z2=n.get("showAbove")?1:0,s.setStyle(n.getModel("itemStyle").getItemStyle()),this.group.add(s)}},t.prototype._renderTitleAndDetail=function(e,t,n,i,a){var r=this,o=e.getData(),s=o.mapDimension("value"),l=+e.get("min"),p=+e.get("max"),c=new Mc,d=[],u=[],m=e.isAnimationEnabled(),h=e.get(["pointer","showAbove"]);o.diff(this._data).add((function(e){d[e]=new Pm({silent:!0}),u[e]=new Pm({silent:!0})})).update((function(e,t){d[e]=r._titleEls[t],u[e]=r._detailEls[t]})).execute(),o.each((function(t){var n=o.getItemModel(t),r=o.get(s,t),g=new Mc,f=i(Lc(r,[l,p],[0,1],!0)),y=n.getModel("title");if(y.get("show")){var v=y.get("offsetCenter"),x=a.cx+Vc(v[0],a.r),b=a.cy+Vc(v[1],a.r);(M=d[t]).attr({z2:h?0:2,style:jf(y,{x:x,y:b,text:o.getName(t),align:"center",verticalAlign:"middle"},{inheritColor:f})}),g.add(M)}var w=n.getModel("detail");if(w.get("show")){var S=w.get("offsetCenter"),C=a.cx+Vc(S[0],a.r),_=a.cy+Vc(S[1],a.r),T=Vc(w.get("width"),a.r),I=Vc(w.get("height"),a.r),E=e.get(["progress","show"])?o.getItemVisual(t,"style").fill:f,M=u[t],k=w.get("formatter");M.attr({z2:h?0:2,style:jf(w,{x:C,y:_,text:pA(r,k),width:isNaN(T)?null:T,height:isNaN(I)?null:I,align:"center",verticalAlign:"middle"},{inheritColor:E})}),Qf(M,{normal:w},r,(function(e){return pA(e,k)})),m&&Jf(M,t,o,e,{getFormattedLabel:function(e,t,n,i,a,o){return pA(o?o.interpolatedValue:r,k)}}),g.add(M)}c.add(g)})),this.group.add(c),this._titleEls=d,this._detailEls=u},t.type="gauge"})(vw),function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.visualStyleAccessPath="itemStyle",n}ze(t,e),t.prototype.getInitialData=function(e,t){return VM(this,["value"])},t.type="series.gauge",t.defaultOption={z:2,colorBy:"data",center:["50%","50%"],legendHoverLink:!0,radius:"75%",startAngle:225,endAngle:-45,clockwise:!0,min:0,max:100,splitNumber:10,axisLine:{show:!0,roundCap:!1,lineStyle:{color:[[1,"#E6EBF8"]],width:10}},progress:{show:!1,overlap:!0,width:10,roundCap:!1,clip:!0},splitLine:{show:!0,length:10,distance:10,lineStyle:{color:"#63677A",width:3,type:"solid"}},axisTick:{show:!0,splitNumber:5,length:6,distance:10,lineStyle:{color:"#63677A",width:1,type:"solid"}},axisLabel:{show:!0,distance:15,color:"#464646",fontSize:12,rotate:0},pointer:{icon:null,offsetCenter:[0,0],show:!0,showAbove:!0,length:"60%",width:6,keepAspect:!1},anchor:{show:!1,showAbove:!1,size:6,icon:"circle",offsetCenter:[0,0],keepAspect:!1,itemStyle:{color:"#fff",borderWidth:0,borderColor:"#5470c6"}},title:{show:!0,offsetCenter:[0,"20%"],color:"#464646",fontSize:16,valueAnimation:!1},detail:{show:!0,backgroundColor:"rgba(0,0,0,0)",borderWidth:0,borderColor:"#ccc",width:100,height:null,padding:[5,10],offsetCenter:[0,"40%"],color:"#464646",fontSize:30,fontWeight:"bold",lineHeight:30,valueAnimation:!1}}}(ow);var cA=["itemStyle","opacity"],dA=function(e){function t(t,n){var i=e.call(this)||this,a=i,r=new Dg,o=new Pm;return a.setTextContent(o),i.setTextGuideLine(r),i.updateData(t,n,!0),i}return ze(t,e),t.prototype.updateData=function(e,t,n){var i=this,a=e.hostModel,r=e.getItemModel(t),o=e.getItemLayout(t),s=r.getModel("emphasis"),l=r.get(cA);l=null==l?1:l,n||lf(i),i.useStyle(e.getItemVisual(t,"style")),i.style.lineJoin="round",n?(i.setShape({points:o.points}),i.style.opacity=0,nf(i,{style:{opacity:l}},a,t)):tf(i,{style:{opacity:l},shape:{points:o.points}},a,t),Bh(i,r),this._updateLabel(e,t),Oh(this,s.get("focus"),s.get("blurScope"),s.get("disabled"))},t.prototype._updateLabel=function(e,t){var n=this,i=this.getTextGuideLine(),a=n.getTextContent(),r=e.hostModel,o=e.getItemModel(t),s=e.getItemLayout(t).label,l=e.getItemVisual(t,"style"),p=l.fill;zf(a,Uf(o),{labelFetcher:e.hostModel,labelDataIndex:t,defaultOpacity:l.opacity,defaultText:e.getName(t)},{normal:{align:s.textAlign,verticalAlign:s.verticalAlign}}),n.setTextConfig({local:!0,inside:!!s.inside,insideStroke:p,outsideFill:p});var c=s.linePoints;i.setShape({points:c}),n.textGuideLineConfig={anchor:c?new ws(c[0][0],c[0][1]):null},tf(a,{style:{x:s.x,y:s.y}},r,t),a.attr({rotation:s.rotation,originX:s.x,originY:s.y,z2:10}),QI(n,JI(o),{stroke:p})},t}(kg);(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.ignoreLabelLineUpdate=!0,n}ze(t,e),t.prototype.render=function(e,t,n){var i=e.getData(),a=this._data,r=this.group;i.diff(a).add((function(e){var t=new dA(i,e);i.setItemGraphicEl(e,t),r.add(t)})).update((function(e,t){var n=a.getItemGraphicEl(t);n.updateData(i,e),r.add(n),i.setItemGraphicEl(e,n)})).remove((function(t){sf(a.getItemGraphicEl(t),e,t)})).execute(),this._data=i},t.prototype.remove=function(){this.group.removeAll(),this._data=null},t.prototype.dispose=function(){},t.type="funnel"})(vw),function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.init=function(t){e.prototype.init.apply(this,arguments),this.legendVisualProvider=new qM(Gr(this.getData,this),Gr(this.getRawData,this)),this._defaultLabelLine(t)},t.prototype.getInitialData=function(e,t){return VM(this,{coordDimensions:["value"],encodeDefaulter:zr(qv,this)})},t.prototype._defaultLabelLine=function(e){ud(e,"labelLine",["show"]);var t=e.labelLine,n=e.emphasis.labelLine;t.show=t.show&&e.label.show,n.show=n.show&&e.emphasis.label.show},t.prototype.getDataParams=function(t){var n=this.getData(),i=e.prototype.getDataParams.call(this,t),a=n.mapDimension("value"),r=n.getSum(a);return i.percent=r?+(n.get(a,t)/r*100).toFixed(2):0,i.$vars.push("percent"),i},t.type="series.funnel",t.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",left:80,top:60,right:80,bottom:60,minSize:"0%",maxSize:"100%",sort:"descending",orient:"vertical",gap:0,funnelAlign:"center",label:{show:!0,position:"outer"},labelLine:{show:!0,length:20,lineStyle:{width:1}},itemStyle:{borderColor:"#fff",borderWidth:1},emphasis:{label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}}}(ow);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._dataGroup=new Mc,n._initialized=!1,n}ze(t,e),t.prototype.init=function(){this.group.add(this._dataGroup)},t.prototype.render=function(e,t,n,i){this._progressiveEls=null;var a=this._dataGroup,r=e.getData(),o=this._data,s=e.coordinateSystem,l=s.dimensions,p=hA(e);if(r.diff(o).add((function(e){gA(mA(r,a,e,l,s),r,e,p)})).update((function(t,n){var i=o.getItemGraphicEl(n),a=uA(r,t,l,s);r.setItemGraphicEl(t,i),tf(i,{shape:{points:a}},e,t),lf(i),gA(i,r,t,p)})).remove((function(e){var t=o.getItemGraphicEl(e);a.remove(t)})).execute(),!this._initialized){this._initialized=!0;var c=function(e,t,n){var i=e.model,a=e.getRect(),r=new Em({shape:{x:a.x,y:a.y,width:a.width,height:a.height}}),o="horizontal"===i.get("layout")?"width":"height";return r.setShape(o,0),nf(r,{shape:{width:a.width,height:a.height}},t,n),r}(s,e,(function(){setTimeout((function(){a.removeClipPath()}))}));a.setClipPath(c)}this._data=r},t.prototype.incrementalPrepareRender=function(e,t,n){this._initialized=!0,this._data=null,this._dataGroup.removeAll()},t.prototype.incrementalRender=function(e,t,n){for(var i=t.getData(),a=t.coordinateSystem,r=a.dimensions,o=hA(t),s=this._progressiveEls=[],l=e.start;l5)return;var i=this._model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX,e.offsetY]);"none"!==i.behavior&&this._dispatchExpand({axisExpandWindow:i.axisExpandWindow})}this._mouseDownPoint=null},mousemove:function(e){if(!this._mouseDownPoint&&vA(this,"mousemove")){var t=this._model,n=t.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX,e.offsetY]),i=n.behavior;"jump"===i&&this._throttledDispatchExpand.debounceNextCall(t.get("axisExpandDebounce")),this._throttledDispatchExpand("none"===i?null:{axisExpandWindow:n.axisExpandWindow,animation:"jump"===i?null:{duration:0}})}}};function vA(e,t){var n=e._model;return n.get("axisExpandable")&&n.get("axisExpandTriggerOn")===t}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.init=function(){e.prototype.init.apply(this,arguments),this.mergeOption({})},t.prototype.mergeOption=function(e){var t=this.option;e&&Er(t,e,!0),this._initDimensions()},t.prototype.contains=function(e,t){var n=e.get("parallelIndex");return null!=n&&t.getComponent("parallel",n)===this},t.prototype.setAxisExpand=function(e){Rr(["axisExpandable","axisExpandCenter","axisExpandCount","axisExpandWidth","axisExpandWindow"],(function(t){e.hasOwnProperty(t)&&(this.option[t]=e[t])}),this)},t.prototype._initDimensions=function(){var e=this.dimensions=[],t=this.parallelAxisIndex=[];Rr(Lr(this.ecModel.queryComponents({mainType:"parallelAxis"}),(function(e){return(e.get("parallelIndex")||0)===this.componentIndex}),this),(function(n){e.push("dim"+n.get("dim")),t.push(n.componentIndex)}))},t.type="parallel",t.dependencies=["parallelAxis"],t.layoutMode="box",t.defaultOption={z:0,left:80,top:60,right:80,bottom:60,layout:"horizontal",axisExpandable:!1,axisExpandCenter:null,axisExpandCount:0,axisExpandWidth:50,axisExpandRate:17,axisExpandDebounce:50,axisExpandSlideTriggerArea:[-.15,.05,.4],axisExpandTriggerOn:"click",parallelAxisDefault:null}}(Sv);var xA=function(e){function t(t,n,i,a,r){var o=e.call(this,t,n,i)||this;return o.type=a||"value",o.axisIndex=r,o}return ze(t,e),t.prototype.isHorizontal=function(){return"horizontal"!==this.coordinateSystem.getModel().get("layout")},t}(MI);function bA(e,t,n,i,a,r){e=e||0;var o=n[1]-n[0];if(null!=a&&(a=SA(a,[0,o])),null!=r&&(r=Math.max(r,null!=a?a:0)),"all"===i){var s=Math.abs(t[1]-t[0]);s=SA(s,[0,o]),a=r=SA(s,[a,r]),i=0}t[0]=SA(t[0],n),t[1]=SA(t[1],n);var l=wA(t,i);t[i]+=e;var p,c=a||0,d=n.slice();return l.sign<0?d[0]+=c:d[1]-=c,t[i]=SA(t[i],d),p=wA(t,i),null!=a&&(p.sign!==l.sign||p.spanr&&(t[1-i]=t[i]+p.sign*r),t}function wA(e,t){var n=e[t]-e[1-t];return{span:Math.abs(n),sign:n>0?-1:n<0?1:t?-1:1}}function SA(e,t){return Math.min(null!=t[1]?t[1]:1/0,Math.max(null!=t[0]?t[0]:-1/0,e))}var CA=Rr,_A=Math.min,TA=Math.max,IA=Math.floor,EA=Math.ceil,MA=qc,kA=Math.PI;!function(){function e(e,t,n){this.type="parallel",this._axesMap=fo(),this._axesLayout={},this.dimensions=e.dimensions,this._model=e,this._init(e,t,n)}e.prototype._init=function(e,t,n){var i=e.dimensions,a=e.parallelAxisIndex;CA(i,(function(e,n){var i=a[n],r=t.getComponent("parallelAxis",i),o=this._axesMap.set(e,new xA(e,KT(r),[0,0],r.get("type"),i)),s="category"===o.type;o.onBand=s&&r.get("boundaryGap"),o.inverse=r.get("inverse"),r.axis=o,o.model=r,o.coordinateSystem=r.coordinateSystem=this}),this)},e.prototype.update=function(e,t){this._updateAxesFromSeries(this._model,e)},e.prototype.containPoint=function(e){var t=this._makeLayoutInfo(),n=t.axisBase,i=t.layoutBase,a=t.pixelDimIndex,r=e[1-a],o=e[a];return r>=n&&r<=n+t.axisLength&&o>=i&&o<=i+t.layoutLength},e.prototype.getModel=function(){return this._model},e.prototype._updateAxesFromSeries=function(e,t){t.eachSeries((function(n){if(e.contains(n,t)){var i=n.getData();CA(this.dimensions,(function(e){var t=this._axesMap.get(e);t.scale.unionExtentFromData(i,i.mapDimension(e)),$T(t.scale,t.model)}),this)}}),this)},e.prototype.resize=function(e,t){this._rect=gv(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()}),this._layoutAxes()},e.prototype.getRect=function(){return this._rect},e.prototype._makeLayoutInfo=function(){var e,t=this._model,n=this._rect,i=["x","y"],a=["width","height"],r=t.get("layout"),o="horizontal"===r?0:1,s=n[a[o]],l=[0,s],p=this.dimensions.length,c=PA(t.get("axisExpandWidth"),l),d=PA(t.get("axisExpandCount")||0,[0,p]),u=t.get("axisExpandable")&&p>3&&p>d&&d>1&&c>0&&s>0,m=t.get("axisExpandWindow");m?(e=PA(m[1]-m[0],l),m[1]=m[0]+e):(e=PA(c*(d-1),l),(m=[c*(t.get("axisExpandCenter")||IA(p/2))-e/2])[1]=m[0]+e);var h=(s-e)/(p-d);h<3&&(h=0);var g=[IA(MA(m[0]/c,1))+1,EA(MA(m[1]/c,1))-1],f=h/c*m[0];return{layout:r,pixelDimIndex:o,layoutBase:n[i[o]],layoutLength:s,axisBase:n[i[1-o]],axisLength:n[a[1-o]],axisExpandable:u,axisExpandWidth:c,axisCollapseWidth:h,axisExpandWindow:m,axisCount:p,winInnerIndices:g,axisExpandWindow0Pos:f}},e.prototype._layoutAxes=function(){var e=this._rect,t=this._axesMap,n=this.dimensions,i=this._makeLayoutInfo(),a=i.layout;t.each((function(e){var t=[0,i.axisLength],n=e.inverse?1:0;e.setExtent(t[n],t[1-n])})),CA(n,(function(t,n){var r=(i.axisExpandable?OA:DA)(n,i),o={horizontal:{x:r.position,y:i.axisLength},vertical:{x:0,y:r.position}},s={horizontal:kA/2,vertical:0},l=[o[a].x+e.x,o[a].y+e.y],p=s[a],c=[1,0,0,1,0,0];ys(c,c,p),fs(c,c,l),this._axesLayout[t]={position:l,rotation:p,transform:c,axisNameAvailableWidth:r.axisNameAvailableWidth,axisLabelShow:r.axisLabelShow,nameTruncateMaxWidth:r.nameTruncateMaxWidth,tickDirection:1,labelDirection:1}}),this)},e.prototype.getAxis=function(e){return this._axesMap.get(e)},e.prototype.dataToPoint=function(e,t){return this.axisCoordToPoint(this._axesMap.get(t).dataToCoord(e),t)},e.prototype.eachActiveState=function(e,t,n,i){null==n&&(n=0),null==i&&(i=e.count());var a=this._axesMap,r=this.dimensions,o=[],s=[];Rr(r,(function(t){o.push(e.mapDimension(t)),s.push(a.get(t).model)}));for(var l=this.hasAxisBrushed(),p=n;pa*(1-c[0])?(l="jump",o=s-a*(1-c[2])):(o=s-a*c[1])>=0&&(o=s-a*(1-c[1]))<=0&&(o=0),(o*=t.axisExpandWidth/p)?bA(o,i,r,"all"):l="none";else{var u=i[1]-i[0];(i=[TA(0,r[1]*s/u-u/2)])[1]=_A(r[1],i[0]+u),i[0]=i[1]-u}return{axisExpandWindow:i,behavior:l}}}();function PA(e,t){return _A(TA(e,t[0]),t[1])}function DA(e,t){var n=t.layoutLength/(t.axisCount-1);return{position:n*e,axisNameAvailableWidth:n,axisLabelShow:!0}}function OA(e,t){var n,i,a=t.layoutLength,r=t.axisExpandWidth,o=t.axisCount,s=t.axisCollapseWidth,l=t.winInnerIndices,p=s,c=!1;return e=0;n--)Gc(t[n])},t.prototype.getActiveState=function(e){var t=this.activeIntervals;if(!t.length)return"normal";if(null==e||isNaN(+e))return"inactive";if(1===t.length){var n=t[0];if(n[0]<=e&&e<=n[1])return"active"}else for(var i=0,a=t.length;i6}(e)||r){if(o&&!r){"single"===s.brushMode&&QA(e);var l=Ir(s);l.brushType=gF(l.brushType,o),l.panelId=o===FA?null:o.panelId,r=e._creatingCover=jA(e,l),e._covers.push(r)}if(r){var p=vF[gF(e._brushType,o)];r.__brushOption.range=p.getCreatingRange(dF(e,r,e._track)),i&&(HA(e,r),p.updateCommon(e,r)),WA(e,r),a={isEnd:i}}}else i&&"single"===s.brushMode&&s.removeOnClick&&XA(e,t,n)&&QA(e)&&(a={isEnd:i,removeOnClick:!0});return a}function gF(e,t){return"auto"===e?t.defaultBrushType:e}var fF={mousedown:function(e){if(this._dragging)yF(this,e);else if(!e.target||!e.target.draggable){uF(e);var t=this.group.transformCoordToLocal(e.offsetX,e.offsetY);this._creatingCover=null,(this._creatingPanel=XA(this,e,t))&&(this._dragging=!0,this._track=[t.slice()])}},mousemove:function(e){var t=e.offsetX,n=e.offsetY,i=this.group.transformCoordToLocal(t,n);if(function(e,t,n){if(e._brushType&&!function(e,t,n){var i=e._zr;return t<0||t>i.getWidth()||n<0||n>i.getHeight()}(e,t.offsetX,t.offsetY)){var i=e._zr,a=e._covers,r=XA(e,t,n);if(!e._dragging)for(var o=0;o=0&&(r[a[o].depth]=new uy(a[o],this,t));if(i&&n){var s=oA(i,n,this,!0,(function(e,t){e.wrapMethod("getItemModel",(function(e,t){var n=e.parentModel,i=n.getData().getItemLayout(t);if(i){var a=i.depth,r=n.levelModels[a];r&&(e.parentModel=r)}return e})),t.wrapMethod("getItemModel",(function(e,t){var n=e.parentModel,i=n.getGraph().getEdgeByIndex(t).node1.getLayout();if(i){var a=i.depth,r=n.levelModels[a];r&&(e.parentModel=r)}return e}))}));return s.data}},t.prototype.setNodePosition=function(e,t){var n=(this.option.data||this.option.nodes)[e];n.localX=t[0],n.localY=t[1]},t.prototype.getGraph=function(){return this.getData().graph},t.prototype.getEdgeData=function(){return this.getGraph().edgeData},t.prototype.formatTooltip=function(e,t,n){function i(e){return isNaN(e)||null==e}if("edge"===n){var a=this.getDataParams(e,n),r=a.data,o=a.value;return jb("nameValue",{name:r.source+" -- "+r.target,value:o,noValue:i(o)})}var s=this.getGraph().getNodeByIndex(e).getLayout().value,l=this.getDataParams(e,n).data.name;return jb("nameValue",{name:null!=l?l+"":null,value:s,noValue:i(s)})},t.prototype.optionUpdated=function(){},t.prototype.getDataParams=function(t,n){var i=e.prototype.getDataParams.call(this,t,n);if(null==i.value&&"node"===n){var a=this.getGraph().getNodeByIndex(t).getLayout().value;i.value=a}return i},t.type="series.sankey",t.defaultOption={z:2,coordinateSystem:"view",left:"5%",top:"5%",right:"20%",bottom:"5%",orient:"horizontal",nodeWidth:20,nodeGap:8,draggable:!0,layoutIterations:32,label:{show:!0,position:"right",fontSize:12},edgeLabel:{show:!1,fontSize:12},levels:[],nodeAlign:"justify",lineStyle:{color:"#314656",opacity:.2,curveness:.5},emphasis:{label:{show:!0},lineStyle:{opacity:.5}},select:{itemStyle:{borderColor:"#212121"}},animationEasing:"linear",animationDuration:1e3}}(ow);var MF=function(){function e(){}return e.prototype.getInitialData=function(e,t){var n,i,a=t.getComponent("xAxis",this.get("xAxisIndex")),r=t.getComponent("yAxis",this.get("yAxisIndex")),o=a.get("type"),s=r.get("type");"category"===o?(e.layout="horizontal",n=a.getOrdinalMeta(),i=!0):"category"===s?(e.layout="vertical",n=r.getOrdinalMeta(),i=!0):e.layout=e.layout||"horizontal";var l=["x","y"],p="horizontal"===e.layout?0:1,c=this._baseAxisDim=l[p],d=l[1-p],u=[a,r],m=u[p].get("type"),h=u[1-p].get("type"),g=e.data;if(g&&i){var f=[];Rr(g,(function(e,t){var n;Ur(e)?(n=e.slice(),e.unshift(t)):Ur(e.value)?((n=kr({},e)).value=n.value.slice(),e.value.unshift(t)):n=e,f.push(n)})),e.data=f}var y=this.defaultValueDimensions,v=[{name:c,type:v_(m),ordinalMeta:n,otherDims:{tooltip:!1,itemName:0},dimsDef:["base"]},{name:d,type:v_(h),dimsDef:y.slice()}];return VM(this,{coordDimensions:v,dimensionsCount:y.length+1,encodeDefaulter:zr(Vv,v,this)})},e.prototype.getBaseAxis=function(){var e=this._baseAxisDim;return this.ecModel.getComponent(e+"Axis",this.get(e+"AxisIndex")).axis},e}(),kF=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.defaultValueDimensions=[{name:"min",defaultTooltip:!0},{name:"Q1",defaultTooltip:!0},{name:"median",defaultTooltip:!0},{name:"Q3",defaultTooltip:!0},{name:"max",defaultTooltip:!0}],n.visualDrawType="stroke",n}return ze(t,e),t.type="series.boxplot",t.dependencies=["xAxis","yAxis","grid"],t.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,boxWidth:[7,50],itemStyle:{color:"#fff",borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2,shadowBlur:5,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0,0,0,0.2)"}},animationDuration:800},t}(ow);Ar(kF,MF,!0);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.render=function(e,t,n){var i=e.getData(),a=this.group,r=this._data;this._data||a.removeAll();var o="horizontal"===e.get("layout")?1:0;i.diff(r).add((function(e){if(i.hasValue(e)){var t=OF(i.getItemLayout(e),i,e,o,!0);i.setItemGraphicEl(e,t),a.add(t)}})).update((function(e,t){var n=r.getItemGraphicEl(t);if(i.hasValue(e)){var s=i.getItemLayout(e);n?(lf(n),AF(s,n,i,e)):n=OF(s,i,e,o),a.add(n),i.setItemGraphicEl(e,n)}else a.remove(n)})).remove((function(e){var t=r.getItemGraphicEl(e);t&&a.remove(t)})).execute(),this._data=i},t.prototype.remove=function(e){var t=this.group,n=this._data;this._data=null,n&&n.eachItemGraphicEl((function(e){e&&t.remove(e)}))},t.type="boxplot"}(vw);var PF=function(){},DF=function(e){function t(t){var n=e.call(this,t)||this;return n.type="boxplotBoxPath",n}return ze(t,e),t.prototype.getDefaultShape=function(){return new PF},t.prototype.buildPath=function(e,t){var n=t.points,i=0;for(e.moveTo(n[i][0],n[i][1]),i++;i<4;i++)e.lineTo(n[i][0],n[i][1]);for(e.closePath();i0?"borderColor":"borderColor0"])||n.get(["itemStyle",e>0?"color":"color0"]);0===e&&(a=n.get(["itemStyle","borderColorDoji"]));var r=n.getModel("itemStyle").getItemStyle(RF);t.useStyle(r),t.style.fill=null,t.style.stroke=a}var WF=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.defaultValueDimensions=[{name:"open",defaultTooltip:!0},{name:"close",defaultTooltip:!0},{name:"lowest",defaultTooltip:!0},{name:"highest",defaultTooltip:!0}],n}return ze(t,e),t.prototype.getShadowDim=function(){return"open"},t.prototype.brushSelector=function(e,t,n){var i=t.getItemLayout(e);return i&&n.rect(i.brushRect)},t.type="series.candlestick",t.dependencies=["xAxis","yAxis","grid"],t.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,clip:!0,itemStyle:{color:"#eb5454",color0:"#47b262",borderColor:"#eb5454",borderColor0:"#47b262",borderColorDoji:null,borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2}},barMaxWidth:null,barMinWidth:null,barWidth:null,large:!0,largeThreshold:600,progressive:3e3,progressiveThreshold:1e4,progressiveChunkMode:"mod",animationEasing:"linear",animationDuration:300},t}(ow);Ar(WF,MF,!0);var $F=["itemStyle","borderColor"],KF=["itemStyle","borderColor0"],YF=["itemStyle","borderColorDoji"],XF=["itemStyle","color"],ZF=["itemStyle","color0"];gw(),gw();function QF(e,t,n,i,a,r){return n>i?-1:n0?e.get(a,t-1)<=i?1:-1:1}function JF(e,t){var n=t.rippleEffectColor||t.color;e.eachChild((function(e){e.attr({z:t.z,zlevel:t.zlevel,style:{stroke:"stroke"===t.brushType?n:null,fill:"fill"===t.brushType?n:null}})}))}var eR=function(e){function t(t,n){var i=e.call(this)||this,a=new vE(t,n),r=new Mc;return i.add(a),i.add(r),i.updateData(t,n),i}return ze(t,e),t.prototype.stopEffectAnimation=function(){this.childAt(1).removeAll()},t.prototype.startEffectAnimation=function(e){for(var t=e.symbolType,n=e.color,i=e.rippleNumber,a=this.childAt(1),r=0;r0&&(r=this._getLineLength(i)/l*1e3),r!==this._period||o!==this._loop||s!==this._roundTrip){i.stopAnimation();var c=void 0;c=jr(p)?p(n):p,i.__t>0&&(c=-r*i.__t),this._animateSymbol(i,r,c,o,s)}this._period=r,this._loop=o,this._roundTrip=s}},t.prototype._animateSymbol=function(e,t,n,i,a){if(t>0){e.__t=0;var r=this,o=e.animate("",i).when(a?2*t:t,{__t:a?2:1}).delay(n).during((function(){r._updateSymbolPosition(e)}));i||o.done((function(){r.remove(e)})),o.start()}},t.prototype._getLineLength=function(e){return Fo(e.__p1,e.__cp1)+Fo(e.__cp1,e.__p2)},t.prototype._updateAnimationPoints=function(e,t){e.__p1=t[0],e.__p2=t[1],e.__cp1=t[2]||[(t[0][0]+t[1][0])/2,(t[0][1]+t[1][1])/2]},t.prototype.updateData=function(e,t,n){this.childAt(0).updateData(e,t,n),this._updateEffectSymbol(e,t)},t.prototype._updateSymbolPosition=function(e){var t=e.__p1,n=e.__p2,i=e.__cp1,a=e.__t<1?e.__t:2-e.__t,r=[e.x,e.y],o=r.slice(),s=yl,l=vl;r[0]=s(t[0],i[0],n[0],a),r[1]=s(t[1],i[1],n[1],a);var p=e.__t<1?l(t[0],i[0],n[0],a):l(n[0],i[0],t[0],1-a),c=e.__t<1?l(t[1],i[1],n[1],a):l(n[1],i[1],t[1],1-a);e.rotation=-Math.atan2(c,p)-Math.PI/2,"line"!==this._symbolType&&"rect"!==this._symbolType&&"roundRect"!==this._symbolType||(void 0!==e.__lastT&&e.__lastT=0&&!(i[r]<=t);r--);r=Math.min(r,a-2)}else{for(r=o;rt);r++);r=Math.min(r-1,a-2)}var s=(t-i[r])/(i[r+1]-i[r]),l=n[r],p=n[r+1];e.x=l[0]*(1-s)+s*p[0],e.y=l[1]*(1-s)+s*p[1];var c=e.__t<1?p[0]-l[0]:l[0]-p[0],d=e.__t<1?p[1]-l[1]:l[1]-p[1];e.rotation=-Math.atan2(d,c)-Math.PI/2,this._lastFrame=r,this._lastFramePercent=t,e.ignore=!1}},t}(tR),aR=function(){this.polyline=!1,this.curveness=0,this.segs=[]},rR=function(e){function t(t){var n=e.call(this,t)||this;return n._off=0,n.hoverDataIdx=-1,n}return ze(t,e),t.prototype.reset=function(){this.notClear=!1,this._off=0},t.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},t.prototype.getDefaultShape=function(){return new aR},t.prototype.buildPath=function(e,t){var n,i=t.segs,a=t.curveness;if(t.polyline)for(n=this._off;n0){e.moveTo(i[n++],i[n++]);for(var o=1;o0){var d=(s+p)/2-(l-c)*a,u=(l+c)/2-(p-s)*a;e.quadraticCurveTo(d,u,p,c)}else e.lineTo(p,c)}this.incremental&&(this._off=n,this.notClear=!0)},t.prototype.findDataIndex=function(e,t){var n=this.shape,i=n.segs,a=n.curveness,r=this.style.lineWidth;if(n.polyline)for(var o=0,s=0;s0)for(var p=i[s++],c=i[s++],d=1;d0){if(Qu(p,c,(p+u)/2-(c-m)*a,(c+m)/2-(u-p)*a,u,m,r,e,t))return o}else if(Xu(p,c,u,m,r,e,t))return o;o++}return-1},t.prototype.contain=function(e,t){var n=this.transformCoordToLocal(e,t),i=this.getBoundingRect();return e=n[0],t=n[1],i.contain(e,t)?(this.hoverDataIdx=this.findDataIndex(e,t))>=0:(this.hoverDataIdx=-1,!1)},t.prototype.getBoundingRect=function(){var e=this._rect;if(!e){for(var t=this.shape.segs,n=1/0,i=1/0,a=-1/0,r=-1/0,o=0;o0&&(r.dataIndex=n+e.__startIndex)}))},e.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},e}(),sR={seriesType:"lines",plan:gw(),reset:function(e){var t=e.coordinateSystem;if(t){var n=e.get("polyline"),i=e.pipelineContext.large;return{progress:function(a,r){var o=[];if(i){var s=void 0,l=a.end-a.start;if(n){for(var p=0,c=a.start;c0&&(l||s.configLayer(r,{motionBlur:!0,lastFrameAlpha:Math.max(Math.min(o/10+.9,1),0)})),a.updateData(i);var p=e.get("clip",!0)&&BE(e.coordinateSystem,!1,e);p?this.group.setClipPath(p):this.group.removeClipPath(),this._lastZlevel=r,this._finished=!0},t.prototype.incrementalPrepareRender=function(e,t,n){var i=e.getData();this._updateLineDraw(i,e).incrementalPrepareUpdate(i),this._clearLayer(n),this._finished=!1},t.prototype.incrementalRender=function(e,t,n){this._lineDraw.incrementalUpdate(e,t.getData()),this._finished=e.end===t.getData().count()},t.prototype.eachRendered=function(e){this._lineDraw&&this._lineDraw.eachRendered(e)},t.prototype.updateTransform=function(e,t,n){var i=e.getData(),a=e.pipelineContext;if(!this._finished||a.large||a.progressiveRender)return{update:!0};var r=sR.reset(e,t,n);r.progress&&r.progress({start:0,end:i.count(),count:i.count()},i),this._lineDraw.updateLayout(),this._clearLayer(n)},t.prototype._updateLineDraw=function(e,t){var n=this._lineDraw,i=this._showEffect(t),a=!!t.get("polyline"),r=t.pipelineContext.large;return n&&i===this._hasEffet&&a===this._isPolyline&&r===this._isLargeDraw||(n&&n.remove(),n=this._lineDraw=r?new oR:new zO(a?i?iR:nR:i?tR:GO),this._hasEffet=i,this._isPolyline=a,this._isLargeDraw=r),this.group.add(n.group),n},t.prototype._showEffect=function(e){return!!e.get(["effect","show"])},t.prototype._clearLayer=function(e){var t=e.getZr();"svg"===t.painter.getType()||null==this._lastZlevel||t.painter.getLayer(this._lastZlevel).clear(!0)},t.prototype.remove=function(e,t){this._lineDraw&&this._lineDraw.remove(),this._lineDraw=null,this._clearLayer(t)},t.prototype.dispose=function(e,t){this.remove(e,t)},t.type="lines"}(vw),"undefined"==typeof Uint32Array?Array:Uint32Array),pR="undefined"==typeof Float64Array?Array:Float64Array;function cR(e){var t=e.data;t&&t[0]&&t[0][0]&&t[0][0].coord&&(e.data=Br(t,(function(e){var t={coords:[e[0].coord,e[1].coord]};return e[0].name&&(t.fromName=e[0].name),e[1].name&&(t.toName=e[1].name),Mr([t,e[0],e[1]])})))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.visualStyleAccessPath="lineStyle",n.visualDrawType="stroke",n}ze(t,e),t.prototype.init=function(t){t.data=t.data||[],cR(t);var n=this._processFlatCoordsArray(t.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(t.data=new Float32Array(n.count)),e.prototype.init.apply(this,arguments)},t.prototype.mergeOption=function(t){if(cR(t),t.data){var n=this._processFlatCoordsArray(t.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(t.data=new Float32Array(n.count))}e.prototype.mergeOption.apply(this,arguments)},t.prototype.appendData=function(e){var t=this._processFlatCoordsArray(e.data);t.flatCoords&&(this._flatCoords?(this._flatCoords=yo(this._flatCoords,t.flatCoords),this._flatCoordsOffset=yo(this._flatCoordsOffset,t.flatCoordsOffset)):(this._flatCoords=t.flatCoords,this._flatCoordsOffset=t.flatCoordsOffset),e.data=new Float32Array(t.count)),this.getRawData().appendData(e.data)},t.prototype._getCoordsFromItemModel=function(e){var t=this.getData().getItemModel(e),n=t.option instanceof Array?t.option:t.getShallow("coords");return n},t.prototype.getLineCoordsCount=function(e){return this._flatCoordsOffset?this._flatCoordsOffset[2*e+1]:this._getCoordsFromItemModel(e).length},t.prototype.getLineCoords=function(e,t){if(this._flatCoordsOffset){for(var n=this._flatCoordsOffset[2*e],i=this._flatCoordsOffset[2*e+1],a=0;a ")})},t.prototype.preventIncremental=function(){return!!this.get(["effect","show"])},t.prototype.getProgressive=function(){var e=this.option.progressive;return null==e?this.option.large?1e4:this.get("progressive"):e},t.prototype.getProgressiveThreshold=function(){var e=this.option.progressiveThreshold;return null==e?this.option.large?2e4:this.get("progressiveThreshold"):e},t.prototype.getZLevelKey=function(){var e=this.getModel("effect"),t=e.get("trailLength");return this.getData().count()>this.getProgressiveThreshold()?this.id:e.get("show")&&t>0?t+"":""},t.type="series.lines",t.dependencies=["grid","polar","geo","calendar"],t.defaultOption={coordinateSystem:"geo",z:2,legendHoverLink:!0,xAxisIndex:0,yAxisIndex:0,symbol:["none","none"],symbolSize:[10,10],geoIndex:0,effect:{show:!1,period:4,constantSpeed:0,symbol:"circle",symbolSize:3,loop:!0,trailLength:.2},large:!1,largeThreshold:2e3,polyline:!1,clip:!0,label:{show:!1,position:"end"},lineStyle:{opacity:.5}}}(ow);var dR=function(){function e(){this.blurSize=30,this.pointSize=20,this.maxOpacity=1,this.minOpacity=0,this._gradientPixels={inRange:null,outOfRange:null};var e=dr.createCanvas();this.canvas=e}return e.prototype.update=function(e,t,n,i,a,r){var o=this._getBrush(),s=this._getGradient(a,"inRange"),l=this._getGradient(a,"outOfRange"),p=this.pointSize+this.blurSize,c=this.canvas,d=c.getContext("2d"),u=e.length;c.width=t,c.height=n;for(var m=0;m0){var T=r(y)?s:l;y>0&&(y=y*C+S),x[b++]=T[_],x[b++]=T[_+1],x[b++]=T[_+2],x[b++]=T[_+3]*y*256}else b+=4}return d.putImageData(v,0,0),c},e.prototype._getBrush=function(){var e=this._brushCanvas||(this._brushCanvas=dr.createCanvas()),t=this.pointSize+this.blurSize,n=2*t;e.width=n,e.height=n;var i=e.getContext("2d");return i.clearRect(0,0,n,n),i.shadowOffsetX=n,i.shadowBlur=this.blurSize,i.shadowColor="#000",i.beginPath(),i.arc(-t,t,this.pointSize,0,2*Math.PI,!0),i.closePath(),i.fill(),e},e.prototype._getGradient=function(e,t){for(var n=this._gradientPixels,i=n[t]||(n[t]=new Uint8ClampedArray(1024)),a=[0,0,0,0],r=0,o=0;o<256;o++)e[t](o/255,!0,a),i[r++]=a[0],i[r++]=a[1],i[r++]=a[2],i[r++]=a[3];return i},e}();function uR(e){var t=e.dimensions;return"lng"===t[0]&&"lat"===t[1]}(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.render=function(e,t,n){var i;t.eachComponent("visualMap",(function(t){t.eachTargetSeries((function(n){n===e&&(i=t)}))})),this._progressiveEls=null,this.group.removeAll();var a=e.coordinateSystem;"cartesian2d"===a.type||"calendar"===a.type?this._renderOnCartesianAndCalendar(e,n,0,e.getData().count()):uR(a)&&this._renderOnGeo(a,e,i,n)},t.prototype.incrementalPrepareRender=function(e,t,n){this.group.removeAll()},t.prototype.incrementalRender=function(e,t,n,i){var a=t.coordinateSystem;a&&(uR(a)?this.render(t,n,i):(this._progressiveEls=[],this._renderOnCartesianAndCalendar(t,i,e.start,e.end,!0)))},t.prototype.eachRendered=function(e){Nf(this._progressiveEls||this.group,e)},t.prototype._renderOnCartesianAndCalendar=function(e,t,n,i,a){var r,o,s,l,p=e.coordinateSystem,c=NE(p,"cartesian2d");if(c){var d=p.getAxis("x"),u=p.getAxis("y");0,r=d.getBandWidth()+.5,o=u.getBandWidth()+.5,s=d.scale.getExtent(),l=u.scale.getExtent()}for(var m=this.group,h=e.getData(),g=e.getModel(["emphasis","itemStyle"]).getItemStyle(),f=e.getModel(["blur","itemStyle"]).getItemStyle(),y=e.getModel(["select","itemStyle"]).getItemStyle(),v=e.get(["itemStyle","borderRadius"]),x=Uf(e),b=e.getModel("emphasis"),w=b.get("focus"),S=b.get("blurScope"),C=b.get("disabled"),_=c?[h.mapDimension("x"),h.mapDimension("y"),h.mapDimension("value")]:[h.mapDimension("time"),h.mapDimension("value")],T=n;Ts[1]||kl[1])continue;var P=p.dataToPoint([M,k]);I=new Em({shape:{x:P[0]-r/2,y:P[1]-o/2,width:r,height:o},style:E})}else{if(isNaN(h.get(_[1],T)))continue;I=new Em({z2:1,shape:p.dataToRect([h.get(_[0],T)]).contentShape,style:E})}if(h.hasItemOption){var D=h.getItemModel(T),O=D.getModel("emphasis");g=O.getModel("itemStyle").getItemStyle(),f=D.getModel(["blur","itemStyle"]).getItemStyle(),y=D.getModel(["select","itemStyle"]).getItemStyle(),v=D.get(["itemStyle","borderRadius"]),w=O.get("focus"),S=O.get("blurScope"),C=O.get("disabled"),x=Uf(D)}I.shape.r=v;var A=e.getRawValue(T),F="-";A&&null!=A[2]&&(F=A[2]+""),zf(I,x,{labelFetcher:e,labelDataIndex:T,defaultOpacity:E.opacity,defaultText:F}),I.ensureState("emphasis").style=g,I.ensureState("blur").style=f,I.ensureState("select").style=y,Oh(I,w,S,C),I.incremental=a,a&&(I.states.emphasis.hoverLayer=!0),m.add(I),h.setItemGraphicEl(T,I),this._progressiveEls&&this._progressiveEls.push(I)}},t.prototype._renderOnGeo=function(e,t,n,i){var a=n.targetVisuals.inRange,r=n.targetVisuals.outOfRange,o=t.getData(),s=this._hmLayer||this._hmLayer||new dR;s.blurSize=t.get("blurSize"),s.pointSize=t.get("pointSize"),s.minOpacity=t.get("minOpacity"),s.maxOpacity=t.get("maxOpacity");var l=e.getViewRect().clone(),p=e.getRoamTransform();l.applyTransform(p);var c=Math.max(l.x,0),d=Math.max(l.y,0),u=Math.min(l.width+l.x,i.getWidth()),m=Math.min(l.height+l.y,i.getHeight()),h=u-c,g=m-d,f=[o.mapDimension("lng"),o.mapDimension("lat"),o.mapDimension("value")],y=o.mapArray(f,(function(t,n,i){var a=e.dataToPoint([t,n]);return a[0]-=c,a[1]-=d,a.push(i),a})),v=n.getExtent(),x="visualMap.continuous"===n.type?function(e,t){var n=e[1]-e[0];return t=[(t[0]-e[0])/n,(t[1]-e[0])/n],function(e){return e>=t[0]&&e<=t[1]}}(v,n.option.range):function(e,t,n){var i=e[1]-e[0],a=(t=Br(t,(function(t){return{interval:[(t.interval[0]-e[0])/i,(t.interval[1]-e[0])/i]}}))).length,r=0;return function(e){var i;for(i=r;i=0;i--){var o;if((o=t[i].interval)[0]<=e&&e<=o[1]){r=i;break}}return i>=0&&i0?1:-1}(n,r,a,i,d),function(e,t,n,i,a,r,o,s,l,p){var c,d=l.valueDim,u=l.categoryDim,m=Math.abs(n[u.wh]),h=e.getItemVisual(t,"symbolSize");c=Ur(h)?h.slice():null==h?["100%","100%"]:[h,h];c[u.index]=Vc(c[u.index],m),c[d.index]=Vc(c[d.index],i?m:Math.abs(r)),p.symbolSize=c;var g=p.symbolScale=[c[0]/s,c[1]/s];g[d.index]*=(l.isHorizontal?-1:1)*o}(e,t,a,r,0,d.boundingLength,d.pxSign,p,i,d),function(e,t,n,i,a){var r=e.get(mR)||0;r&&(gR.attr({scaleX:t[0],scaleY:t[1],rotation:n}),gR.updateTransform(),r/=gR.getLineScale(),r*=t[i.valueDim.index]);a.valueLineWidth=r||0}(n,d.symbolScale,l,i,d);var u=d.symbolSize,m=OS(n.get("symbolOffset"),u);return function(e,t,n,i,a,r,o,s,l,p,c,d){var u=c.categoryDim,m=c.valueDim,h=d.pxSign,g=Math.max(t[m.index]+s,0),f=g;if(i){var y=Math.abs(l),v=no(e.get("symbolMargin"),"15%")+"",x=!1;v.lastIndexOf("!")===v.length-1&&(x=!0,v=v.slice(0,v.length-1));var b=Vc(v,t[m.index]),w=Math.max(g+2*b,0),S=x?0:2*b,C=nd(i),_=C?i:OR((y+S)/w);w=g+2*(b=(y-_*g)/2/(x?_:Math.max(_-1,1))),S=x?0:2*b,C||"fixed"===i||(_=p?OR((Math.abs(p)+S)/w):0),f=_*w-S,d.repeatTimes=_,d.symbolMargin=b}var T=h*(f/2),I=d.pathPosition=[];I[u.index]=n[u.wh]/2,I[m.index]="start"===o?T:"end"===o?l-T:l/2,r&&(I[0]+=r[0],I[1]+=r[1]);var E=d.bundlePosition=[];E[u.index]=n[u.xy],E[m.index]=n[m.xy];var M=d.barRectShape=kr({},n);M[m.wh]=h*Math.max(Math.abs(n[m.wh]),Math.abs(I[m.index]+T)),M[u.wh]=n[u.wh];var k=d.clipShape={};k[u.xy]=-n[u.xy],k[u.wh]=c.ecSize[u.wh],k[m.xy]=0,k[m.wh]=n[m.wh]}(n,u,a,r,0,m,s,d.valueLineWidth,d.boundingLength,d.repeatCutLength,i,d),d}function yR(e,t){return e.toGlobalCoord(e.dataToCoord(e.scale.parse(t)))}function vR(e){var t=e.symbolPatternSize,n=PS(e.symbolType,-t/2,-t/2,t,t);return n.attr({culling:!0}),"image"!==n.type&&n.setStyle({strokeNoScale:!0}),n}function xR(e,t,n,i){var a=e.__pictorialBundle,r=n.symbolSize,o=n.valueLineWidth,s=n.pathPosition,l=t.valueDim,p=n.repeatTimes||0,c=0,d=r[t.valueDim.index]+o+2*n.symbolMargin;for(kR(e,(function(e){e.__pictorialAnimationIndex=c,e.__pictorialRepeatTimes=p,c0:i<0)&&(a=p-1-e),t[l.index]=d*(a-p/2+.5)+s[l.index],{x:t[0],y:t[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation}}}function bR(e,t,n,i){var a=e.__pictorialBundle,r=e.__pictorialMainPath;r?PR(r,null,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation},n,i):(r=e.__pictorialMainPath=vR(n),a.add(r),PR(r,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:0,scaleY:0,rotation:n.rotation},{scaleX:n.symbolScale[0],scaleY:n.symbolScale[1]},n,i))}function wR(e,t,n){var i=kr({},t.barRectShape),a=e.__pictorialBarRect;a?PR(a,null,{shape:i},t,n):((a=e.__pictorialBarRect=new Em({z2:2,shape:i,silent:!0,style:{stroke:"transparent",fill:"transparent",lineWidth:0}})).disableMorphing=!0,e.add(a))}function SR(e,t,n,i){if(n.symbolClip){var a=e.__pictorialClipPath,r=kr({},n.clipShape),o=t.valueDim,s=n.animationModel,l=n.dataIndex;if(a)tf(a,{shape:r},s,l);else{r[o.wh]=0,a=new Em({shape:r}),e.__pictorialBundle.setClipPath(a),e.__pictorialClipPath=a;var p={};p[o.wh]=n.clipShape[o.wh],Lf[i?"updateProps":"initProps"](a,{shape:p},s,l)}}}function CR(e,t){var n=e.getItemModel(t);return n.getAnimationDelayParams=_R,n.isAnimationEnabled=TR,n}function _R(e){return{index:e.__pictorialAnimationIndex,count:e.__pictorialRepeatTimes}}function TR(){return this.parentModel.isAnimationEnabled()&&!!this.getShallow("animation")}function IR(e,t,n,i){var a=new Mc,r=new Mc;return a.add(r),a.__pictorialBundle=r,r.x=n.bundlePosition[0],r.y=n.bundlePosition[1],n.symbolRepeat?xR(a,t,n):bR(a,0,n),wR(a,n,i),SR(a,t,n,i),a.__pictorialShapeStr=MR(e,n),a.__pictorialSymbolMeta=n,a}function ER(e,t,n,i){var a=i.__pictorialBarRect;a&&a.removeTextContent();var r=[];kR(i,(function(e){r.push(e)})),i.__pictorialMainPath&&r.push(i.__pictorialMainPath),i.__pictorialClipPath&&(n=null),Rr(r,(function(e){rf(e,{scaleX:0,scaleY:0},n,t,(function(){i.parent&&i.parent.remove(i)}))})),e.setItemGraphicEl(t,null)}function MR(e,t){return[e.getItemVisual(t.dataIndex,"symbol")||"none",!!t.symbolRepeat,!!t.symbolClip].join(":")}function kR(e,t,n){Rr(e.__pictorialBundle.children(),(function(i){i!==e.__pictorialBarRect&&t.call(n,i)}))}function PR(e,t,n,i,a,r){t&&e.attr(t),i.symbolClip&&!a?n&&e.attr(n):n&&Lf[a?"updateProps":"initProps"](e,n,i.animationModel,i.dataIndex,r)}function DR(e,t,n){var i=n.dataIndex,a=n.itemModel,r=a.getModel("emphasis"),o=r.getModel("itemStyle").getItemStyle(),s=a.getModel(["blur","itemStyle"]).getItemStyle(),l=a.getModel(["select","itemStyle"]).getItemStyle(),p=a.getShallow("cursor"),c=r.get("focus"),d=r.get("blurScope"),u=r.get("scale");kR(e,(function(e){if(e instanceof bm){var t=e.style;e.useStyle(kr({image:t.image,x:t.x,y:t.y,width:t.width,height:t.height},n.style))}else e.useStyle(n.style);var i=e.ensureState("emphasis");i.style=o,u&&(i.scaleX=1.1*e.scaleX,i.scaleY=1.1*e.scaleY),e.ensureState("blur").style=s,e.ensureState("select").style=l,p&&(e.cursor=p),e.z2=n.z2}));var m=t.valueDim.posDesc[+(n.boundingLength>0)],h=e.__pictorialBarRect;h.ignoreClip=!0,zf(h,Uf(a),{labelFetcher:t.seriesModel,labelDataIndex:i,defaultText:fE(t.seriesModel.getData(),i),inheritColor:n.style.fill,defaultOpacity:n.style.opacity,defaultOutsidePosition:m}),Oh(e,c,d,r.get("disabled"))}function OR(e){var t=Math.round(e);return Math.abs(e-t)<1e-4?t:Math.ceil(e)}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.hasSymbolVisual=!0,n.defaultSymbol="roundRect",n}ze(t,e),t.prototype.getInitialData=function(t){return t.stack=null,e.prototype.getInitialData.apply(this,arguments)},t.type="series.pictorialBar",t.dependencies=["grid"],t.defaultOption=gy(eM.defaultOption,{symbol:"circle",symbolSize:null,symbolRotate:null,symbolPosition:null,symbolOffset:null,symbolMargin:null,symbolRepeat:!1,symbolRepeatDirection:"end",symbolClip:!1,symbolBoundingData:null,symbolPatternSize:400,barGap:"-100%",clip:!1,progressive:0,emphasis:{scale:!1},select:{itemStyle:{borderColor:"#212121"}}})}(eM);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._layers=[],n}ze(t,e),t.prototype.render=function(e,t,n){var i=e.getData(),a=this,r=this.group,o=e.getLayerSeries(),s=i.getLayout("layoutInfo"),l=s.rect,p=s.boundaryGap;function c(e){return e.name}r.x=0,r.y=l.y+p[0];var d=new g_(this._layersSeries||[],o,c,c),u=[];function m(t,n,s){var l=a._layers;if("remove"!==t){for(var p,c,d=[],m=[],h=o[n].indices,g=0;gT&&!Kc(E-T)&&E0?(a.virtualPiece?a.virtualPiece.updateData(!1,i,e,t,n):(a.virtualPiece=new AR(i,e,t,n),l.add(a.virtualPiece)),r.piece.off("click"),a.virtualPiece.on("click",(function(e){a._rootToNode(r.parentNode)}))):a.virtualPiece&&(l.remove(a.virtualPiece),a.virtualPiece=null)}(o,s),this._initEvents(),this._oldChildren=c},t.prototype._initEvents=function(){var e=this;this.group.off("click"),this.group.on("click",(function(t){var n=!1;e.seriesModel.getViewRoot().eachNode((function(i){if(!n&&i.piece&&i.piece===t.target){var a=i.getModel().get("nodeClick");if("rootToNode"===a)e._rootToNode(i);else if("link"===a){var r=i.getModel(),o=r.get("link");if(o)pv(o,r.get("target",!0)||"_blank")}n=!0}}))}))},t.prototype._rootToNode=function(e){e!==this.seriesModel.getViewRoot()&&this.api.dispatchAction({type:FR,from:this.uid,seriesId:this.seriesModel.id,targetNode:e})},t.prototype.containPoint=function(e,t){var n=t.getData().getItemLayout(0);if(n){var i=e[0]-n.cx,a=e[1]-n.cy,r=Math.sqrt(i*i+a*a);return r<=n.r&&r>=n.r0}},t.type="sunburst"})(vw),function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.ignoreStyleOnData=!0,n}ze(t,e),t.prototype.getInitialData=function(e,t){var n={name:e.name,children:e.data};RR(n);var i=this._levelModels=Br(e.levels||[],(function(e){return new uy(e,this,t)}),this),a=ND.createTree(n,this,(function(e){e.wrapMethod("getItemModel",(function(e,t){var n=a.getNodeByDataIndex(t),r=i[n.depth];return r&&(e.parentModel=r),e}))}));return a.data},t.prototype.optionUpdated=function(){this.resetViewRoot()},t.prototype.getDataParams=function(t){var n=e.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return n.treePathInfo=GD(i,this),n},t.prototype.getLevelModel=function(e){return this._levelModels&&this._levelModels[e.depth]},t.prototype.getViewRoot=function(){return this._viewRoot},t.prototype.resetViewRoot=function(e){e?this._viewRoot=e:e=this._viewRoot;var t=this.getRawData().tree.root;e&&(e===t||t.contains(e))||(this._viewRoot=t)},t.prototype.enableAriaDecal=function(){zD(this)},t.type="series.sunburst",t.defaultOption={z:2,center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,stillShowZeroSum:!0,nodeClick:"rootToNode",renderLabelForZeroData:!1,label:{rotate:"radial",show:!0,opacity:1,align:"center",position:"inside",distance:5,silent:!0},itemStyle:{borderWidth:1,borderColor:"white",borderType:"solid",shadowBlur:0,shadowColor:"rgba(0, 0, 0, 0.2)",shadowOffsetX:0,shadowOffsetY:0,opacity:1},emphasis:{focus:"descendant"},blur:{itemStyle:{opacity:.2},label:{opacity:.1}},animationType:"expansion",animationDuration:1e3,animationDurationUpdate:500,data:[],sort:"desc"}}(ow);function RR(e){var t=0;Rr(e.children,(function(e){RR(e);var n=e.value;Ur(n)&&(n=n[0]),t+=n}));var n=e.value;Ur(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=t),n<0&&(n=0),Ur(e.value)?e.value[0]=n:e.value=n}Math.PI;var BR={color:"fill",borderColor:"stroke"},NR={symbol:1,symbolSize:1,symbolKeepAspect:1,legendIcon:1,visualMeta:1,liftZ:1,decal:1},LR=Cd(),VR=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.optionUpdated=function(){this.currentZLevel=this.get("zlevel",!0),this.currentZ=this.get("z",!0)},t.prototype.getInitialData=function(e,t){return K_(null,this)},t.prototype.getDataParams=function(t,n,i){var a=e.prototype.getDataParams.call(this,t,n);return i&&(a.info=LR(i).info),a},t.type="series.custom",t.dependencies=["grid","polar","geo","singleAxis","calendar"],t.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,clip:!1},t}(ow);function qR(e,t){return t=t||[0,0],Br(["x","y"],(function(n,i){var a=this.getAxis(n),r=t[i],o=e[i]/2;return"category"===a.type?a.getBandWidth():Math.abs(a.dataToCoord(r-o)-a.dataToCoord(r+o))}),this)}function GR(e,t){return t=t||[0,0],Br([0,1],(function(n){var i=t[n],a=e[n]/2,r=[],o=[];return r[n]=i-a,o[n]=i+a,r[1-n]=o[1-n]=t[1-n],Math.abs(this.dataToPoint(r)[n]-this.dataToPoint(o)[n])}),this)}function zR(e,t){var n=this.getAxis(),i=t instanceof Array?t[0]:t,a=(e instanceof Array?e[0]:e)/2;return"category"===n.type?n.getBandWidth():Math.abs(n.dataToCoord(i-a)-n.dataToCoord(i+a))}function UR(e,t){return t=t||[0,0],Br(["Radius","Angle"],(function(n,i){var a=this["get"+n+"Axis"](),r=t[i],o=e[i]/2,s="category"===a.type?a.getBandWidth():Math.abs(a.dataToCoord(r-o)-a.dataToCoord(r+o));return"Angle"===n&&(s=s*Math.PI/180),s}),this)}function jR(e,t,n,i){return e&&(e.legacy||!1!==e.legacy&&!n&&!i&&"tspan"!==t&&("text"===t||bo(e,"text")))}function HR(e,t,n){var i,a,r,o=e;if("text"===t)r=o;else{r={},bo(o,"text")&&(r.text=o.text),bo(o,"rich")&&(r.rich=o.rich),bo(o,"textFill")&&(r.fill=o.textFill),bo(o,"textStroke")&&(r.stroke=o.textStroke),bo(o,"fontFamily")&&(r.fontFamily=o.fontFamily),bo(o,"fontSize")&&(r.fontSize=o.fontSize),bo(o,"fontStyle")&&(r.fontStyle=o.fontStyle),bo(o,"fontWeight")&&(r.fontWeight=o.fontWeight),a={type:"text",style:r,silent:!0},i={};var s=bo(o,"textPosition");n?i.position=s?o.textPosition:"inside":s&&(i.position=o.textPosition),bo(o,"textPosition")&&(i.position=o.textPosition),bo(o,"textOffset")&&(i.offset=o.textOffset),bo(o,"textRotation")&&(i.rotation=o.textRotation),bo(o,"textDistance")&&(i.distance=o.textDistance)}return WR(r,e),Rr(r.rich,(function(e){WR(e,e)})),{textConfig:i,textContent:a}}function WR(e,t){t&&(t.font=t.textFont||t.font,bo(t,"textStrokeWidth")&&(e.lineWidth=t.textStrokeWidth),bo(t,"textAlign")&&(e.align=t.textAlign),bo(t,"textVerticalAlign")&&(e.verticalAlign=t.textVerticalAlign),bo(t,"textLineHeight")&&(e.lineHeight=t.textLineHeight),bo(t,"textWidth")&&(e.width=t.textWidth),bo(t,"textHeight")&&(e.height=t.textHeight),bo(t,"textBackgroundColor")&&(e.backgroundColor=t.textBackgroundColor),bo(t,"textPadding")&&(e.padding=t.textPadding),bo(t,"textBorderColor")&&(e.borderColor=t.textBorderColor),bo(t,"textBorderWidth")&&(e.borderWidth=t.textBorderWidth),bo(t,"textBorderRadius")&&(e.borderRadius=t.textBorderRadius),bo(t,"textBoxShadowColor")&&(e.shadowColor=t.textBoxShadowColor),bo(t,"textBoxShadowBlur")&&(e.shadowBlur=t.textBoxShadowBlur),bo(t,"textBoxShadowOffsetX")&&(e.shadowOffsetX=t.textBoxShadowOffsetX),bo(t,"textBoxShadowOffsetY")&&(e.shadowOffsetY=t.textBoxShadowOffsetY))}function $R(e,t,n){var i=e;i.textPosition=i.textPosition||n.position||"inside",null!=n.offset&&(i.textOffset=n.offset),null!=n.rotation&&(i.textRotation=n.rotation),null!=n.distance&&(i.textDistance=n.distance);var a=i.textPosition.indexOf("inside")>=0,r=e.fill||"#000";KR(i,t);var o=null==i.textFill;return a?o&&(i.textFill=n.insideFill||"#fff",!i.textStroke&&n.insideStroke&&(i.textStroke=n.insideStroke),!i.textStroke&&(i.textStroke=r),null==i.textStrokeWidth&&(i.textStrokeWidth=2)):(o&&(i.textFill=e.fill||n.outsideFill||"#000"),!i.textStroke&&n.outsideStroke&&(i.textStroke=n.outsideStroke)),i.text=t.text,i.rich=t.rich,Rr(t.rich,(function(e){KR(e,e)})),i}function KR(e,t){t&&(bo(t,"fill")&&(e.textFill=t.fill),bo(t,"stroke")&&(e.textStroke=t.fill),bo(t,"lineWidth")&&(e.textStrokeWidth=t.lineWidth),bo(t,"font")&&(e.font=t.font),bo(t,"fontStyle")&&(e.fontStyle=t.fontStyle),bo(t,"fontWeight")&&(e.fontWeight=t.fontWeight),bo(t,"fontSize")&&(e.fontSize=t.fontSize),bo(t,"fontFamily")&&(e.fontFamily=t.fontFamily),bo(t,"align")&&(e.textAlign=t.align),bo(t,"verticalAlign")&&(e.textVerticalAlign=t.verticalAlign),bo(t,"lineHeight")&&(e.textLineHeight=t.lineHeight),bo(t,"width")&&(e.textWidth=t.width),bo(t,"height")&&(e.textHeight=t.height),bo(t,"backgroundColor")&&(e.textBackgroundColor=t.backgroundColor),bo(t,"padding")&&(e.textPadding=t.padding),bo(t,"borderColor")&&(e.textBorderColor=t.borderColor),bo(t,"borderWidth")&&(e.textBorderWidth=t.borderWidth),bo(t,"borderRadius")&&(e.textBorderRadius=t.borderRadius),bo(t,"shadowColor")&&(e.textBoxShadowColor=t.shadowColor),bo(t,"shadowBlur")&&(e.textBoxShadowBlur=t.shadowBlur),bo(t,"shadowOffsetX")&&(e.textBoxShadowOffsetX=t.shadowOffsetX),bo(t,"shadowOffsetY")&&(e.textBoxShadowOffsetY=t.shadowOffsetY),bo(t,"textShadowColor")&&(e.textShadowColor=t.textShadowColor),bo(t,"textShadowBlur")&&(e.textShadowBlur=t.textShadowBlur),bo(t,"textShadowOffsetX")&&(e.textShadowOffsetX=t.textShadowOffsetX),bo(t,"textShadowOffsetY")&&(e.textShadowOffsetY=t.textShadowOffsetY))}var YR={position:["x","y"],scale:["scaleX","scaleY"],origin:["originX","originY"]},XR=qr(YR),ZR=(Nr(sc,(function(e,t){return e[t]=1,e}),{}),sc.join(", "),["","style","shape","extra"]),QR=Cd();function JR(e,t,n,i,a){var r=e+"Animation",o=Jg(e,i,a)||{},s=QR(t).userDuring;return o.duration>0&&(o.during=s?Gr(oB,{el:t,userDuring:s}):null,o.setToFinal=!0,o.scope=e),kr(o,n[r]),o}function eB(e,t,n,i){var a=(i=i||{}).dataIndex,r=i.isInit,o=i.clearStyle,s=n.isAnimationEnabled(),l=QR(e),p=t.style;l.userDuring=t.during;var c={},d={};if(function(e,t,n){for(var i=0;i=0)){var d=e.getAnimationStyleProps(),u=d?d.style:null;if(u){!a&&(a=i.style={});var m=qr(n);for(p=0;p0&&e.animateFrom(u,m)}else!function(e,t,n,i,a){if(a){var r=JR("update",e,t,i,n);r.duration>0&&e.animateFrom(a,r)}}(e,t,a||0,n,c);tB(e,t),p?e.dirty():e.markRedraw()}function tB(e,t){for(var n=QR(e).leaveToProps,i=0;i=0){!r&&(r=i[e]={});var u=qr(o);for(c=0;ci[1]&&i.reverse(),{coordSys:{type:"polar",cx:e.cx,cy:e.cy,r:i[1],r0:i[0]},api:{coord:function(i){var a=t.dataToRadius(i[0]),r=n.dataToAngle(i[1]),o=e.coordToPoint([a,r]);return o.push(a,r*Math.PI/180),o},size:Gr(UR,e)}}},calendar:function(e){var t=e.getRect(),n=e.getRangeInfo();return{coordSys:{type:"calendar",x:t.x,y:t.y,width:t.width,height:t.height,cellWidth:e.getCellWidth(),cellHeight:e.getCellHeight(),rangeInfo:{start:n.start,end:n.end,weeks:n.weeks,dayCount:n.allDay}},api:{coord:function(t,n){return e.dataToPoint(t,n)}}}}};function _B(e){return e instanceof gm}function TB(e){return e instanceof mu}var IB=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.render=function(e,t,n,i){this._progressiveEls=null;var a=this._data,r=e.getData(),o=this.group,s=DB(e,r,t,n);a||o.removeAll(),r.diff(a).add((function(t){AB(n,null,t,s(t,i),e,o,r)})).remove((function(t){var n=a.getItemGraphicEl(t);n&&nB(n,LR(n).option,e)})).update((function(t,l){var p=a.getItemGraphicEl(l);AB(n,p,t,s(t,i),e,o,r)})).execute();var l=e.get("clip",!0)?BE(e.coordinateSystem,!1,e):null;l?o.setClipPath(l):o.removeClipPath(),this._data=r},t.prototype.incrementalPrepareRender=function(e,t,n){this.group.removeAll(),this._data=null},t.prototype.incrementalRender=function(e,t,n,i,a){var r=t.getData(),o=DB(t,r,n,i),s=this._progressiveEls=[];function l(e){e.isGroup||(e.incremental=!0,e.ensureState("emphasis").hoverLayer=!0)}for(var p=e.start;p=0?t.getStore().get(a,n):void 0}var r=t.get(i.name,n),o=i&&i.ordinalMeta;return o?o.categories[r]:r},styleEmphasis:function(n,i){0;null==i&&(i=s);var a=v(i,hB).getItemStyle(),r=x(i,hB),o=jf(r,null,null,!0,!0);o.text=r.getShallow("show")?ao(e.getFormattedLabel(i,hB),e.getFormattedLabel(i,gB),fE(t,i)):null;var l=Hf(r,null,!0);return w(n,a),a=$R(a,o,l),n&&b(a,n),a.legacy=!0,a},visual:function(e,n){if(null==n&&(n=s),bo(BR,e)){var i=t.getItemVisual(n,"style");return i?i[BR[e]]:null}if(bo(NR,e))return t.getItemVisual(n,e)},barLayout:function(e){if("cartesian2d"===r.type){return function(e){var t=[],n=e.axis,i="axis0";if("category"===n.type){for(var a=n.getBandWidth(),r=0;r=d;h--){var g=t.childAt(h);VB(t,g,a)}}(e,d,n,i,a),o>=0?r.replaceAt(d,o):r.add(d),d}function RB(e,t,n){var i,a=LR(e),r=t.type,o=t.shape,s=t.style;return n.isUniversalTransitionEnabled()||null!=r&&r!==a.customGraphicType||"path"===r&&((i=o)&&(bo(i,"pathData")||bo(i,"d")))&&UB(o)!==a.customPathData||"image"===r&&bo(s,"image")&&s.image!==a.customImagePath}function BB(e,t,n){var i=t?NB(e,t):e,a=t?LB(e,i,hB):e.style,r=e.type,o=i?i.textConfig:null,s=e.textContent,l=s?t?NB(s,t):s:null;if(a&&(n.isLegacy||jR(a,r,!!o,!!l))){n.isLegacy=!0;var p=HR(a,r,!t);!o&&p.textConfig&&(o=p.textConfig),!l&&p.textContent&&(l=p.textContent)}if(!t&&l){var c=l;!c.type&&(c.type="text")}var d=t?n[t]:n.normal;d.cfg=o,d.conOpt=l}function NB(e,t){return t?e?e[t]:null:e}function LB(e,t,n){var i=t&&t.style;return null==i&&n===hB&&e&&(i=e.styleEmphasis),i}function VB(e,t,n){t&&nB(t,LR(e).option,n)}function qB(e,t){var n=e&&e.name;return null!=n?n:"e\0\0"+t}function GB(e,t){var n=this.context,i=null!=e?n.newChildren[e]:null,a=null!=t?n.oldChildren[t]:null;FB(n.api,a,n.dataIndex,i,n.seriesModel,n.group)}function zB(e){var t=this.context,n=t.oldChildren[e];n&&nB(n,LR(n).option,t.seriesModel)}function UB(e){return e&&(e.pathData||e.d)}function jB(e){e.registerChartView(IB),e.registerSeriesModel(VR)}var HB=Cd(),WB=Ir,$B=Gr,KB=function(){function e(){this._dragging=!1,this.animationThreshold=15}return e.prototype.render=function(e,t,n,i){var a=t.get("value"),r=t.get("status");if(this._axisModel=e,this._axisPointerModel=t,this._api=n,i||this._lastValue!==a||this._lastStatus!==r){this._lastValue=a,this._lastStatus=r;var o=this._group,s=this._handle;if(!r||"hide"===r)return o&&o.hide(),void(s&&s.hide());o&&o.show(),s&&s.show();var l={};this.makeElOption(l,a,e,t,n);var p=l.graphicKey;p!==this._lastGraphicKey&&this.clear(n),this._lastGraphicKey=p;var c=this._moveAnimation=this.determineAnimation(e,t);if(o){var d=zr(YB,t,c);this.updatePointerEl(o,l,d),this.updateLabelEl(o,l,d,t)}else o=this._group=new Mc,this.createPointerEl(o,l,e,t),this.createLabelEl(o,l,e,t),n.getZr().add(o);JB(o,t,!0),this._renderHandle(a)}},e.prototype.remove=function(e){this.clear(e)},e.prototype.dispose=function(e){this.clear(e)},e.prototype.determineAnimation=function(e,t){var n=t.get("animation"),i=e.axis,a="category"===i.type,r=t.get("snap");if(!r&&!a)return!1;if("auto"===n||null==n){var o=this.animationThreshold;if(a&&i.getBandWidth()>o)return!0;if(r){var s=Tk(e).seriesDataCount,l=i.getExtent();return Math.abs(l[0]-l[1])/s>o}return!1}return!0===n},e.prototype.makeElOption=function(e,t,n,i,a){},e.prototype.createPointerEl=function(e,t,n,i){var a=t.pointer;if(a){var r=HB(e).pointerEl=new Lf[a.type](WB(t.pointer));e.add(r)}},e.prototype.createLabelEl=function(e,t,n,i){if(t.label){var a=HB(e).labelEl=new Pm(WB(t.label));e.add(a),ZB(a,i)}},e.prototype.updatePointerEl=function(e,t,n){var i=HB(e).pointerEl;i&&t.pointer&&(i.setStyle(t.pointer.style),n(i,{shape:t.pointer.shape}))},e.prototype.updateLabelEl=function(e,t,n,i){var a=HB(e).labelEl;a&&(a.setStyle(t.label.style),n(a,{x:t.label.x,y:t.label.y}),ZB(a,i))},e.prototype._renderHandle=function(e){if(!this._dragging&&this.updateHandleTransform){var t,n=this._axisPointerModel,i=this._api.getZr(),a=this._handle,r=n.getModel("handle"),o=n.get("status");if(!r.get("show")||!o||"hide"===o)return a&&i.remove(a),void(this._handle=null);this._handle||(t=!0,a=this._handle=Df(r.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(e){ls(e.event)},onmousedown:$B(this._onHandleDragMove,this,0,0),drift:$B(this._onHandleDragMove,this),ondragend:$B(this._onHandleDragEnd,this)}),i.add(a)),JB(a,n,!1),a.setStyle(r.getItemStyle(null,["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]));var s=r.get("size");Ur(s)||(s=[s,s]),a.scaleX=s[0]/2,a.scaleY=s[1]/2,Mw(this,"_doDispatchAxisPointer",r.get("throttle")||0,"fixRate"),this._moveHandleToValue(e,t)}},e.prototype._moveHandleToValue=function(e,t){YB(this._axisPointerModel,!t&&this._moveAnimation,this._handle,QB(this.getHandleTransform(e,this._axisModel,this._axisPointerModel)))},e.prototype._onHandleDragMove=function(e,t){var n=this._handle;if(n){this._dragging=!0;var i=this.updateHandleTransform(QB(n),[e,t],this._axisModel,this._axisPointerModel);this._payloadInfo=i,n.stopAnimation(),n.attr(QB(i)),HB(n).lastProp=null,this._doDispatchAxisPointer()}},e.prototype._doDispatchAxisPointer=function(){if(this._handle){var e=this._payloadInfo,t=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:e.cursorPoint[0],y:e.cursorPoint[1],tooltipOption:e.tooltipOption,axesInfo:[{axisDim:t.axis.dim,axisIndex:t.componentIndex}]})}},e.prototype._onHandleDragEnd=function(){if(this._dragging=!1,this._handle){var e=this._axisPointerModel.get("value");this._moveHandleToValue(e),this._api.dispatchAction({type:"hideTip"})}},e.prototype.clear=function(e){this._lastValue=null,this._lastStatus=null;var t=e.getZr(),n=this._group,i=this._handle;t&&n&&(this._lastGraphicKey=null,n&&t.remove(n),i&&t.remove(i),this._group=null,this._handle=null,this._payloadInfo=null),kw(this,"_doDispatchAxisPointer")},e.prototype.doClear=function(){},e.prototype.buildLabel=function(e,t,n){return{x:e[n=n||0],y:e[1-n],width:t[n],height:t[1-n]}},e}();function YB(e,t,n,i){XB(HB(n).lastProp,i)||(HB(n).lastProp=i,t?tf(n,i,e):(n.stopAnimation(),n.attr(i)))}function XB(e,t){if(Kr(e)&&Kr(t)){var n=!0;return Rr(t,(function(t,i){n=n&&XB(e[i],t)})),!!n}return e===t}function ZB(e,t){e[t.get(["label","show"])?"show":"hide"]()}function QB(e){return{x:e.x||0,y:e.y||0,rotation:e.rotation||0}}function JB(e,t,n){var i=t.get("z"),a=t.get("zlevel");e&&e.traverse((function(e){"group"!==e.type&&(null!=i&&(e.z=i),null!=a&&(e.zlevel=a),e.silent=n)}))}function eN(e){var t,n=e.get("type"),i=e.getModel(n+"Style");return"line"===n?(t=i.getLineStyle()).fill=null:"shadow"===n&&((t=i.getAreaStyle()).stroke=null),t}function tN(e,t,n,i,a){var r=nN(n.get("value"),t.axis,t.ecModel,n.get("seriesDataIndices"),{precision:n.get(["label","precision"]),formatter:n.get(["label","formatter"])}),o=n.getModel("label"),s=nv(o.get("padding")||0),l=o.getFont(),p=uc(r,l),c=a.position,d=p.width+s[1]+s[3],u=p.height+s[0]+s[2],m=a.align;"right"===m&&(c[0]-=d),"center"===m&&(c[0]-=d/2);var h=a.verticalAlign;"bottom"===h&&(c[1]-=u),"middle"===h&&(c[1]-=u/2),function(e,t,n,i){var a=i.getWidth(),r=i.getHeight();e[0]=Math.min(e[0]+t,a)-t,e[1]=Math.min(e[1]+n,r)-n,e[0]=Math.max(e[0],0),e[1]=Math.max(e[1],0)}(c,d,u,i);var g=o.get("backgroundColor");g&&"auto"!==g||(g=t.get(["axisLine","lineStyle","color"])),e.label={x:c[0],y:c[1],style:jf(o,{text:r,font:l,fill:o.getTextColor(),padding:s,backgroundColor:g}),z2:10}}function nN(e,t,n,i,a){e=t.scale.parse(e);var r=t.scale.getLabel({value:e},{precision:a.precision}),o=a.formatter;if(o){var s={value:XT(t,{value:e}),axisDimension:t.dim,axisIndex:t.index,seriesData:[]};Rr(i,(function(e){var t=n.getSeriesByIndex(e.seriesIndex),i=e.dataIndexInside,a=t&&t.getDataParams(i);a&&s.seriesData.push(a)})),Hr(o)?r=o.replace("{value}",r):jr(o)&&(r=o(s))}return r}function iN(e,t,n){var i=[1,0,0,1,0,0];return ys(i,i,n.rotation),fs(i,i,n.position),Tf([e.dataToCoord(t),(n.labelOffset||0)+(n.labelDirection||1)*(n.labelMargin||0)],i)}function aN(e,t,n,i,a,r){var o=yk.innerTextLayout(n.rotation,0,n.labelDirection);n.labelMargin=a.get(["label","margin"]),tN(t,i,a,r,{position:iN(i.axis,e,n),align:o.textAlign,verticalAlign:o.textVerticalAlign})}function rN(e,t,n){return{x1:e[n=n||0],y1:e[1-n],x2:t[n],y2:t[1-n]}}function oN(e,t,n){return{x:e[n=n||0],y:e[1-n],width:t[n],height:t[1-n]}}function sN(e,t,n,i,a,r){return{cx:e,cy:t,r0:n,r:i,startAngle:a,endAngle:r,clockwise:!0}}var lN=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.makeElOption=function(e,t,n,i,a){var r=n.axis,o=r.grid,s=i.get("type"),l=pN(o,r).getOtherAxis(r).getGlobalExtent(),p=r.toGlobalCoord(r.dataToCoord(t,!0));if(s&&"none"!==s){var c=eN(i),d=cN[s](r,p,l);d.style=c,e.graphicKey=d.type,e.pointer=d}aN(t,e,sk(o.model,n),n,i,a)},t.prototype.getHandleTransform=function(e,t,n){var i=sk(t.axis.grid.model,t,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var a=iN(t.axis,e,i);return{x:a[0],y:a[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},t.prototype.updateHandleTransform=function(e,t,n,i){var a=n.axis,r=a.grid,o=a.getGlobalExtent(!0),s=pN(r,a).getOtherAxis(a).getGlobalExtent(),l="x"===a.dim?0:1,p=[e.x,e.y];p[l]+=t[l],p[l]=Math.min(o[1],p[l]),p[l]=Math.max(o[0],p[l]);var c=(s[1]+s[0])/2,d=[c,c];d[l]=p[l];return{x:p[0],y:p[1],rotation:e.rotation,cursorPoint:d,tooltipOption:[{verticalAlign:"middle"},{align:"center"}][l]}},t}(KB);function pN(e,t){var n={};return n[t.dim+"AxisIndex"]=t.index,e.getCartesian(n)}var cN={line:function(e,t,n){return{type:"Line",subPixelOptimize:!0,shape:rN([t,n[0]],[t,n[1]],dN(e))}},shadow:function(e,t,n){var i=Math.max(1,e.getBandWidth()),a=n[1]-n[0];return{type:"Rect",shape:oN([t-i/2,n[0]],[i,a],dN(e))}}};function dN(e){return"x"===e.dim?0:1}var uN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="axisPointer",t.defaultOption={show:"auto",z:50,type:"line",snap:!1,triggerTooltip:!0,triggerEmphasis:!0,value:null,status:null,link:[],animation:null,animationDurationUpdate:200,lineStyle:{color:"#B9BEC9",width:1,type:"dashed"},shadowStyle:{color:"rgba(210,219,238,0.2)"},label:{show:!0,formatter:null,precision:"auto",margin:3,color:"#fff",padding:[5,7,5,7],backgroundColor:"auto",borderColor:null,borderWidth:0,borderRadius:3},handle:{show:!1,icon:"M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z",size:45,margin:50,color:"#333",shadowBlur:3,shadowColor:"#aaa",shadowOffsetX:0,shadowOffsetY:2,throttle:40}},t}(Sv),mN=Cd(),hN=Rr;function gN(e,t,n){if(!rr.node){var i=t.getZr();mN(i).records||(mN(i).records={}),function(e,t){if(mN(e).initialized)return;function n(n,i){e.on(n,(function(n){var a=function(e){var t={showTip:[],hideTip:[]},n=function(i){var a=t[i.type];a?a.push(i):(i.dispatchAction=n,e.dispatchAction(i))};return{dispatchAction:n,pendings:t}}(t);hN(mN(e).records,(function(e){e&&i(e,n,a.dispatchAction)})),function(e,t){var n,i=e.showTip.length,a=e.hideTip.length;i?n=e.showTip[i-1]:a&&(n=e.hideTip[a-1]);n&&(n.dispatchAction=null,t.dispatchAction(n))}(a.pendings,t)}))}mN(e).initialized=!0,n("click",zr(yN,"click")),n("mousemove",zr(yN,"mousemove")),n("globalout",fN)}(i,t),(mN(i).records[e]||(mN(i).records[e]={})).handler=n}}function fN(e,t,n){e.handler("leave",null,n)}function yN(e,t,n,i){t.handler(e,n,i)}function vN(e,t){if(!rr.node){var n=t.getZr();(mN(n).records||{})[e]&&(mN(n).records[e]=null)}}var xN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.render=function(e,t,n){var i=t.getComponent("tooltip"),a=e.get("triggerOn")||i&&i.get("triggerOn")||"mousemove|click";gN("axisPointer",n,(function(e,t,n){"none"!==a&&("leave"===e||a.indexOf(e)>=0)&&n({type:"updateAxisPointer",currTrigger:e,x:t&&t.offsetX,y:t&&t.offsetY})}))},t.prototype.remove=function(e,t){vN("axisPointer",t)},t.prototype.dispose=function(e,t){vN("axisPointer",t)},t.type="axisPointer",t}(hw);function bN(e,t){var n,i=[],a=e.seriesIndex;if(null==a||!(n=t.getSeriesByIndex(a)))return{point:[]};var r=n.getData(),o=Sd(r,e);if(null==o||o<0||Ur(o))return{point:[]};var s=r.getItemGraphicEl(o),l=n.coordinateSystem;if(n.getTooltipPosition)i=n.getTooltipPosition(o)||[];else if(l&&l.dataToPoint)if(e.isStacked){var p=l.getBaseAxis(),c=l.getOtherAxis(p).dim,d=p.dim,u="x"===c||"radius"===c?1:0,m=r.mapDimension(d),h=[];h[u]=r.get(m,o),h[1-u]=r.get(r.getCalculationInfo("stackResultDimension"),o),i=l.dataToPoint(h)||[]}else i=l.dataToPoint(r.getValues(Br(l.dimensions,(function(e){return r.mapDimension(e)})),o))||[];else if(s){var g=s.getBoundingRect().clone();g.applyTransform(s.transform),i=[g.x+g.width/2,g.y+g.height/2]}return{point:i,el:s}}var wN=Cd();function SN(e,t,n){var i=e.currTrigger,a=[e.x,e.y],r=e,o=e.dispatchAction||Gr(n.dispatchAction,n),s=t.getComponent("axisPointer").coordSysAxesInfo;if(s){EN(a)&&(a=bN({seriesIndex:r.seriesIndex,dataIndex:r.dataIndex},t).point);var l=EN(a),p=r.axesInfo,c=s.axesInfo,d="leave"===i||EN(a),u={},m={},h={list:[],map:{}},g={showPointer:zr(_N,m),showTooltip:zr(TN,h)};Rr(s.coordSysMap,(function(e,t){var n=l||e.containPoint(a);Rr(s.coordSysAxesInfo[t],(function(e,t){var i=e.axis,r=function(e,t){for(var n=0;n<(e||[]).length;n++){var i=e[n];if(t.axis.dim===i.axisDim&&t.axis.model.componentIndex===i.axisIndex)return i}}(p,e);if(!d&&n&&(!p||r)){var o=r&&r.value;null!=o||l||(o=i.pointToData(a)),null!=o&&CN(e,o,g,!1,u)}}))}));var f={};return Rr(c,(function(e,t){var n=e.linkGroup;n&&!m[t]&&Rr(n.axesInfo,(function(t,i){var a=m[i];if(t!==e&&a){var r=a.value;n.mapper&&(r=e.axis.scale.parse(n.mapper(r,IN(t),IN(e)))),f[e.key]=r}}))})),Rr(f,(function(e,t){CN(c[t],e,g,!0,u)})),function(e,t,n){var i=n.axesInfo=[];Rr(t,(function(t,n){var a=t.axisPointerModel.option,r=e[n];r?(!t.useHandle&&(a.status="show"),a.value=r.value,a.seriesDataIndices=(r.payloadBatch||[]).slice()):!t.useHandle&&(a.status="hide"),"show"===a.status&&i.push({axisDim:t.axis.dim,axisIndex:t.axis.model.componentIndex,value:a.value})}))}(m,c,u),function(e,t,n,i){if(EN(t)||!e.list.length)return void i({type:"hideTip"});var a=((e.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};i({type:"showTip",escapeConnect:!0,x:t[0],y:t[1],tooltipOption:n.tooltipOption,position:n.position,dataIndexInside:a.dataIndexInside,dataIndex:a.dataIndex,seriesIndex:a.seriesIndex,dataByCoordSys:e.list})}(h,a,e,o),function(e,t,n){var i=n.getZr(),a="axisPointerLastHighlights",r=wN(i)[a]||{},o=wN(i)[a]={};Rr(e,(function(e,t){var n=e.axisPointerModel.option;"show"===n.status&&e.triggerEmphasis&&Rr(n.seriesDataIndices,(function(e){var t=e.seriesIndex+" | "+e.dataIndex;o[t]=e}))}));var s=[],l=[];Rr(r,(function(e,t){!o[t]&&l.push(e)})),Rr(o,(function(e,t){!r[t]&&s.push(e)})),l.length&&n.dispatchAction({type:"downplay",escapeConnect:!0,notBlur:!0,batch:l}),s.length&&n.dispatchAction({type:"highlight",escapeConnect:!0,notBlur:!0,batch:s})}(c,0,n),u}}function CN(e,t,n,i,a){var r=e.axis;if(!r.scale.isBlank()&&r.containData(t))if(e.involveSeries){var o=function(e,t){var n=t.axis,i=n.dim,a=e,r=[],o=Number.MAX_VALUE,s=-1;return Rr(t.seriesModels,(function(t,l){var p,c,d=t.getData().mapDimensionsAll(i);if(t.getAxisTooltipData){var u=t.getAxisTooltipData(d,e,n);c=u.dataIndices,p=u.nestestValue}else{if(!(c=t.getData().indicesOfNearest(d[0],e,"category"===n.type?.5:null)).length)return;p=t.getData().get(d[0],c[0])}if(null!=p&&isFinite(p)){var m=e-p,h=Math.abs(m);h<=o&&((h=0&&s<0)&&(o=h,s=m,a=p,r.length=0),Rr(c,(function(e){r.push({seriesIndex:t.seriesIndex,dataIndexInside:e,dataIndex:t.getData().getRawIndex(e)})})))}})),{payloadBatch:r,snapToValue:a}}(t,e),s=o.payloadBatch,l=o.snapToValue;s[0]&&null==a.seriesIndex&&kr(a,s[0]),!i&&e.snap&&r.containData(l)&&null!=l&&(t=l),n.showPointer(e,t,s),n.showTooltip(e,o,l)}else n.showPointer(e,t)}function _N(e,t,n,i){e[t.key]={value:n,payloadBatch:i}}function TN(e,t,n,i){var a=n.payloadBatch,r=t.axis,o=r.model,s=t.axisPointerModel;if(t.triggerTooltip&&a.length){var l=t.coordSys.model,p=Ek(l),c=e.map[p];c||(c=e.map[p]={coordSysId:l.id,coordSysIndex:l.componentIndex,coordSysType:l.type,coordSysMainType:l.mainType,dataByAxis:[]},e.list.push(c)),c.dataByAxis.push({axisDim:r.dim,axisIndex:o.componentIndex,axisType:o.type,axisId:o.id,value:i,valueLabelOpt:{precision:s.get(["label","precision"]),formatter:s.get(["label","formatter"])},seriesDataIndices:a.slice()})}}function IN(e){var t=e.axis.model,n={},i=n.axisDim=e.axis.dim;return n.axisIndex=n[i+"AxisIndex"]=t.componentIndex,n.axisName=n[i+"AxisName"]=t.name,n.axisId=n[i+"AxisId"]=t.id,n}function EN(e){return!e||null==e[0]||isNaN(e[0])||null==e[1]||isNaN(e[1])}function MN(e){kk.registerAxisPointerClass("CartesianAxisPointer",lN),e.registerComponentModel(uN),e.registerComponentView(xN),e.registerPreprocessor((function(e){if(e){(!e.axisPointer||0===e.axisPointer.length)&&(e.axisPointer={});var t=e.axisPointer.link;t&&!Ur(t)&&(e.axisPointer.link=[t])}})),e.registerProcessor(e.PRIORITY.PROCESSOR.STATISTIC,(function(e,t){e.getComponent("axisPointer").coordSysAxesInfo=Ck(e,t)})),e.registerAction({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},SN)}function kN(e){aI(Gk),aI(MN)}var PN=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.makeElOption=function(e,t,n,i,a){var r=n.axis;"angle"===r.dim&&(this.animationThreshold=Math.PI/18);var o=r.polar,s=o.getOtherAxis(r).getExtent(),l=r.dataToCoord(t),p=i.get("type");if(p&&"none"!==p){var c=eN(i),d=DN[p](r,o,l,s);d.style=c,e.graphicKey=d.type,e.pointer=d}var u=function(e,t,n,i,a){var r=t.axis,o=r.dataToCoord(e),s=i.getAngleAxis().getExtent()[0];s=s/180*Math.PI;var l,p,c,d=i.getRadiusAxis().getExtent();if("radius"===r.dim){var u=[1,0,0,1,0,0];ys(u,u,s),fs(u,u,[i.cx,i.cy]),l=Tf([o,-a],u);var m=t.getModel("axisLabel").get("rotate")||0,h=yk.innerTextLayout(s,m*Math.PI/180,-1);p=h.textAlign,c=h.textVerticalAlign}else{var g=d[1];l=i.coordToPoint([g+a,o]);var f=i.cx,y=i.cy;p=Math.abs(l[0]-f)/g<.3?"center":l[0]>f?"left":"right",c=Math.abs(l[1]-y)/g<.3?"middle":l[1]>y?"top":"bottom"}return{position:l,align:p,verticalAlign:c}}(t,n,0,o,i.get(["label","margin"]));tN(e,n,i,a,u)},t}(KB);var DN={line:function(e,t,n,i){return"angle"===e.dim?{type:"Line",shape:rN(t.coordToPoint([i[0],n]),t.coordToPoint([i[1],n]))}:{type:"Circle",shape:{cx:t.cx,cy:t.cy,r:n}}},shadow:function(e,t,n,i){var a=Math.max(1,e.getBandWidth()),r=Math.PI/180;return"angle"===e.dim?{type:"Sector",shape:sN(t.cx,t.cy,i[0],i[1],(-n-a/2)*r,(a/2-n)*r)}:{type:"Sector",shape:sN(t.cx,t.cy,n-a/2,n+a/2,0,2*Math.PI)}}},ON=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.findAxisModel=function(e){var t;return this.ecModel.eachComponent(e,(function(e){e.getCoordSysModel()===this&&(t=e)}),this),t},t.type="polar",t.dependencies=["radiusAxis","angleAxis"],t.defaultOption={z:0,center:["50%","50%"],radius:"80%"},t}(Sv),AN=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return ze(t,e),t.prototype.getCoordSysModel=function(){return this.getReferringComponents("polar",Ed).models[0]},t.type="polarAxis",t}(Sv);Ar(AN,tI);var FN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="angleAxis",t}(AN),RN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="radiusAxis",t}(AN),BN=function(e){function t(t,n){return e.call(this,"radius",t,n)||this}return ze(t,e),t.prototype.pointToData=function(e,t){return this.polar.pointToData(e,t)["radius"===this.dim?0:1]},t}(MI);BN.prototype.dataToRadius=MI.prototype.dataToCoord,BN.prototype.radiusToData=MI.prototype.coordToData;var NN=Cd(),LN=function(e){function t(t,n){return e.call(this,"angle",t,n||[0,360])||this}return ze(t,e),t.prototype.pointToData=function(e,t){return this.polar.pointToData(e,t)["radius"===this.dim?0:1]},t.prototype.calculateCategoryInterval=function(){var e=this,t=e.getLabelModel(),n=e.scale,i=n.getExtent(),a=n.count();if(i[1]-i[0]<1)return 0;var r=i[0],o=e.dataToCoord(r+1)-e.dataToCoord(r),s=Math.abs(o),l=uc(null==r?"":r+"",t.getFont(),"center","top"),p=Math.max(l.height,7)/s;isNaN(p)&&(p=1/0);var c=Math.max(0,Math.floor(p)),d=NN(e.model),u=d.lastAutoInterval,m=d.lastTickCount;return null!=u&&null!=m&&Math.abs(u-c)<=1&&Math.abs(m-a)<=1&&u>c?c=u:(d.lastTickCount=a,d.lastAutoInterval=c),c},t}(MI);LN.prototype.dataToAngle=MI.prototype.dataToCoord,LN.prototype.angleToData=MI.prototype.coordToData;var VN=["radius","angle"],qN=function(){function e(e){this.dimensions=VN,this.type="polar",this.cx=0,this.cy=0,this._radiusAxis=new BN,this._angleAxis=new LN,this.axisPointerEnabled=!0,this.name=e||"",this._radiusAxis.polar=this._angleAxis.polar=this}return e.prototype.containPoint=function(e){var t=this.pointToCoord(e);return this._radiusAxis.contain(t[0])&&this._angleAxis.contain(t[1])},e.prototype.containData=function(e){return this._radiusAxis.containData(e[0])&&this._angleAxis.containData(e[1])},e.prototype.getAxis=function(e){return this["_"+e+"Axis"]},e.prototype.getAxes=function(){return[this._radiusAxis,this._angleAxis]},e.prototype.getAxesByScale=function(e){var t=[],n=this._angleAxis,i=this._radiusAxis;return n.scale.type===e&&t.push(n),i.scale.type===e&&t.push(i),t},e.prototype.getAngleAxis=function(){return this._angleAxis},e.prototype.getRadiusAxis=function(){return this._radiusAxis},e.prototype.getOtherAxis=function(e){var t=this._angleAxis;return e===t?this._radiusAxis:t},e.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAngleAxis()},e.prototype.getTooltipAxes=function(e){var t=null!=e&&"auto"!==e?this.getAxis(e):this.getBaseAxis();return{baseAxes:[t],otherAxes:[this.getOtherAxis(t)]}},e.prototype.dataToPoint=function(e,t){return this.coordToPoint([this._radiusAxis.dataToRadius(e[0],t),this._angleAxis.dataToAngle(e[1],t)])},e.prototype.pointToData=function(e,t){var n=this.pointToCoord(e);return[this._radiusAxis.radiusToData(n[0],t),this._angleAxis.angleToData(n[1],t)]},e.prototype.pointToCoord=function(e){var t=e[0]-this.cx,n=e[1]-this.cy,i=this.getAngleAxis(),a=i.getExtent(),r=Math.min(a[0],a[1]),o=Math.max(a[0],a[1]);i.inverse?r=o-360:o=r+360;var s=Math.sqrt(t*t+n*n);t/=s,n/=s;for(var l=Math.atan2(-n,t)/Math.PI*180,p=lo;)l+=360*p;return[s,l]},e.prototype.coordToPoint=function(e){var t=e[0],n=e[1]/180*Math.PI;return[Math.cos(n)*t+this.cx,-Math.sin(n)*t+this.cy]},e.prototype.getArea=function(){var e=this.getAngleAxis(),t=this.getRadiusAxis().getExtent().slice();t[0]>t[1]&&t.reverse();var n=e.getExtent(),i=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:t[0],r:t[1],startAngle:-n[0]*i,endAngle:-n[1]*i,clockwise:e.inverse,contain:function(e,t){var n=e-this.cx,i=t-this.cy,a=n*n+i*i-1e-4,r=this.r,o=this.r0;return a<=r*r&&a>=o*o}}},e.prototype.convertToPixel=function(e,t,n){return GN(t)===this?this.dataToPoint(n):null},e.prototype.convertFromPixel=function(e,t,n){return GN(t)===this?this.pointToData(n):null},e}();function GN(e){var t=e.seriesModel,n=e.polarModel;return n&&n.coordinateSystem||t&&t.coordinateSystem}function zN(e,t){var n=this,i=n.getAngleAxis(),a=n.getRadiusAxis();if(i.scale.setExtent(1/0,-1/0),a.scale.setExtent(1/0,-1/0),e.eachSeries((function(e){if(e.coordinateSystem===n){var t=e.getData();Rr(eI(t,"radius"),(function(e){a.scale.unionExtentFromData(t,e)})),Rr(eI(t,"angle"),(function(e){i.scale.unionExtentFromData(t,e)}))}})),$T(i.scale,i.model),$T(a.scale,a.model),"category"===i.type&&!i.onBand){var r=i.getExtent(),o=360/i.scale.count();i.inverse?r[1]+=o:r[1]-=o,i.setExtent(r[0],r[1])}}function UN(e,t){var n;if(e.type=t.get("type"),e.scale=KT(t),e.onBand=t.get("boundaryGap")&&"category"===e.type,e.inverse=t.get("inverse"),function(e){return"angleAxis"===e.mainType}(t)){e.inverse=e.inverse!==t.get("clockwise");var i=t.get("startAngle"),a=null!==(n=t.get("endAngle"))&&void 0!==n?n:i+(e.inverse?-360:360);e.setExtent(i,a)}t.axis=e,e.model=t}var jN={dimensions:VN,create:function(e,t){var n=[];return e.eachComponent("polar",(function(e,i){var a=new qN(i+"");a.update=zN;var r=a.getRadiusAxis(),o=a.getAngleAxis(),s=e.findAxisModel("radiusAxis"),l=e.findAxisModel("angleAxis");UN(r,s),UN(o,l),function(e,t,n){var i=t.get("center"),a=n.getWidth(),r=n.getHeight();e.cx=Vc(i[0],a),e.cy=Vc(i[1],r);var o=e.getRadiusAxis(),s=Math.min(a,r)/2,l=t.get("radius");null==l?l=[0,"100%"]:Ur(l)||(l=[0,l]);var p=[Vc(l[0],s),Vc(l[1],s)];o.inverse?o.setExtent(p[1],p[0]):o.setExtent(p[0],p[1])}(a,e,t),n.push(a),e.coordinateSystem=a,a.model=e})),e.eachSeries((function(e){if("polar"===e.get("coordinateSystem")){var t=e.getReferringComponents("polar",Ed).models[0];0,e.coordinateSystem=t.coordinateSystem}})),n}},HN=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function WN(e,t,n){t[1]>t[0]&&(t=t.slice().reverse());var i=e.coordToPoint([t[0],n]),a=e.coordToPoint([t[1],n]);return{x1:i[0],y1:i[1],x2:a[0],y2:a[1]}}function $N(e){return e.getRadiusAxis().inverse?0:1}function KN(e){var t=e[0],n=e[e.length-1];t&&n&&Math.abs(Math.abs(t.coord-n.coord)-360)<1e-4&&e.pop()}var YN=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.axisPointerClass="PolarAxisPointer",n}return ze(t,e),t.prototype.render=function(e,t){if(this.group.removeAll(),e.get("show")){var n=e.axis,i=n.polar,a=i.getRadiusAxis().getExtent(),r=n.getTicksCoords(),o=n.getMinorTicksCoords(),s=Br(n.getViewLabels(),(function(e){e=Ir(e);var t=n.scale,i="ordinal"===t.type?t.getRawOrdinalNumber(e.tickValue):e.tickValue;return e.coord=n.dataToCoord(i),e}));KN(s),KN(r),Rr(HN,(function(t){!e.get([t,"show"])||n.scale.isBlank()&&"axisLine"!==t||XN[t](this.group,e,i,r,o,a,s)}),this)}},t.type="angleAxis",t}(kk),XN={axisLine:function(e,t,n,i,a,r){var o,s=t.getModel(["axisLine","lineStyle"]),l=n.getAngleAxis(),p=Math.PI/180,c=l.getExtent(),d=$N(n),u=d?0:1,m=360===Math.abs(c[1]-c[0])?"Circle":"Arc";(o=0===r[u]?new Lf[m]({shape:{cx:n.cx,cy:n.cy,r:r[d],startAngle:-c[0]*p,endAngle:-c[1]*p,clockwise:l.inverse},style:s.getLineStyle(),z2:1,silent:!0}):new Ig({shape:{cx:n.cx,cy:n.cy,r:r[d],r0:r[u]},style:s.getLineStyle(),z2:1,silent:!0})).style.fill=null,e.add(o)},axisTick:function(e,t,n,i,a,r){var o=t.getModel("axisTick"),s=(o.get("inside")?-1:1)*o.get("length"),l=r[$N(n)],p=Br(i,(function(e){return new Fg({shape:WN(n,[l,l+s],e.coord)})}));e.add(bf(p,{style:Pr(o.getModel("lineStyle").getLineStyle(),{stroke:t.get(["axisLine","lineStyle","color"])})}))},minorTick:function(e,t,n,i,a,r){if(a.length){for(var o=t.getModel("axisTick"),s=t.getModel("minorTick"),l=(o.get("inside")?-1:1)*s.get("length"),p=r[$N(n)],c=[],d=0;dh?"left":"right",y=Math.abs(m[1]-g)/u<.3?"middle":m[1]>g?"top":"bottom";if(s&&s[d]){var v=s[d];Kr(v)&&v.textStyle&&(o=new uy(v.textStyle,l,l.ecModel))}var x=new Pm({silent:yk.isLabelSilent(t),style:jf(o,{x:m[0],y:m[1],fill:o.getTextColor()||t.get(["axisLine","lineStyle","color"]),text:i.formattedLabel,align:f,verticalAlign:y})});if(e.add(x),c){var b=yk.makeAxisEventDataBase(t);b.targetType="axisLabel",b.value=i.rawLabel,Um(x).eventData=b}}),this)},splitLine:function(e,t,n,i,a,r){var o=t.getModel("splitLine").getModel("lineStyle"),s=o.get("color"),l=0;s=s instanceof Array?s:[s];for(var p=[],c=0;c=0?"p":"n",I=b;v&&(i[s][_]||(i[s][_]={p:b,n:b}),I=i[s][_][T]);var E=void 0,M=void 0,k=void 0,P=void 0;if("radius"===d.dim){var D=d.dataToCoord(C)-b,O=r.dataToCoord(_);Math.abs(D)=P})}}}))}var aL={startAngle:90,clockwise:!0,splitNumber:12,axisLabel:{rotate:0}},rL={splitNumber:5},oL=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="polar",t}(hw);function sL(e){aI(MN),kk.registerAxisPointerClass("PolarAxisPointer",PN),e.registerCoordinateSystem("polar",jN),e.registerComponentModel(ON),e.registerComponentView(oL),ek(e,"angle",FN,aL),ek(e,"radius",RN,rL),e.registerComponentView(YN),e.registerComponentView(JN),e.registerLayout(zr(iL,"bar"))}function lL(e,t){t=t||{};var n=e.coordinateSystem,i=e.axis,a={},r=i.position,o=i.orient,s=n.getRect(),l=[s.x,s.x+s.width,s.y,s.y+s.height],p={horizontal:{top:l[2],bottom:l[3]},vertical:{left:l[0],right:l[1]}};a.position=["vertical"===o?p.vertical[r]:l[0],"horizontal"===o?p.horizontal[r]:l[3]];a.rotation=Math.PI/2*{horizontal:0,vertical:1}[o];a.labelDirection=a.tickDirection=a.nameDirection={top:-1,bottom:1,right:1,left:-1}[r],e.get(["axisTick","inside"])&&(a.tickDirection=-a.tickDirection),no(t.labelInside,e.get(["axisLabel","inside"]))&&(a.labelDirection=-a.labelDirection);var c=t.rotate;return null==c&&(c=e.get(["axisLabel","rotate"])),a.labelRotation="top"===r?-c:c,a.z2=1,a}var pL=["axisLine","axisTickLabel","axisName"],cL=["splitArea","splitLine"],dL=(function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.axisPointerClass="SingleAxisPointer",n}ze(t,e),t.prototype.render=function(t,n,i,a){var r=this.group;r.removeAll();var o=this._axisGroup;this._axisGroup=new Mc;var s=lL(t),l=new yk(t,s);Rr(pL,l.add,l),r.add(this._axisGroup),r.add(l.getGroup()),Rr(cL,(function(e){t.get([e,"show"])&&dL[e](this,this.group,this._axisGroup,t)}),this),Mf(o,this._axisGroup,t),e.prototype.render.call(this,t,n,i,a)},t.prototype.remove=function(){Ok(this)},t.type="singleAxis"}(kk),{splitLine:function(e,t,n,i){var a=i.axis;if(!a.scale.isBlank()){var r=i.getModel("splitLine"),o=r.getModel("lineStyle"),s=o.get("color");s=s instanceof Array?s:[s];for(var l=o.get("width"),p=i.coordinateSystem.getRect(),c=a.isHorizontal(),d=[],u=0,m=a.getTicksCoords({tickModel:r}),h=[],g=[],f=0;f=t.y&&e[1]<=t.y+t.height:n.contain(n.toLocalCoord(e[1]))&&e[0]>=t.y&&e[0]<=t.y+t.height},e.prototype.pointToData=function(e){var t=this.getAxis();return[t.coordToData(t.toLocalCoord(e["horizontal"===t.orient?0:1]))]},e.prototype.dataToPoint=function(e){var t=this.getAxis(),n=this.getRect(),i=[],a="horizontal"===t.orient?0:1;return e instanceof Array&&(e=e[0]),i[a]=t.toGlobalCoord(t.dataToCoord(+e)),i[1-a]=0===a?n.y+n.height/2:n.x+n.width/2,i},e.prototype.convertToPixel=function(e,t,n){return gL(t)===this?this.dataToPoint(n):null},e.prototype.convertFromPixel=function(e,t,n){return gL(t)===this?this.pointToData(n):null}}();function gL(e){var t=e.seriesModel,n=e.singleAxisModel;return n&&n.coordinateSystem||t&&t.coordinateSystem}var fL=["x","y"],yL=["width","height"],vL=(function(e){function t(){return null!==e&&e.apply(this,arguments)||this}ze(t,e),t.prototype.makeElOption=function(e,t,n,i,a){var r=n.axis,o=r.coordinateSystem,s=bL(o,1-xL(r)),l=o.dataToPoint(t)[0],p=i.get("type");if(p&&"none"!==p){var c=eN(i),d=vL[p](r,l,s);d.style=c,e.graphicKey=d.type,e.pointer=d}aN(t,e,lL(n),n,i,a)},t.prototype.getHandleTransform=function(e,t,n){var i=lL(t,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var a=iN(t.axis,e,i);return{x:a[0],y:a[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},t.prototype.updateHandleTransform=function(e,t,n,i){var a=n.axis,r=a.coordinateSystem,o=xL(a),s=bL(r,o),l=[e.x,e.y];l[o]+=t[o],l[o]=Math.min(s[1],l[o]),l[o]=Math.max(s[0],l[o]);var p=bL(r,1-o),c=(p[1]+p[0])/2,d=[c,c];return d[o]=l[o],{x:l[0],y:l[1],rotation:e.rotation,cursorPoint:d,tooltipOption:{verticalAlign:"middle"}}}}(KB),{line:function(e,t,n){return{type:"Line",subPixelOptimize:!0,shape:rN([t,n[0]],[t,n[1]],xL(e))}},shadow:function(e,t,n){var i=e.getBandWidth(),a=n[1]-n[0];return{type:"Rect",shape:oN([t-i/2,n[0]],[i,a],xL(e))}}});function xL(e){return e.isHorizontal()?0:1}function bL(e,t){var n=e.getRect();return[n[fL[t]],n[fL[t]]+n[yL[t]]]}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.type="single"}(hw);!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.init=function(t,n,i){var a=xv(t);e.prototype.init.apply(this,arguments),wL(t,a)},t.prototype.mergeOption=function(t){e.prototype.mergeOption.apply(this,arguments),wL(this.option,t)},t.prototype.getCellSize=function(){return this.option.cellSize},t.type="calendar",t.defaultOption={z:2,left:80,top:60,cellSize:20,orient:"horizontal",splitLine:{show:!0,lineStyle:{color:"#000",width:1,type:"solid"}},itemStyle:{color:"#fff",borderWidth:1,borderColor:"#ccc"},dayLabel:{show:!0,firstDay:0,position:"start",margin:"50%",color:"#000"},monthLabel:{show:!0,position:"start",margin:5,align:"center",formatter:null,color:"#000"},yearLabel:{show:!0,position:null,margin:30,formatter:null,color:"#ccc",fontFamily:"sans-serif",fontWeight:"bolder",fontSize:20}}}(Sv);function wL(e,t){var n,i=e.cellSize;1===(n=Ur(i)?i:e.cellSize=[i,i]).length&&(n[1]=n[0]);var a=Br([0,1],(function(e){return function(e,t){return null!=e[uv[t][0]]||null!=e[uv[t][1]]&&null!=e[uv[t][2]]}(t,e)&&(n[e]="auto"),null!=n[e]&&"auto"!==n[e]}));vv(e,t,{type:"box",ignoreSize:a})}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.render=function(e,t,n){var i=this.group;i.removeAll();var a=e.coordinateSystem,r=a.getRangeInfo(),o=a.getOrient(),s=t.getLocaleModel();this._renderDayRect(e,r,i),this._renderLines(e,r,o,i),this._renderYearText(e,r,o,i),this._renderMonthText(e,s,o,i),this._renderWeekText(e,s,r,o,i)},t.prototype._renderDayRect=function(e,t,n){for(var i=e.coordinateSystem,a=e.getModel("itemStyle").getItemStyle(),r=i.getCellWidth(),o=i.getCellHeight(),s=t.start.time;s<=t.end.time;s=i.getNextNDay(s,1).time){var l=i.dataToRect([s],!1).tl,p=new Em({shape:{x:l[0],y:l[1],width:r,height:o},cursor:"default",style:a});n.add(p)}},t.prototype._renderLines=function(e,t,n,i){var a=this,r=e.coordinateSystem,o=e.getModel(["splitLine","lineStyle"]).getLineStyle(),s=e.get(["splitLine","show"]),l=o.lineWidth;this._tlpoints=[],this._blpoints=[],this._firstDayOfMonth=[],this._firstDayPoints=[];for(var p=t.start,c=0;p.time<=t.end.time;c++){u(p.formatedDate),0===c&&(p=r.getDateInfo(t.start.y+"-"+t.start.m));var d=p.date;d.setMonth(d.getMonth()+1),p=r.getDateInfo(d)}function u(t){a._firstDayOfMonth.push(r.getDateInfo(t)),a._firstDayPoints.push(r.dataToRect([t],!1).tl);var l=a._getLinePointsOfOneWeek(e,t,n);a._tlpoints.push(l[0]),a._blpoints.push(l[l.length-1]),s&&a._drawSplitline(l,o,i)}u(r.getNextNDay(t.end.time,1).formatedDate),s&&this._drawSplitline(a._getEdgesPoints(a._tlpoints,l,n),o,i),s&&this._drawSplitline(a._getEdgesPoints(a._blpoints,l,n),o,i)},t.prototype._getEdgesPoints=function(e,t,n){var i=[e[0].slice(),e[e.length-1].slice()],a="horizontal"===n?0:1;return i[0][a]=i[0][a]-t/2,i[1][a]=i[1][a]+t/2,i},t.prototype._drawSplitline=function(e,t,n){var i=new Dg({z2:20,shape:{points:e},style:t});n.add(i)},t.prototype._getLinePointsOfOneWeek=function(e,t,n){for(var i=e.coordinateSystem,a=i.getDateInfo(t),r=[],o=0;o<7;o++){var s=i.getNextNDay(a.time,o),l=i.dataToRect([s.time],!1);r[2*s.day]=l.tl,r[2*s.day+1]=l["horizontal"===n?"bl":"tr"]}return r},t.prototype._formatterLabel=function(e,t){return Hr(e)&&e?(n=e,Rr(t,(function(e,t){n=n.replace("{"+t+"}",i?Jo(e):e)})),n):jr(e)?e(t):t.nameMap;var n,i},t.prototype._yearTextPositionControl=function(e,t,n,i,a){var r=t[0],o=t[1],s=["center","bottom"];"bottom"===i?(o+=a,s=["center","top"]):"left"===i?r-=a:"right"===i?(r+=a,s=["center","top"]):o-=a;var l=0;return"left"!==i&&"right"!==i||(l=Math.PI/2),{rotation:l,x:r,y:o,style:{align:s[0],verticalAlign:s[1]}}},t.prototype._renderYearText=function(e,t,n,i){var a=e.getModel("yearLabel");if(a.get("show")){var r=a.get("margin"),o=a.get("position");o||(o="horizontal"!==n?"top":"left");var s=[this._tlpoints[this._tlpoints.length-1],this._blpoints[0]],l=(s[0][0]+s[1][0])/2,p=(s[0][1]+s[1][1])/2,c="horizontal"===n?0:1,d={top:[l,s[c][1]],bottom:[l,s[1-c][1]],left:[s[1-c][0],p],right:[s[c][0],p]},u=t.start.y;+t.end.y>+t.start.y&&(u=u+"-"+t.end.y);var m=a.get("formatter"),h={start:t.start.y,end:t.end.y,nameMap:u},g=this._formatterLabel(m,h),f=new Pm({z2:30,style:jf(a,{text:g})});f.attr(this._yearTextPositionControl(f,d[o],n,o,r)),i.add(f)}},t.prototype._monthTextPositionControl=function(e,t,n,i,a){var r="left",o="top",s=e[0],l=e[1];return"horizontal"===n?(l+=a,t&&(r="center"),"start"===i&&(o="bottom")):(s+=a,t&&(o="middle"),"start"===i&&(r="right")),{x:s,y:l,align:r,verticalAlign:o}},t.prototype._renderMonthText=function(e,t,n,i){var a=e.getModel("monthLabel");if(a.get("show")){var r=a.get("nameMap"),o=a.get("margin"),s=a.get("position"),l=a.get("align"),p=[this._tlpoints,this._blpoints];r&&!Hr(r)||(r&&(t=Cy(r)||t),r=t.get(["time","monthAbbr"])||[]);var c="start"===s?0:1,d="horizontal"===n?0:1;o="start"===s?-o:o;for(var u="center"===l,m=0;m=i.start.time&&n.timeo.end.time&&e.reverse(),e},e.prototype._getRangeInfo=function(e){var t,n=[this.getDateInfo(e[0]),this.getDateInfo(e[1])];n[0].time>n[1].time&&(t=!0,n.reverse());var i=Math.floor(n[1].time/SL)-Math.floor(n[0].time/SL)+1,a=new Date(n[0].time),r=a.getDate(),o=n[1].date.getDate();a.setDate(r+i-1);var s=a.getDate();if(s!==o)for(var l=a.getTime()-n[1].time>0?1:-1;(s=a.getDate())!==o&&(a.getTime()-n[1].time)*l>0;)i-=l,a.setDate(s-l);var p=Math.floor((i+n[0].day+6)/7),c=t?1-p:p-1;return t&&n.reverse(),{range:[n[0].formatedDate,n[1].formatedDate],start:n[0],end:n[1],allDay:i,weeks:p,nthWeek:c,fweek:n[0].day,lweek:n[1].day}},e.prototype._getDateByWeeksAndDay=function(e,t,n){var i=this._getRangeInfo(n);if(e>i.weeks||0===e&&ti.lweek)return null;var a=7*(e-1)-i.fweek+t,r=new Date(i.start.time);return r.setDate(+i.start.d+a),this.getDateInfo(r)},e.create=function(t,n){var i=[];return t.eachComponent("calendar",(function(a){var r=new e(a,t,n);i.push(r),a.coordinateSystem=r})),t.eachSeries((function(e){"calendar"===e.get("coordinateSystem")&&(e.coordinateSystem=i[e.get("calendarIndex")||0])})),i},e.dimensions=["time","value"]}();function CL(e){var t=e.calendarModel,n=e.seriesModel;return t?t.coordinateSystem:n?n.coordinateSystem:null}function _L(e,t){var n;return Rr(t,(function(t){null!=e[t]&&"auto"!==e[t]&&(n=!0)})),n}var TL=["transition","enterFrom","leaveTo"],IL=TL.concat(["enterAnimation","updateAnimation","leaveAnimation"]);function EL(e,t,n){if(n&&(!e[n]&&t[n]&&(e[n]={}),e=e[n],t=t[n]),e&&t)for(var i=n?TL:IL,a=0;a=0;l--){var u,m,h;if(h=null!=(m=xd((u=n[l]).id,null))?a.get(m):null){var g=h.parent,f=(d=kL(g),{}),y=fv(h,u,g===i?{width:r,height:o}:{width:d.width,height:d.height},null,{hv:u.hv,boundingMode:u.bounding},f);if(!kL(h).isNew&&y){for(var v=u.transition,x={},b=0;b=0)?x[w]=S:h[w]=S}tf(h,x,e,0)}else h.attr(f)}}},t.prototype._clear=function(){var e=this,t=this._elMap;t.each((function(n){OL(n,kL(n).option,t,e._lastGraphicModel)})),this._elMap=fo()},t.prototype.dispose=function(){this._clear()},t.type="graphic"}(hw);function PL(e){var t=bo(ML,e)?ML[e]:ff(e);var n=new t({});return kL(n).type=e,n}function DL(e,t,n,i){var a=PL(n);return t.add(a),i.set(e,a),kL(a).id=e,kL(a).isNew=!0,a}function OL(e,t,n,i){e&&e.parent&&("group"===e.type&&e.traverse((function(e){OL(e,t,n,i)})),nB(e,t,i),n.removeKey(kL(e).id))}function AL(e,t,n,i){e.isGroup||Rr([["cursor",mu.prototype.cursor],["zlevel",i||0],["z",n||0],["z2",0]],(function(n){var i=n[0];bo(t,i)?e[i]=io(t[i],n[1]):null==e[i]&&(e[i]=n[1])})),Rr(qr(t),(function(n){if(0===n.indexOf("on")){var i=t[n];e[n]=jr(i)?i:null}})),bo(t,"draggable")&&(e.draggable=t.draggable),null!=t.name&&(e.name=t.name),null!=t.id&&(e.id=t.id)}var FL=["x","y","radius","angle","single"],RL=["cartesian2d","polar","singleAxis"];function BL(e){return e+"Axis"}function NL(e,t){var n,i=fo(),a=[],r=fo();e.eachComponent({mainType:"dataZoom",query:t},(function(e){r.get(e.uid)||s(e)}));do{n=!1,e.eachComponent("dataZoom",o)}while(n);function o(e){!r.get(e.uid)&&function(e){var t=!1;return e.eachTargetAxis((function(e,n){var a=i.get(e);a&&a[n]&&(t=!0)})),t}(e)&&(s(e),n=!0)}function s(e){r.set(e.uid,!0),a.push(e),e.eachTargetAxis((function(e,t){(i.get(e)||i.set(e,[]))[t]=!0}))}return a}function LL(e){var t=e.ecModel,n={infoList:[],infoMap:fo()};return e.eachTargetAxis((function(e,i){var a=t.getComponent(BL(e),i);if(a){var r=a.getCoordSysModel();if(r){var o=r.uid,s=n.infoMap.get(o);s||(s={model:r,axisModels:[]},n.infoList.push(s),n.infoMap.set(o,s)),s.axisModels.push(a)}}})),n}var VL=function(){function e(){this.indexList=[],this.indexMap=[]}return e.prototype.add=function(e){this.indexMap[e]||(this.indexList.push(e),this.indexMap[e]=!0)},e}(),qL=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._autoThrottle=!0,n._noTarget=!0,n._rangePropMode=["percent","percent"],n}return ze(t,e),t.prototype.init=function(e,t,n){var i=GL(e);this.settledOption=i,this.mergeDefaultAndTheme(e,n),this._doInit(i)},t.prototype.mergeOption=function(e){var t=GL(e);Er(this.option,e,!0),Er(this.settledOption,t,!0),this._doInit(t)},t.prototype._doInit=function(e){var t=this.option;this._setDefaultThrottle(e),this._updateRangeUse(e);var n=this.settledOption;Rr([["start","startValue"],["end","endValue"]],(function(e,i){"value"===this._rangePropMode[i]&&(t[e[0]]=n[e[0]]=null)}),this),this._resetTarget()},t.prototype._resetTarget=function(){var e=this.get("orient",!0),t=this._targetAxisInfoMap=fo();this._fillSpecifiedTargetAxis(t)?this._orient=e||this._makeAutoOrientByTargetAxis():(this._orient=e||"horizontal",this._fillAutoTargetAxisByOrient(t,this._orient)),this._noTarget=!0,t.each((function(e){e.indexList.length&&(this._noTarget=!1)}),this)},t.prototype._fillSpecifiedTargetAxis=function(e){var t=!1;return Rr(FL,(function(n){var i=this.getReferringComponents(BL(n),Md);if(i.specified){t=!0;var a=new VL;Rr(i.models,(function(e){a.add(e.componentIndex)})),e.set(n,a)}}),this),t},t.prototype._fillAutoTargetAxisByOrient=function(e,t){var n=this.ecModel,i=!0;if(i){var a="vertical"===t?"y":"x";r(n.findComponents({mainType:a+"Axis"}),a)}i&&r(n.findComponents({mainType:"singleAxis",filter:function(e){return e.get("orient",!0)===t}}),"single");function r(t,n){var a=t[0];if(a){var r=new VL;if(r.add(a.componentIndex),e.set(n,r),i=!1,"x"===n||"y"===n){var o=a.getReferringComponents("grid",Ed).models[0];o&&Rr(t,(function(e){a.componentIndex!==e.componentIndex&&o===e.getReferringComponents("grid",Ed).models[0]&&r.add(e.componentIndex)}))}}}i&&Rr(FL,(function(t){if(i){var a=n.findComponents({mainType:BL(t),filter:function(e){return"category"===e.get("type",!0)}});if(a[0]){var r=new VL;r.add(a[0].componentIndex),e.set(t,r),i=!1}}}),this)},t.prototype._makeAutoOrientByTargetAxis=function(){var e;return this.eachTargetAxis((function(t){!e&&(e=t)}),this),"y"===e?"vertical":"horizontal"},t.prototype._setDefaultThrottle=function(e){if(e.hasOwnProperty("throttle")&&(this._autoThrottle=!1),this._autoThrottle){var t=this.ecModel.option;this.option.throttle=t.animation&&t.animationDurationUpdate>0?100:20}},t.prototype._updateRangeUse=function(e){var t=this._rangePropMode,n=this.get("rangeMode");Rr([["start","startValue"],["end","endValue"]],(function(i,a){var r=null!=e[i[0]],o=null!=e[i[1]];r&&!o?t[a]="percent":!r&&o?t[a]="value":n?t[a]=n[a]:r&&(t[a]="percent")}))},t.prototype.noTarget=function(){return this._noTarget},t.prototype.getFirstTargetAxisModel=function(){var e;return this.eachTargetAxis((function(t,n){null==e&&(e=this.ecModel.getComponent(BL(t),n))}),this),e},t.prototype.eachTargetAxis=function(e,t){this._targetAxisInfoMap.each((function(n,i){Rr(n.indexList,(function(n){e.call(t,i,n)}))}))},t.prototype.getAxisProxy=function(e,t){var n=this.getAxisModel(e,t);if(n)return n.__dzAxisProxy},t.prototype.getAxisModel=function(e,t){var n=this._targetAxisInfoMap.get(e);if(n&&n.indexMap[t])return this.ecModel.getComponent(BL(e),t)},t.prototype.setRawRange=function(e){var t=this.option,n=this.settledOption;Rr([["start","startValue"],["end","endValue"]],(function(i){null==e[i[0]]&&null==e[i[1]]||(t[i[0]]=n[i[0]]=e[i[0]],t[i[1]]=n[i[1]]=e[i[1]])}),this),this._updateRangeUse(e)},t.prototype.setCalculatedRange=function(e){var t=this.option;Rr(["start","startValue","end","endValue"],(function(n){t[n]=e[n]}))},t.prototype.getPercentRange=function(){var e=this.findRepresentativeAxisProxy();if(e)return e.getDataPercentWindow()},t.prototype.getValueRange=function(e,t){var n;if(null==e&&null==t){if(n=this.findRepresentativeAxisProxy())return n.getDataValueWindow()}else if(n=this.getAxisProxy(e,t))return n.getDataValueWindow()},t.prototype.findRepresentativeAxisProxy=function(e){if(e)return e.__dzAxisProxy;for(var t,n=this._targetAxisInfoMap.keys(),i=0;i=0}(t)){var n=BL(this._dimName),i=t.getReferringComponents(n,Ed).models[0];i&&this._axisIndex===i.componentIndex&&e.push(t)}}),this),e},e.prototype.getAxisModel=function(){return this.ecModel.getComponent(this._dimName+"Axis",this._axisIndex)},e.prototype.getMinMaxSpan=function(){return Ir(this._minMaxSpan)},e.prototype.calculateDataWindow=function(e){var t,n=this._dataExtent,i=this.getAxisModel().axis.scale,a=this._dataZoomModel.getRangePropMode(),r=[0,100],o=[],s=[];UL(["start","end"],(function(l,p){var c=e[l],d=e[l+"Value"];"percent"===a[p]?(null==c&&(c=r[p]),d=i.parse(Lc(c,r,n))):(t=!0,c=Lc(d=null==d?n[p]:i.parse(d),n,r)),s[p]=null==d||isNaN(d)?n[p]:d,o[p]=null==c||isNaN(c)?r[p]:c})),jL(s),jL(o);var l=this._minMaxSpan;function p(e,t,n,a,r){var o=r?"Span":"ValueSpan";bA(0,e,n,"all",l["min"+o],l["max"+o]);for(var s=0;s<2;s++)t[s]=Lc(e[s],n,a,!0),r&&(t[s]=i.parse(t[s]))}return t?p(s,o,n,r,!1):p(o,s,r,n,!0),{valueWindow:s,percentWindow:o}},e.prototype.reset=function(e){if(e===this._dataZoomModel){var t=this.getTargetSeriesModels();this._dataExtent=function(e,t,n){var i=[1/0,-1/0];UL(n,(function(e){!function(e,t,n){t&&Rr(eI(t,n),(function(n){var i=t.getApproximateExtent(n);i[0]e[1]&&(e[1]=i[1])}))}(i,e.getData(),t)}));var a=e.getAxisModel(),r=jT(a.axis.scale,a,i).calculate();return[r.min,r.max]}(this,this._dimName,t),this._updateMinMaxSpan();var n=this.calculateDataWindow(e.settledOption);this._valueWindow=n.valueWindow,this._percentWindow=n.percentWindow,this._setAxisModel()}},e.prototype.filterData=function(e,t){if(e===this._dataZoomModel){var n=this._dimName,i=this.getTargetSeriesModels(),a=e.get("filterMode"),r=this._valueWindow;"none"!==a&&UL(i,(function(e){var t=e.getData(),i=t.mapDimensionsAll(n);if(i.length){if("weakFilter"===a){var o=t.getStore(),s=Br(i,(function(e){return t.getDimensionIndex(e)}),t);t.filterSelf((function(e){for(var t,n,a,l=0;lr[1];if(c&&!d&&!u)return!0;c&&(a=!0),d&&(t=!0),u&&(n=!0)}return a&&t&&n}))}else UL(i,(function(n){if("empty"===a)e.setData(t=t.map(n,(function(e){return function(e){return e>=r[0]&&e<=r[1]}(e)?e:NaN})));else{var i={};i[n]=r,t.selectRange(i)}}));UL(i,(function(e){t.setApproximateExtent(r,e)}))}}))}},e.prototype._updateMinMaxSpan=function(){var e=this._minMaxSpan={},t=this._dataZoomModel,n=this._dataExtent;UL(["min","max"],(function(i){var a=t.get(i+"Span"),r=t.get(i+"ValueSpan");null!=r&&(r=this.getAxisModel().axis.scale.parse(r)),null!=r?a=Lc(n[0]+r,n,[0,100],!0):null!=a&&(r=Lc(a,[0,100],n,!0)-n[0]),e[i+"Span"]=a,e[i+"ValueSpan"]=r}),this)},e.prototype._setAxisModel=function(){var e=this.getAxisModel(),t=this._percentWindow,n=this._valueWindow;if(t){var i=jc(n,[0,500]);i=Math.min(i,20);var a=e.axis.scale.rawExtentInfo;0!==t[0]&&a.setDeterminedMinMax("min",+n[0].toFixed(i)),100!==t[1]&&a.setDeterminedMinMax("max",+n[1].toFixed(i)),a.freeze()}},e}();var WL={getTargetSeries:function(e){function t(t){e.eachComponent("dataZoom",(function(n){n.eachTargetAxis((function(i,a){var r=e.getComponent(BL(i),a);t(i,a,r,n)}))}))}t((function(e,t,n,i){n.__dzAxisProxy=null}));var n=[];t((function(t,i,a,r){a.__dzAxisProxy||(a.__dzAxisProxy=new HL(t,i,r,e),n.push(a.__dzAxisProxy))}));var i=fo();return Rr(n,(function(e){Rr(e.getTargetSeriesModels(),(function(e){i.set(e.uid,e)}))})),i},overallReset:function(e,t){e.eachComponent("dataZoom",(function(e){e.eachTargetAxis((function(t,n){var i=e.getAxisProxy(t,n);i&&i.reset(e)})),e.eachTargetAxis((function(n,i){var a=e.getAxisProxy(n,i);a&&a.filterData(e,t)}))})),e.eachComponent("dataZoom",(function(e){var t=e.findRepresentativeAxisProxy();if(t){var n=t.getDataPercentWindow(),i=t.getDataValueWindow();e.setCalculatedRange({start:n[0],end:n[1],startValue:i[0],endValue:i[1]})}}))}};var $L=!1;function KL(e){$L||($L=!0,e.registerProcessor(e.PRIORITY.PROCESSOR.FILTER,WL),function(e){e.registerAction("dataZoom",(function(e,t){Rr(NL(t,e),(function(t){t.setRawRange({start:e.start,end:e.end,startValue:e.startValue,endValue:e.endValue})}))}))}(e),e.registerSubTypeDefaulter("dataZoom",(function(){return"slider"})))}var YL=function(){},XL={};function ZL(e){return XL[e]}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.optionUpdated=function(){e.prototype.optionUpdated.apply(this,arguments);var t=this.ecModel;Rr(this.option.feature,(function(e,n){var i=ZL(n);i&&(i.getDefaultOption&&(i.defaultOption=i.getDefaultOption(t)),Er(e,i.defaultOption))}))},t.type="toolbox",t.layoutMode={type:"box",ignoreSize:!0},t.defaultOption={show:!0,z:6,orient:"horizontal",left:"right",top:"top",backgroundColor:"transparent",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemSize:15,itemGap:8,showTitle:!0,iconStyle:{borderColor:"#666",color:"none"},emphasis:{iconStyle:{borderColor:"#3E98C5"}},tooltip:{show:!1,position:"bottom"}}}(Sv);function QL(e,t){var n=nv(t.get("padding")),i=t.getItemStyle(["color","opacity"]);return i.fill=t.get("backgroundColor"),e=new Em({shape:{x:e.x-n[3],y:e.y-n[0],width:e.width+n[1]+n[3],height:e.height+n[0]+n[2],r:t.get("borderRadius")},style:i,silent:!0,z2:-1})}!function(e){function t(){return null!==e&&e.apply(this,arguments)||this}ze(t,e),t.prototype.render=function(e,t,n,i){var a=this.group;if(a.removeAll(),e.get("show")){var r=+e.get("itemSize"),o="vertical"===e.get("orient"),s=e.get("feature")||{},l=this._features||(this._features={}),p=[];Rr(s,(function(e,t){p.push(t)})),new g_(this._featureNames||[],p).add(c).update(c).remove(zr(c,null)).execute(),this._featureNames=p,function(e,t,n){var i=t.getBoxLayoutParams(),a=t.get("padding"),r={width:n.getWidth(),height:n.getHeight()},o=gv(i,r,a);hv(t.get("orient"),e,t.get("itemGap"),o.width,o.height),fv(e,i,r,a)}(a,e,n),a.add(QL(a.getBoundingRect(),e)),o||a.eachChild((function(e){var t=e.__title,i=e.ensureState("emphasis"),o=i.textConfig||(i.textConfig={}),s=e.getTextContent(),l=s&&s.ensureState("emphasis");if(l&&!jr(l)&&t){var p=l.style||(l.style={}),c=uc(t,Pm.makeFont(p)),d=e.x+a.x,u=!1;e.y+a.y+r+c.height>n.getHeight()&&(o.position="top",u=!0);var m=u?-5-c.height:r+10;d+c.width/2>n.getWidth()?(o.position=["100%",m],p.align="right"):d-c.width/2<0&&(o.position=[0,m],p.align="left")}}))}function c(c,d){var u,m=p[c],h=p[d],g=s[m],f=new uy(g,e,e.ecModel);if(i&&null!=i.newTitle&&i.featureName===m&&(g.title=i.newTitle),m&&!h){if(function(e){return 0===e.indexOf("my")}(m))u={onclick:f.option.onclick,featureName:m};else{var y=ZL(m);if(!y)return;u=new y}l[m]=u}else if(!(u=l[h]))return;u.uid=hy("toolbox-feature"),u.model=f,u.ecModel=t,u.api=n;var v=u instanceof YL;m||!h?!f.get("show")||v&&u.unusable?v&&u.remove&&u.remove(t,n):(!function(i,s,l){var p,c,d=i.getModel("iconStyle"),u=i.getModel(["emphasis","iconStyle"]),m=s instanceof YL&&s.getIcons?s.getIcons():i.get("icon"),h=i.get("title")||{};Hr(m)?(p={})[l]=m:p=m;Hr(h)?(c={})[l]=h:c=h;var g=i.iconPaths={};Rr(p,(function(l,p){var m=Df(l,{},{x:-r/2,y:-r/2,width:r,height:r});m.setStyle(d.getItemStyle()),m.ensureState("emphasis").style=u.getItemStyle();var h=new Pm({style:{text:c[p],align:u.get("textAlign"),borderRadius:u.get("textBorderRadius"),padding:u.get("textPadding"),fill:null,font:Xf({fontStyle:u.get("textFontStyle"),fontFamily:u.get("textFontFamily"),fontSize:u.get("textFontSize"),fontWeight:u.get("textFontWeight")},t)},ignore:!0});m.setTextContent(h),Rf({el:m,componentModel:e,itemName:p,formatterParamsExtra:{title:c[p]}}),m.__title=c[p],m.on("mouseover",(function(){var t=u.getItemStyle(),i=o?null==e.get("right")&&"right"!==e.get("left")?"right":"left":null==e.get("bottom")&&"bottom"!==e.get("top")?"bottom":"top";h.setStyle({fill:u.get("textFill")||t.fill||t.stroke||"#000",backgroundColor:u.get("textBackgroundColor")}),m.setTextConfig({position:u.get("textPosition")||i}),h.ignore=!e.get("showTitle"),n.enterEmphasis(this)})).on("mouseout",(function(){"emphasis"!==i.get(["iconStatus",p])&&n.leaveEmphasis(this),h.hide()})),("emphasis"===i.get(["iconStatus",p])?vh:xh)(m),a.add(m),m.on("click",Gr(s.onclick,s,t,n,p)),g[p]=m}))}(f,u,m),f.setIconStatus=function(e,t){var n=this.option,i=this.iconPaths;n.iconStatus=n.iconStatus||{},n.iconStatus[e]=t,i[e]&&("emphasis"===t?vh:xh)(i[e])},u instanceof YL&&u.render&&u.render(f,t,n,i)):v&&u.dispose&&u.dispose(t,n)}},t.prototype.updateView=function(e,t,n,i){Rr(this._features,(function(e){e instanceof YL&&e.updateView&&e.updateView(e.model,t,n,i)}))},t.prototype.remove=function(e,t){Rr(this._features,(function(n){n instanceof YL&&n.remove&&n.remove(e,t)})),this.group.removeAll()},t.prototype.dispose=function(e,t){Rr(this._features,(function(n){n instanceof YL&&n.dispose&&n.dispose(e,t)}))},t.type="toolbox"}(hw);!function(e){function t(){return null!==e&&e.apply(this,arguments)||this}ze(t,e),t.prototype.onclick=function(e,t){var n=this.model,i=n.get("name")||e.get("title.0.text")||"echarts",a="svg"===t.getZr().painter.getType(),r=a?"svg":n.get("type",!0)||"png",o=t.getConnectedDataURL({type:r,backgroundColor:n.get("backgroundColor",!0)||e.get("backgroundColor")||"#fff",connectedBackgroundColor:n.get("connectedBackgroundColor"),excludeComponents:n.get("excludeComponents"),pixelRatio:n.get("pixelRatio")}),s=rr.browser;if(jr(MouseEvent)&&(s.newEdge||!s.ie&&!s.edge)){var l=document.createElement("a");l.download=i+"."+r,l.target="_blank",l.href=o;var p=new MouseEvent("click",{view:document.defaultView,bubbles:!0,cancelable:!1});l.dispatchEvent(p)}else if(window.navigator.msSaveOrOpenBlob||a){var c=o.split(","),d=c[0].indexOf("base64")>-1,u=a?decodeURIComponent(c[1]):c[1];d&&(u=window.atob(u));var m=i+"."+r;if(window.navigator.msSaveOrOpenBlob){for(var h=u.length,g=new Uint8Array(h);h--;)g[h]=u.charCodeAt(h);var f=new Blob([g]);window.navigator.msSaveOrOpenBlob(f,m)}else{var y=document.createElement("iframe");document.body.appendChild(y);var v=y.contentWindow,x=v.document;x.open("image/svg+xml","replace"),x.write(u),x.close(),v.focus(),x.execCommand("SaveAs",!0,m),document.body.removeChild(y)}}else{var b=n.get("lang"),w='',S=window.open();S.document.write(w),S.document.title=i}},t.getDefaultOption=function(e){return{show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:e.getLocaleModel().get(["toolbox","saveAsImage","title"]),type:"png",connectedBackgroundColor:"#fff",name:"",excludeComponents:["toolbox"],lang:e.getLocaleModel().get(["toolbox","saveAsImage","lang"])}}}(YL);var JL="__ec_magicType_stack__",eV=[["line","bar"],["stack"]],tV=(function(e){function t(){return null!==e&&e.apply(this,arguments)||this}ze(t,e),t.prototype.getIcons=function(){var e=this.model,t=e.get("icon"),n={};return Rr(e.get("type"),(function(e){t[e]&&(n[e]=t[e])})),n},t.getDefaultOption=function(e){return{show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z"},title:e.getLocaleModel().get(["toolbox","magicType","title"]),option:{},seriesIndex:{}}},t.prototype.onclick=function(e,t,n){var i=this.model,a=i.get(["seriesIndex",n]);if(tV[n]){var r,o={series:[]};Rr(eV,(function(e){Dr(e,n)>=0&&Rr(e,(function(e){i.setIconStatus(e,"normal")}))})),i.setIconStatus(n,"emphasis"),e.eachComponent({mainType:"series",query:null==a?null:{seriesIndex:a}},(function(e){var t=e.subType,a=e.id,r=tV[n](t,a,e,i);r&&(Pr(r,e.option),o.series.push(r));var s=e.coordinateSystem;if(s&&"cartesian2d"===s.type&&("line"===n||"bar"===n)){var l=s.getAxesByScale("ordinal")[0];if(l){var p=l.dim+"Axis",c=e.getReferringComponents(p,Ed).models[0].componentIndex;o[p]=o[p]||[];for(var d=0;d<=c;d++)o[p][c]=o[p][c]||{};o[p][c].boundaryGap="bar"===n}}}));var s=n;"stack"===n&&(r=Er({stack:i.option.title.tiled,tiled:i.option.title.stack},i.option.title),"emphasis"!==i.get(["iconStatus",n])&&(s="tiled")),t.dispatchAction({type:"changeMagicType",currentType:s,newOption:o,newTitle:r,featureName:"magicType"})}}}(YL),{line:function(e,t,n,i){if("bar"===e)return Er({id:t,type:"line",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","line"])||{},!0)},bar:function(e,t,n,i){if("line"===e)return Er({id:t,type:"bar",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","bar"])||{},!0)},stack:function(e,t,n,i){var a=n.get("stack")===JL;if("line"===e||"bar"===e)return i.setIconStatus("stack",a?"normal":"emphasis"),Er({id:t,stack:a?"":JL},i.get(["option","stack"])||{},!0)}});s_({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},(function(e,t){t.mergeOption(e.newOption)}));var nV=new Array(60).join("-"),iV="\t";function aV(e){return e.replace(/^\s\s*/,"").replace(/\s\s*$/,"")}var rV=new RegExp("[\t]+","g");function oV(e,t){var n=e.split(new RegExp("\n*"+nV+"\n*","g")),i={series:[]};return Rr(n,(function(e,n){if(function(e){if(e.slice(0,e.indexOf("\n")).indexOf(iV)>=0)return!0}(e)){var a=function(e){for(var t=e.split(/\n+/g),n=[],i=Br(aV(t.shift()).split(rV),(function(e){return{name:e,data:[]}})),a=0;a=0)&&e(a,i._targetInfoList)}))}return e.prototype.setOutputRanges=function(e,t){return this.matchOutputRanges(e,t,(function(e,t,n){if((e.coordRanges||(e.coordRanges=[])).push(t),!e.coordRange){e.coordRange=t;var i=vV[e.brushType](0,n,t);e.__rangeOffset={offset:bV[e.brushType](i.values,e.range,[1,1]),xyMinMax:i.xyMinMax}}})),e},e.prototype.matchOutputRanges=function(e,t,n){Rr(e,(function(e){var i=this.findTargetInfo(e,t);i&&!0!==i&&Rr(i.coordSyses,(function(i){var a=vV[e.brushType](1,i,e.range,!0);n(e,a.values,i,t)}))}),this)},e.prototype.setInputRanges=function(e,t){Rr(e,(function(e){var n,i,a,r,o,s=this.findTargetInfo(e,t);if(e.range=e.range||[],s&&!0!==s){e.panelId=s.panelId;var l=vV[e.brushType](0,s.coordSys,e.coordRange),p=e.__rangeOffset;e.range=p?bV[e.brushType](l.values,p.offset,(n=l.xyMinMax,i=p.xyMinMax,a=SV(n),r=SV(i),o=[a[0]/r[0],a[1]/r[1]],isNaN(o[0])&&(o[0]=1),isNaN(o[1])&&(o[1]=1),o)):l.values}}),this)},e.prototype.makePanelOpts=function(e,t){return Br(this._targetInfoList,(function(n){var i=n.getPanelRect();return{panelId:n.panelId,defaultBrushType:t?t(n):null,clipPath:bF(i),isTargetByCursor:SF(i,e,n.coordSysModel),getLinearBrushOtherExtent:wF(i)}}))},e.prototype.controlSeries=function(e,t,n){var i=this.findTargetInfo(e,n);return!0===i||i&&Dr(i.coordSyses,t.coordinateSystem)>=0},e.prototype.findTargetInfo=function(e,t){for(var n=this._targetInfoList,i=hV(t,e),a=0;ae[1]&&e.reverse(),e}function hV(e,t){return Td(e,t,{includeMainTypes:dV})}var gV={grid:function(e,t){var n=e.xAxisModels,i=e.yAxisModels,a=e.gridModels,r=fo(),o={},s={};(n||i||a)&&(Rr(n,(function(e){var t=e.axis.grid.model;r.set(t.id,t),o[t.id]=!0})),Rr(i,(function(e){var t=e.axis.grid.model;r.set(t.id,t),s[t.id]=!0})),Rr(a,(function(e){r.set(e.id,e),o[e.id]=!0,s[e.id]=!0})),r.each((function(e){var a=e.coordinateSystem,r=[];Rr(a.getCartesians(),(function(e,t){(Dr(n,e.getAxis("x").model)>=0||Dr(i,e.getAxis("y").model)>=0)&&r.push(e)})),t.push({panelId:"grid--"+e.id,gridModel:e,coordSysModel:e,coordSys:r[0],coordSyses:r,getPanelRect:yV.grid,xAxisDeclared:o[e.id],yAxisDeclared:s[e.id]})})))},geo:function(e,t){Rr(e.geoModels,(function(e){var n=e.coordinateSystem;t.push({panelId:"geo--"+e.id,geoModel:e,coordSysModel:e,coordSys:n,coordSyses:[n],getPanelRect:yV.geo})}))}},fV=[function(e,t){var n=e.xAxisModel,i=e.yAxisModel,a=e.gridModel;return!a&&n&&(a=n.axis.grid.model),!a&&i&&(a=i.axis.grid.model),a&&a===t.gridModel},function(e,t){var n=e.geoModel;return n&&n===t.geoModel}],yV={grid:function(){return this.coordSys.master.getRect().clone()},geo:function(){var e=this.coordSys,t=e.getBoundingRect().clone();return t.applyTransform(_f(e)),t}},vV={lineX:zr(xV,0),lineY:zr(xV,1),rect:function(e,t,n,i){var a=e?t.pointToData([n[0][0],n[1][0]],i):t.dataToPoint([n[0][0],n[1][0]],i),r=e?t.pointToData([n[0][1],n[1][1]],i):t.dataToPoint([n[0][1],n[1][1]],i),o=[mV([a[0],r[0]]),mV([a[1],r[1]])];return{values:o,xyMinMax:o}},polygon:function(e,t,n,i){var a=[[1/0,-1/0],[1/0,-1/0]];return{values:Br(n,(function(n){var r=e?t.pointToData(n,i):t.dataToPoint(n,i);return a[0][0]=Math.min(a[0][0],r[0]),a[1][0]=Math.min(a[1][0],r[1]),a[0][1]=Math.max(a[0][1],r[0]),a[1][1]=Math.max(a[1][1],r[1]),r})),xyMinMax:a}}};function xV(e,t,n,i){var a=n.getAxis(["x","y"][e]),r=mV(Br([0,1],(function(e){return t?a.coordToData(a.toLocalCoord(i[e]),!0):a.toGlobalCoord(a.dataToCoord(i[e]))}))),o=[];return o[e]=r,o[1-e]=[NaN,NaN],{values:r,xyMinMax:o}}var bV={lineX:zr(wV,0),lineY:zr(wV,1),rect:function(e,t,n){return[[e[0][0]-n[0]*t[0][0],e[0][1]-n[0]*t[0][1]],[e[1][0]-n[1]*t[1][0],e[1][1]-n[1]*t[1][1]]]},polygon:function(e,t,n){return Br(e,(function(e,i){return[e[0]-n[0]*t[i][0],e[1]-n[1]*t[i][1]]}))}};function wV(e,t,n,i){return[t[0]-i[e]*n[0],t[1]-i[e]*n[1]]}function SV(e){return e?[e[0][1]-e[0][0],e[1][1]-e[1][0]]:[NaN,NaN]}var CV,_V,TV=Rr,IV=cd+"toolbox-dataZoom_",EV=(function(e){function t(){return null!==e&&e.apply(this,arguments)||this}ze(t,e),t.prototype.render=function(e,t,n,i){this._brushController||(this._brushController=new UA(n.getZr()),this._brushController.on("brush",Gr(this._onBrush,this)).mount()),function(e,t,n,i,a){var r=n._isZoomActive;i&&"takeGlobalCursor"===i.type&&(r="dataZoomSelect"===i.key&&i.dataZoomSelectActive);n._isZoomActive=r,e.setIconStatus("zoom",r?"emphasis":"normal");var o=new uV(MV(e),t,{include:["grid"]}),s=o.makePanelOpts(a,(function(e){return e.xAxisDeclared&&!e.yAxisDeclared?"lineX":!e.xAxisDeclared&&e.yAxisDeclared?"lineY":"rect"}));n._brushController.setPanels(s).enableBrush(!(!r||!s.length)&&{brushType:"auto",brushStyle:e.getModel("brushStyle").getItemStyle()})}(e,t,this,i,n),function(e,t){e.setIconStatus("back",function(e){return cV(e).length}(t)>1?"emphasis":"normal")}(e,t)},t.prototype.onclick=function(e,t,n){EV[n].call(this)},t.prototype.remove=function(e,t){this._brushController&&this._brushController.unmount()},t.prototype.dispose=function(e,t){this._brushController&&this._brushController.dispose()},t.prototype._onBrush=function(e){var t=e.areas;if(e.isEnd&&t.length){var n={},i=this.ecModel;this._brushController.updateCovers([]),new uV(MV(this.model),i,{include:["grid"]}).matchOutputRanges(t,i,(function(e,t,n){if("cartesian2d"===n.type){var i=e.brushType;"rect"===i?(a("x",n,t[0]),a("y",n,t[1])):a({lineX:"x",lineY:"y"}[i],n,t)}})),function(e,t){var n=cV(e);lV(t,(function(t,i){for(var a=n.length-1;a>=0&&!n[a][i];a--);if(a<0){var r=e.queryComponents({mainType:"dataZoom",subType:"select",id:i})[0];if(r){var o=r.getPercentRange();n[0][i]={dataZoomId:i,start:o[0],end:o[1]}}}})),n.push(t)}(i,n),this._dispatchZoomAction(n)}function a(e,t,a){var r=t.getAxis(e),o=r.model,s=function(e,t,n){var i;return n.eachComponent({mainType:"dataZoom",subType:"select"},(function(n){n.getAxisModel(e,t.componentIndex)&&(i=n)})),i}(e,o,i),l=s.findRepresentativeAxisProxy(o);if(l){var p=l.getMinMaxSpan();null==p.minValueSpan&&null==p.maxValueSpan||(a=bA(0,a.slice(),r.scale.getExtent(),0,p.minValueSpan,p.maxValueSpan))}s&&(n[s.id]={dataZoomId:s.id,startValue:a[0],endValue:a[1]})}},t.prototype._dispatchZoomAction=function(e){var t=[];TV(e,(function(e,n){t.push(Ir(e))})),t.length&&this.api.dispatchAction({type:"dataZoom",from:this.uid,batch:t})},t.getDefaultOption=function(e){return{show:!0,filterMode:"filter",icon:{zoom:"M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1",back:"M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26"},title:e.getLocaleModel().get(["toolbox","dataZoom","title"]),brushStyle:{borderWidth:0,color:"rgba(210,219,238,0.2)"}}}}(YL),{zoom:function(){var e=!this._isZoomActive;this.api.dispatchAction({type:"takeGlobalCursor",key:"dataZoomSelect",dataZoomSelectActive:e})},back:function(){this._dispatchZoomAction(function(e){var t=cV(e),n=t[t.length-1];t.length>1&&t.pop();var i={};return lV(n,(function(e,n){for(var a=t.length-1;a>=0;a--)if(e=t[a][n]){i[n]=e;break}})),i}(this.ecModel))}});function MV(e){var t={xAxisIndex:e.get("xAxisIndex",!0),yAxisIndex:e.get("yAxisIndex",!0),xAxisId:e.get("xAxisId",!0),yAxisId:e.get("yAxisId",!0)};return null==t.xAxisIndex&&null==t.xAxisId&&(t.xAxisIndex="all"),null==t.yAxisIndex&&null==t.yAxisId&&(t.yAxisIndex="all"),t}CV="dataZoom",_V=function(e){var t=e.getComponent("toolbox",0),n=["feature","dataZoom"];if(t&&null!=t.get(n)){var i=t.getModel(n),a=[],r=Td(e,MV(i));return TV(r.xAxisModels,(function(e){return o(e,"xAxis","xAxisIndex")})),TV(r.yAxisModels,(function(e){return o(e,"yAxis","yAxisIndex")})),a}function o(e,t,n){var r=e.componentIndex,o={type:"select",$fromToolbox:!0,filterMode:i.get("filterMode",!0)||"filter",id:IV+t+r};o[n]=r,a.push(o)}},so(null==jv.get(CV)&&_V),jv.set(CV,_V);var kV=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="tooltip",t.dependencies=["axisPointer"],t.defaultOption={z:60,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",renderMode:"auto",confine:null,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"#fff",shadowBlur:10,shadowColor:"rgba(0, 0, 0, .2)",shadowOffsetX:1,shadowOffsetY:2,borderRadius:4,borderWidth:1,padding:null,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#666",fontSize:14}},t}(Sv);function PV(e){var t=e.get("confine");return null!=t?!!t:"richText"===e.get("renderMode")}function DV(e){if(rr.domSupported)for(var t=document.documentElement.style,n=0,i=e.length;n-1?(p+="top:50%",c+="translateY(-50%) rotate("+(o="left"===s?-225:-45)+"deg)"):(p+="left:50%",c+="translateX(-50%) rotate("+(o="top"===s?225:45)+"deg)");var d=o*Math.PI/180,u=l+a,m=u*Math.abs(Math.cos(d))+u*Math.abs(Math.sin(d)),h=t+" solid "+a+"px;";return'
'}(n,i,a)),Hr(e))r.innerHTML=e+o;else if(e){r.innerHTML="",Ur(e)||(e=[e]);for(var s=0;s=0?this._tryShow(n,i):"leave"===t&&this._hide(i))}),this))},t.prototype._keepShow=function(){var e=this._tooltipModel,t=this._ecModel,n=this._api,i=e.get("triggerOn");if(null!=this._lastX&&null!=this._lastY&&"none"!==i&&"click"!==i){var a=this;clearTimeout(this._refreshUpdateTimeout),this._refreshUpdateTimeout=setTimeout((function(){!n.isDisposed()&&a.manuallyShowTip(e,t,n,{x:a._lastX,y:a._lastY,dataByCoordSys:a._lastDataByCoordSys})}))}},t.prototype.manuallyShowTip=function(e,t,n,i){if(i.from!==this.uid&&!rr.node&&n.getDom()){var a=KV(i,n);this._ticket="";var r=i.dataByCoordSys,o=function(e,t,n){var i=Id(e).queryOptionMap,a=i.keys()[0];if(!a||"series"===a)return;var r=kd(t,a,i.get(a),{useDefault:!1,enableAll:!1,enableNone:!1}),o=r.models[0];if(!o)return;var s,l=n.getViewOfComponentModel(o);if(l.group.traverse((function(t){var n=Um(t).tooltipConfig;if(n&&n.name===e.name)return s=t,!0})),s)return{componentMainType:a,componentIndex:o.componentIndex,el:s}}(i,t,n);if(o){var s=o.el.getBoundingRect().clone();s.applyTransform(o.el.transform),this._tryShow({offsetX:s.x+s.width/2,offsetY:s.y+s.height/2,target:o.el,position:i.position,positionDefault:"bottom"},a)}else if(i.tooltip&&null!=i.x&&null!=i.y){var l=HV;l.x=i.x,l.y=i.y,l.update(),Um(l).tooltipConfig={name:null,option:i.tooltip},this._tryShow({offsetX:i.x,offsetY:i.y,target:l},a)}else if(r)this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,dataByCoordSys:r,tooltipOption:i.tooltipOption},a);else if(null!=i.seriesIndex){if(this._manuallyAxisShowTip(e,t,n,i))return;var p=bN(i,t),c=p.point[0],d=p.point[1];null!=c&&null!=d&&this._tryShow({offsetX:c,offsetY:d,target:p.el,position:i.position,positionDefault:"bottom"},a)}else null!=i.x&&null!=i.y&&(n.dispatchAction({type:"updateAxisPointer",x:i.x,y:i.y}),this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,target:n.getZr().findHover(i.x,i.y).target},a))}},t.prototype.manuallyHideTip=function(e,t,n,i){var a=this._tooltipContent;this._tooltipModel&&a.hideLater(this._tooltipModel.get("hideDelay")),this._lastX=this._lastY=this._lastDataByCoordSys=null,i.from!==this.uid&&this._hide(KV(i,n))},t.prototype._manuallyAxisShowTip=function(e,t,n,i){var a=i.seriesIndex,r=i.dataIndex,o=t.getComponent("axisPointer").coordSysAxesInfo;if(null!=a&&null!=r&&null!=o){var s=t.getSeriesByIndex(a);if(s)if("axis"===$V([s.getData().getItemModel(r),s,(s.coordinateSystem||{}).model],this._tooltipModel).get("trigger"))return n.dispatchAction({type:"updateAxisPointer",seriesIndex:a,dataIndex:r,position:i.position}),!0}},t.prototype._tryShow=function(e,t){var n=e.target;if(this._tooltipModel){this._lastX=e.offsetX,this._lastY=e.offsetY;var i=e.dataByCoordSys;if(i&&i.length)this._showAxisTooltip(i,e);else if(n){var a,r;if("legend"===Um(n).ssrType)return;this._lastDataByCoordSys=null,vS(n,(function(e){return null!=Um(e).dataIndex?(a=e,!0):null!=Um(e).tooltipConfig?(r=e,!0):void 0}),!0),a?this._showSeriesItemTooltip(e,a,t):r?this._showComponentItemTooltip(e,r,t):this._hide(t)}else this._lastDataByCoordSys=null,this._hide(t)}},t.prototype._showOrMove=function(e,t){var n=e.get("showDelay");t=Gr(t,this),clearTimeout(this._showTimout),n>0?this._showTimout=setTimeout(t,n):t()},t.prototype._showAxisTooltip=function(e,t){var n=this._ecModel,i=this._tooltipModel,a=[t.offsetX,t.offsetY],r=$V([t.tooltipOption],i),o=this._renderMode,s=[],l=jb("section",{blocks:[],noHeader:!0}),p=[],c=new tw;Rr(e,(function(e){Rr(e.dataByAxis,(function(e){var t=n.getComponent(e.axisDim+"Axis",e.axisIndex),a=e.value;if(t&&t.axis&&null!=a){var r=nN(a,t.axis,n,e.seriesDataIndices,e.valueLabelOpt),d=jb("section",{header:r,noHeader:!lo(r),sortBlocks:!0,blocks:[]});l.blocks.push(d),Rr(e.seriesDataIndices,(function(l){var u=n.getSeriesByIndex(l.seriesIndex),m=l.dataIndexInside,h=u.getDataParams(m);if(!(h.dataIndex<0)){h.axisDim=e.axisDim,h.axisIndex=e.axisIndex,h.axisType=e.axisType,h.axisId=e.axisId,h.axisValue=XT(t.axis,{value:a}),h.axisValueLabel=r,h.marker=c.makeTooltipMarker("item",lv(h.color),o);var g=ob(u.formatTooltip(m,!0,null)),f=g.frag;if(f){var y=$V([u],i).get("valueFormatter");d.blocks.push(y?kr({valueFormatter:y},f):f)}g.text&&p.push(g.text),s.push(h)}}))}}))})),l.blocks.reverse(),p.reverse();var d=t.position,u=r.get("order"),m=Xb(l,c,o,u,n.get("useUTC"),r.get("textStyle"));m&&p.unshift(m);var h="richText"===o?"\n\n":"
",g=p.join(h);this._showOrMove(r,(function(){this._updateContentNotChangedOnAxis(e,s)?this._updatePosition(r,d,a[0],a[1],this._tooltipContent,s):this._showTooltipContent(r,g,s,Math.random()+"",a[0],a[1],d,null,c)}))},t.prototype._showSeriesItemTooltip=function(e,t,n){var i=this._ecModel,a=Um(t),r=a.seriesIndex,o=i.getSeriesByIndex(r),s=a.dataModel||o,l=a.dataIndex,p=a.dataType,c=s.getData(p),d=this._renderMode,u=e.positionDefault,m=$V([c.getItemModel(l),s,o&&(o.coordinateSystem||{}).model],this._tooltipModel,u?{position:u}:null),h=m.get("trigger");if(null==h||"item"===h){var g=s.getDataParams(l,p),f=new tw;g.marker=f.makeTooltipMarker("item",lv(g.color),d);var y=ob(s.formatTooltip(l,!1,p)),v=m.get("order"),x=m.get("valueFormatter"),b=y.frag,w=b?Xb(x?kr({valueFormatter:x},b):b,f,d,v,i.get("useUTC"),m.get("textStyle")):y.text,S="item_"+s.name+"_"+l;this._showOrMove(m,(function(){this._showTooltipContent(m,w,g,S,e.offsetX,e.offsetY,e.position,e.target,f)})),n({type:"showTip",dataIndexInside:l,dataIndex:c.getRawIndex(l),seriesIndex:r,from:this.uid})}},t.prototype._showComponentItemTooltip=function(e,t,n){var i=Um(t),a=i.tooltipConfig.option||{};if(Hr(a)){a={content:a,formatter:a}}var r=[a],o=this._ecModel.getComponent(i.componentMainType,i.componentIndex);o&&r.push(o),r.push({formatter:a.content});var s=e.positionDefault,l=$V(r,this._tooltipModel,s?{position:s}:null),p=l.get("content"),c=Math.random()+"",d=new tw;this._showOrMove(l,(function(){var n=Ir(l.get("formatterParams")||{});this._showTooltipContent(l,p,n,c,e.offsetX,e.offsetY,e.position,t,d)})),n({type:"showTip",from:this.uid})},t.prototype._showTooltipContent=function(e,t,n,i,a,r,o,s,l){if(this._ticket="",e.get("showContent")&&e.get("show")){var p=this._tooltipContent;p.setEnterable(e.get("enterable"));var c=e.get("formatter");o=o||e.get("position");var d=t,u=this._getNearestPoint([a,r],n,e.get("trigger"),e.get("borderColor")).color;if(c)if(Hr(c)){var m=e.ecModel.get("useUTC"),h=Ur(n)?n[0]:n;d=c,h&&h.axisType&&h.axisType.indexOf("time")>=0&&(d=Ny(h.axisValue,d,m)),d=ov(d,n,!0)}else if(jr(c)){var g=Gr((function(t,i){t===this._ticket&&(p.setContent(i,l,e,u,o),this._updatePosition(e,o,a,r,p,n,s))}),this);this._ticket=i,d=c(n,i,g)}else d=c;p.setContent(d,l,e,u,o),p.show(e,u),this._updatePosition(e,o,a,r,p,n,s)}},t.prototype._getNearestPoint=function(e,t,n,i){return"axis"===n||Ur(t)?{color:i||("html"===this._renderMode?"#fff":"none")}:Ur(t)?void 0:{color:i||t.color||t.borderColor}},t.prototype._updatePosition=function(e,t,n,i,a,r,o){var s=this._api.getWidth(),l=this._api.getHeight();t=t||e.get("position");var p=a.getSize(),c=e.get("align"),d=e.get("verticalAlign"),u=o&&o.getBoundingRect().clone();if(o&&u.applyTransform(o.transform),jr(t)&&(t=t([n,i],r,a.el,u,{viewSize:[s,l],contentSize:p.slice()})),Ur(t))n=Vc(t[0],s),i=Vc(t[1],l);else if(Kr(t)){var m=t;m.width=p[0],m.height=p[1];var h=gv(m,{width:s,height:l});n=h.x,i=h.y,c=null,d=null}else if(Hr(t)&&o){var g=function(e,t,n,i){var a=n[0],r=n[1],o=Math.ceil(Math.SQRT2*i)+8,s=0,l=0,p=t.width,c=t.height;switch(e){case"inside":s=t.x+p/2-a/2,l=t.y+c/2-r/2;break;case"top":s=t.x+p/2-a/2,l=t.y-r-o;break;case"bottom":s=t.x+p/2-a/2,l=t.y+c+o;break;case"left":s=t.x-a-o,l=t.y+c/2-r/2;break;case"right":s=t.x+p+o,l=t.y+c/2-r/2}return[s,l]}(t,u,p,e.get("borderWidth"));n=g[0],i=g[1]}else{g=function(e,t,n,i,a,r,o){var s=n.getSize(),l=s[0],p=s[1];null!=r&&(e+l+r+2>i?e-=l+r:e+=r);null!=o&&(t+p+o>a?t-=p+o:t+=o);return[e,t]}(n,i,a,s,l,c?null:20,d?null:20);n=g[0],i=g[1]}if(c&&(n-=YV(c)?p[0]/2:"right"===c?p[0]:0),d&&(i-=YV(d)?p[1]/2:"bottom"===d?p[1]:0),PV(e)){g=function(e,t,n,i,a){var r=n.getSize(),o=r[0],s=r[1];return e=Math.min(e+o,i)-o,t=Math.min(t+s,a)-s,e=Math.max(e,0),t=Math.max(t,0),[e,t]}(n,i,a,s,l);n=g[0],i=g[1]}a.moveTo(n,i)},t.prototype._updateContentNotChangedOnAxis=function(e,t){var n=this._lastDataByCoordSys,i=this._cbParamsList,a=!!n&&n.length===e.length;return a&&Rr(n,(function(n,r){var o=n.dataByAxis||[],s=(e[r]||{}).dataByAxis||[];(a=a&&o.length===s.length)&&Rr(o,(function(e,n){var r=s[n]||{},o=e.seriesDataIndices||[],l=r.seriesDataIndices||[];(a=a&&e.value===r.value&&e.axisType===r.axisType&&e.axisId===r.axisId&&o.length===l.length)&&Rr(o,(function(e,t){var n=l[t];a=a&&e.seriesIndex===n.seriesIndex&&e.dataIndex===n.dataIndex})),i&&Rr(e.seriesDataIndices,(function(e){var n=e.seriesIndex,r=t[n],o=i[n];r&&o&&o.data!==r.data&&(a=!1)}))}))})),this._lastDataByCoordSys=e,this._cbParamsList=t,!!a},t.prototype._hide=function(e){this._lastDataByCoordSys=null,e({type:"hideTip",from:this.uid})},t.prototype.dispose=function(e,t){!rr.node&&t.getDom()&&(kw(this,"_updatePosition"),this._tooltipContent.dispose(),vN("itemTooltip",t))},t.type="tooltip",t}(hw);function $V(e,t,n){var i,a=t.ecModel;n?(i=new uy(n,a,a),i=new uy(t.option,i,a)):i=t;for(var r=e.length-1;r>=0;r--){var o=e[r];o&&(o instanceof uy&&(o=o.get("tooltip",!0)),Hr(o)&&(o={formatter:o}),o&&(i=new uy(o,i,a)))}return i}function KV(e,t){return e.dispatchAction||Gr(t.dispatchAction,t)}function YV(e){return"center"===e||"middle"===e}function XV(e){aI(MN),e.registerComponentModel(kV),e.registerComponentView(WV),e.registerAction({type:"showTip",event:"showTip",update:"tooltip:manuallyShowTip"},wo),e.registerAction({type:"hideTip",event:"hideTip",update:"tooltip:manuallyHideTip"},wo)}var ZV=Rr;function QV(e){if(e)for(var t in e)if(e.hasOwnProperty(t))return!0}function JV(e,t,n){var i={};return ZV(t,(function(t){var a,r=i[t]=((a=function(){}).prototype.__hidden=a.prototype,new a);ZV(e[t],(function(e,i){if(aO.isValidType(i)){var a={type:i,visual:e};n&&n(a,t),r[i]=new aO(a),"opacity"===i&&((a=Ir(a)).type="colorAlpha",r.__hidden.__alphaForOpacity=new aO(a))}}))})),i}function eq(e,t,n){var i;Rr(n,(function(e){t.hasOwnProperty(e)&&QV(t[e])&&(i=!0)})),i&&Rr(n,(function(n){t.hasOwnProperty(n)&&QV(t[n])?e[n]=Ir(t[n]):delete e[n]}))}tq(0),tq(1);function tq(e){var t=["x","y"],n=["width","height"];return{point:function(t,n,i){if(t){var a=i.range;return nq(t[e],a)}},rect:function(i,a,r){if(i){var o=r.range,s=[i[t[e]],i[t[e]]+i[n[e]]];return s[1]=t&&(e=t-1),e<0&&(e=0)),this.option.currentIndex=e},t.prototype.getCurrentIndex=function(){return this.option.currentIndex},t.prototype.isIndexMax=function(){return this.getCurrentIndex()>=this._data.count()-1},t.prototype.setPlayState=function(e){this.option.autoPlay=!!e},t.prototype.getPlayState=function(){return!!this.option.autoPlay},t.prototype._initData=function(){var e,t=this.option,n=t.data||[],i=t.axisType,a=this._names=[];"category"===i?(e=[],Rr(n,(function(t,n){var i,r=xd(hd(t),"");Kr(t)?(i=Ir(t)).value=n:i=n,e.push(i),a.push(r)}))):e=n;var r={category:"ordinal",time:"time",value:"number"}[i]||"number";(this._data=new V_([{name:"value",type:r}],this)).initData(e,a)},t.prototype.getData=function(){return this._data},t.prototype.getCategories=function(){if("category"===this.get("axisType"))return this._names.slice()},t.type="timeline",t.defaultOption={z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},t}(Sv),sq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="timeline.slider",t.defaultOption=gy(oq.defaultOption,{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"circle",symbolSize:12,lineStyle:{show:!0,width:2,color:"#DAE1F5"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#A4B1D7"},itemStyle:{color:"#A4B1D7",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:15,color:"#316bf3",borderColor:"#fff",borderWidth:2,shadowBlur:2,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0, 0, 0, 0.3)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:24,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z",prevIcon:"M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z",prevBtnSize:18,nextBtnSize:18,color:"#A4B1D7",borderColor:"#A4B1D7",borderWidth:1},emphasis:{label:{show:!0,color:"#6f778d"},itemStyle:{color:"#316BF3"},controlStyle:{color:"#316BF3",borderColor:"#316BF3",borderWidth:2}},progress:{lineStyle:{color:"#316BF3"},itemStyle:{color:"#316BF3"},label:{color:"#6f778d"}},data:[]}),t}(oq);Ar(sq,rb.prototype);var lq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="timeline",t}(hw),pq=function(e){function t(t,n,i,a){var r=e.call(this,t,n,i)||this;return r.type=a||"value",r}return ze(t,e),t.prototype.getLabelModel=function(){return this.model.getModel("label")},t.prototype.isHorizontal=function(){return"horizontal"===this.model.get("orient")},t}(MI),cq=Math.PI,dq=Cd();!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.init=function(e,t){this.api=t},t.prototype.render=function(e,t,n){if(this.model=e,this.api=n,this.ecModel=t,this.group.removeAll(),e.get("show",!0)){var i=this._layout(e,n),a=this._createGroup("_mainGroup"),r=this._createGroup("_labelGroup"),o=this._axis=this._createAxis(i,e);e.formatTooltip=function(e){return jb("nameValue",{noName:!0,value:o.scale.getLabel({value:e})})},Rr(["AxisLine","AxisTick","Control","CurrentPointer"],(function(t){this["_render"+t](i,a,o,e)}),this),this._renderAxisLabel(i,r,o,e),this._position(i,e)}this._doPlayStop(),this._updateTicksStatus()},t.prototype.remove=function(){this._clearTimer(),this.group.removeAll()},t.prototype.dispose=function(){this._clearTimer()},t.prototype._layout=function(e,t){var n,i,a,r,o=e.get(["label","position"]),s=e.get("orient"),l=function(e,t){return gv(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()},e.get("padding"))}(e,t),p={horizontal:"center",vertical:(n=null==o||"auto"===o?"horizontal"===s?l.y+l.height/2=0||"+"===n?"left":"right"},c={horizontal:n>=0||"+"===n?"top":"bottom",vertical:"middle"},d={horizontal:0,vertical:cq/2},u="vertical"===s?l.height:l.width,m=e.getModel("controlStyle"),h=m.get("show",!0),g=h?m.get("itemSize"):0,f=h?m.get("itemGap"):0,y=g+f,v=e.get(["label","rotate"])||0;v=v*cq/180;var x=m.get("position",!0),b=h&&m.get("showPlayBtn",!0),w=h&&m.get("showPrevBtn",!0),S=h&&m.get("showNextBtn",!0),C=0,_=u;"left"===x||"bottom"===x?(b&&(i=[0,0],C+=y),w&&(a=[C,0],C+=y),S&&(r=[_-g,0],_-=y)):(b&&(i=[_-g,0],_-=y),w&&(a=[0,0],C+=y),S&&(r=[_-g,0],_-=y));var T=[C,_];return e.get("inverse")&&T.reverse(),{viewRect:l,mainLength:u,orient:s,rotation:d[s],labelRotation:v,labelPosOpt:n,labelAlign:e.get(["label","align"])||p[s],labelBaseline:e.get(["label","verticalAlign"])||e.get(["label","baseline"])||c[s],playPosition:i,prevBtnPosition:a,nextBtnPosition:r,axisExtent:T,controlSize:g,controlGap:f}},t.prototype._position=function(e,t){var n=this._mainGroup,i=this._labelGroup,a=e.viewRect;if("vertical"===e.orient){var r=[1,0,0,1,0,0],o=a.x,s=a.y+a.height;fs(r,r,[-o,-s]),ys(r,r,-cq/2),fs(r,r,[o,s]),(a=a.clone()).applyTransform(r)}var l=f(a),p=f(n.getBoundingRect()),c=f(i.getBoundingRect()),d=[n.x,n.y],u=[i.x,i.y];u[0]=d[0]=l[0][0];var m,h=e.labelPosOpt;null==h||Hr(h)?(y(d,p,l,1,m="+"===h?0:1),y(u,c,l,1,1-m)):(y(d,p,l,1,m=h>=0?0:1),u[1]=d[1]+h);function g(e){e.originX=l[0][0]-e.x,e.originY=l[1][0]-e.y}function f(e){return[[e.x,e.x+e.width],[e.y,e.y+e.height]]}function y(e,t,n,i,a){e[i]+=n[i][a]-t[i][a]}n.setPosition(d),i.setPosition(u),n.rotation=i.rotation=e.rotation,g(n),g(i)},t.prototype._createAxis=function(e,t){var n=t.getData(),i=t.get("axisType"),a=function(e,t){if(t=t||e.get("type"),t)switch(t){case"category":return new sT({ordinalMeta:e.getCategories(),extent:[1/0,-1/0]});case"time":return new CT({locale:e.ecModel.getLocaleModel(),useUTC:e.ecModel.get("useUTC")});default:return new pT}}(t,i);a.getTicks=function(){return n.mapArray(["value"],(function(e){return{value:e}}))};var r=n.getDataExtent("value");a.setExtent(r[0],r[1]),a.calcNiceTicks();var o=new pq("value",a,e.axisExtent,i);return o.model=t,o},t.prototype._createGroup=function(e){var t=this[e]=new Mc;return this.group.add(t),t},t.prototype._renderAxisLine=function(e,t,n,i){var a=n.getExtent();if(i.get(["lineStyle","show"])){var r=new Fg({shape:{x1:a[0],y1:0,x2:a[1],y2:0},style:kr({lineCap:"round"},i.getModel("lineStyle").getLineStyle()),silent:!0,z2:1});t.add(r);var o=this._progressLine=new Fg({shape:{x1:a[0],x2:this._currentPointer?this._currentPointer.x:a[0],y1:0,y2:0},style:Pr({lineCap:"round",lineWidth:r.style.lineWidth},i.getModel(["progress","lineStyle"]).getLineStyle()),silent:!0,z2:1});t.add(o)}},t.prototype._renderAxisTick=function(e,t,n,i){var a=this,r=i.getData(),o=n.scale.getTicks();this._tickSymbols=[],Rr(o,(function(e){var o=n.dataToCoord(e.value),s=r.getItemModel(e.value),l=s.getModel("itemStyle"),p=s.getModel(["emphasis","itemStyle"]),c=s.getModel(["progress","itemStyle"]),d={x:o,y:0,onclick:Gr(a._changeTimeline,a,e.value)},u=uq(s,l,t,d);u.ensureState("emphasis").style=p.getItemStyle(),u.ensureState("progress").style=c.getItemStyle(),Dh(u);var m=Um(u);s.get("tooltip")?(m.dataIndex=e.value,m.dataModel=i):m.dataIndex=m.dataModel=null,a._tickSymbols.push(u)}))},t.prototype._renderAxisLabel=function(e,t,n,i){var a=this;if(n.getLabelModel().get("show")){var r=i.getData(),o=n.getViewLabels();this._tickLabels=[],Rr(o,(function(i){var o=i.tickValue,s=r.getItemModel(o),l=s.getModel("label"),p=s.getModel(["emphasis","label"]),c=s.getModel(["progress","label"]),d=n.dataToCoord(i.tickValue),u=new Pm({x:d,y:0,rotation:e.labelRotation-e.rotation,onclick:Gr(a._changeTimeline,a,o),silent:!1,style:jf(l,{text:i.formattedLabel,align:e.labelAlign,verticalAlign:e.labelBaseline})});u.ensureState("emphasis").style=jf(p),u.ensureState("progress").style=jf(c),t.add(u),Dh(u),dq(u).dataIndex=o,a._tickLabels.push(u)}))}},t.prototype._renderControl=function(e,t,n,i){var a=e.controlSize,r=e.rotation,o=i.getModel("controlStyle").getItemStyle(),s=i.getModel(["emphasis","controlStyle"]).getItemStyle(),l=i.getPlayState(),p=i.get("inverse",!0);function c(e,n,l,p){if(e){var c=fc(io(i.get(["controlStyle",n+"BtnSize"]),a),a),d=function(e,t,n,i){var a=i.style,r=Df(e.get(["controlStyle",t]),i||{},new Ps(n[0],n[1],n[2],n[3]));a&&r.setStyle(a);return r}(i,n+"Icon",[0,-c/2,c,c],{x:e[0],y:e[1],originX:a/2,originY:0,rotation:p?-r:0,rectHover:!0,style:o,onclick:l});d.ensureState("emphasis").style=s,t.add(d),Dh(d)}}c(e.nextBtnPosition,"next",Gr(this._changeTimeline,this,p?"-":"+")),c(e.prevBtnPosition,"prev",Gr(this._changeTimeline,this,p?"+":"-")),c(e.playPosition,l?"stop":"play",Gr(this._handlePlayClick,this,!l),!0)},t.prototype._renderCurrentPointer=function(e,t,n,i){var a=i.getData(),r=i.getCurrentIndex(),o=a.getItemModel(r).getModel("checkpointStyle"),s=this,l={onCreate:function(e){e.draggable=!0,e.drift=Gr(s._handlePointerDrag,s),e.ondragend=Gr(s._handlePointerDragend,s),mq(e,s._progressLine,r,n,i,!0)},onUpdate:function(e){mq(e,s._progressLine,r,n,i)}};this._currentPointer=uq(o,o,this._mainGroup,{},this._currentPointer,l)},t.prototype._handlePlayClick=function(e){this._clearTimer(),this.api.dispatchAction({type:"timelinePlayChange",playState:e,from:this.uid})},t.prototype._handlePointerDrag=function(e,t,n){this._clearTimer(),this._pointerChangeTimeline([n.offsetX,n.offsetY])},t.prototype._handlePointerDragend=function(e){this._pointerChangeTimeline([e.offsetX,e.offsetY],!0)},t.prototype._pointerChangeTimeline=function(e,t){var n=this._toAxisCoord(e)[0],i=Gc(this._axis.getExtent().slice());n>i[1]&&(n=i[1]),n=0&&(o[r]=+o[r].toFixed(d)),[o,c]}var bq={min:zr(xq,"min"),max:zr(xq,"max"),average:zr(xq,"average"),median:zr(xq,"median")};function wq(e,t){if(t){var n=e.getData(),i=e.coordinateSystem,a=i&&i.dimensions;if(!function(e){return!isNaN(parseFloat(e.x))&&!isNaN(parseFloat(e.y))}(t)&&!Ur(t.coord)&&Ur(a)){var r=Sq(t,n,i,e);if((t=Ir(t)).type&&bq[t.type]&&r.baseAxis&&r.valueAxis){var o=Dr(a,r.baseAxis.dim),s=Dr(a,r.valueAxis.dim),l=bq[t.type](n,r.baseDataDim,r.valueDataDim,o,s);t.coord=l[0],t.value=l[1]}else t.coord=[null!=t.xAxis?t.xAxis:t.radiusAxis,null!=t.yAxis?t.yAxis:t.angleAxis]}if(null!=t.coord&&Ur(a))for(var p=t.coord,c=0;c<2;c++)bq[p[c]]&&(p[c]=Tq(n,n.mapDimension(a[c]),p[c]));else t.coord=[];return t}}function Sq(e,t,n,i){var a={};return null!=e.valueIndex||null!=e.valueDim?(a.valueDataDim=null!=e.valueIndex?t.getDimension(e.valueIndex):e.valueDim,a.valueAxis=n.getAxis(function(e,t){var n=e.getData().getDimensionInfo(t);return n&&n.coordDim}(i,a.valueDataDim)),a.baseAxis=n.getOtherAxis(a.valueAxis),a.baseDataDim=t.mapDimension(a.baseAxis.dim)):(a.baseAxis=i.getBaseAxis(),a.valueAxis=n.getOtherAxis(a.baseAxis),a.baseDataDim=t.mapDimension(a.baseAxis.dim),a.valueDataDim=t.mapDimension(a.valueAxis.dim)),a}function Cq(e,t){return!(e&&e.containData&&t.coord&&!vq(t))||e.containData(t.coord)}function _q(e,t){return e?function(e,n,i,a){return cb(a<2?e.coord&&e.coord[a]:e.value,t[a])}:function(e,n,i,a){return cb(e.value,t[a])}}function Tq(e,t,n){if("average"===n){var i=0,a=0;return e.each(t,(function(e,t){isNaN(e)||(i+=e,a++)})),i/a}return"median"===n?e.getMedian(t):e.getDataExtent(t)["max"===n?1:0]}var Iq=Cd(),Eq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.init=function(){this.markerGroupMap=fo()},t.prototype.render=function(e,t,n){var i=this,a=this.markerGroupMap;a.each((function(e){Iq(e).keep=!1})),t.eachSeries((function(e){var a=yq.getMarkerModelFromSeries(e,i.type);a&&i.renderSeries(e,a,t,n)})),a.each((function(e){!Iq(e).keep&&i.group.remove(e.group)}))},t.prototype.markKeep=function(e){Iq(e).keep=!0},t.prototype.toggleBlurSeries=function(e,t){var n=this;Rr(e,(function(e){var i=yq.getMarkerModelFromSeries(e,n.type);i&&i.getData().eachItemGraphicEl((function(e){e&&(t?bh(e):wh(e))}))}))},t.type="marker",t}(hw);function Mq(e,t,n){var i=t.coordinateSystem;e.each((function(a){var r,o=e.getItemModel(a),s=Vc(o.get("x"),n.getWidth()),l=Vc(o.get("y"),n.getHeight());if(isNaN(s)||isNaN(l)){if(t.getMarkerPosition)r=t.getMarkerPosition(e.getValues(e.dimensions,a));else if(i){var p=e.get(i.dimensions[0],a),c=e.get(i.dimensions[1],a);r=i.dataToPoint([p,c])}}else r=[s,l];isNaN(s)||(r[0]=s),isNaN(l)||(r[1]=l),e.setItemLayout(a,r)}))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.updateTransform=function(e,t,n){t.eachSeries((function(e){var t=yq.getMarkerModelFromSeries(e,"markPoint");t&&(Mq(t.getData(),e,n),this.markerGroupMap.get(e.id).updateLayout())}),this)},t.prototype.renderSeries=function(e,t,n,i){var a=e.coordinateSystem,r=e.id,o=e.getData(),s=this.markerGroupMap,l=s.get(r)||s.set(r,new CE),p=function(e,t,n){var i;i=e?Br(e&&e.dimensions,(function(e){return kr(kr({},t.getData().getDimensionInfo(t.getData().mapDimension(e))||{}),{name:e,ordinalMeta:null})})):[{name:"value",type:"float"}];var a=new V_(i,n),r=Br(n.get("data"),zr(wq,t));e&&(r=Lr(r,zr(Cq,e)));var o=_q(!!e,i);return a.initData(r,null,o),a}(a,e,t);t.setData(p),Mq(t.getData(),e,i),p.each((function(e){var n=p.getItemModel(e),i=n.getShallow("symbol"),a=n.getShallow("symbolSize"),r=n.getShallow("symbolRotate"),s=n.getShallow("symbolOffset"),l=n.getShallow("symbolKeepAspect");if(jr(i)||jr(a)||jr(r)||jr(s)){var c=t.getRawValue(e),d=t.getDataParams(e);jr(i)&&(i=i(c,d)),jr(a)&&(a=a(c,d)),jr(r)&&(r=r(c,d)),jr(s)&&(s=s(c,d))}var u=n.getModel("itemStyle").getItemStyle(),m=hS(o,"color");u.fill||(u.fill=m),p.setItemVisual(e,{symbol:i,symbolSize:a,symbolRotate:r,symbolOffset:s,symbolKeepAspect:l,style:u})})),l.updateData(p),this.group.add(l.group),p.eachItemGraphicEl((function(e){e.traverse((function(e){Um(e).dataModel=t}))})),this.markKeep(l),l.group.silent=t.get("silent")||e.get("silent")},t.type="markPoint"}(Eq);var kq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.createMarkerModelFromSeries=function(e,n,i){return new t(e,n,i)},t.type="markLine",t.defaultOption={z:5,symbol:["circle","arrow"],symbolSize:[8,16],symbolOffset:0,precision:2,tooltip:{trigger:"item"},label:{show:!0,position:"end",distance:5},lineStyle:{type:"dashed"},emphasis:{label:{show:!0},lineStyle:{width:3}},animationEasing:"linear"},t}(yq),Pq=Cd(),Dq=function(e,t,n,i){var a,r=e.getData();if(Ur(i))a=i;else{var o=i.type;if("min"===o||"max"===o||"average"===o||"median"===o||null!=i.xAxis||null!=i.yAxis){var s=void 0,l=void 0;if(null!=i.yAxis||null!=i.xAxis)s=t.getAxis(null!=i.yAxis?"y":"x"),l=no(i.yAxis,i.xAxis);else{var p=Sq(i,r,t,e);s=p.valueAxis,l=Tq(r,$_(r,p.valueDataDim),o)}var c="x"===s.dim?0:1,d=1-c,u=Ir(i),m={coord:[]};u.type=null,u.coord=[],u.coord[d]=-1/0,m.coord[d]=1/0;var h=n.get("precision");h>=0&&$r(l)&&(l=+l.toFixed(Math.min(h,20))),u.coord[c]=m.coord[c]=l,a=[u,m,{type:o,valueIndex:i.valueIndex,value:l}]}else a=[]}var g=[wq(e,a[0]),wq(e,a[1]),kr({},a[2])];return g[2].type=g[2].type||null,Er(g[2],g[0]),Er(g[2],g[1]),g};function Oq(e){return!isNaN(e)&&!isFinite(e)}function Aq(e,t,n,i){var a=1-e,r=i.dimensions[e];return Oq(t[a])&&Oq(n[a])&&t[e]===n[e]&&i.getAxis(r).containData(t[e])}function Fq(e,t){if("cartesian2d"===e.type){var n=t[0].coord,i=t[1].coord;if(n&&i&&(Aq(1,n,i,e)||Aq(0,n,i,e)))return!0}return Cq(e,t[0])&&Cq(e,t[1])}function Rq(e,t,n,i,a){var r,o=i.coordinateSystem,s=e.getItemModel(t),l=Vc(s.get("x"),a.getWidth()),p=Vc(s.get("y"),a.getHeight());if(isNaN(l)||isNaN(p)){if(i.getMarkerPosition)r=i.getMarkerPosition(e.getValues(e.dimensions,t));else{var c=o.dimensions,d=e.get(c[0],t),u=e.get(c[1],t);r=o.dataToPoint([d,u])}if(NE(o,"cartesian2d")){var m=o.getAxis("x"),h=o.getAxis("y");c=o.dimensions;Oq(e.get(c[0],t))?r[0]=m.toGlobalCoord(m.getExtent()[n?0:1]):Oq(e.get(c[1],t))&&(r[1]=h.toGlobalCoord(h.getExtent()[n?0:1]))}isNaN(l)||(r[0]=l),isNaN(p)||(r[1]=p)}else r=[l,p];e.setItemLayout(t,r)}var Bq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.updateTransform=function(e,t,n){t.eachSeries((function(e){var t=yq.getMarkerModelFromSeries(e,"markLine");if(t){var i=t.getData(),a=Pq(t).from,r=Pq(t).to;a.each((function(t){Rq(a,t,!0,e,n),Rq(r,t,!1,e,n)})),i.each((function(e){i.setItemLayout(e,[a.getItemLayout(e),r.getItemLayout(e)])})),this.markerGroupMap.get(e.id).updateLayout()}}),this)},t.prototype.renderSeries=function(e,t,n,i){var a=e.coordinateSystem,r=e.id,o=e.getData(),s=this.markerGroupMap,l=s.get(r)||s.set(r,new zO);this.group.add(l.group);var p=function(e,t,n){var i;i=e?Br(e&&e.dimensions,(function(e){return kr(kr({},t.getData().getDimensionInfo(t.getData().mapDimension(e))||{}),{name:e,ordinalMeta:null})})):[{name:"value",type:"float"}];var a=new V_(i,n),r=new V_(i,n),o=new V_([],n),s=Br(n.get("data"),zr(Dq,t,e,n));e&&(s=Lr(s,zr(Fq,e)));var l=_q(!!e,i);return a.initData(Br(s,(function(e){return e[0]})),null,l),r.initData(Br(s,(function(e){return e[1]})),null,l),o.initData(Br(s,(function(e){return e[2]}))),o.hasItemOption=!0,{from:a,to:r,line:o}}(a,e,t),c=p.from,d=p.to,u=p.line;Pq(t).from=c,Pq(t).to=d,t.setData(u);var m=t.get("symbol"),h=t.get("symbolSize"),g=t.get("symbolRotate"),f=t.get("symbolOffset");function y(t,n,a){var r=t.getItemModel(n);Rq(t,n,a,e,i);var s=r.getModel("itemStyle").getItemStyle();null==s.fill&&(s.fill=hS(o,"color")),t.setItemVisual(n,{symbolKeepAspect:r.get("symbolKeepAspect"),symbolOffset:io(r.get("symbolOffset",!0),f[a?0:1]),symbolRotate:io(r.get("symbolRotate",!0),g[a?0:1]),symbolSize:io(r.get("symbolSize"),h[a?0:1]),symbol:io(r.get("symbol",!0),m[a?0:1]),style:s})}Ur(m)||(m=[m,m]),Ur(h)||(h=[h,h]),Ur(g)||(g=[g,g]),Ur(f)||(f=[f,f]),p.from.each((function(e){y(c,e,!0),y(d,e,!1)})),u.each((function(e){var t=u.getItemModel(e).getModel("lineStyle").getLineStyle();u.setItemLayout(e,[c.getItemLayout(e),d.getItemLayout(e)]),null==t.stroke&&(t.stroke=c.getItemVisual(e,"style").fill),u.setItemVisual(e,{fromSymbolKeepAspect:c.getItemVisual(e,"symbolKeepAspect"),fromSymbolOffset:c.getItemVisual(e,"symbolOffset"),fromSymbolRotate:c.getItemVisual(e,"symbolRotate"),fromSymbolSize:c.getItemVisual(e,"symbolSize"),fromSymbol:c.getItemVisual(e,"symbol"),toSymbolKeepAspect:d.getItemVisual(e,"symbolKeepAspect"),toSymbolOffset:d.getItemVisual(e,"symbolOffset"),toSymbolRotate:d.getItemVisual(e,"symbolRotate"),toSymbolSize:d.getItemVisual(e,"symbolSize"),toSymbol:d.getItemVisual(e,"symbol"),style:t})})),l.updateData(u),p.line.eachItemGraphicEl((function(e){Um(e).dataModel=t,e.traverse((function(e){Um(e).dataModel=t}))})),this.markKeep(l),l.group.silent=t.get("silent")||e.get("silent")},t.type="markLine",t}(Eq);function Nq(e){e.registerComponentModel(kq),e.registerComponentView(Bq),e.registerPreprocessor((function(e){hq(e.series,"markLine")&&(e.markLine=e.markLine||{})}))}!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.createMarkerModelFromSeries=function(e,n,i){return new t(e,n,i)},t.type="markArea",t.defaultOption={z:1,tooltip:{trigger:"item"},animation:!1,label:{show:!0,position:"top"},itemStyle:{borderWidth:0},emphasis:{label:{show:!0,position:"top"}}}}(yq);var Lq=Cd(),Vq=function(e,t,n,i){var a=i[0],r=i[1];if(a&&r){var o=wq(e,a),s=wq(e,r),l=o.coord,p=s.coord;l[0]=no(l[0],-1/0),l[1]=no(l[1],-1/0),p[0]=no(p[0],1/0),p[1]=no(p[1],1/0);var c=Mr([{},o,s]);return c.coord=[o.coord,s.coord],c.x0=o.x,c.y0=o.y,c.x1=s.x,c.y1=s.y,c}};function qq(e){return!isNaN(e)&&!isFinite(e)}function Gq(e,t,n,i){var a=1-e;return qq(t[a])&&qq(n[a])}function zq(e,t){var n=t.coord[0],i=t.coord[1],a={coord:n,x:t.x0,y:t.y0},r={coord:i,x:t.x1,y:t.y1};return NE(e,"cartesian2d")?!(!n||!i||!Gq(1,n,i)&&!Gq(0,n,i))||function(e,t,n){return!(e&&e.containZone&&t.coord&&n.coord&&!vq(t)&&!vq(n))||e.containZone(t.coord,n.coord)}(e,a,r):Cq(e,a)||Cq(e,r)}function Uq(e,t,n,i,a){var r,o=i.coordinateSystem,s=e.getItemModel(t),l=Vc(s.get(n[0]),a.getWidth()),p=Vc(s.get(n[1]),a.getHeight());if(isNaN(l)||isNaN(p)){if(i.getMarkerPosition){var c=e.getValues(["x0","y0"],t),d=e.getValues(["x1","y1"],t),u=o.clampData(c),m=o.clampData(d),h=[];"x0"===n[0]?h[0]=u[0]>m[0]?d[0]:c[0]:h[0]=u[0]>m[0]?c[0]:d[0],"y0"===n[1]?h[1]=u[1]>m[1]?d[1]:c[1]:h[1]=u[1]>m[1]?c[1]:d[1],r=i.getMarkerPosition(h,n,!0)}else{var g=[v=e.get(n[0],t),x=e.get(n[1],t)];o.clampData&&o.clampData(g,g),r=o.dataToPoint(g,!0)}if(NE(o,"cartesian2d")){var f=o.getAxis("x"),y=o.getAxis("y"),v=e.get(n[0],t),x=e.get(n[1],t);qq(v)?r[0]=f.toGlobalCoord(f.getExtent()["x0"===n[0]?0:1]):qq(x)&&(r[1]=y.toGlobalCoord(y.getExtent()["y0"===n[1]?0:1]))}isNaN(l)||(r[0]=l),isNaN(p)||(r[1]=p)}else r=[l,p];return r}var jq=[["x0","y0"],["x1","y0"],["x1","y1"],["x0","y1"]];!function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}ze(t,e),t.prototype.updateTransform=function(e,t,n){t.eachSeries((function(e){var t=yq.getMarkerModelFromSeries(e,"markArea");if(t){var i=t.getData();i.each((function(t){var a=Br(jq,(function(a){return Uq(i,t,a,e,n)}));i.setItemLayout(t,a),i.getItemGraphicEl(t).setShape("points",a)}))}}),this)},t.prototype.renderSeries=function(e,t,n,i){var a=e.coordinateSystem,r=e.id,o=e.getData(),s=this.markerGroupMap,l=s.get(r)||s.set(r,{group:new Mc});this.group.add(l.group),this.markKeep(l);var p=function(e,t,n){var i,a,r=["x0","y0","x1","y1"];if(e){var o=Br(e&&e.dimensions,(function(e){var n=t.getData();return kr(kr({},n.getDimensionInfo(n.mapDimension(e))||{}),{name:e,ordinalMeta:null})}));a=Br(r,(function(e,t){return{name:e,type:o[t%2].type}})),i=new V_(a,n)}else i=new V_(a=[{name:"value",type:"float"}],n);var s=Br(n.get("data"),zr(Vq,t,e,n));e&&(s=Lr(s,zr(zq,e)));var l=e?function(e,t,n,i){return cb(e.coord[Math.floor(i/2)][i%2],a[i])}:function(e,t,n,i){return cb(e.value,a[i])};return i.initData(s,null,l),i.hasItemOption=!0,i}(a,e,t);t.setData(p),p.each((function(t){var n=Br(jq,(function(n){return Uq(p,t,n,e,i)})),r=a.getAxis("x").scale,s=a.getAxis("y").scale,l=r.getExtent(),c=s.getExtent(),d=[r.parse(p.get("x0",t)),r.parse(p.get("x1",t))],u=[s.parse(p.get("y0",t)),s.parse(p.get("y1",t))];Gc(d),Gc(u);var m=!!(l[0]>d[1]||l[1]u[1]||c[1]=0},t.prototype.getOrient=function(){return"vertical"===this.get("orient")?{index:1,name:"vertical"}:{index:0,name:"horizontal"}},t.type="legend.plain",t.dependencies=["series"],t.defaultOption={z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,symbolRotate:"inherit",symbolKeepAspect:!0,inactiveColor:"#ccc",inactiveBorderColor:"#ccc",inactiveBorderWidth:"auto",itemStyle:{color:"inherit",opacity:"inherit",borderColor:"inherit",borderWidth:"auto",borderCap:"inherit",borderJoin:"inherit",borderDashOffset:"inherit",borderMiterLimit:"inherit"},lineStyle:{width:"auto",color:"inherit",inactiveColor:"#ccc",inactiveWidth:2,opacity:"inherit",type:"inherit",cap:"inherit",join:"inherit",dashOffset:"inherit",miterLimit:"inherit"},textStyle:{color:"#333"},selectedMode:!0,selector:!1,selectorLabel:{show:!0,borderRadius:10,padding:[3,5,3,5],fontSize:12,fontFamily:"sans-serif",color:"#666",borderWidth:1,borderColor:"#666"},emphasis:{selectorLabel:{show:!0,color:"#eee",backgroundColor:"#666"}},selectorPosition:"auto",selectorItemGap:7,selectorButtonGap:10,tooltip:{show:!1}},t}(Sv),Wq=zr,$q=Rr,Kq=Mc,Yq=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.newlineDisabled=!1,n}return ze(t,e),t.prototype.init=function(){this.group.add(this._contentGroup=new Kq),this.group.add(this._selectorGroup=new Kq),this._isFirstRender=!0},t.prototype.getContentGroup=function(){return this._contentGroup},t.prototype.getSelectorGroup=function(){return this._selectorGroup},t.prototype.render=function(e,t,n){var i=this._isFirstRender;if(this._isFirstRender=!1,this.resetInner(),e.get("show",!0)){var a=e.get("align"),r=e.get("orient");a&&"auto"!==a||(a="right"===e.get("left")&&"vertical"===r?"right":"left");var o=e.get("selector",!0),s=e.get("selectorPosition",!0);!o||s&&"auto"!==s||(s="horizontal"===r?"end":"start"),this.renderInner(a,e,t,n,o,r,s);var l=e.getBoxLayoutParams(),p={width:n.getWidth(),height:n.getHeight()},c=e.get("padding"),d=gv(l,p,c),u=this.layoutInner(e,a,d,i,o,s),m=gv(Pr({width:u.width,height:u.height},l),p,c);this.group.x=m.x-u.x,this.group.y=m.y-u.y,this.group.markRedraw(),this.group.add(this._backgroundEl=QL(u,e))}},t.prototype.resetInner=function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl),this.getSelectorGroup().removeAll()},t.prototype.renderInner=function(e,t,n,i,a,r,o){var s=this.getContentGroup(),l=fo(),p=t.get("selectedMode"),c=[];n.eachRawSeries((function(e){!e.get("legendHoverLink")&&c.push(e.id)})),$q(t.getData(),(function(a,r){var o=a.get("name");if(!this.newlineDisabled&&(""===o||"\n"===o)){var d=new Kq;return d.newline=!0,void s.add(d)}var u=n.getSeriesByName(o)[0];if(!l.get(o)){if(u){var m=u.getData(),h=m.getVisual("legendLineStyle")||{},g=m.getVisual("legendIcon"),f=m.getVisual("style"),y=this._createItem(u,o,r,a,t,e,h,f,g,p,i);y.on("click",Wq(Xq,o,null,i,c)).on("mouseover",Wq(Qq,u.name,null,i,c)).on("mouseout",Wq(Jq,u.name,null,i,c)),n.ssr&&y.eachChild((function(e){var t=Um(e);t.seriesIndex=u.seriesIndex,t.dataIndex=r,t.ssrType="legend"})),l.set(o,!0)}else n.eachRawSeries((function(s){if(!l.get(o)&&s.legendVisualProvider){var d=s.legendVisualProvider;if(!d.containName(o))return;var u=d.indexOfName(o),m=d.getItemVisual(u,"style"),h=d.getItemVisual(u,"legendIcon"),g=Gl(m.fill);g&&0===g[3]&&(g[3]=.2,m=kr(kr({},m),{fill:Kl(g,"rgba")}));var f=this._createItem(s,o,r,a,t,e,{},m,h,p,i);f.on("click",Wq(Xq,null,o,i,c)).on("mouseover",Wq(Qq,null,o,i,c)).on("mouseout",Wq(Jq,null,o,i,c)),n.ssr&&f.eachChild((function(e){var t=Um(e);t.seriesIndex=s.seriesIndex,t.dataIndex=r,t.ssrType="legend"})),l.set(o,!0)}}),this);0}}),this),a&&this._createSelector(a,t,i,r,o)},t.prototype._createSelector=function(e,t,n,i,a){var r=this.getSelectorGroup();$q(e,(function(e){var i=e.type,a=new Pm({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){n.dispatchAction({type:"all"===i?"legendAllSelect":"legendInverseSelect"})}});r.add(a),zf(a,{normal:t.getModel("selectorLabel"),emphasis:t.getModel(["emphasis","selectorLabel"])},{defaultText:e.title}),Dh(a)}))},t.prototype._createItem=function(e,t,n,i,a,r,o,s,l,p,c){var d=e.visualDrawType,u=a.get("itemWidth"),m=a.get("itemHeight"),h=a.isSelected(t),g=i.get("symbolRotate"),f=i.get("symbolKeepAspect"),y=i.get("icon"),v=function(e,t,n,i,a,r,o){function s(e,t){"auto"===e.lineWidth&&(e.lineWidth=t.lineWidth>0?2:0),$q(e,(function(n,i){"inherit"===e[i]&&(e[i]=t[i])}))}var l=t.getModel("itemStyle"),p=l.getItemStyle(),c=0===e.lastIndexOf("empty",0)?"fill":"stroke",d=l.getShallow("decal");p.decal=d&&"inherit"!==d?iC(d,o):i.decal,"inherit"===p.fill&&(p.fill=i[a]);"inherit"===p.stroke&&(p.stroke=i[c]);"inherit"===p.opacity&&(p.opacity=("fill"===a?i:n).opacity);s(p,i);var u=t.getModel("lineStyle"),m=u.getLineStyle();if(s(m,n),"auto"===p.fill&&(p.fill=i.fill),"auto"===p.stroke&&(p.stroke=i.fill),"auto"===m.stroke&&(m.stroke=i.fill),!r){var h=t.get("inactiveBorderWidth"),g=p[c];p.lineWidth="auto"===h?i.lineWidth>0&&g?2:0:p.lineWidth,p.fill=t.get("inactiveColor"),p.stroke=t.get("inactiveBorderColor"),m.stroke=u.get("inactiveColor"),m.lineWidth=u.get("inactiveWidth")}return{itemStyle:p,lineStyle:m}}(l=y||l||"roundRect",i,o,s,d,h,c),x=new Kq,b=i.getModel("textStyle");if(!jr(e.getLegendIcon)||y&&"inherit"!==y){var w="inherit"===y&&e.getData().getVisual("symbol")?"inherit"===g?e.getData().getVisual("symbolRotate"):g:0;x.add(function(e){var t=e.icon||"roundRect",n=PS(t,0,0,e.itemWidth,e.itemHeight,e.itemStyle.fill,e.symbolKeepAspect);n.setStyle(e.itemStyle),n.rotation=(e.iconRotate||0)*Math.PI/180,n.setOrigin([e.itemWidth/2,e.itemHeight/2]),t.indexOf("empty")>-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2);return n}({itemWidth:u,itemHeight:m,icon:l,iconRotate:w,itemStyle:v.itemStyle,lineStyle:v.lineStyle,symbolKeepAspect:f}))}else x.add(e.getLegendIcon({itemWidth:u,itemHeight:m,icon:l,iconRotate:g,itemStyle:v.itemStyle,lineStyle:v.lineStyle,symbolKeepAspect:f}));var S="left"===r?u+5:-5,C=r,_=a.get("formatter"),T=t;Hr(_)&&_?T=_.replace("{name}",null!=t?t:""):jr(_)&&(T=_(t));var I=h?b.getTextColor():i.get("inactiveColor");x.add(new Pm({style:jf(b,{text:T,x:S,y:m/2,fill:I,align:C,verticalAlign:"middle"},{inheritColor:I})}));var E=new Em({shape:x.getBoundingRect(),style:{fill:"transparent"}}),M=i.getModel("tooltip");return M.get("show")&&Rf({el:E,componentModel:a,itemName:t,itemTooltipOption:M.option}),x.add(E),x.eachChild((function(e){e.silent=!0})),E.silent=!p,this.getContentGroup().add(x),Dh(x),x.__legendDataIndex=n,x},t.prototype.layoutInner=function(e,t,n,i,a,r){var o=this.getContentGroup(),s=this.getSelectorGroup();hv(e.get("orient"),o,e.get("itemGap"),n.width,n.height);var l=o.getBoundingRect(),p=[-l.x,-l.y];if(s.markRedraw(),o.markRedraw(),a){hv("horizontal",s,e.get("selectorItemGap",!0));var c=s.getBoundingRect(),d=[-c.x,-c.y],u=e.get("selectorButtonGap",!0),m=e.getOrient().index,h=0===m?"width":"height",g=0===m?"height":"width",f=0===m?"y":"x";"end"===r?d[m]+=l[h]+u:p[m]+=c[h]+u,d[1-m]+=l[g]/2-c[g]/2,s.x=d[0],s.y=d[1],o.x=p[0],o.y=p[1];var y={x:0,y:0};return y[h]=l[h]+u+c[h],y[g]=Math.max(l[g],c[g]),y[f]=Math.min(0,c[f]+d[1-m]),y}return o.x=p[0],o.y=p[1],this.group.getBoundingRect()},t.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},t.type="legend.plain",t}(hw);function Xq(e,t,n,i){Jq(e,t,n,i),n.dispatchAction({type:"legendToggleSelect",name:null!=e?e:t}),Qq(e,t,n,i)}function Zq(e){for(var t,n=e.getZr().storage.getDisplayList(),i=0,a=n.length;in[a],h=[-d.x,-d.y];t||(h[i]=l[s]);var g=[0,0],f=[-u.x,-u.y],y=io(e.get("pageButtonGap",!0),e.get("itemGap",!0));m&&("end"===e.get("pageButtonPosition",!0)?f[i]+=n[a]-u[a]:g[i]+=u[a]+y);f[1-i]+=d[r]/2-u[r]/2,l.setPosition(h),p.setPosition(g),c.setPosition(f);var v={x:0,y:0};if(v[a]=m?n[a]:d[a],v[r]=Math.max(d[r],u[r]),v[o]=Math.min(0,u[o]+f[1-i]),p.__rectSize=n[a],m){var x={x:0,y:0};x[a]=Math.max(n[a]-u[a]-y,0),x[r]=v[r],p.setClipPath(new Em({shape:x})),p.__rectSize=x[a]}else c.eachChild((function(e){e.attr({invisible:!0,silent:!0})}));var b=this._getPageInfo(e);return null!=b.pageIndex&&tf(l,{x:b.contentPosition[0],y:b.contentPosition[1]},m?e:null),this._updatePageInfoView(e,b),v},t.prototype._pageGo=function(e,t,n){var i=this._getPageInfo(t)[e];null!=i&&n.dispatchAction({type:"legendScroll",scrollDataIndex:i,legendId:t.id})},t.prototype._updatePageInfoView=function(e,t){var n=this._controllerGroup;Rr(["pagePrev","pageNext"],(function(i){var a=null!=t[i+"DataIndex"],r=n.childOfName(i);r&&(r.setStyle("fill",a?e.get("pageIconColor",!0):e.get("pageIconInactiveColor",!0)),r.cursor=a?"pointer":"default")}));var i=n.childOfName("pageText"),a=e.get("pageFormatter"),r=t.pageIndex,o=null!=r?r+1:0,s=t.pageCount;i&&a&&i.setStyle("text",Hr(a)?a.replace("{current}",null==o?"":o+"").replace("{total}",null==s?"":s+""):a({current:o,total:s}))},t.prototype._getPageInfo=function(e){var t=e.get("scrollDataIndex",!0),n=this.getContentGroup(),i=this._containerGroup.__rectSize,a=e.getOrient().index,r=nG[a],o=iG[a],s=this._findTargetItemIndex(t),l=n.children(),p=l[s],c=l.length,d=c?1:0,u={contentPosition:[n.x,n.y],pageCount:d,pageIndex:d-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!p)return u;var m=v(p);u.contentPosition[a]=-m.s;for(var h=s+1,g=m,f=m,y=null;h<=c;++h)(!(y=v(l[h]))&&f.e>g.s+i||y&&!x(y,g.s))&&(g=f.i>g.i?f:y)&&(null==u.pageNextDataIndex&&(u.pageNextDataIndex=g.i),++u.pageCount),f=y;for(h=s-1,g=m,f=m,y=null;h>=-1;--h)(y=v(l[h]))&&x(f,y.s)||!(g.i=t&&e.s<=t+i}},t.prototype._findTargetItemIndex=function(e){return this._showController?(this.getContentGroup().eachChild((function(i,a){var r=i.__legendDataIndex;null==n&&null!=r&&(n=a),r===e&&(t=a)})),null!=t?t:n):0;var t,n},t.type="legend.scroll"}(Yq);var aG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="dataZoom.inside",t.defaultOption=gy(qL.defaultOption,{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),t}(qL),rG=Cd();function oG(e,t){if(t){e.removeKey(t.model.uid);var n=t.controller;n&&n.dispose()}}function sG(e,t){e.isDisposed()||e.dispatchAction({type:"dataZoom",animation:{easing:"cubicOut",duration:100},batch:t})}function lG(e,t,n,i){return e.coordinateSystem.containPoint([n,i])}function pG(e){e.registerProcessor(e.PRIORITY.PROCESSOR.FILTER,(function(e,t){var n=rG(t),i=n.coordSysRecordMap||(n.coordSysRecordMap=fo());i.each((function(e){e.dataZoomInfoMap=null})),e.eachComponent({mainType:"dataZoom",subType:"inside"},(function(e){Rr(LL(e).infoList,(function(n){var a=n.model.uid,r=i.get(a)||i.set(a,function(e,t){var n={model:t,containsPoint:zr(lG,t),dispatchAction:zr(sG,e),dataZoomInfoMap:null,controller:null},i=n.controller=new oP(e.getZr());return Rr(["pan","zoom","scrollMove"],(function(e){i.on(e,(function(t){var i=[];n.dataZoomInfoMap.each((function(a){if(t.isAvailableBehavior(a.model.option)){var r=(a.getRange||{})[e],o=r&&r(a.dzReferCoordSysInfo,n.model.mainType,n.controller,t);!a.model.get("disabled",!0)&&o&&i.push({dataZoomId:a.model.id,start:o[0],end:o[1]})}})),i.length&&n.dispatchAction(i)}))})),n}(t,n.model));(r.dataZoomInfoMap||(r.dataZoomInfoMap=fo())).set(e.uid,{dzReferCoordSysInfo:n,model:e,getRange:null})}))})),i.each((function(e){var t,n=e.controller,a=e.dataZoomInfoMap;if(a){var r=a.keys()[0];null!=r&&(t=a.get(r))}if(t){var o=function(e){var t,n="type_",i={type_true:2,type_move:1,type_false:0,type_undefined:-1},a=!0;return e.each((function(e){var r=e.model,o=!r.get("disabled",!0)&&(!r.get("zoomLock",!0)||"move");i[n+o]>i[n+t]&&(t=o),a=a&&r.get("preventDefaultMouseMove",!0)})),{controlType:t,opt:{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!0,preventDefaultMouseMove:!!a}}}(a);n.enable(o.controlType,o.opt),n.setPointerChecker(e.containsPoint),Mw(e,"dispatchAction",t.model.get("throttle",!0),"fixRate")}else oG(i,e)}))}))}var cG=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type="dataZoom.inside",t}return ze(t,e),t.prototype.render=function(t,n,i){e.prototype.render.apply(this,arguments),t.noTarget()?this._clear():(this.range=t.getPercentRange(),function(e,t,n){rG(e).coordSysRecordMap.each((function(e){var i=e.dataZoomInfoMap.get(t.uid);i&&(i.getRange=n)}))}(i,t,{pan:Gr(dG.pan,this),zoom:Gr(dG.zoom,this),scrollMove:Gr(dG.scrollMove,this)}))},t.prototype.dispose=function(){this._clear(),e.prototype.dispose.apply(this,arguments)},t.prototype._clear=function(){!function(e,t){for(var n=rG(e).coordSysRecordMap,i=n.keys(),a=0;a0?s.pixelStart+s.pixelLength-s.pixel:s.pixel-s.pixelStart)/s.pixelLength*(r[1]-r[0])+r[0],p=Math.max(1/i.scale,0);r[0]=(r[0]-l)*p+l,r[1]=(r[1]-l)*p+l;var c=this.dataZoomModel.findRepresentativeAxisProxy();if(c){var d=c.getMinMaxSpan();if(bA(0,r,[0,100],0,d.minSpan,d.maxSpan),this.range=r,a[0]!==r[0]||a[1]!==r[1])return r}}},pan:uG((function(e,t,n,i,a,r){var o=mG[i]([r.oldX,r.oldY],[r.newX,r.newY],t,a,n);return o.signal*(e[1]-e[0])*o.pixel/o.pixelLength})),scrollMove:uG((function(e,t,n,i,a,r){return mG[i]([0,0],[r.scrollDelta,r.scrollDelta],t,a,n).signal*(e[1]-e[0])*r.scrollDelta}))};function uG(e){return function(t,n,i,a){var r=this.range,o=r.slice(),s=t.axisModels[0];if(s)return bA(e(o,s,t,n,i,a),o,[0,100],"all"),this.range=o,r[0]!==o[0]||r[1]!==o[1]?o:void 0}}var mG={grid:function(e,t,n,i,a){var r=n.axis,o={},s=a.model.coordinateSystem.getRect();return e=e||[0,0],"x"===r.dim?(o.pixel=t[0]-e[0],o.pixelLength=s.width,o.pixelStart=s.x,o.signal=r.inverse?1:-1):(o.pixel=t[1]-e[1],o.pixelLength=s.height,o.pixelStart=s.y,o.signal=r.inverse?-1:1),o},polar:function(e,t,n,i,a){var r=n.axis,o={},s=a.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),p=s.getAngleAxis().getExtent();return e=e?s.pointToCoord(e):[0,0],t=s.pointToCoord(t),"radiusAxis"===n.mainType?(o.pixel=t[0]-e[0],o.pixelLength=l[1]-l[0],o.pixelStart=l[0],o.signal=r.inverse?1:-1):(o.pixel=t[1]-e[1],o.pixelLength=p[1]-p[0],o.pixelStart=p[0],o.signal=r.inverse?-1:1),o},singleAxis:function(e,t,n,i,a){var r=n.axis,o=a.model.coordinateSystem.getRect(),s={};return e=e||[0,0],"horizontal"===r.orient?(s.pixel=t[0]-e[0],s.pixelLength=o.width,s.pixelStart=o.x,s.signal=r.inverse?1:-1):(s.pixel=t[1]-e[1],s.pixelLength=o.height,s.pixelStart=o.y,s.signal=r.inverse?-1:1),s}};function hG(e){KL(e),e.registerComponentModel(aG),e.registerComponentView(cG),pG(e)}var gG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.type="dataZoom.slider",t.layoutMode="box",t.defaultOption=gy(qL.defaultOption,{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,borderColor:"#d2dbee",borderRadius:3,backgroundColor:"rgba(47,69,84,0)",dataBackground:{lineStyle:{color:"#d2dbee",width:.5},areaStyle:{color:"#d2dbee",opacity:.2}},selectedDataBackground:{lineStyle:{color:"#8fb0f7",width:.5},areaStyle:{color:"#8fb0f7",opacity:.2}},fillerColor:"rgba(135,175,274,0.2)",handleIcon:"path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z",handleSize:"100%",handleStyle:{color:"#fff",borderColor:"#ACB8D1"},moveHandleSize:7,moveHandleIcon:"path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z",moveHandleStyle:{color:"#D2DBEE",opacity:.7},showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:"#6E7079"},brushSelect:!0,brushStyle:{color:"rgba(135,175,274,0.15)"},emphasis:{handleStyle:{borderColor:"#8FB0F7"},moveHandleStyle:{color:"#8FB0F7"}}}),t}(qL),fG=Em,yG="horizontal",vG="vertical",xG=["line","bar","candlestick","scatter","custom"],bG={easing:"cubicOut",duration:100,delay:0},wG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._displayables={},n}return ze(t,e),t.prototype.init=function(e,t){this.api=t,this._onBrush=Gr(this._onBrush,this),this._onBrushEnd=Gr(this._onBrushEnd,this)},t.prototype.render=function(t,n,i,a){if(e.prototype.render.apply(this,arguments),Mw(this,"_dispatchZoomAction",t.get("throttle"),"fixRate"),this._orient=t.getOrient(),!1!==t.get("show")){if(t.noTarget())return this._clear(),void this.group.removeAll();a&&"dataZoom"===a.type&&a.from===this.uid||this._buildView(),this._updateView()}else this.group.removeAll()},t.prototype.dispose=function(){this._clear(),e.prototype.dispose.apply(this,arguments)},t.prototype._clear=function(){kw(this,"_dispatchZoomAction");var e=this.api.getZr();e.off("mousemove",this._onBrush),e.off("mouseup",this._onBrushEnd)},t.prototype._buildView=function(){var e=this.group;e.removeAll(),this._brushing=!1,this._displayables.brushRect=null,this._resetLocation(),this._resetInterval();var t=this._displayables.sliderGroup=new Mc;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),e.add(t),this._positionGroup()},t.prototype._resetLocation=function(){var e=this.dataZoomModel,t=this.api,n=e.get("brushSelect")?7:0,i=this._findCoordRect(),a={width:t.getWidth(),height:t.getHeight()},r=this._orient===yG?{right:a.width-i.x-i.width,top:a.height-30-7-n,width:i.width,height:30}:{right:7,top:i.y,width:30,height:i.height},o=xv(e.option);Rr(["right","top","width","height"],(function(e){"ph"===o[e]&&(o[e]=r[e])}));var s=gv(o,a);this._location={x:s.x,y:s.y},this._size=[s.width,s.height],this._orient===vG&&this._size.reverse()},t.prototype._positionGroup=function(){var e=this.group,t=this._location,n=this._orient,i=this.dataZoomModel.getFirstTargetAxisModel(),a=i&&i.get("inverse"),r=this._displayables.sliderGroup,o=(this._dataShadowInfo||{}).otherAxisInverse;r.attr(n!==yG||a?n===yG&&a?{scaleY:o?1:-1,scaleX:-1}:n!==vG||a?{scaleY:o?-1:1,scaleX:-1,rotation:Math.PI/2}:{scaleY:o?-1:1,scaleX:1,rotation:Math.PI/2}:{scaleY:o?1:-1,scaleX:1});var s=e.getBoundingRect([r]);e.x=t.x-s.x,e.y=t.y-s.y,e.markRedraw()},t.prototype._getViewExtent=function(){return[0,this._size[0]]},t.prototype._renderBackground=function(){var e=this.dataZoomModel,t=this._size,n=this._displayables.sliderGroup,i=e.get("brushSelect");n.add(new fG({silent:!0,shape:{x:0,y:0,width:t[0],height:t[1]},style:{fill:e.get("backgroundColor")},z2:-40}));var a=new fG({shape:{x:0,y:0,width:t[0],height:t[1]},style:{fill:"transparent"},z2:0,onclick:Gr(this._onClickPanel,this)}),r=this.api.getZr();i?(a.on("mousedown",this._onBrushStart,this),a.cursor="crosshair",r.on("mousemove",this._onBrush),r.on("mouseup",this._onBrushEnd)):(r.off("mousemove",this._onBrush),r.off("mouseup",this._onBrushEnd)),n.add(a)},t.prototype._renderDataShadow=function(){var e=this._dataShadowInfo=this._prepareDataShadowInfo();if(this._displayables.dataShadowSegs=[],e){var t=this._size,n=this._shadowSize||[],i=e.series,a=i.getRawData(),r=i.getShadowDim&&i.getShadowDim(),o=r&&a.getDimensionInfo(r)?i.getShadowDim():e.otherDim;if(null!=o){var s=this._shadowPolygonPts,l=this._shadowPolylinePts;if(a!==this._shadowData||o!==this._shadowDim||t[0]!==n[0]||t[1]!==n[1]){var p=a.getDataExtent(o),c=.3*(p[1]-p[0]);p=[p[0]-c,p[1]+c];var d,u=[0,t[1]],m=[0,t[0]],h=[[t[0],0],[0,0]],g=[],f=m[1]/(a.count()-1),y=0,v=Math.round(a.count()/t[0]);a.each([o],(function(e,t){if(v>0&&t%v)y+=f;else{var n=null==e||isNaN(e)||""===e,i=n?0:Lc(e,p,u,!0);n&&!d&&t?(h.push([h[h.length-1][0],0]),g.push([g[g.length-1][0],0])):!n&&d&&(h.push([y,0]),g.push([y,0])),h.push([y,i]),g.push([y,i]),y+=f,d=n}})),s=this._shadowPolygonPts=h,l=this._shadowPolylinePts=g}this._shadowData=a,this._shadowDim=o,this._shadowSize=[t[0],t[1]];for(var x=this.dataZoomModel,b=0;b<3;b++){var w=S(1===b);this._displayables.sliderGroup.add(w),this._displayables.dataShadowSegs.push(w)}}}function S(e){var t=x.getModel(e?"selectedDataBackground":"dataBackground"),n=new Mc,i=new kg({shape:{points:s},segmentIgnoreThreshold:1,style:t.getModel("areaStyle").getAreaStyle(),silent:!0,z2:-20}),a=new Dg({shape:{points:l},segmentIgnoreThreshold:1,style:t.getModel("lineStyle").getLineStyle(),silent:!0,z2:-19});return n.add(i),n.add(a),n}},t.prototype._prepareDataShadowInfo=function(){var e=this.dataZoomModel,t=e.get("showDataShadow");if(!1!==t){var n,i=this.ecModel;return e.eachTargetAxis((function(a,r){var o=e.getAxisProxy(a,r);o&&Rr(o.getTargetSeriesModels(),(function(e){if(!(n||!0!==t&&Dr(xG,e.get("type"))<0)){var o,s=i.getComponent(BL(a),r).axis,l=function(e){var t={x:"y",y:"x",radius:"angle",angle:"radius"};return t[e]}(a),p=e.coordinateSystem;null!=l&&p.getOtherAxis&&(o=p.getOtherAxis(s).inverse),l=e.getData().mapDimension(l),n={thisAxis:s,series:e,thisDim:a,otherDim:l,otherAxisInverse:o}}}),this)}),this),n}},t.prototype._renderHandle=function(){var e=this.group,t=this._displayables,n=t.handles=[null,null],i=t.handleLabels=[null,null],a=this._displayables.sliderGroup,r=this._size,o=this.dataZoomModel,s=this.api,l=o.get("borderRadius")||0,p=o.get("brushSelect"),c=t.filler=new fG({silent:p,style:{fill:o.get("fillerColor")},textConfig:{position:"inside"}});a.add(c),a.add(new fG({silent:!0,subPixelOptimize:!0,shape:{x:0,y:0,width:r[0],height:r[1],r:l},style:{stroke:o.get("dataBackgroundColor")||o.get("borderColor"),lineWidth:1,fill:"rgba(0,0,0,0)"}})),Rr([0,1],(function(t){var r=o.get("handleIcon");!ES[r]&&r.indexOf("path://")<0&&r.indexOf("image://")<0&&(r="path://"+r);var s=PS(r,-1,0,2,2,null,!0);s.attr({cursor:SG(this._orient),draggable:!0,drift:Gr(this._onDragMove,this,t),ondragend:Gr(this._onDragEnd,this),onmouseover:Gr(this._showDataInfo,this,!0),onmouseout:Gr(this._showDataInfo,this,!1),z2:5});var l=s.getBoundingRect(),p=o.get("handleSize");this._handleHeight=Vc(p,this._size[1]),this._handleWidth=l.width/l.height*this._handleHeight,s.setStyle(o.getModel("handleStyle").getItemStyle()),s.style.strokeNoScale=!0,s.rectHover=!0,s.ensureState("emphasis").style=o.getModel(["emphasis","handleStyle"]).getItemStyle(),Dh(s);var c=o.get("handleColor");null!=c&&(s.style.fill=c),a.add(n[t]=s);var d=o.getModel("textStyle");e.add(i[t]=new Pm({silent:!0,invisible:!0,style:jf(d,{x:0,y:0,text:"",verticalAlign:"middle",align:"center",fill:d.getTextColor(),font:d.getFont()}),z2:10}))}),this);var d=c;if(p){var u=Vc(o.get("moveHandleSize"),r[1]),m=t.moveHandle=new Em({style:o.getModel("moveHandleStyle").getItemStyle(),silent:!0,shape:{r:[0,0,2,2],y:r[1]-.5,height:u}}),h=.8*u,g=t.moveHandleIcon=PS(o.get("moveHandleIcon"),-h/2,-h/2,h,h,"#fff",!0);g.silent=!0,g.y=r[1]+u/2-.5,m.ensureState("emphasis").style=o.getModel(["emphasis","moveHandleStyle"]).getItemStyle();var f=Math.min(r[1]/2,Math.max(u,10));(d=t.moveZone=new Em({invisible:!0,shape:{y:r[1]-f,height:u+f}})).on("mouseover",(function(){s.enterEmphasis(m)})).on("mouseout",(function(){s.leaveEmphasis(m)})),a.add(m),a.add(g),a.add(d)}d.attr({draggable:!0,cursor:SG(this._orient),drift:Gr(this._onDragMove,this,"all"),ondragstart:Gr(this._showDataInfo,this,!0),ondragend:Gr(this._onDragEnd,this),onmouseover:Gr(this._showDataInfo,this,!0),onmouseout:Gr(this._showDataInfo,this,!1)})},t.prototype._resetInterval=function(){var e=this._range=this.dataZoomModel.getPercentRange(),t=this._getViewExtent();this._handleEnds=[Lc(e[0],[0,100],t,!0),Lc(e[1],[0,100],t,!0)]},t.prototype._updateInterval=function(e,t){var n=this.dataZoomModel,i=this._handleEnds,a=this._getViewExtent(),r=n.findRepresentativeAxisProxy();if(r){var o=r.getMinMaxSpan(),s=[0,100];bA(t,i,a,n.get("zoomLock")?"all":e,null!=o.minSpan?Lc(o.minSpan,s,a,!0):null,null!=o.maxSpan?Lc(o.maxSpan,s,a,!0):null);var l=this._range,p=this._range=Gc([Lc(i[0],a,s,!0),Lc(i[1],a,s,!0)]);return!l||l[0]!==p[0]||l[1]!==p[1]}return!1},t.prototype._updateView=function(e){var t=this._displayables,n=this._handleEnds,i=Gc(n.slice()),a=this._size;Rr([0,1],(function(e){var i=t.handles[e],r=this._handleHeight;i.attr({scaleX:r/2,scaleY:r/2,x:n[e]+(e?-1:1),y:a[1]/2-r/2})}),this),t.filler.setShape({x:i[0],y:0,width:i[1]-i[0],height:a[1]});var r={x:i[0],width:i[1]-i[0]};t.moveHandle&&(t.moveHandle.setShape(r),t.moveZone.setShape(r),t.moveZone.getBoundingRect(),t.moveHandleIcon&&t.moveHandleIcon.attr("x",r.x+r.width/2));for(var o=t.dataShadowSegs,s=[0,i[0],i[1],a[0]],l=0;lt[0]||n[1]<0||n[1]>t[1])){var i=this._handleEnds,a=(i[0]+i[1])/2,r=this._updateInterval("all",n[0]-a);this._updateView(),r&&this._dispatchZoomAction(!1)}},t.prototype._onBrushStart=function(e){var t=e.offsetX,n=e.offsetY;this._brushStart=new ws(t,n),this._brushing=!0,this._brushStartTime=+new Date},t.prototype._onBrushEnd=function(e){if(this._brushing){var t=this._displayables.brushRect;if(this._brushing=!1,t){t.attr("ignore",!0);var n=t.shape;if(!(+new Date-this._brushStartTime<200&&Math.abs(n.width)<5)){var i=this._getViewExtent(),a=[0,100];this._range=Gc([Lc(n.x,i,a,!0),Lc(n.x+n.width,i,a,!0)]),this._handleEnds=[n.x,n.x+n.width],this._updateView(),this._dispatchZoomAction(!1)}}}},t.prototype._onBrush=function(e){this._brushing&&(ls(e.event),this._updateBrushRect(e.offsetX,e.offsetY))},t.prototype._updateBrushRect=function(e,t){var n=this._displayables,i=this.dataZoomModel,a=n.brushRect;a||(a=n.brushRect=new fG({silent:!0,style:i.getModel("brushStyle").getItemStyle()}),n.sliderGroup.add(a)),a.attr("ignore",!1);var r=this._brushStart,o=this._displayables.sliderGroup,s=o.transformCoordToLocal(e,t),l=o.transformCoordToLocal(r.x,r.y),p=this._size;s[0]=Math.max(Math.min(p[0],s[0]),0),a.setShape({x:l[0],y:0,width:s[0]-l[0],height:p[1]})},t.prototype._dispatchZoomAction=function(e){var t=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,animation:e?bG:null,start:t[0],end:t[1]})},t.prototype._findCoordRect=function(){var e,t=LL(this.dataZoomModel).infoList;if(!e&&t.length){var n=t[0].model.coordinateSystem;e=n.getRect&&n.getRect()}if(!e){var i=this.api.getWidth(),a=this.api.getHeight();e={x:.2*i,y:.2*a,width:.6*i,height:.6*a}}return e},t.type="dataZoom.slider",t}(zL);function SG(e){return"vertical"===e?"ns-resize":"ew-resize"}function CG(e){e.registerComponentModel(gG),e.registerComponentView(wG),KL(e)}function _G(e){aI(hG),aI(CG)}var TG=function(e,t,n){var i=Ir((IG[e]||{})[t]);return n&&Ur(i)?i[i.length-1]:i},IG={color:{active:["#006edd","#e0ffff"],inactive:["rgba(0,0,0,0)"]},colorHue:{active:[0,360],inactive:[0,0]},colorSaturation:{active:[.3,1],inactive:[0,0]},colorLightness:{active:[.9,.5],inactive:[0,0]},colorAlpha:{active:[.3,1],inactive:[0,0]},opacity:{active:[.3,1],inactive:[0,0]},symbol:{active:["circle","roundRect","diamond"],inactive:["none"]},symbolSize:{active:[10,50],inactive:[0,0]}},EG=aO.mapVisual,MG=aO.eachVisual,kG=Ur,PG=Rr,DG=Gc,OG=Lc,AG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n.stateList=["inRange","outOfRange"],n.replacableOptionKeys=["inRange","outOfRange","target","controller","color"],n.layoutMode={type:"box",ignoreSize:!0},n.dataBound=[-1/0,1/0],n.targetVisuals={},n.controllerVisuals={},n}return ze(t,e),t.prototype.init=function(e,t,n){this.mergeDefaultAndTheme(e,n)},t.prototype.optionUpdated=function(e,t){var n=this.option;!t&&eq(n,e,this.replacableOptionKeys),this.textStyleModel=this.getModel("textStyle"),this.resetItemSize(),this.completeVisualOption()},t.prototype.resetVisual=function(e){var t=this.stateList;e=Gr(e,this),this.controllerVisuals=JV(this.option.controller,t,e),this.targetVisuals=JV(this.option.target,t,e)},t.prototype.getItemSymbol=function(){return null},t.prototype.getTargetSeriesIndices=function(){var e=this.option.seriesIndex,t=[];return null==e||"all"===e?this.ecModel.eachSeries((function(e,n){t.push(n)})):t=dd(e),t},t.prototype.eachTargetSeries=function(e,t){Rr(this.getTargetSeriesIndices(),(function(n){var i=this.ecModel.getSeriesByIndex(n);i&&e.call(t,i)}),this)},t.prototype.isTargetSeries=function(e){var t=!1;return this.eachTargetSeries((function(n){n===e&&(t=!0)})),t},t.prototype.formatValueText=function(e,t,n){var i,a=this.option,r=a.precision,o=this.dataBound,s=a.formatter;n=n||["<",">"],Ur(e)&&(e=e.slice(),i=!0);var l=t?e:i?[p(e[0]),p(e[1])]:p(e);return Hr(s)?s.replace("{value}",i?l[0]:l).replace("{value2}",i?l[1]:l):jr(s)?i?s(e[0],e[1]):s(e):i?e[0]===o[0]?n[0]+" "+l[1]:e[1]===o[1]?n[1]+" "+l[0]:l[0]+" - "+l[1]:l;function p(e){return e===o[0]?"min":e===o[1]?"max":(+e).toFixed(Math.min(r,20))}},t.prototype.resetExtent=function(){var e=this.option,t=DG([e.min,e.max]);this._dataExtent=t},t.prototype.getDataDimensionIndex=function(e){var t=this.option.dimension;if(null!=t)return e.getDimensionIndex(t);for(var n=e.dimensions,i=n.length-1;i>=0;i--){var a=n[i],r=e.getDimensionInfo(a);if(!r.isCalculationCoord)return r.storeDimIndex}},t.prototype.getExtent=function(){return this._dataExtent.slice()},t.prototype.completeVisualOption=function(){var e=this.ecModel,t=this.option,n={inRange:t.inRange,outOfRange:t.outOfRange},i=t.target||(t.target={}),a=t.controller||(t.controller={});Er(i,n),Er(a,n);var r=this.isCategory();function o(n){kG(t.color)&&!n.inRange&&(n.inRange={color:t.color.slice().reverse()}),n.inRange=n.inRange||{color:e.get("gradientColor")}}o.call(this,i),o.call(this,a),function(e,t,n){var i=e[t],a=e[n];i&&!a&&(a=e[n]={},PG(i,(function(e,t){if(aO.isValidType(t)){var n=TG(t,"inactive",r);null!=n&&(a[t]=n,"color"!==t||a.hasOwnProperty("opacity")||a.hasOwnProperty("colorAlpha")||(a.opacity=[0,0]))}})))}.call(this,i,"inRange","outOfRange"),function(e){var t=(e.inRange||{}).symbol||(e.outOfRange||{}).symbol,n=(e.inRange||{}).symbolSize||(e.outOfRange||{}).symbolSize,i=this.get("inactiveColor"),a=this.getItemSymbol()||"roundRect";PG(this.stateList,(function(o){var s=this.itemSize,l=e[o];l||(l=e[o]={color:r?i:[i]}),null==l.symbol&&(l.symbol=t&&Ir(t)||(r?a:[a])),null==l.symbolSize&&(l.symbolSize=n&&Ir(n)||(r?s[0]:[s[0],s[0]])),l.symbol=EG(l.symbol,(function(e){return"none"===e?a:e}));var p=l.symbolSize;if(null!=p){var c=-1/0;MG(p,(function(e){e>c&&(c=e)})),l.symbolSize=EG(p,(function(e){return OG(e,[0,c],[0,s[0]],!0)}))}}),this)}.call(this,a)},t.prototype.resetItemSize=function(){this.itemSize=[parseFloat(this.get("itemWidth")),parseFloat(this.get("itemHeight"))]},t.prototype.isCategory=function(){return!!this.option.categories},t.prototype.setSelected=function(e){},t.prototype.getSelected=function(){return null},t.prototype.getValueState=function(e){return null},t.prototype.getVisualMeta=function(e){return null},t.type="visualMap",t.dependencies=["series"],t.defaultOption={show:!0,z:4,seriesIndex:"all",min:0,max:200,left:0,right:null,top:null,bottom:0,itemWidth:null,itemHeight:null,inverse:!1,orient:"vertical",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",contentColor:"#5793f3",inactiveColor:"#aaa",borderWidth:0,padding:5,textGap:10,precision:0,textStyle:{color:"#333"}},t}(Sv),FG=[20,140],RG=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.optionUpdated=function(t,n){e.prototype.optionUpdated.apply(this,arguments),this.resetExtent(),this.resetVisual((function(e){e.mappingMethod="linear",e.dataExtent=this.getExtent()})),this._resetRange()},t.prototype.resetItemSize=function(){e.prototype.resetItemSize.apply(this,arguments);var t=this.itemSize;(null==t[0]||isNaN(t[0]))&&(t[0]=FG[0]),(null==t[1]||isNaN(t[1]))&&(t[1]=FG[1])},t.prototype._resetRange=function(){var e=this.getExtent(),t=this.option.range;!t||t.auto?(e.auto=1,this.option.range=e):Ur(t)&&(t[0]>t[1]&&t.reverse(),t[0]=Math.max(t[0],e[0]),t[1]=Math.min(t[1],e[1]))},t.prototype.completeVisualOption=function(){e.prototype.completeVisualOption.apply(this,arguments),Rr(this.stateList,(function(e){var t=this.option.controller[e].symbolSize;t&&t[0]!==t[1]&&(t[0]=t[1]/3)}),this)},t.prototype.setSelected=function(e){this.option.range=e.slice(),this._resetRange()},t.prototype.getSelected=function(){var e=this.getExtent(),t=Gc((this.get("range")||[]).slice());return t[0]>e[1]&&(t[0]=e[1]),t[1]>e[1]&&(t[1]=e[1]),t[0]=n[1]||e<=t[1])?"inRange":"outOfRange"},t.prototype.findTargetDataIndices=function(e){var t=[];return this.eachTargetSeries((function(n){var i=[],a=n.getData();a.each(this.getDataDimensionIndex(a),(function(t,n){e[0]<=t&&t<=e[1]&&i.push(n)}),this),t.push({seriesId:n.id,dataIndex:i})}),this),t},t.prototype.getVisualMeta=function(e){var t=BG(this,"outOfRange",this.getExtent()),n=BG(this,"inRange",this.option.range.slice()),i=[];function a(t,n){i.push({value:t,color:e(t,n)})}for(var r=0,o=0,s=n.length,l=t.length;oe[1])break;n.push({color:this.getControllerVisual(r,"color",t),offset:a/100})}return n.push({color:this.getControllerVisual(e[1],"color",t),offset:1}),n},t.prototype._createBarPoints=function(e,t){var n=this.visualMapModel.itemSize;return[[n[0]-t[0],e[0]],[n[0],e[0]],[n[0],e[1]],[n[0]-t[1],e[1]]]},t.prototype._createBarGroup=function(e){var t=this._orient,n=this.visualMapModel.get("inverse");return new Mc("horizontal"!==t||n?"horizontal"===t&&n?{scaleX:"bottom"===e?-1:1,rotation:-Math.PI/2}:"vertical"!==t||n?{scaleX:"left"===e?1:-1}:{scaleX:"left"===e?1:-1,scaleY:-1}:{scaleX:"bottom"===e?1:-1,rotation:Math.PI/2})},t.prototype._updateHandle=function(e,t){if(this._useHandle){var n=this._shapes,i=this.visualMapModel,a=n.handleThumbs,r=n.handleLabels,o=i.itemSize,s=i.getExtent();zG([0,1],(function(l){var p=a[l];p.setStyle("fill",t.handlesColor[l]),p.y=e[l];var c=GG(e[l],[0,o[1]],s,!0),d=this.getControllerVisual(c,"symbolSize");p.scaleX=p.scaleY=d/o[0],p.x=o[0]-d/2;var u=Tf(n.handleLabelPoints[l],_f(p,this.group));r[l].setStyle({x:u[0],y:u[1],text:i.formatValueText(this._dataInterval[l]),verticalAlign:"middle",align:"vertical"===this._orient?this._applyTransform("left",n.mainGroup):"center"})}),this)}},t.prototype._showIndicator=function(e,t,n,i){var a=this.visualMapModel,r=a.getExtent(),o=a.itemSize,s=[0,o[1]],l=this._shapes,p=l.indicator;if(p){p.attr("invisible",!1);var c=this.getControllerVisual(e,"color",{convertOpacityToAlpha:!0}),d=this.getControllerVisual(e,"symbolSize"),u=GG(e,r,s,!0),m=o[0]-d/2,h={x:p.x,y:p.y};p.y=u,p.x=m;var g=Tf(l.indicatorLabelPoint,_f(p,this.group)),f=l.indicatorLabel;f.attr("invisible",!1);var y=this._applyTransform("left",l.mainGroup),v="horizontal"===this._orient;f.setStyle({text:(n||"")+a.formatValueText(t),verticalAlign:v?y:"middle",align:v?"center":y});var x={x:m,y:u,style:{fill:c}},b={style:{x:g[0],y:g[1]}};if(a.ecModel.isAnimationEnabled()&&!this._firstShowIndicator){var w={duration:100,easing:"cubicInOut",additive:!0};p.x=h.x,p.y=h.y,p.animateTo(x,w),f.animateTo(b,w)}else p.attr(x),f.attr(b);this._firstShowIndicator=!1;var S=this._shapes.handleLabels;if(S)for(var C=0;Ca[1]&&(p[1]=1/0),t&&(p[0]===-1/0?this._showIndicator(l,p[1],"< ",o):p[1]===1/0?this._showIndicator(l,p[0],"> ",o):this._showIndicator(l,l,"≈ ",o));var c=this._hoverLinkDataIndices,d=[];(t||$G(n))&&(d=this._hoverLinkDataIndices=n.findTargetDataIndices(p));var u=function(e,t){var n={},i={};return a(e||[],n),a(t||[],i,n),[r(n),r(i)];function a(e,t,n){for(var i=0,a=e.length;i=0&&(a.dimension=r,i.push(a))}})),e.getData().setVisual("visualMeta",i)}}];function QG(e,t,n,i){for(var a=t.targetVisuals[i],r=aO.prepareVisualTypes(a),o={color:hS(e.getData(),"color")},s=0,l=r.length;s0:e.splitNumber>0)&&!e.calculable?"piecewise":"continuous"})),e.registerAction(YG,XG),Rr(ZG,(function(t){e.registerVisual(e.PRIORITY.VISUAL.COMPONENT,t)})),e.registerPreprocessor(ez))}function az(e){e.registerComponentModel(RG),e.registerComponentView(HG),iz(e)}var rz=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n._pieceList=[],n}return ze(t,e),t.prototype.optionUpdated=function(t,n){e.prototype.optionUpdated.apply(this,arguments),this.resetExtent();var i=this._mode=this._determineMode();this._pieceList=[],oz[this._mode].call(this,this._pieceList),this._resetSelected(t,n);var a=this.option.categories;this.resetVisual((function(e,t){"categories"===i?(e.mappingMethod="category",e.categories=Ir(a)):(e.dataExtent=this.getExtent(),e.mappingMethod="piecewise",e.pieceList=Br(this._pieceList,(function(e){return e=Ir(e),"inRange"!==t&&(e.visual=null),e})))}))},t.prototype.completeVisualOption=function(){var t=this.option,n={},i=aO.listVisualTypes(),a=this.isCategory();function r(e,t,n){return e&&e[t]&&e[t].hasOwnProperty(n)}Rr(t.pieces,(function(e){Rr(i,(function(t){e.hasOwnProperty(t)&&(n[t]=1)}))})),Rr(n,(function(e,n){var i=!1;Rr(this.stateList,(function(e){i=i||r(t,e,n)||r(t.target,e,n)}),this),!i&&Rr(this.stateList,(function(e){(t[e]||(t[e]={}))[n]=TG(n,"inRange"===e?"active":"inactive",a)}))}),this),e.prototype.completeVisualOption.apply(this,arguments)},t.prototype._resetSelected=function(e,t){var n=this.option,i=this._pieceList,a=(t?n:e).selected||{};if(n.selected=a,Rr(i,(function(e,t){var n=this.getSelectedMapKey(e);a.hasOwnProperty(n)||(a[n]=!0)}),this),"single"===n.selectedMode){var r=!1;Rr(i,(function(e,t){var n=this.getSelectedMapKey(e);a[n]&&(r?a[n]=!1:r=!0)}),this)}},t.prototype.getItemSymbol=function(){return this.get("itemSymbol")},t.prototype.getSelectedMapKey=function(e){return"categories"===this._mode?e.value+"":e.index+""},t.prototype.getPieceList=function(){return this._pieceList},t.prototype._determineMode=function(){var e=this.option;return e.pieces&&e.pieces.length>0?"pieces":this.option.categories?"categories":"splitNumber"},t.prototype.setSelected=function(e){this.option.selected=Ir(e)},t.prototype.getValueState=function(e){var t=aO.findPieceIndex(e,this._pieceList);return null!=t&&this.option.selected[this.getSelectedMapKey(this._pieceList[t])]?"inRange":"outOfRange"},t.prototype.findTargetDataIndices=function(e){var t=[],n=this._pieceList;return this.eachTargetSeries((function(i){var a=[],r=i.getData();r.each(this.getDataDimensionIndex(r),(function(t,i){aO.findPieceIndex(t,n)===e&&a.push(i)}),this),t.push({seriesId:i.id,dataIndex:a})}),this),t},t.prototype.getRepresentValue=function(e){var t;if(this.isCategory())t=e.value;else if(null!=e.value)t=e.value;else{var n=e.interval||[];t=n[0]===-1/0&&n[1]===1/0?0:(n[0]+n[1])/2}return t},t.prototype.getVisualMeta=function(e){if(!this.isCategory()){var t=[],n=["",""],i=this,a=this._pieceList.slice();if(a.length){var r=a[0].interval[0];r!==-1/0&&a.unshift({interval:[-1/0,r]}),(r=a[a.length-1].interval[1])!==1/0&&a.push({interval:[r,1/0]})}else a.push({interval:[-1/0,1/0]});var o=-1/0;return Rr(a,(function(e){var t=e.interval;t&&(t[0]>o&&s([o,t[0]],"outOfRange"),s(t.slice()),o=t[1])}),this),{stops:t,outerColors:n}}function s(a,r){var o=i.getRepresentValue({interval:a});r||(r=i.getValueState(o));var s=e(o,r);a[0]===-1/0?n[0]=s:a[1]===1/0?n[1]=s:t.push({value:a[0],color:s},{value:a[1],color:s})}},t.type="visualMap.piecewise",t.defaultOption=gy(AG.defaultOption,{selected:null,minOpen:!1,maxOpen:!1,align:"auto",itemWidth:20,itemHeight:14,itemSymbol:"roundRect",pieces:null,categories:null,splitNumber:5,selectedMode:"multiple",itemGap:10,hoverLink:!0}),t}(AG),oz={splitNumber:function(e){var t=this.option,n=Math.min(t.precision,20),i=this.getExtent(),a=t.splitNumber;a=Math.max(parseInt(a,10),1),t.splitNumber=a;for(var r=(i[1]-i[0])/a;+r.toFixed(n)!==r&&n<5;)n++;t.precision=n,r=+r.toFixed(n),t.minOpen&&e.push({interval:[-1/0,i[0]],close:[0,0]});for(var o=0,s=i[0];o","≥"][t[0]]];e.text=e.text||this.formatValueText(null!=e.value?e.value:e.interval,!1,n)}),this)}};function sz(e,t){var n=e.inverse;("vertical"===e.orient?!n:n)&&t.reverse()}var lz=function(e){function t(){var n=null!==e&&e.apply(this,arguments)||this;return n.type=t.type,n}return ze(t,e),t.prototype.doRender=function(){var e=this.group;e.removeAll();var t=this.visualMapModel,n=t.get("textGap"),i=t.textStyleModel,a=i.getFont(),r=i.getTextColor(),o=this._getItemAlign(),s=t.itemSize,l=this._getViewData(),p=l.endsText,c=no(t.get("showLabel",!0),!p);p&&this._renderEndsText(e,p[0],s,c,o),Rr(l.viewPieceList,(function(i){var l=i.piece,p=new Mc;p.onclick=Gr(this._onItemClick,this,l),this._enableHoverLink(p,i.indexInModelPieceList);var d=t.getRepresentValue(l);if(this._createItemSymbol(p,d,[0,0,s[0],s[1]]),c){var u=this.visualMapModel.getValueState(d);p.add(new Pm({style:{x:"right"===o?-n:s[0]+n,y:s[1]/2,text:l.text,verticalAlign:"middle",align:o,font:a,fill:r,opacity:"outOfRange"===u?.5:1}}))}e.add(p)}),this),p&&this._renderEndsText(e,p[1],s,c,o),hv(t.get("orient"),e,t.get("itemGap")),this.renderBackground(e),this.positionGroup(e)},t.prototype._enableHoverLink=function(e,t){var n=this;e.on("mouseover",(function(){return i("highlight")})).on("mouseout",(function(){return i("downplay")}));var i=function(e){var i=n.visualMapModel;i.option.hoverLink&&n.api.dispatchAction({type:e,batch:qG(i.findTargetDataIndices(t),i)})}},t.prototype._getItemAlign=function(){var e=this.visualMapModel,t=e.option;if("vertical"===t.orient)return VG(e,this.api,e.itemSize);var n=t.align;return n&&"auto"!==n||(n="left"),n},t.prototype._renderEndsText=function(e,t,n,i,a){if(t){var r=new Mc,o=this.visualMapModel.textStyleModel;r.add(new Pm({style:jf(o,{x:i?"right"===a?n[0]:0:n[0]/2,y:n[1]/2,verticalAlign:"middle",align:i?a:"center",text:t})})),e.add(r)}},t.prototype._getViewData=function(){var e=this.visualMapModel,t=Br(e.getPieceList(),(function(e,t){return{piece:e,indexInModelPieceList:t}})),n=e.get("text"),i=e.get("orient"),a=e.get("inverse");return("horizontal"===i?a:!a)?t.reverse():n&&(n=n.slice().reverse()),{viewPieceList:t,endsText:n}},t.prototype._createItemSymbol=function(e,t,n){e.add(PS(this.getControllerVisual(t,"symbol"),n[0],n[1],n[2],n[3],this.getControllerVisual(t,"color")))},t.prototype._onItemClick=function(e){var t=this.visualMapModel,n=t.option,i=n.selectedMode;if(i){var a=Ir(n.selected),r=t.getSelectedMapKey(e);"single"===i||!0===i?(a[r]=!0,Rr(a,(function(e,t){a[t]=t===r}))):a[r]=!a[r],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:a})}},t.type="visualMap.piecewise",t}(NG);function pz(e){e.registerComponentModel(rz),e.registerComponentView(lz),iz(e)}function cz(e){aI(az),aI(pz)}Cd();var dz={value:"eq","<":"lt","<=":"lte",">":"gt",">=":"gte","=":"eq","!=":"ne","<>":"ne"},uz=function(){function e(e){if(null==(this._condVal=Hr(e)?new RegExp(e):eo(e)?e:null)){var t="";0,sd(t)}}return e.prototype.evaluate=function(e){var t=typeof e;return Hr(t)?this._condVal.test(e):!!$r(t)&&this._condVal.test(e+"")},e}(),mz=function(){function e(){}return e.prototype.evaluate=function(){return this.value},e}(),hz=function(){function e(){}return e.prototype.evaluate=function(){for(var e=this.children,t=0;t=_z:-l>=_z),u=l>0?l%_z:l%_z+_z,m=!1;m=!!d||!tp(c)&&u>=Cz==!!p;var h=e+n*Sz(r),g=t+i*wz(r);this._start&&this._add("M",h,g);var f=Math.round(a*Tz);if(d){var y=1/this._p,v=(p?1:-1)*(_z-y);this._add("A",n,i,f,1,+p,e+n*Sz(r+v),t+i*wz(r+v)),y>.01&&this._add("A",n,i,f,0,+p,h,g)}else{var x=e+n*Sz(o),b=t+i*wz(o);this._add("A",n,i,f,+m,+p,x,b)}},e.prototype.rect=function(e,t,n,i){this._add("M",e,t),this._add("l",n,0),this._add("l",0,i),this._add("l",-n,0),this._add("Z")},e.prototype.closePath=function(){this._d.length>0&&this._add("Z")},e.prototype._add=function(e,t,n,i,a,r,o,s,l){for(var p=[],c=this._p,d=1;d"}(a,r)+("style"!==a?Jo(o):o||"")+(i?""+n+Br(i,(function(t){return e(t)})).join(n)+n:"")+("")}(e)}function Lz(e){return{zrId:e,shadowCache:{},patternCache:{},gradientCache:{},clipPathCache:{},defs:{},cssNodes:{},cssAnims:{},cssStyleCache:{},cssAnimIdx:0,shadowIdx:0,gradientIdx:0,patternIdx:0,clipPathIdx:0}}function Vz(e,t,n,i){return Bz("svg","root",{width:e,height:t,xmlns:Oz,"xmlns:xlink":Az,version:"1.1",baseProfile:"full",viewBox:!!i&&"0 0 "+e+" "+t},n)}var qz=0;function Gz(){return qz++}var zz={cubicIn:"0.32,0,0.67,0",cubicOut:"0.33,1,0.68,1",cubicInOut:"0.65,0,0.35,1",quadraticIn:"0.11,0,0.5,0",quadraticOut:"0.5,1,0.89,1",quadraticInOut:"0.45,0,0.55,1",quarticIn:"0.5,0,0.75,0",quarticOut:"0.25,1,0.5,1",quarticInOut:"0.76,0,0.24,1",quinticIn:"0.64,0,0.78,0",quinticOut:"0.22,1,0.36,1",quinticInOut:"0.83,0,0.17,1",sinusoidalIn:"0.12,0,0.39,0",sinusoidalOut:"0.61,1,0.88,1",sinusoidalInOut:"0.37,0,0.63,1",exponentialIn:"0.7,0,0.84,0",exponentialOut:"0.16,1,0.3,1",exponentialInOut:"0.87,0,0.13,1",circularIn:"0.55,0,1,0.45",circularOut:"0,0.55,0.45,1",circularInOut:"0.85,0,0.15,1"},Uz="transform-origin";function jz(e,t,n){var i=kr({},e.shape);kr(i,t),e.buildPath(n,i);var a=new Iz;return a.reset(dp(e)),n.rebuildPath(a,1),a.generateStr(),a.getStr()}function Hz(e,t){var n=t.originX,i=t.originY;(n||i)&&(e[Uz]=n+"px "+i+"px")}var Wz={fill:"fill",opacity:"opacity",lineWidth:"stroke-width",lineDashOffset:"stroke-dashoffset"};function $z(e,t){var n=t.zrId+"-ani-"+t.cssAnimIdx++;return t.cssAnims[n]=e,n}function Kz(e){return Hr(e)?zz[e]?"cubic-bezier("+zz[e]+")":_l(e)?e:"":""}function Yz(e,t,n,i){var a=e.animators,r=a.length,o=[];if(e instanceof Gg){var s=function(e,t,n){var i,a,r=e.shape.paths,o={};if(Rr(r,(function(e){var t=Lz(n.zrId);t.animation=!0,Yz(e,{},t,!0);var r=t.cssAnims,s=t.cssNodes,l=qr(r),p=l.length;if(p){var c=r[a=l[p-1]];for(var d in c){var u=c[d];o[d]=o[d]||{d:""},o[d].d+=u.d||""}for(var m in s){var h=s[m].animation;h.indexOf(a)>=0&&(i=h)}}})),i){t.d=!1;var s=$z(o,n);return i.replace(a,s)}}(e,t,n);if(s)o.push(s);else if(!r)return}else if(!r)return;for(var l={},p=0;p0})).length)return $z(c,n)+" "+a[0]+" both"}for(var f in l){(s=g(l[f]))&&o.push(s)}if(o.length){var y=n.zrId+"-cls-"+Gz();n.cssNodes["."+y]={animation:o.join(",")},t.class=y}}function Xz(e,t,n,i){var a=JSON.stringify(e),r=n.cssStyleCache[a];r||(r=n.zrId+"-cls-"+Gz(),n.cssStyleCache[a]=r,n.cssNodes["."+r+(i?":hover":"")]=e),t.class=t.class?t.class+" "+r:r}var Zz=Math.round;function Qz(e){return e&&Hr(e.src)}function Jz(e){return e&&jr(e.toDataURL)}function eU(e,t,n,i){Dz((function(a,r){var o="fill"===a||"stroke"===a;o&&pp(r)?dU(t,e,a,i):o&&op(r)?uU(n,e,a,i):e[a]=o&&"none"===r?"transparent":r}),t,n,!1),function(e,t,n){var i=e.style;if(function(e){return e&&(e.shadowBlur||e.shadowOffsetX||e.shadowOffsetY)}(i)){var a=function(e){var t=e.style,n=e.getGlobalScale();return[t.shadowColor,(t.shadowBlur||0).toFixed(2),(t.shadowOffsetX||0).toFixed(2),(t.shadowOffsetY||0).toFixed(2),n[0],n[1]].join(",")}(e),r=n.shadowCache,o=r[a];if(!o){var s=e.getGlobalScale(),l=s[0],p=s[1];if(!l||!p)return;var c=i.shadowOffsetX||0,d=i.shadowOffsetY||0,u=i.shadowBlur,m=Jl(i.shadowColor),h=m.opacity,g=m.color,f=u/2/l+" "+u/2/p;o=n.zrId+"-s"+n.shadowIdx++,n.defs[o]=Bz("filter",o,{id:o,x:"-100%",y:"-100%",width:"300%",height:"300%"},[Bz("feDropShadow","",{dx:c/l,dy:d/p,stdDeviation:f,"flood-color":g,"flood-opacity":h})]),r[a]=o}t.filter=cp(o)}}(n,e,i)}function tU(e,t){var n=Rc(t);n&&(n.each((function(t,n){null!=t&&(e[(Fz+n).toLowerCase()]=t+"")})),t.isSilent()&&(e[Fz+"silent"]="true"))}function nU(e){return tp(e[0]-1)&&tp(e[1])&&tp(e[2])&&tp(e[3]-1)}function iU(e,t,n){if(t&&(!function(e){return tp(e[4])&&tp(e[5])}(t)||!nU(t))){var i=n?10:1e4;e.transform=nU(t)?"translate("+Zz(t[4]*i)/i+" "+Zz(t[5]*i)/i+")":function(e){return"matrix("+np(e[0])+","+np(e[1])+","+np(e[2])+","+np(e[3])+","+ip(e[4])+","+ip(e[5])+")"}(t)}}function aU(e,t,n){for(var i=e.points,a=[],r=0;r=0&&o||r;s&&(a=Zl(s))}var l=i.lineWidth;l&&(l/=!i.strokeNoScale&&e.transform?e.transform[0]:1);var p={cursor:"pointer"};a&&(p.fill=a),i.stroke&&(p.stroke=i.stroke),l&&(p["stroke-width"]=l),Xz(p,t,n,!0)}}(e,r,t),Bz(s,e.id+"",r)}function cU(e,t){return e instanceof gm?pU(e,t):e instanceof bm?function(e,t){var n=e.style,i=n.image;if(i&&!Hr(i)&&(Qz(i)?i=i.src:Jz(i)&&(i=i.toDataURL())),i){var a=n.x||0,r=n.y||0,o={href:i,width:n.width,height:n.height};return a&&(o.x=a),r&&(o.y=r),iU(o,e.transform),eU(o,n,e,t),tU(o,e),t.animation&&Yz(e,o,t),Bz("image",e.id+"",o)}}(e,t):e instanceof ym?function(e,t){var n=e.style,i=n.text;if(null!=i&&(i+=""),i&&!isNaN(n.x)&&!isNaN(n.y)){var a=n.font||sr,r=n.x||0,o=function(e,t,n){return"top"===n?e+=t/2:"bottom"===n&&(e-=t/2),e}(n.y||0,gc(a),n.textBaseline),s={"dominant-baseline":"central","text-anchor":ap[n.textAlign]||n.textAlign};if(Bm(n)){var l="",p=n.fontStyle,c=Fm(n.fontSize);if(!parseFloat(c))return;var d=n.fontFamily||or,u=n.fontWeight;l+="font-size:"+c+";font-family:"+d+";",p&&"normal"!==p&&(l+="font-style:"+p+";"),u&&"normal"!==u&&(l+="font-weight:"+u+";"),s.style=l}else s.style="font: "+a;return i.match(/\s/)&&(s["xml:space"]="preserve"),r&&(s.x=r),o&&(s.y=o),iU(s,e.transform),eU(s,n,e,t),tU(s,e),t.animation&&Yz(e,s,t),Bz("text",e.id+"",s,void 0,i)}}(e,t):void 0}function dU(e,t,n,i){var a,r=e[n],o={gradientUnits:r.global?"userSpaceOnUse":"objectBoundingBox"};if(sp(r))a="linearGradient",o.x1=r.x,o.y1=r.y,o.x2=r.x2,o.y2=r.y2;else{if(!lp(r))return void 0;a="radialGradient",o.cx=io(r.x,.5),o.cy=io(r.y,.5),o.r=io(r.r,.5)}for(var s=r.colorStops,l=[],p=0,c=s.length;pl?EU(e,null==n[d+1]?null:n[d+1].elm,n,s,d):MU(e,t,o,l))}(n,i,a):CU(a)?(CU(e.text)&&bU(n,""),EU(n,null,a,0,a.length-1)):CU(i)?MU(n,i,0,i.length-1):CU(e.text)&&bU(n,""):e.text!==t.text&&(CU(i)&&MU(n,i,0,i.length-1),bU(n,t.text)))}var DU=0,OU=function(){function e(e,t,n){if(this.type="svg",this.refreshHover=AU("refreshHover"),this.configLayer=AU("configLayer"),this.storage=t,this._opts=n=kr({},n),this.root=e,this._id="zr"+DU++,this._oldVNode=Vz(n.width,n.height),e&&!n.ssr){var i=this._viewport=document.createElement("div");i.style.cssText="position:relative;overflow:hidden";var a=this._svgDom=this._oldVNode.elm=Rz("svg");kU(null,this._oldVNode),i.appendChild(a),e.appendChild(i)}this.resize(n.width,n.height)}return e.prototype.getType=function(){return this.type},e.prototype.getViewportRoot=function(){return this._viewport},e.prototype.getViewportRootOffset=function(){var e=this.getViewportRoot();if(e)return{offsetLeft:e.offsetLeft||0,offsetTop:e.offsetTop||0}},e.prototype.getSvgDom=function(){return this._svgDom},e.prototype.refresh=function(){if(this.root){var e=this.renderToVNode({willUpdate:!0});e.attrs.style="position:absolute;left:0;top:0;user-select:none",function(e,t){if(TU(e,t))PU(e,t);else{var n=e.elm,i=vU(n);IU(t),null!==i&&(gU(i,t.elm,xU(n)),MU(i,[e],0,0))}}(this._oldVNode,e),this._oldVNode=e}},e.prototype.renderOneToVNode=function(e){return cU(e,Lz(this._id))},e.prototype.renderToVNode=function(e){e=e||{};var t=this.storage.getDisplayList(!0),n=this._width,i=this._height,a=Lz(this._id);a.animation=e.animation,a.willUpdate=e.willUpdate,a.compress=e.compress,a.emphasis=e.emphasis;var r=[],o=this._bgVNode=function(e,t,n,i){var a;if(n&&"none"!==n)if(a=Bz("rect","bg",{width:e,height:t,x:"0",y:"0"}),pp(n))dU({fill:n},a.attrs,"fill",i);else if(op(n))uU({style:{fill:n},dirty:wo,getBoundingRect:function(){return{width:e,height:t}}},a.attrs,"fill",i);else{var r=Jl(n),o=r.color,s=r.opacity;a.attrs.fill=o,s<1&&(a.attrs["fill-opacity"]=s)}return a}(n,i,this._backgroundColor,a);o&&r.push(o);var s=e.compress?null:this._mainVNode=Bz("g","main",{},[]);this._paintList(t,a,s?s.children:r),s&&r.push(s);var l=Br(qr(a.defs),(function(e){return a.defs[e]}));if(l.length&&r.push(Bz("defs","defs",{},l)),e.animation){var p=function(e,t,n){var i=(n=n||{}).newline?"\n":"",a=" {"+i,r=i+"}",o=Br(qr(e),(function(t){return t+a+Br(qr(e[t]),(function(n){return n+":"+e[t][n]+";"})).join(i)+r})).join(i),s=Br(qr(t),(function(e){return"@keyframes "+e+a+Br(qr(t[e]),(function(n){return n+a+Br(qr(t[e][n]),(function(i){var a=t[e][n][i];return"d"===i&&(a='path("'+a+'")'),i+":"+a+";"})).join(i)+r})).join(i)+r})).join(i);return o||s?[""].join(i):""}(a.cssNodes,a.cssAnims,{newline:!0});if(p){var c=Bz("style","stl",{},[],p);r.push(c)}}return Vz(n,i,r,e.useViewBox)},e.prototype.renderToString=function(e){return e=e||{},Nz(this.renderToVNode({animation:io(e.cssAnimation,!0),emphasis:io(e.cssEmphasis,!0),willUpdate:!1,compress:!0,useViewBox:io(e.useViewBox,!0)}),{newline:!0})},e.prototype.setBackgroundColor=function(e){this._backgroundColor=e},e.prototype.getSvgRoot=function(){return this._mainVNode&&this._mainVNode.elm},e.prototype._paintList=function(e,t,n){for(var i,a,r=e.length,o=[],s=0,l=0,p=0;p=0&&(!d||!a||d[h]!==a[h]);h--);for(var g=m-1;g>h;g--)i=o[--s-1];for(var f=h+1;f=o)}}for(var c=this.__startIndex;c15)break}n.prevElClipPaths&&d.restore()};if(m)if(0===m.length)s=l.__endIndex;else for(var b=u.dpr,w=0;w0&&e>i[0]){for(s=0;se);s++);o=n[i[s]]}if(i.splice(s+1,0,e),n[e]=t,!t.virtual)if(o){var l=o.dom;l.nextSibling?r.insertBefore(t.dom,l.nextSibling):r.appendChild(t.dom)}else r.firstChild?r.insertBefore(t.dom,r.firstChild):r.appendChild(t.dom);t.painter||(t.painter=this)}},e.prototype.eachLayer=function(e,t){for(var n=this._zlevelList,i=0;i0?VU:0),this._needsManuallyCompositing),p.__builtin__||Tr("ZLevel "+l+" has been used by unkown layer "+p.id),p!==r&&(p.__used=!0,p.__startIndex!==a&&(p.__dirty=!0),p.__startIndex=a,p.incremental?p.__drawIndex=-1:p.__drawIndex=a,t(a),r=p),1&s.__dirty&&!s.__inHover&&(p.__dirty=!0,p.incremental&&p.__drawIndex<0&&(p.__drawIndex=a))}t(a),this.eachBuiltinLayer((function(e,t){!e.__used&&e.getElementCount()>0&&(e.__dirty=!0,e.__startIndex=e.__endIndex=e.__drawIndex=0),e.__dirty&&e.__drawIndex<0&&(e.__drawIndex=e.__startIndex)}))},e.prototype.clear=function(){return this.eachBuiltinLayer(this._clearLayer),this},e.prototype._clearLayer=function(e){e.clear()},e.prototype.setBackgroundColor=function(e){this._backgroundColor=e,Rr(this._layers,(function(e){e.setUnpainted()}))},e.prototype.configLayer=function(e,t){if(t){var n=this._layerConfig;n[e]?Er(n[e],t,!0):n[e]=t;for(var i=0;i({legendKey:e,left:!0}),jU=e=>({background:e});function HU(e,n){1&e&&t.ɵɵelementContainer(0)}function WU(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,HU,1,0,"ng-container",8),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e.widgetTitlePanel)}}function $U(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Min")))}function KU(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Max")))}function YU(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Avg")))}function XU(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Total")))}function ZU(e,n){1&e&&(t.ɵɵelementStart(0,"th",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"legend.Latest")))}function QU(e,n){1&e&&t.ɵɵelementContainer(0)}function JU(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].min,"html"),t.ɵɵsanitizeHtml)}}function ej(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].max,"html"),t.ɵɵsanitizeHtml)}}function tj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].avg,"html"),t.ɵɵsanitizeHtml)}}function nj(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].total,"html"),t.ɵɵsanitizeHtml)}}function ij(e,n){if(1&e&&(t.ɵɵelement(0,"td",16),t.ɵɵpipe(1,"safe")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(1,1,n.legendData.data[e.dataIndex].latest,"html"),t.ɵɵsanitizeHtml)}}function aj(e,n){if(1&e&&(t.ɵɵelementStart(0,"tr")(1,"th"),t.ɵɵtemplate(2,QU,1,0,"ng-container",14),t.ɵɵelementEnd(),t.ɵɵtemplate(3,JU,2,4,"td",15)(4,ej,2,4,"td",15)(5,tj,2,4,"td",15)(6,nj,2,4,"td",15)(7,ij,2,4,"td",15),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2),a=t.ɵɵreference(8);t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",a)("ngTemplateOutletContext",t.ɵɵpureFunction1(7,UU,e)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showMin),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showMax),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showAvg),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showTotal),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===i.legendConfig.showLatest)}}function rj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"table",10)(2,"thead")(3,"tr"),t.ɵɵelement(4,"th"),t.ɵɵtemplate(5,$U,3,3,"th",11)(6,KU,3,3,"th",11)(7,YU,3,3,"th",11)(8,XU,3,3,"th",11)(9,ZU,3,3,"th",11),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"tbody"),t.ɵɵtemplate(11,aj,8,9,"tr",12),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngIf",!0===e.legendConfig.showMin),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showMax),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showAvg),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showTotal),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!0===e.legendConfig.showLatest),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",e.legendKeys)}}function oj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",18),t.ɵɵelement(2,"div",19)(3,"div",20),t.ɵɵpipe(4,"safe"),t.ɵɵelementEnd()()),2&e){const e=n.legendKey,i=n.left;t.ɵɵclassProp("left",i),t.ɵɵadvance(2),t.ɵɵstyleMap(t.ɵɵpureFunction1(8,jU,e.dataKey.color)),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(4,5,e.dataKey.label,"html"),t.ɵɵsanitizeHtml)}}class sj{constructor(e,t,n){this.renderer=e,this.sanitizer=t,this.widgetComponent=n,this.updateXAxisTimeWindow=(e,t)=>{e.min=t.minTime,e.max=t.maxTime}}ngOnInit(){this.initEchart(),this.initLegend()}ngAfterViewInit(){this.myChart=function(e,t,n){var i=!(n&&n.ssr);if(i){var a=n_(e);if(a)return a}var r=new GC(e,t,n);return r.id="ec_"+e_++,QC[r.id]=r,i&&Pd(e,t_,r.id),BC(r),sC.trigger("afterinit",r),r}(this.echartContainer.nativeElement,null,{renderer:"svg"}),this.initResize(),this.xAxis=this.setupXAxis(),this.yAxis=this.setupYAxis(),this.option={...this.setupAnimationSettings(),formatter:e=>this.setupTooltipElement(e),backgroundColor:"transparent",darkMode:!1,tooltip:{show:!0,trigger:"axis",confine:!0,padding:[8,12],appendTo:"body",textStyle:{fontFamily:"Roboto",fontSize:12,fontWeight:"normal",lineHeight:16}},grid:[{backgroundColor:null,borderColor:"#ccc",borderWidth:1,bottom:45,left:5,right:5,show:!1,top:10}],xAxis:[this.xAxis],yAxis:[this.yAxis],series:this.setupChartLines(),dataZoom:[{type:"inside",disabled:!1,realtime:!0,filterMode:"none"},{type:"slider",show:!0,showDetail:!1,realtime:!0,filterMode:"none",bottom:5}]},this.myChart.setOption(this.option),this.updateAxisOffset(!1)}onDataUpdated(){const e=[];this.onResize(),this.updateXAxisTimeWindow(this.xAxis,this.ctx.defaultSubscription.timeWindow);for(const t in this.ctx.data){e[t]=[];for(const[n,i]of this.ctx.data[t].data)e[t].push({name:n,value:[n,i]})}const t=[];for(const n of e)t.push({data:n});this.option.series=t,this.myChart.setOption(this.option),this.updateAxisOffset()}updateAxisOffset(e=!0){const t=Ze(this.myChart,this.yAxis.mainType,this.yAxis.id),n=Qe(this.myChart,this.yAxis.mainType,this.yAxis.id,this.yAxis.name),i=Ze(this.myChart,this.xAxis.mainType,this.xAxis.id),a=t+n,r=i+Qe(this.myChart,this.yAxis.mainType,this.yAxis.id,this.yAxis.name)+35;this.option.grid[0].left===a&&this.option.grid[0].bottom===r||(this.option.grid[0].left=a,this.yAxis.nameGap=t,this.option.grid[0].bottom=r,this.xAxis.nameGap=i,this.myChart.setOption(this.option,{replaceMerge:["yAxis","xAxis","grid"],lazyUpdate:e}))}initEchart(){aI([XV,kN,cz,_G,Nq,sL,tP,JE,TM,UM,nP,jB,hE,GU,FU])}initLegend(){this.showLegend=this.ctx.settings.showLegend,this.showLegend&&(this.legendConfig=this.ctx.settings.legendConfig,this.legendData=this.ctx.defaultSubscription.legendData,this.legendKeys=this.legendData.keys,this.legendClass=`legend-${this.legendConfig.position}`,this.legendConfig.sortDataKeys?this.legendKeys=this.legendData.keys.sort(((e,t)=>e.dataKey.label.localeCompare(t.dataKey.label))):this.legendKeys=this.legendData.keys)}initResize(){this.shapeResize$=new ResizeObserver((()=>{this.onResize()})),this.shapeResize$.observe(this.echartContainer.nativeElement)}onResize(){this.myChart.resize()}setupTooltipElement(e){const t=this.renderer.createElement("div");if(this.renderer.setStyle(t,"display","flex"),this.renderer.setStyle(t,"flex-direction","column"),this.renderer.setStyle(t,"align-items","flex-start"),this.renderer.setStyle(t,"gap","16px"),e.length){const n=this.renderer.createElement("div");this.renderer.setStyle(n,"display","flex"),this.renderer.setStyle(n,"flex-direction","column"),this.renderer.setStyle(n,"align-items","flex-start"),this.renderer.setStyle(n,"gap","4px"),this.renderer.appendChild(n,this.setTooltipDate(e));for(const[t,i]of e.entries())this.renderer.appendChild(n,this.constructTooltipSeriesElement(i,t));this.renderer.appendChild(t,n)}return t}constructTooltipSeriesElement(e,t){const n=this.renderer.createElement("div");this.renderer.setStyle(n,"display","flex"),this.renderer.setStyle(n,"flex-direction","row"),this.renderer.setStyle(n,"align-items","center"),this.renderer.setStyle(n,"align-self","stretch"),this.renderer.setStyle(n,"gap","12px");const i=this.renderer.createElement("div");this.renderer.setStyle(i,"display","flex"),this.renderer.setStyle(i,"align-items","center"),this.renderer.setStyle(i,"gap","8px"),this.renderer.appendChild(n,i);const a=this.renderer.createElement("div");this.renderer.setStyle(a,"width","8px"),this.renderer.setStyle(a,"height","8px"),this.renderer.setStyle(a,"border-radius","50%"),this.renderer.setStyle(a,"background",e.color),this.renderer.appendChild(i,a);const r=this.renderer.createElement("div");this.renderer.setProperty(r,"innerHTML",this.sanitizer.sanitize(g.HTML,e.seriesName)),this.renderer.setStyle(r,"font-family","Roboto"),this.renderer.setStyle(r,"font-size","12px"),this.renderer.setStyle(r,"font-style","normal"),this.renderer.setStyle(r,"font-weight",400),this.renderer.setStyle(r,"line-height","16px"),this.renderer.setStyle(r,"color","rgba(0, 0, 0, 0.76)"),this.renderer.appendChild(i,r);const o=ke(this.ctx.data[t].dataKey.decimals)?this.ctx.data[t].dataKey.decimals:this.ctx.decimals,s=ke(this.ctx.data[t].dataKey.units)?this.ctx.data[t].dataKey.units:this.ctx.units,l=Pe(e.value[1],o,s,!1),p=this.renderer.createElement("div");return this.renderer.setProperty(p,"innerHTML",this.sanitizer.sanitize(g.HTML,l)),this.renderer.setStyle(p,"flex","1"),this.renderer.setStyle(p,"text-align","end"),this.renderer.setStyle(p,"font-family","Roboto"),this.renderer.setStyle(p,"font-size","12px"),this.renderer.setStyle(p,"font-style","normal"),this.renderer.setStyle(p,"font-weight",500),this.renderer.setStyle(p,"line-height","16px"),this.renderer.setStyle(p,"color","rgba(0, 0, 0, 0.76)"),this.renderer.appendChild(n,p),n}setTooltipDate(e){const t=this.renderer.createElement("div");return this.renderer.appendChild(t,this.renderer.createText(new Date(e[0].value[0]).toLocaleString("en-GB"))),this.renderer.setStyle(t,"font-family","Roboto"),this.renderer.setStyle(t,"font-size","11px"),this.renderer.setStyle(t,"font-style","normal"),this.renderer.setStyle(t,"font-weight","400"),this.renderer.setStyle(t,"line-height","16px"),this.renderer.setStyle(t,"color","rgba(0, 0, 0, 0.76)"),t}setupAnimationSettings(){return{animation:!0,animationDelay:0,animationDelayUpdate:0,animationDuration:500,animationDurationUpdate:300,animationEasing:"cubicOut",animationEasingUpdate:"cubicOut",animationThreshold:2e3}}setupChartLines(){const e=[];for(const[t,n]of this.ctx.datasources[0].dataKeys.entries())e.push({id:t,name:n.label,type:"line",showSymbol:!1,smooth:!1,step:!1,stackStrategy:"all",data:[],lineStyle:{color:n.color},itemStyle:{color:n.color}});return e}setupYAxis(){return{type:"value",position:"left",mainType:"yAxis",id:"yAxis",offset:0,nameLocation:"middle",nameRotate:90,alignTicks:!0,scale:!0,show:!0,axisLabel:{color:"rgba(0, 0, 0, 0.54)",fontFamily:"Roboto",fontSize:12,fontStyle:"normal",fontWeight:400,show:!0,formatter:e=>Pe(e,this.ctx.decimals,this.ctx.units,!1)},splitLine:{show:!0},axisLine:{show:!0,lineStyle:{color:"rgba(0, 0, 0, 0.54)"}},axisTick:{lineStyle:{color:"rgba(0, 0, 0, 0.54)"},show:!0},nameTextStyle:{color:"rgba(0, 0, 0, 0.54)",fontFamily:"Roboto",fontSize:12,fontStyle:"normal",fontWeight:600}}}setupXAxis(){return{id:"xAxis",mainType:"xAxis",show:!0,type:"time",position:"bottom",offset:0,nameLocation:"middle",max:this.ctx.defaultSubscription.timeWindow.maxTime,min:this.ctx.defaultSubscription.timeWindow.minTime,nameTextStyle:{color:"rgba(0, 0, 0, 0.54)",fontStyle:"normal",fontWeight:600,fontFamily:"Roboto",fontSize:12},axisPointer:{shadowStyle:{color:"rgba(210,219,238,0.2)"}},splitLine:{show:!0},axisTick:{show:!0,lineStyle:{color:"rgba(0, 0, 0, 0.54)"}},axisLine:{onZero:!1,show:!0,lineStyle:{color:"rgba(0, 0, 0, 0.54)"}},axisLabel:{color:"rgba(0, 0, 0, 0.54)",fontFamily:"Roboto",fontSize:10,fontStyle:"normal",fontWeight:400,show:!0,hideOverlap:!0}}}static{this.ɵfac=function(e){return new(e||sj)(t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(Je.DomSanitizer),t.ɵɵdirectiveInject(et.WidgetComponent))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:sj,selectors:[["tb-gateway-statistics-chart"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(zU,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.echartContainer=e.first)}},inputs:{ctx:"ctx",widgetTitlePanel:"widgetTitlePanel"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:9,vars:4,consts:[["echartContainer",""],["legendItem",""],[1,"tb-time-series-chart-panel"],[1,"tb-time-series-chart-overlay"],[4,"ngIf"],[1,"tb-time-series-chart-content"],[1,"tb-time-series-chart-shape"],["class","tb-time-series-chart-legend",4,"ngIf"],[4,"ngTemplateOutlet"],[1,"tb-time-series-chart-legend"],[1,"tb-time-series-chart-legend-table","vertical"],["class","tb-time-series-chart-legend-type-label right legend legend-row-color",4,"ngIf"],[4,"ngFor","ngForOf"],[1,"tb-time-series-chart-legend-type-label","right","legend","legend-row-color"],[4,"ngTemplateOutlet","ngTemplateOutletContext"],["class","tb-time-series-chart-legend-value legend",3,"innerHTML",4,"ngIf"],[1,"tb-time-series-chart-legend-value","legend",3,"innerHTML"],[1,"tb-time-series-chart-legend-item"],[1,"tb-time-series-chart-legend-item-label"],[1,"tb-time-series-chart-legend-item-label-circle"],[1,"legend","legend-label-color",3,"innerHTML"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",2),t.ɵɵelement(1,"div",3),t.ɵɵtemplate(2,WU,2,1,"ng-container",4),t.ɵɵelementStart(3,"div",5),t.ɵɵelement(4,"div",6,0),t.ɵɵtemplate(6,rj,12,6,"div",7)(7,oj,5,10,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.widgetComponent.dashboardWidget.showWidgetTitlePanel),t.ɵɵadvance(),t.ɵɵclassMap(n.legendClass),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.showLegend))},dependencies:t.ɵɵgetComponentDepsFactory(sj,[j,_]),styles:['@charset "UTF-8";.tb-time-series-chart-panel[_ngcontent-%COMP%]{width:100%;height:100%;position:relative;display:flex;flex-direction:column;gap:8px;padding:12px}.tb-time-series-chart-panel[_ngcontent-%COMP%] > div[_ngcontent-%COMP%]:not(.tb-time-series-chart-overlay){z-index:1}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-overlay[_ngcontent-%COMP%]{position:absolute;inset:12px}.tb-time-series-chart-panel[_ngcontent-%COMP%] div.tb-widget-title[_ngcontent-%COMP%]{padding:0}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;gap:8px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%]{flex-direction:column-reverse}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-right[_ngcontent-%COMP%]{flex-direction:row}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-left[_ngcontent-%COMP%]{flex-direction:row-reverse}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-shape[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;display:flex;align-items:center;justify-content:center}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-right[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-left[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]{display:inline-grid;grid-auto-flow:column;grid-template-rows:repeat(auto-fit,minmax(16px,min-content));max-width:calc(25% - 8px);height:fit-content;max-height:100%}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-bottom[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]{align-self:center}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%] .tb-time-series-chart-legend.tb-simple-legend[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-bottom[_ngcontent-%COMP%] .tb-time-series-chart-legend.tb-simple-legend[_ngcontent-%COMP%]{justify-content:center}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-top[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]:not(.tb-simple-legend), .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content.legend-bottom[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]:not(.tb-simple-legend){width:100%}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%]{display:flex;align-items:flex-start;align-self:stretch;column-gap:16px;row-gap:8px;flex-wrap:wrap;overflow:auto;width:fit-content;max-width:100%;max-height:calc(35% - 8px)}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%]{border-spacing:0;table-layout:fixed}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table.vertical[_ngcontent-%COMP%]{width:100%;table-layout:auto}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table.vertical[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] th[_ngcontent-%COMP%]{width:95%}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%]:not(:last-child), .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%]:not(:last-child){padding-right:16px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] th[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] td[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:not(:last-child) th[_ngcontent-%COMP%], .tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:not(:last-child) td[_ngcontent-%COMP%]{padding-bottom:8px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%]{align-items:flex-end}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-table[_ngcontent-%COMP%] .tb-time-series-chart-legend-item.left[_ngcontent-%COMP%]{align-items:flex-start}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%]{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;-webkit-user-select:none;user-select:none}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%] .tb-time-series-chart-legend-item-label[_ngcontent-%COMP%]{display:flex;align-items:center;gap:4px;color:#ccc;white-space:nowrap;cursor:pointer}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-item[_ngcontent-%COMP%] .tb-time-series-chart-legend-item-label[_ngcontent-%COMP%] .tb-time-series-chart-legend-item-label-circle[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:50%;background-color:#ccc}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-type-label[_ngcontent-%COMP%]{white-space:nowrap;text-align:left}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-type-label.right[_ngcontent-%COMP%]{text-align:right}.tb-time-series-chart-panel[_ngcontent-%COMP%] .tb-time-series-chart-content[_ngcontent-%COMP%] .tb-time-series-chart-legend[_ngcontent-%COMP%] .tb-time-series-chart-legend-value[_ngcontent-%COMP%]{white-space:nowrap;text-align:right}.tb-time-series-chart-panel[_ngcontent-%COMP%] .legend[_ngcontent-%COMP%]{font-size:12px;font-style:normal;font-weight:500;letter-spacing:normal;line-height:16px}.tb-time-series-chart-panel[_ngcontent-%COMP%] .legend.legend-row-color[_ngcontent-%COMP%]{color:#00000061}.tb-time-series-chart-panel[_ngcontent-%COMP%] .legend.legend-label-color[_ngcontent-%COMP%]{color:#000}']})}}const lj=["statisticChart"];function pj(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",12),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.openEditCommandDialog())})),t.ɵɵelementStart(2,"mat-icon",13),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",12),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.onDeleteClick())})),t.ɵɵelementStart(6,"mat-icon",13),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function cj(e,n){if(1&e&&t.ɵɵelement(0,"tb-gateway-statistics-chart",14,0),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("ctx",e.ctx)}}function dj(e,n){if(1&e&&t.ɵɵelement(0,"tb-custom-statistics-table",15),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("data",e.subscriptionData)}}function uj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10),t.ɵɵtemplate(1,cj,2,1,"tb-gateway-statistics-chart",14)(2,dj,1,1,"tb-custom-statistics-table",15),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵconditional(e.isNumericData?1:2)}}function mj(e,n){1&e&&(t.ɵɵelementStart(0,"div",11),t.ɵɵelement(1,"div",16),t.ɵɵelementStart(2,"div",17),t.ɵɵtext(3,"attribute.no-telemetry-text"),t.ɵɵelementEnd()())}class hj{constructor(e,t,n,i,a,r){this.fb=e,this.attributeService=t,this.destroyRef=n,this.dialog=i,this.dialogService=a,this.utils=r,this.subscriptionData=[],this.statisticForm=this.fb.group({command:[]}),this.isNumericData=!1,this.commands=[],this.subscribed=!1,this.dataTypeDefined=!1,this.subscriptionOptions={callbacks:{onDataUpdated:()=>this.ctx.ngZone.run((()=>{this.isDataOnlyNumbers(),this.isNumericData&&this.statisticChart?.onDataUpdated()})),onDataUpdateError:(e,t)=>this.ctx.ngZone.run((()=>{this.onDataUpdateError(t)}))},useDashboardTimewindow:!1,legendConfig:F(R.timeseries)},this.statisticForm.get("command").valueChanges.pipe(bn()).subscribe((e=>{this.subscribed=!1,this.subscriptionInfo&&e?.attributeOnGateway&&this.createSubscription(this.ctx.defaultSubscription.datasources[0].entity,e.attributeOnGateway)}))}ngAfterViewInit(){if(this.ctx.defaultSubscription.datasources.length){const e=this.ctx.defaultSubscription.datasources[0].entity;if(e.id.id===B)return;this.getGatewayGeneralConfig().pipe(bn(this.destroyRef)).subscribe((t=>{this.commands=t?.statistics.commands.reverse()??[],this.commands.length&&(this.statisticForm.get("command").setValue(this.commands[0]),this.createSubscription(e,this.commands[0].attributeOnGateway))}))}}openEditCommandDialog(){const e=this.statisticForm.get("command").value,t="string"==typeof e||!e,n="string"==typeof e?{attributeOnGateway:e}:e;let i;this.dialog.open(ir,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{titleText:t?"gateway.statistics.create-command":"gateway.statistics.edit-command",buttonText:t?"action.add":"action.apply",command:n,existingCommands:this.commands.map((e=>e.attributeOnGateway))}}).afterClosed().pipe(xe((e=>re(this.getGatewayGeneralConfig(),ae(e)))),xe((([e,t])=>(this.commands=[...e?.statistics.commands.filter((e=>e.attributeOnGateway!==t?.prev?.attributeOnGateway))??[],...t?.current?[{...t.current}]:[]],i=t?.current,this.updateStatisticsCommands(e,this.commands)))),bn(this.destroyRef)).subscribe((()=>{i&&this.statisticForm.get("command").patchValue(i)}))}onDeleteClick(){const e=this.statisticForm.get("command").value.attributeOnGateway;this.dialogService.confirm(this.ctx.translate.instant("gateway.statistics.delete-command",{command:e}),this.ctx.translate.instant("gateway.statistics.delete-command-data"),this.ctx.translate.instant("action.cancel"),this.ctx.translate.instant("action.confirm")).pipe(ve(Boolean),xe((()=>this.getGatewayGeneralConfig())),xe((t=>(this.commands=[...t.statistics.commands.filter((t=>t.attributeOnGateway!==e))],this.updateStatisticsCommands(t,this.commands)))),bn(this.destroyRef)).subscribe()}getGatewayGeneralConfig(){const e=this.ctx.defaultSubscription.datasources[0].entity;return e.id.id===B?ae(null):this.attributeService.getEntityAttributes(e.id,D.SHARED_SCOPE,["general_configuration"]).pipe(pe((e=>e[0]?.value)))}updateStatisticsCommands(e,t){const n=this.ctx.defaultSubscription.datasources[0].entity;return n.id.id!==B&&e?this.attributeService.saveEntityAttributes(n.id,D.SHARED_SCOPE,[{key:"general_configuration",value:{...e,statistics:{...e.statistics,commands:t}}}]):ae(null)}createSubscription(e,t){const n=[{type:N.entity,entityType:L.DEVICE,entityId:e.id.id,entityName:e.name,timeseries:[]}];n[0].timeseries=[{name:t,label:t,settings:{}}],this.subscriptionInfo=n,this.changeSubscription(n)}onDataUpdateError(e){const t=this.utils.parseException(e);let n=t.name;t.message&&(n+=": "+t.message),console.error(n)}isDataOnlyNumbers(){this.subscriptionData=this.ctx.defaultSubscription.data[0]?.data??[],this.subscriptionData.length&&!this.dataTypeDefined&&(this.isNumericData=this.subscriptionData.every((e=>!isNaN(+e[1]))),this.dataTypeDefined=!0),this.ctx.detectChanges()}changeSubscription(e){this.ctx.defaultSubscription?.unsubscribe(),this.ctx.datasources[0].entity&&this.ctx.subscriptionApi.createSubscriptionFromInfo(R.timeseries,e,this.subscriptionOptions,!1,!0).pipe(bn(this.destroyRef)).subscribe((e=>{this.dataTypeDefined=!1,this.ctx.defaultSubscription=e,this.ctx.settings.showLegend=!1,this.ctx.data=e.data,this.ctx.datasources=e.datasources,this.isDataOnlyNumbers(),this.subscribed=!0}))}static{this.ɵfac=function(e){return new(e||hj)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(_e.AttributeService),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(_e.DialogService),t.ɵɵdirectiveInject(_e.UtilsService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:hj,selectors:[["tb-gateway-statistics"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(lj,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.statisticChart=e.first)}},inputs:{ctx:"ctx"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:18,vars:13,consts:[["statisticChart",""],[1,"flex","flex-1","flex-col","max-h-full"],[1,"entry-container","flex","items-center"],[1,"tb-form-panel","stroked","w-full","flex-1",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"entry-container","flex","w-full","gap-2"],["formControlName","command",1,"flex-1",3,"onCreateNewClicked","commands"],["appearance","outline",1,"flex-1"],["matInput","","disabled","",3,"tbTruncateWithTooltip","value"],[1,"actions-container","flex","flex-col","p-2","min-w-16"],[1,"chart-box","flex","flex-1","flex-col","overflow-auto"],[1,"tb-no-data-available","h-full"],["type","button","matSuffix","","mat-icon-button","","aria-label","Edit","matTooltipPosition","above",1,"action-button",3,"click","matTooltip"],[1,"material-icons"],[1,"flex-1",3,"ctx"],[1,"h-full","flex-1",3,"data"],[1,"tb-no-data-bg"],["translate","",1,"tb-no-data-text"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"div",2)(2,"div",3)(3,"div",4),t.ɵɵtext(4,"gateway.statistics.entry"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",5)(6,"tb-statistics-commands-autocomplete",6),t.ɵɵlistener("onCreateNewClicked",(function(){return n.openEditCommandDialog()})),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-form-field",7)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",8),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(14,"div",9),t.ɵɵtemplate(15,pj,8,6),t.ɵɵelementEnd()(),t.ɵɵtemplate(16,uj,3,1,"div",10)(17,mj,4,0,"div",11),t.ɵɵelementEnd()),2&e){let e,i,a,r;t.ɵɵadvance(2),t.ɵɵproperty("formGroup",n.statisticForm),t.ɵɵadvance(4),t.ɵɵproperty("commands",n.commands),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,7,"gateway.statistics.command")),t.ɵɵadvance(2),t.ɵɵproperty("tbTruncateWithTooltip",null!==(e=null==(e=n.statisticForm.get("command").value)?null:e.command)&&void 0!==e?e:t.ɵɵpipeBind1(12,9,"gateway.statistics.no-config-commands-found"))("value",null!==(i=null==(i=n.statisticForm.get("command").value)?null:i.command)&&void 0!==i?i:t.ɵɵpipeBind1(13,11,"gateway.statistics.no-config-commands-found")),t.ɵɵadvance(4),t.ɵɵconditional(null!=(a=n.statisticForm.get("command").value)&&a.attributeOnGateway?15:-1),t.ɵɵadvance(),t.ɵɵconditional(null!=(r=n.statisticForm.get("command").value)&&r.attributeOnGateway&&n.subscriptionData.length&&n.subscribed?16:17)}},dependencies:t.ɵɵgetComponentDepsFactory(hj,[j,_,Dn,Vn,sj]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;padding:4px;display:flex;flex-direction:column}[_nghost-%COMP%] .action-button[_ngcontent-%COMP%]{opacity:.7}@media screen and (max-width: 599px){[_nghost-%COMP%] .entry-container[_ngcontent-%COMP%]{flex-direction:column}[_nghost-%COMP%] .actions-container[_ngcontent-%COMP%]{flex-direction:row}}']})}}var gj;e("GatewayStatisticsComponent",hj),e("BACnetRequestTypes",gj),function(e){e.WriteProperty="writeProperty",e.ReadProperty="readProperty"}(gj||e("BACnetRequestTypes",gj={}));const fj=e("BACnetRequestTypesTranslates",new Map([[gj.WriteProperty,"gateway.rpc.write-property"],[gj.ReadProperty,"gateway.rpc.read-property"]]));var yj;e("BACnetObjectTypes",yj),function(e){e.BinaryInput="binaryInput",e.BinaryOutput="binaryOutput",e.AnalogInput="analogInput",e.AnalogOutput="analogOutput",e.BinaryValue="binaryValue",e.AnalogValue="analogValue"}(yj||e("BACnetObjectTypes",yj={}));const vj=e("BACnetObjectTypesTranslates",new Map([[yj.AnalogOutput,"gateway.rpc.analog-output"],[yj.AnalogInput,"gateway.rpc.analog-input"],[yj.BinaryOutput,"gateway.rpc.binary-output"],[yj.BinaryInput,"gateway.rpc.binary-input"],[yj.BinaryValue,"gateway.rpc.binary-value"],[yj.AnalogValue,"gateway.rpc.analog-value"]]));var xj;e("BLEMethods",xj),function(e){e.WRITE="write",e.READ="read",e.SCAN="scan"}(xj||e("BLEMethods",xj={}));const bj=e("BLEMethodsTranslates",new Map([[xj.WRITE,"gateway.rpc.write"],[xj.READ,"gateway.rpc.read"],[xj.SCAN,"gateway.rpc.scan"]]));var wj,Sj;e("CANByteOrders",wj),function(e){e.LITTLE="LITTLE",e.BIG="BIG"}(wj||e("CANByteOrders",wj={})),e("SocketMethodProcessings",Sj),function(e){e.WRITE="write",e.READ="read"}(Sj||e("SocketMethodProcessings",Sj={}));const Cj=e("SocketMethodProcessingsTranslates",new Map([[Sj.WRITE,"gateway.rpc.write"],[Sj.READ,"gateway.rpc.read"]]));var _j;e("SNMPMethods",_j),function(e){e.SET="set",e.MULTISET="multiset",e.GET="get",e.BULKWALK="bulkwalk",e.TABLE="table",e.MULTIGET="multiget",e.GETNEXT="getnext",e.BULKGET="bulkget",e.WALKS="walk"}(_j||e("SNMPMethods",_j={}));const Tj=e("SNMPMethodsTranslations",new Map([[_j.SET,"gateway.rpc.set"],[_j.MULTISET,"gateway.rpc.multiset"],[_j.GET,"gateway.rpc.get"],[_j.BULKWALK,"gateway.rpc.bulk-walk"],[_j.TABLE,"gateway.rpc.table"],[_j.MULTIGET,"gateway.rpc.multi-get"],[_j.GETNEXT,"gateway.rpc.get-next"],[_j.BULKGET,"gateway.rpc.bulk-get"],[_j.WALKS,"gateway.rpc.walk"]]));var Ij,Ej;e("SocketEncodings",Ij),function(e){e.UTF_8="utf-8"}(Ij||e("SocketEncodings",Ij={})),e("RestSecurityType",Ej),function(e){e.ANONYMOUS="anonymous",e.BASIC="basic"}(Ej||e("RestSecurityType",Ej={}));const Mj=e("RestSecurityTypeTranslationsMap",new Map([[Ej.ANONYMOUS,"gateway.broker.security-types.anonymous"],[Ej.BASIC,"gateway.broker.security-types.basic"]]));class kj{transform(e){return e.map((e=>(e?.value??e).toString())).join(", ")}static{this.ɵfac=function(e){return new(e||kj)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"getRpcTemplateArrayView",type:kj,pure:!0,standalone:!0})}}e("RpcTemplateArrayViewPipe",kj);class Pj{constructor(){this.differs=i(f),this.keyValues=[]}transform(e){if(!e||!(e instanceof Map)&&"object"!=typeof e)return null;this.differ??=this.differs.find(e).create();const t=this.differ.diff(e);return t&&(this.keyValues=[],t.forEachItem((e=>{ke(e.currentValue)&&this.keyValues.push(this.makeKeyValuePair(e.key,e.currentValue))}))),this.keyValues}makeKeyValuePair(e,t){return{key:e,value:t}}static{this.ɵfac=function(e){return new(e||Pj)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"keyValueIsNotEmpty",type:Pj,pure:!1,standalone:!0})}}e("KeyValueIsNotEmptyPipe",Pj);const Dj=e=>({$implicit:e,innerValue:!1}),Oj=e=>({"padding-left":e}),Aj=(e,t)=>({"boolean-true":e,"boolean-false":t}),Fj=e=>({$implicit:e,innerValue:!0});function Rj(e,n){if(1&e&&t.ɵɵelementContainer(0,13),2&e){const e=n.$implicit;t.ɵɵnextContext();const i=t.ɵɵreference(15);t.ɵɵproperty("ngTemplateOutlet",i)("ngTemplateOutletContext",t.ɵɵpureFunction1(2,Dj,e))}}function Bj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",19),t.ɵɵtext(1),t.ɵɵpipe(2,"getRpcTemplateArrayView"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,e.value)," ")}}function Nj(e,n){if(1&e&&t.ɵɵelementContainer(0,20),2&e){t.ɵɵnextContext();const e=t.ɵɵreference(12);t.ɵɵproperty("ngTemplateOutlet",e)}}function Lj(e,n){if(1&e&&t.ɵɵelementContainer(0,20),2&e){t.ɵɵnextContext(2);const e=t.ɵɵreference(10);t.ɵɵproperty("ngTemplateOutlet",e)}}function Vj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div"),t.ɵɵtemplate(1,Lj,1,1,"ng-container",21),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵreference(8),i=t.ɵɵnextContext().$implicit,a=t.ɵɵnextContext();t.ɵɵclassMap(t.ɵɵpureFunction2(4,Aj,!0===e.value,!1===e.value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",i.type===a.ConnectorType.SNMP&&"method"===e.key)("ngIfElse",n)}}function qj(e,n){if(1&e&&t.ɵɵtext(0),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵtextInterpolate(e.value)}}function Gj(e,n){if(1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵtextInterpolate(t.ɵɵpipeBind1(1,1,n.SNMPMethodsTranslations.get(e.value)))}}function zj(e,n){if(1&e&&t.ɵɵelementContainer(0,13),2&e){const e=n.$implicit;t.ɵɵnextContext(3);const i=t.ɵɵreference(15);t.ɵɵproperty("ngTemplateOutlet",i)("ngTemplateOutletContext",t.ɵɵpureFunction1(2,Fj,e))}}function Uj(e,n){if(1&e&&(t.ɵɵtemplate(0,zj,1,4,"ng-container",12),t.ɵɵpipe(1,"keyvalue")),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("ngForOf",t.ɵɵpipeBind2(1,1,e.value,n.originalOrder))}}function jj(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",14)(1,"div",15),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,Bj,3,3,"div",16)(5,Nj,1,1,"ng-container",17)(6,Vj,2,7,"div",18)(7,qj,1,1,"ng-template",null,1,t.ɵɵtemplateRefExtractor)(9,Gj,2,3,"ng-template",null,2,t.ɵɵtemplateRefExtractor)(11,Uj,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=n.innerValue,a=t.ɵɵnextContext(2);t.ɵɵstyleMap(t.ɵɵpureFunction1(10,Oj,i?"16px":"0")),t.ɵɵclassMap(a.getRpcParamsRowClasses(e.value)),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",i?e.key:t.ɵɵpipeBind1(3,8,"gateway.rpc."+e.key)," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",a.isArray(e.value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",a.isObject(e.value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!a.isObject(e.value)&&!a.isArray(e.value))}}function Hj(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-expansion-panel",6)(1,"mat-expansion-panel-header")(2,"mat-panel-title",7)(3,"span",8),t.ɵɵtext(4),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"mat-panel-description")(6,"button",9),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteTemplate(n,i))})),t.ɵɵelementStart(7,"mat-icon",10),t.ɵɵtext(8,"delete"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",11),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.applyTemplate(n,i))})),t.ɵɵelementStart(10,"mat-icon",10),t.ɵɵtext(11,"play_arrow"),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(12,Rj,1,4,"ng-container",12),t.ɵɵpipe(13,"keyValueIsNotEmpty"),t.ɵɵtemplate(14,jj,13,12,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()}if(2&e){const e=n.$implicit;t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",e.name),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(13,3,e.config))}}class Wj{constructor(e){this.attributeService=e,this.saveTemplate=new u,this.useTemplate=new u,this.ConnectorType=ct,this.originalOrder=()=>0,this.isObject=e=>De(e),this.isArray=e=>Array.isArray(e),this.SNMPMethodsTranslations=Tj}applyTemplate(e,t){e.stopPropagation(),this.useTemplate.emit(t)}deleteTemplate(e,t){e.stopPropagation();const n=this.rpcTemplates.findIndex((e=>e.name==t.name));this.rpcTemplates.splice(n,1);const i=`${this.connectorType}_template`;this.attributeService.saveEntityAttributes({id:this.ctx.defaultSubscription.targetDeviceId,entityType:L.DEVICE},D.SERVER_SCOPE,[{key:i,value:this.rpcTemplates}]).subscribe((()=>{}))}getRpcParamsRowClasses(e){return this.isObject(e)?"flex-col":"flex-row justify-between items-center"}static{this.ɵfac=function(e){return new(e||Wj)(t.ɵɵdirectiveInject(_e.AttributeService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Wj,selectors:[["tb-gateway-service-rpc-connector-templates"]],inputs:{connectorType:"connectorType",ctx:"ctx",rpcTemplates:"rpcTemplates"},outputs:{saveTemplate:"saveTemplate",useTemplate:"useTemplate"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:4,vars:4,consts:[["RPCTemplateRef",""],["value",""],["SNMPMethod",""],["RPCObjectRow",""],[1,"mat-subtitle-1","title"],["hideToggle","",4,"ngFor","ngForOf"],["hideToggle",""],[1,"template-name"],["matTooltipPosition","above",3,"matTooltip"],["mat-icon-button","","matTooltip","Delete",3,"click"],[1,"material-icons"],["mat-icon-button","","matTooltip","Use",3,"click"],[3,"ngTemplateOutlet","ngTemplateOutletContext",4,"ngFor","ngForOf"],[3,"ngTemplateOutlet","ngTemplateOutletContext"],[1,"rpc-params-row","flex"],[1,"template-key"],["tbTruncateWithTooltip","","class","array-value",4,"ngIf"],[3,"ngTemplateOutlet",4,"ngIf"],[3,"class",4,"ngIf"],["tbTruncateWithTooltip","",1,"array-value"],[3,"ngTemplateOutlet"],[3,"ngTemplateOutlet",4,"ngIf","ngIfElse"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",4),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(3,Hj,16,5,"mat-expansion-panel",5)),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,"gateway.rpc.templates-title")),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.rpcTemplates))},dependencies:t.ɵɵgetComponentDepsFactory(Wj,[j,_,kj,Pj]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex;flex-direction:column;padding:0}[_nghost-%COMP%] .title[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .template-key[_ngcontent-%COMP%]{color:#00000061;height:32px;line-height:32px}[_nghost-%COMP%] .boolean-true[_ngcontent-%COMP%], [_nghost-%COMP%] .boolean-false[_ngcontent-%COMP%]{border-radius:3px;height:32px;line-height:32px;padding:0 12px;width:fit-content;font-size:14px;text-transform:capitalize}[_nghost-%COMP%] .boolean-false[_ngcontent-%COMP%]{color:#d12730;background-color:#d1273014}[_nghost-%COMP%] .boolean-true[_ngcontent-%COMP%]{color:#198038;background-color:#19803814}[_nghost-%COMP%] mat-expansion-panel[_ngcontent-%COMP%]{margin-top:10px;overflow:visible}[_nghost-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%]{flex-direction:row-reverse;align-items:center;margin-right:0;flex:0}[_nghost-%COMP%] .mat-expansion-panel-header-description[_ngcontent-%COMP%] > mat-icon[_ngcontent-%COMP%]{margin-left:15px;color:#00000061}[_nghost-%COMP%] .mat-expansion-panel-header[_ngcontent-%COMP%]{padding:0 0 0 12px}[_nghost-%COMP%] .mat-expansion-panel-header.mat-expansion-panel-header.mat-expanded[_ngcontent-%COMP%]{height:48px}[_nghost-%COMP%] .mat-expansion-panel-header[_ngcontent-%COMP%] .mat-content.mat-content-hide-toggle[_ngcontent-%COMP%]{margin-right:0}[_nghost-%COMP%] .rpc-params-row[_ngcontent-%COMP%]{overflow:hidden;white-space:nowrap}[_nghost-%COMP%] .rpc-params-row[_ngcontent-%COMP%] [_ngcontent-%COMP%]:not(:first-child){white-space:pre;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .template-name[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;display:block}[_nghost-%COMP%] .mat-content{align-items:center}[_nghost-%COMP%] .mat-expansion-panel-header-title[_ngcontent-%COMP%]{flex:1;margin:0}[_nghost-%COMP%] .array-value[_ngcontent-%COMP%]{margin-left:10px}']})}}function $j(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rpc.template-name-required")," "))}function Kj(e,n){1&e&&(t.ɵɵelementStart(0,"div",12),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rpc.template-name-duplicate")," "))}e("GatewayServiceRPCConnectorTemplatesComponent",Wj);class Yj extends A{constructor(e,t,n,i,a){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.config=this.data.config,this.templates=this.data.templates,this.templateNameCtrl=this.fb.control("",[$.required])}validateDuplicateName(e){const t=e.value.trim();return!!this.templates.find((e=>e.name===t))}close(){this.dialogRef.close()}save(){this.templateNameCtrl.setValue(this.templateNameCtrl.value.trim()),this.templateNameCtrl.valid&&this.dialogRef.close(this.templateNameCtrl.value)}static{this.ɵfac=function(e){return new(e||Yj)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Yj,selectors:[["tb-gateway-service-rpc-connector-template-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:20,vars:10,consts:[["color","primary",1,"justify-between"],["translate",""],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"mat-content","flex","flex-col",2,"width","600px"],[1,"mat-block","tb-value-type",2,"flex-grow","0"],["matInput","","required","",3,"formControl"],[4,"ngIf"],["class","mat-mdc-form-field-error","style","margin-top: -15px; padding-left: 10px; font-size: 14px;",4,"ngIf"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"mat-mdc-form-field-error",2,"margin-top","-15px","padding-left","10px","font-size","14px"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-toolbar",0)(1,"h2",1),t.ɵɵtext(2,"gateway.rpc.save-template"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"button",2),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵelementStart(4,"mat-icon",3),t.ɵɵtext(5,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(6,"div",4)(7,"mat-form-field",5)(8,"mat-label",1),t.ɵɵtext(9,"gateway.rpc.template-name"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",6),t.ɵɵtemplate(11,$j,3,3,"mat-error",7),t.ɵɵelementEnd(),t.ɵɵtemplate(12,Kj,3,3,"div",8),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",9)(14,"button",10),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵlistener("click",(function(){return n.save()})),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(10),t.ɵɵproperty("formControl",n.templateNameCtrl),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.templateNameCtrl.hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.validateDuplicateName(n.templateNameCtrl)),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(16,6,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",!n.templateNameCtrl.valid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(19,8,"action.save")," "))},dependencies:t.ɵɵgetComponentDepsFactory(Yj,[j,_]),encapsulation:2})}}function Xj(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",6),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SecurityTypeTranslationsMap.get(e))," ")}}function Zj(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.username-required"))}function Qj(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.password-required"))}function Jj(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",7)(2,"div",8),t.ɵɵtext(3,"gateway.username"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",9)(5,"mat-form-field",10),t.ɵɵelement(6,"input",11),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,Zj,3,3,"mat-icon",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",7)(10,"div",8),t.ɵɵtext(11,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"div",9)(13,"mat-form-field",10),t.ɵɵelement(14,"input",13),t.ɵɵpipe(15,"translate"),t.ɵɵtemplate(16,Qj,3,3,"mat-icon",12),t.ɵɵelementStart(17,"div",14),t.ɵɵelement(18,"tb-toggle-password",15),t.ɵɵelementEnd()()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,6,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("username").hasError("required")&&e.securityFormGroup.get("username").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("password").hasError("required")&&e.securityFormGroup.get("password").touched),t.ɵɵadvance(),t.ɵɵclassProp("hide-toggle",e.securityFormGroup.get("password").hasError("required"))}}e("GatewayServiceRPCConnectorTemplateDialogComponent",Yj);class eH{constructor(e){this.fb=e,this.BrokerSecurityType=Ej,this.securityTypes=Object.values(Ej),this.SecurityTypeTranslationsMap=Mj,this.destroy$=new te,this.propagateChange=e=>{},this.securityFormGroup=this.fb.group({type:[Ej.ANONYMOUS,[]],username:["",[$.required,$.pattern(an)]],password:["",[$.required,$.pattern(an)]]}),this.observeSecurityForm()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}writeValue(e){e.type||(e.type=Ej.ANONYMOUS),this.securityFormGroup.reset(e),this.updateView(e)}validate(){return this.securityFormGroup.valid?null:{securityForm:{valid:!1}}}updateView(e){this.propagateChange(e)}updateValidators(e){e===Ej.BASIC?(this.securityFormGroup.get("username").enable({emitEvent:!1}),this.securityFormGroup.get("password").enable({emitEvent:!1})):(this.securityFormGroup.get("username").disable({emitEvent:!1}),this.securityFormGroup.get("password").disable({emitEvent:!1}))}observeSecurityForm(){this.securityFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.updateView(e))),this.securityFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.updateValidators(e)))}static{this.ɵfac=function(e){return new(e||eH)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:eH,selectors:[["tb-rest-connector-security"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>eH)),multi:!0},{provide:K,useExisting:c((()=>eH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:7,vars:3,consts:[[1,"tb-form-row","space-between","same-padding","tb-flex","column",3,"formGroup"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fields-label"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[4,"ngIf"],[3,"value"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["translate","",1,"fixed-title-width"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","username",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","password","name","value","formControlName","password",3,"placeholder"],["matSuffix","",1,"tb-flex","no-gap","align-center","fill-height"],[1,"tb-flex","align-center","fill-height"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.security"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"tb-toggle-select",3),t.ɵɵtemplate(5,Xj,3,4,"tb-toggle-option",4),t.ɵɵelementEnd()(),t.ɵɵtemplate(6,Jj,19,10,"ng-container",5),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.securityFormGroup),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.securityTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.securityFormGroup.get("type").value===n.BrokerSecurityType.BASIC))},dependencies:t.ɵɵgetComponentDepsFactory(eH,[_,j]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block;margin-bottom:10px}[_nghost-%COMP%] .fields-label[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .hide-toggle[_ngcontent-%COMP%]{display:none}'],changeDetection:d.OnPush})}}e("RestConnectorSecurityComponent",eH);const tH=e=>({type:e});function nH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.bACnetRequestTypesTranslates.get(e))," ")}}function iH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.bACnetObjectTypesTranslates.get(e))," ")}}function aH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",9),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",10)(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",11),t.ɵɵtemplate(10,nH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field")(12,"mat-label"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",13),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",14)(17,"mat-form-field",15)(18,"mat-label"),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-select",16),t.ɵɵtemplate(22,iH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"mat-form-field",15)(24,"mat-label"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",17),t.ɵɵelementEnd()(),t.ɵɵelementStart(28,"mat-form-field",10)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",18),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,8,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,10,"gateway.rpc.requestType")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.bACnetRequestTypes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,12,"gateway.rpc.requestTimeout")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,14,"gateway.rpc.objectType")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.bACnetObjectTypes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(26,16,"gateway.rpc.identifier")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,18,"gateway.rpc.propertyId"))}}function rH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.bLEMethodsTranslates.get(e))," ")}}function oH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",20),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",21),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-form-field",10)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-select",22),t.ɵɵtemplate(15,rH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"mat-slide-toggle",23),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,5,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,7,"gateway.rpc.characteristicUUID")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,9,"gateway.rpc.methodProcessing")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.bLEMethods),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,11,"gateway.rpc.withResponse")," ")}}function sH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e)," ")}}function lH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",24),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",25),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-slide-toggle",26),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-slide-toggle",27),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-slide-toggle",28),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",14)(20,"mat-form-field",15)(21,"mat-label"),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",29),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",15)(26,"mat-label"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-select",30),t.ɵɵtemplate(30,sH,3,4,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(31,"div",14)(32,"mat-form-field",15)(33,"mat-label"),t.ɵɵtext(34),t.ɵɵpipe(35,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(36,"input",31),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"mat-form-field",15)(38,"mat-label"),t.ɵɵtext(39),t.ɵɵpipe(40,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(41,"input",32),t.ɵɵelementEnd()(),t.ɵɵelementStart(42,"mat-form-field")(43,"mat-label"),t.ɵɵtext(44),t.ɵɵpipe(45,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(46,"input",33),t.ɵɵelementEnd(),t.ɵɵelementStart(47,"mat-form-field")(48,"mat-label"),t.ɵɵtext(49),t.ɵɵpipe(50,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(51,"input",34),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,12,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,14,"gateway.rpc.nodeID")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,16,"gateway.rpc.isExtendedID")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,18,"gateway.rpc.isFD")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,20,"gateway.rpc.bitrateSwitch")," "),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(23,22,"gateway.rpc.dataLength")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,24,"gateway.rpc.dataByteorder")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.cANByteOrders),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(35,26,"gateway.rpc.dataBefore")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(40,28,"gateway.rpc.dataAfter")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(45,30,"gateway.rpc.dataInHEX")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(50,32,"gateway.rpc.dataExpression"))}}function pH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",35),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",36),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,2,"gateway.rpc.methodFilter")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,4,"gateway.rpc.valueExpression")))}function cH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",37),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",36),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-slide-toggle",38),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,5,"gateway.rpc.valueExpression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,7,"gateway.rpc.withResponse")," "))}function dH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",37),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",36),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-slide-toggle",38),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,5,"gateway.rpc.valueExpression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,7,"gateway.rpc.withResponse")," "))}function uH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SNMPMethodsTranslations.get(e))," ")}}function mH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",45)(1,"mat-form-field",46),t.ɵɵelement(2,"input",47),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-icon",48),t.ɵɵpipe(4,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext(3);return t.ɵɵresetView(i.removeSNMPoid(n))})),t.ɵɵtext(5,"delete "),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵproperty("formControl",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(4,2,"gateway.rpc.remove"))}}function hH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",39),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",10)(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",40),t.ɵɵtemplate(10,uH,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-slide-toggle",38),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"fieldset",41)(15,"span",42),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(18,mH,6,4,"div",43),t.ɵɵelementStart(19,"button",44),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.addSNMPoid())})),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,7,"gateway.rpc.requestFilter")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,9,"gateway.rpc.method")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.sNMPMethods),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,11,"gateway.rpc.withResponse")," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(17,13,"gateway.rpc.oids"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",e.getFormArrayControls("oid")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(21,15,"gateway.rpc.add-oid")," ")}}function gH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function fH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",59),t.ɵɵelementContainerStart(1,63),t.ɵɵelementStart(2,"mat-form-field",64),t.ɵɵelement(3,"input",65),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",64),t.ɵɵelement(5,"input",66),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-icon",67),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext(4);return t.ɵɵresetView(i.removeHTTPHeader(n))})),t.ɵɵtext(8,"delete "),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()}if(2&e){const e=n.index;t.ɵɵadvance(),t.ɵɵproperty("formGroupName",e),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,2,"gateway.rpc.remove"))}}function yH(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",58)(1,"div",59)(2,"span",60),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"span",60),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"span",61),t.ɵɵelementEnd(),t.ɵɵelement(9,"mat-divider"),t.ɵɵtemplate(10,fH,9,4,"div",62),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rpc.header-name")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,5,"gateway.rpc.value")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.getFormArrayControls("httpHeaders"))}}function vH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",49),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",14)(6,"mat-form-field",50)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-select",51),t.ɵɵtemplate(11,gH,2,2,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",15)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",52),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",14)(18,"mat-form-field",15)(19,"mat-label"),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(22,"input",53),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",15)(24,"mat-label"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",54),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-form-field",15)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",55),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"mat-form-field")(34,"mat-label"),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",36),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"fieldset",56)(39,"span",42),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(42,yH,11,7,"div",57),t.ɵɵelementStart(43,"button",44),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.addHTTPHeader())})),t.ɵɵtext(44),t.ɵɵpipe(45,"translate"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,11,"gateway.rpc.methodFilter")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,13,"gateway.rpc.httpMethod")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.hTTPMethods),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,15,"gateway.rpc.requestUrlExpression")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(21,17,"gateway.rpc.responseTimeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(26,19,"gateway.rpc.timeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,21,"gateway.rpc.tries")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,23,"gateway.rpc.valueExpression")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,25,"gateway.rpc.httpHeaders")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.getFormArrayControls("httpHeaders").length),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(45,27,"gateway.rpc.add-header")," ")}}function xH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",19),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function bH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",59),t.ɵɵelementContainerStart(1,63),t.ɵɵelementStart(2,"mat-form-field",64),t.ɵɵelement(3,"input",73),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",64),t.ɵɵelement(6,"input",74),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-icon",67),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext(4);return t.ɵɵresetView(i.removeHTTPHeader(n))})),t.ɵɵtext(9,"delete "),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()}if(2&e){const e=n.index;t.ɵɵadvance(),t.ɵɵproperty("formGroupName",e),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(4,3,"gateway.rpc.set")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,5,"gateway.rpc.remove"))}}function wH(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",58)(1,"div",59)(2,"span",60),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"span",60),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"span",61),t.ɵɵelementEnd(),t.ɵɵelement(9,"mat-divider"),t.ɵɵtemplate(10,bH,10,7,"div",62),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rpc.header-name")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,5,"gateway.rpc.value")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.getFormArrayControls("httpHeaders"))}}function SH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",68),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",59)(6,"mat-form-field",50)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-select",51),t.ɵɵtemplate(11,xH,2,2,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",15)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",52),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",59)(18,"mat-form-field",15)(19,"mat-label"),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(22,"input",53),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",15)(24,"mat-label"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",69),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-form-field",15)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",70),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"mat-form-field")(34,"mat-label"),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",71),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"mat-form-field")(39,"mat-label"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(42,"input",72),t.ɵɵelementEnd(),t.ɵɵelementStart(43,"fieldset",56)(44,"span",42),t.ɵɵtext(45),t.ɵɵpipe(46,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(47,wH,11,7,"div",57),t.ɵɵelementStart(48,"button",44),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.addHTTPHeader())})),t.ɵɵtext(49),t.ɵɵpipe(50,"translate"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,12,"gateway.rpc.methodFilter")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,14,"gateway.rpc.httpMethod")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.hTTPMethods),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,16,"gateway.rpc.requestUrlExpression")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(21,18,"gateway.rpc.responseTimeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(26,20,"gateway.rpc.timeout")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,22,"gateway.rpc.tries")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,24,"gateway.rpc.requestValueExpression")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,26,"gateway.rpc.responseValueExpression")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(46,28,"gateway.rpc.httpHeaders")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.getFormArrayControls("httpHeaders").length),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(50,30,"gateway.rpc.add-header")," ")}}function CH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rpc.json-value-invalid")," "))}function _H(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",75),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field")(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",76),t.ɵɵelementStart(10,"mat-icon",77),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext(2);return t.ɵɵresetView(i.openEditJSONDialog(n))})),t.ɵɵtext(12,"edit "),t.ɵɵelementEnd(),t.ɵɵtemplate(13,CH,3,3,"mat-error",78),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,4,"gateway.statistics.command")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,6,"widget-config.datasource-parameters")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,8,"gateway.rpc-command-edit-params")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.commandForm.get("params").hasError("invalidJSON"))}}function TH(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,6),t.ɵɵtemplate(1,aH,33,20,"ng-template",7)(2,oH,19,13,"ng-template",7)(3,lH,52,34,"ng-template",7)(4,pH,10,6,"ng-template",7)(5,cH,13,9,"ng-template",7)(6,dH,13,9,"ng-template",7)(7,hH,22,17,"ng-template",7)(8,vH,46,29,"ng-template",7)(9,SH,51,32,"ng-template",7)(10,_H,14,10,"ng-template",8),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("ngSwitch",e.connectorType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.BACNET),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.BLE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.CAN),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.FTP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.OCPP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.XMPP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.SNMP),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.REST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.REQUEST)}}class IH{constructor(e,t){this.fb=e,this.dialog=t,this.sendCommand=new u,this.saveTemplate=new u,this.ConnectorType=ct,this.bACnetRequestTypes=Object.values(gj),this.bACnetObjectTypes=Object.values(yj),this.bLEMethods=Object.values(xj),this.cANByteOrders=Object.values(wj),this.sNMPMethods=Object.values(_j),this.hTTPMethods=Object.values(ht),this.bACnetRequestTypesTranslates=fj,this.bACnetObjectTypesTranslates=vj,this.bLEMethodsTranslates=bj,this.SNMPMethodsTranslations=Tj,this.gatewayConnectorDefaultTypesTranslates=dt,this.urlPattern=/^[-a-zA-Zd_$:{}?~+=\/.0-9-]*$/,this.numbersOnlyPattern=/^[0-9]*$/,this.hexOnlyPattern=/^[0-9A-Fa-f ]+$/,this.propagateChange=e=>{},this.destroy$=new te}ngOnInit(){this.commandForm=this.connectorParamsFormGroupByType(this.connectorType),this.observeFormChanges()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}connectorParamsFormGroupByType(e){let t;switch(e){case ct.BACNET:t=this.fb.group({method:[null,[$.required,$.pattern(an)]],requestType:[null,[$.required,$.pattern(an)]],requestTimeout:[null,[$.required,$.min(10),$.pattern(this.numbersOnlyPattern)]],objectType:[null,[]],identifier:[null,[$.required,$.min(1),$.pattern(this.numbersOnlyPattern)]],propertyId:[null,[$.required,$.pattern(an)]]});break;case ct.BLE:t=this.fb.group({methodRPC:[null,[$.required,$.pattern(an)]],characteristicUUID:["00002A00-0000-1000-8000-00805F9B34FB",[$.required,$.pattern(an)]],methodProcessing:[null,[$.required]],withResponse:[!1,[]]});break;case ct.CAN:t=this.fb.group({method:[null,[$.required,$.pattern(an)]],nodeID:[null,[$.required,$.min(0),$.pattern(this.numbersOnlyPattern)]],isExtendedID:[!1,[]],isFD:[!1,[]],bitrateSwitch:[!1,[]],dataLength:[null,[$.min(1),$.pattern(this.numbersOnlyPattern)]],dataByteorder:[null,[]],dataBefore:[null,[$.pattern(an),$.pattern(this.hexOnlyPattern)]],dataAfter:[null,[$.pattern(an),$.pattern(this.hexOnlyPattern)]],dataInHEX:[null,[$.pattern(an),$.pattern(this.hexOnlyPattern)]],dataExpression:[null,[$.pattern(an)]]});break;case ct.FTP:t=this.fb.group({methodFilter:[null,[$.required,$.pattern(an)]],valueExpression:[null,[$.required,$.pattern(an)]]});break;case ct.OCPP:case ct.XMPP:t=this.fb.group({methodRPC:[null,[$.required,$.pattern(an)]],valueExpression:[null,[$.required,$.pattern(an)]],withResponse:[!1,[]]});break;case ct.SNMP:t=this.fb.group({requestFilter:[null,[$.required,$.pattern(an)]],method:[null,[$.required]],withResponse:[!1,[]],oid:this.fb.array([],[$.required])});break;case ct.REST:t=this.fb.group({methodFilter:[null,[$.required,$.pattern(an)]],httpMethod:[null,[$.required]],requestUrlExpression:[null,[$.required,$.pattern(this.urlPattern)]],responseTimeout:[null,[$.required,$.min(10),$.pattern(this.numbersOnlyPattern)]],timeout:[null,[$.required,$.min(10),$.pattern(this.numbersOnlyPattern)]],tries:[null,[$.required,$.min(1),$.pattern(this.numbersOnlyPattern)]],valueExpression:[null,[$.required,$.pattern(an)]],httpHeaders:this.fb.array([]),security:[{},[$.required]]});break;case ct.REQUEST:t=this.fb.group({methodFilter:[null,[$.required,$.pattern(an)]],httpMethod:[null,[$.required]],requestUrlExpression:[null,[$.required,$.pattern(this.urlPattern)]],responseTimeout:[null,[$.required,$.min(10),$.pattern(this.numbersOnlyPattern)]],timeout:[null,[$.required,$.min(10),$.pattern(this.numbersOnlyPattern)]],tries:[null,[$.required,$.min(1),$.pattern(this.numbersOnlyPattern)]],requestValueExpression:[null,[$.required,$.pattern(an)]],responseValueExpression:[null,[$.pattern(an)]],httpHeaders:this.fb.array([])});break;default:t=this.fb.group({command:[null,[$.required,$.pattern(an)]],params:[{},[st]]})}return t}addSNMPoid(e=null){const t=this.commandForm.get("oid");t&&t.push(this.fb.control(e,[$.required,$.pattern(an)]),{emitEvent:!1})}removeSNMPoid(e){this.commandForm.get("oid").removeAt(e)}addHTTPHeader(e={headerName:null,value:null}){const t=this.commandForm.get("httpHeaders"),n=this.fb.group({headerName:[e.headerName,[$.required,$.pattern(an)]],value:[e.value,[$.required,$.pattern(an)]]});t&&t.push(n,{emitEvent:!1})}removeHTTPHeader(e){this.commandForm.get("httpHeaders").removeAt(e)}getFormArrayControls(e){return this.commandForm.get(e).controls}openEditJSONDialog(e){e&&e.stopPropagation(),this.dialog.open(tt,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{jsonValue:this.commandForm.get("params").value,required:!0}}).afterClosed().subscribe((e=>{e&&this.commandForm.get("params").setValue(e)}))}save(){this.saveTemplate.emit()}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}clearFromArrayByName(e){const t=this.commandForm.get(e);for(;0!==t.length;)t.removeAt(0)}writeValue(e){if("object"==typeof e){switch(e=Oe(e),this.connectorType){case ct.SNMP:this.clearFromArrayByName("oid"),e.oid.forEach((e=>{this.addSNMPoid(e)})),delete e.oid;break;case ct.REQUEST:case ct.REST:this.clearFromArrayByName("httpHeaders"),e.httpHeaders&&Object.entries(e.httpHeaders).forEach((e=>{this.addHTTPHeader({headerName:e[0],value:e[1]})})),delete e.httpHeaders}this.commandForm.patchValue(e,{onlySelf:!1})}}observeFormChanges(){this.commandForm.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.connectorType!==ct.REST&&this.connectorType!==ct.REQUEST||(e.httpHeaders=e.httpHeaders.reduce(((e,t)=>(e[t.headerName]=t.value,e)),{})),this.commandForm.valid&&this.propagateChange({...this.commandForm.value,...e})}))}static{this.ɵfac=function(e){return new(e||IH)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(qe.MatDialog))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:IH,selectors:[["tb-gateway-service-rpc-connector"]],inputs:{connectorType:"connectorType"},outputs:{sendCommand:"sendCommand",saveTemplate:"saveTemplate"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>IH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:12,vars:16,consts:[[1,"command-form","flex","flex-col",3,"formGroup"],[1,"mat-subtitle-1","title"],[3,"ngIf"],[1,"template-actions","flex","flex-row","justify-end","gap-2.5"],["mat-raised-button","",3,"click","disabled"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"ngSwitch"],[3,"ngSwitchCase"],["ngSwitchDefault",""],["matInput","","formControlName","method","placeholder","set_state"],[1,"mat-block"],["formControlName","requestType"],[3,"value",4,"ngFor","ngForOf"],["matInput","","formControlName","requestTimeout","type","number","min","10","step","1","placeholder","1000"],[1,"flex","flex-1","flex-row","gap-2.5"],[1,"flex-1"],["formControlName","objectType"],["matInput","","formControlName","identifier","type","number","min","1","step","1","placeholder","1"],["matInput","","formControlName","propertyId","placeholder","presentValue"],[3,"value"],["matInput","","formControlName","methodRPC","placeholder","rpcMethod1"],["matInput","","formControlName","characteristicUUID","placeholder","00002A00-0000-1000-8000-00805F9B34FB"],["formControlName","methodProcessing"],["formControlName","withResponse",1,"mat-slide"],["matInput","","formControlName","method","placeholder","sendSameData"],["matInput","","formControlName","nodeID","type","number","placeholder","4","min","0","step","1"],["formControlName","isExtendedID",1,"mat-slide","margin"],["formControlName","isFD",1,"mat-slide","margin"],["formControlName","bitrateSwitch",1,"mat-slide","margin"],["matInput","","formControlName","dataLength","type","number","placeholder","2","min","1","step","1"],["formControlName","dataByteorder"],["matInput","","formControlName","dataBefore","placeholder","00AA"],["matInput","","formControlName","dataAfter","placeholder","0102"],["matInput","","formControlName","dataInHEX","placeholder","aa bb cc dd ee ff aa bb aa bb cc d ee ff"],["matInput","","formControlName","dataExpression","placeholder","userSpeed if maxAllowedSpeed > userSpeed else maxAllowedSpeed"],["matInput","","formControlName","methodFilter","placeholder","read"],["matInput","","formControlName","valueExpression","placeholder","${params}"],["matInput","","formControlName","methodRPC","placeholder","rpc1"],["formControlName","withResponse",1,"mat-slide","margin"],["matInput","","formControlName","requestFilter","placeholder","setData"],["formControlName","method"],["formArrayName","oid",1,"fields","flex","flex-col","gap-2.5","border"],[1,"fields-label"],["class","flex flex-1 flex-row items-center justify-center gap-2.5",4,"ngFor","ngForOf"],["mat-raised-button","",1,"self-start",3,"click"],[1,"flex","flex-1","flex-row","items-center","justify-center","gap-2.5"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","flex-1"],["matInput","","required","",3,"formControl"],[1,"flex-[1_1_30px]",2,"cursor","pointer","max-width","30px","min-width","30px",3,"click","matTooltip"],["matInput","","formControlName","methodFilter","placeholder","post_attributes"],[1,"max-w-4/12","flex-[1_1_33%]"],["formControlName","httpMethod"],["matInput","","formControlName","requestUrlExpression","placeholder","http://127.0.0.1:5000/my_devices"],["matInput","","formControlName","responseTimeout","type","number","step","1","min","10","placeholder","10"],["matInput","","formControlName","timeout","type","number","step","1","min","10","placeholder","1000"],["matInput","","formControlName","tries","type","number","step","1","min","1","placeholder","3"],["formArrayName","httpHeaders",1,"fields","flex","flex-col","gap-2.5","border"],["class","flex flex-col gap-2.5 border",4,"ngIf"],[1,"flex","flex-col","gap-2.5","border"],[1,"flex","flex-row","items-center","justify-center","gap-2.5"],[1,"title","flex-1"],[2,"width","30px"],["class","flex flex-row items-center justify-center gap-2.5",4,"ngFor","ngForOf"],[3,"formGroupName"],["appearance","outline",1,"flex-1"],["matInput","","formControlName","headerName"],["matInput","","formControlName","value","placeholder","application/json"],[2,"cursor","pointer","width","30px",3,"click","matTooltip"],["matInput","","formControlName","methodFilter","placeholder","echo"],["matInput","","formControlName","timeout","type","number","step","1","min","10","placeholder","10"],["matInput","","formControlName","tries","type","number","step","1","min","1","placeholder","1"],["matInput","","formControlName","requestValueExpression","placeholder","${params}"],["matInput","","formControlName","responseValueExpression","placeholder","${temp}"],["matInput","","formControlName","headerName",3,"placeholder"],["matInput","","formControlName","value"],["matInput","","formControlName","command"],["matInput","","formControlName","params","type","JSON","tb-json-to-string",""],["aria-hidden","false","aria-label","help-icon","matIconSuffix","",1,"material-icons-outlined",2,"cursor","pointer",3,"click","matTooltip"],[4,"ngIf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,TH,11,10,"ng-template",2),t.ɵɵelementStart(5,"div",3)(6,"button",4),t.ɵɵlistener("click",(function(){return n.save()})),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",5),t.ɵɵlistener("click",(function(){return n.sendCommand.emit()})),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.commandForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(3,7,"gateway.rpc.title",t.ɵɵpureFunction1(14,tH,n.gatewayConnectorDefaultTypesTranslates.get(n.connectorType)))),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.connectorType),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.commandForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,10,"gateway.rpc-command-save-template")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.commandForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,12,"gateway.rpc-command-send")," "))},dependencies:t.ɵɵgetComponentDepsFactory(IH,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:flex;flex-direction:column;padding:0}[_nghost-%COMP%] .title[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%]{flex-wrap:nowrap}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%] > button[_ngcontent-%COMP%]{margin-top:10px}[_nghost-%COMP%] .mat-mdc-slide-toggle.margin[_ngcontent-%COMP%]{margin-bottom:10px;margin-left:10px}[_nghost-%COMP%] .fields[_ngcontent-%COMP%] .fields-label[_ngcontent-%COMP%]{font-weight:500}[_nghost-%COMP%] .border[_ngcontent-%COMP%]{padding:16px;margin-bottom:10px;box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;border:solid 1px #e0e0e0;border-radius:4px}[_nghost-%COMP%] .border[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{color:#0000008a}[_nghost-%COMP%] .border[_ngcontent-%COMP%] .mat-icon[_ngcontent-%COMP%]{color:#00000061}[_nghost-%COMP%] .border[_ngcontent-%COMP%] .mat-divider[_ngcontent-%COMP%]{margin-left:-16px;margin-right:-16px;margin-bottom:16px}']})}}function EH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",11),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function MH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",11),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.ModbusFunctionCodeTranslationsMap.get(e)))}}function kH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",12),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-required"))}function PH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",12),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function DH(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13)(1,"mat-form-field",3)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",14),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,PH,3,3,"mat-icon",8),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rpc.value")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.rpcParametersFormGroup.get("value").hasError("required")&&e.rpcParametersFormGroup.get("value").touched)}}class OH{constructor(e){this.fb=e,this.ModbusEditableDataTypes=$t,this.ModbusFunctionCodeTranslationsMap=Zt,this.modbusDataTypes=Object.values(Wt),this.writeFunctionCodes=[5,6,15,16],this.defaultFunctionCodes=[3,4,6,16],this.readFunctionCodes=[1,2,3,4],this.bitsFunctionCodes=[...this.readFunctionCodes,...this.writeFunctionCodes],this.destroy$=new te,this.rpcParametersFormGroup=this.fb.group({type:[Wt.BYTES,[$.required]],functionCode:[this.defaultFunctionCodes[0],[$.required]],value:[{value:"",disabled:!0},[$.required,$.pattern(an)]],address:[null,[$.required]],objectsCount:[1,[$.required]]}),this.updateFunctionCodes(this.rpcParametersFormGroup.get("type").value),this.observeValueChanges(),this.observeKeyDataType(),this.observeFunctionCode()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.rpcParametersFormGroup.valid?null:{rpcParametersFormGroup:{valid:!1}}}writeValue(e){this.rpcParametersFormGroup.patchValue(e,{emitEvent:!1})}observeValueChanges(){this.rpcParametersFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}observeKeyDataType(){this.rpcParametersFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.ModbusEditableDataTypes.includes(e)||this.rpcParametersFormGroup.get("objectsCount").patchValue(Kt[e],{emitEvent:!1}),this.updateFunctionCodes(e)}))}observeFunctionCode(){this.rpcParametersFormGroup.get("functionCode").valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.updateValueEnabling(e)))}updateValueEnabling(e){this.writeFunctionCodes.includes(e)?this.rpcParametersFormGroup.get("value").enable({emitEvent:!1}):(this.rpcParametersFormGroup.get("value").setValue(null),this.rpcParametersFormGroup.get("value").disable({emitEvent:!1}))}updateFunctionCodes(e){this.functionCodes=e===Wt.BITS?this.bitsFunctionCodes:this.defaultFunctionCodes,this.functionCodes.includes(this.rpcParametersFormGroup.get("functionCode").value)||this.rpcParametersFormGroup.get("functionCode").patchValue(this.functionCodes[0],{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||OH)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:OH,selectors:[["tb-gateway-modbus-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>OH)),multi:!0},{provide:K,useExisting:c((()=>OH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:35,vars:30,consts:[[3,"formGroup"],[1,"tb-form-hint","tb-primary-fill","no-padding-top","hint-container"],[1,"flex","flex-1","flex-row","gap-2.5"],[1,"flex-1"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["formControlName","functionCode"],["matInput","","type","number","min","0","max","50000","name","value","formControlName","address",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","number","min","1","max","50000","name","value","formControlName","objectsCount",3,"placeholder","readonly"],["class","flex",4,"ngIf"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[1,"flex"],["matInput","","name","value","formControlName","value",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelement(4,"br"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",2)(8,"mat-form-field",3)(9,"mat-label"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-select",4),t.ɵɵtemplate(13,EH,2,2,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-form-field",3)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"mat-select",6),t.ɵɵtemplate(19,MH,3,4,"mat-option",5),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",2)(21,"mat-form-field",3)(22,"mat-label"),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",7),t.ɵɵpipe(26,"translate"),t.ɵɵtemplate(27,kH,3,3,"mat-icon",8),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-form-field",3)(29,"mat-label"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(32,"input",9),t.ɵɵpipe(33,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(34,DH,8,7,"div",10),t.ɵɵelementContainerEnd()),2&e&&(t.ɵɵproperty("formGroup",n.rpcParametersFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,14,"gateway.rpc.hint.modbus-response-reading"),""),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,16,"gateway.rpc.hint.modbus-writing-functions")," "),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(11,18,"gateway.rpc.type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.modbusDataTypes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(17,20,"gateway.rpc.functionCode")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.functionCodes),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(24,22,"gateway.rpc.address")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(26,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.rpcParametersFormGroup.get("address").hasError("required")&&n.rpcParametersFormGroup.get("address").touched),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(31,26,"gateway.rpc.objectsCount")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(33,28,"gateway.set")),t.ɵɵproperty("readonly",!n.ModbusEditableDataTypes.includes(n.rpcParametersFormGroup.get("type").value)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.writeFunctionCodes.includes(n.rpcParametersFormGroup.get("functionCode").value)))},dependencies:t.ɵɵgetComponentDepsFactory(OH,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{margin-bottom:12px}'],changeDetection:d.OnPush})}}function AH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",6),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rpc.responseTopicExpression")))}function FH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field")(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",7),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rpc.responseTimeout")))}class RH{constructor(e){this.fb=e,this.onChange=e=>{},this.onTouched=()=>{},this.destroy$=new te,this.rpcParametersFormGroup=this.fb.group({methodFilter:[null,[$.required,$.pattern(an)]],requestTopicExpression:[null,[$.required,$.pattern(an)]],responseTopicExpression:[{value:null,disabled:!0},[$.required,$.pattern(an)]],responseTimeout:[{value:null,disabled:!0},[$.min(10),$.pattern(rn)]],valueExpression:[null,[$.required,$.pattern(an)]],withResponse:[!1,[]]}),this.observeValueChanges(),this.observeWithResponse()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.rpcParametersFormGroup.valid?null:{rpcParametersFormGroup:{valid:!1}}}writeValue(e){this.rpcParametersFormGroup.patchValue(e,{emitEvent:!1}),this.toggleResponseFields(e.withResponse)}observeValueChanges(){this.rpcParametersFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}observeWithResponse(){this.rpcParametersFormGroup.get("withResponse").valueChanges.pipe(me((e=>this.toggleResponseFields(e))),le(this.destroy$)).subscribe()}toggleResponseFields(e){const t=this.rpcParametersFormGroup.get("responseTopicExpression"),n=this.rpcParametersFormGroup.get("responseTimeout");e?(t.enable(),n.enable()):(t.disable(),n.disable())}static{this.ɵfac=function(e){return new(e||RH)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:RH,selectors:[["tb-gateway-mqtt-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>RH)),multi:!0},{provide:K,useExisting:c((()=>RH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:21,vars:15,consts:[[3,"formGroup"],["matInput","","formControlName","methodFilter","placeholder","echo"],["matInput","","formControlName","requestTopicExpression","placeholder","sensor/${deviceName}/request/${methodName}/${requestId}"],["formControlName","withResponse",1,"margin",3,"click"],[4,"ngIf"],["matInput","","formControlName","valueExpression","placeholder","${params}"],["matInput","","formControlName","responseTopicExpression","placeholder","sensor/${deviceName}/response/${methodName}/${requestId}"],["matInput","","formControlName","responseTimeout","type","number","placeholder","10000","min","10","step","1"]],template:function(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"mat-form-field")(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",1),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field")(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",2),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-slide-toggle",3),t.ɵɵlistener("click",(function(e){return e.stopPropagation()})),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(14,AH,5,3,"mat-form-field",4)(15,FH,5,3,"mat-form-field",4),t.ɵɵelementStart(16,"mat-form-field")(17,"mat-label"),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(20,"input",5),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e){let e,i;t.ɵɵproperty("formGroup",n.rpcParametersFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,7,"gateway.rpc.method-name")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,9,"gateway.rpc.requestTopicExpression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,11,"gateway.rpc.withResponse")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",null==(e=n.rpcParametersFormGroup.get("withResponse"))?null:e.value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",null==(i=n.rpcParametersFormGroup.get("withResponse"))?null:i.value),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(19,13,"gateway.rpc.valueExpression"))}},dependencies:t.ɵɵgetComponentDepsFactory(RH,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:flex;flex-direction:column}[_nghost-%COMP%] .mat-mdc-slide-toggle.margin[_ngcontent-%COMP%]{margin-bottom:10px;margin-left:10px}'],changeDetection:d.OnPush})}}function BH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",26),t.ɵɵelement(1,"mat-icon",27),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("svgIcon",i.valueTypes.get(e).icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,i.valueTypes.get(e).name))}}function NH(e,n){1&e&&(t.ɵɵelement(0,"input",28),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function LH(e,n){1&e&&(t.ɵɵelement(0,"input",29),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function VH(e,n){1&e&&(t.ɵɵelement(0,"input",30),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function qH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-select",31)(1,"mat-option",26),t.ɵɵtext(2,"true"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-option",26),t.ɵɵtext(4,"false"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵproperty("value",!0),t.ɵɵadvance(2),t.ɵɵproperty("value",!1))}function GH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",32),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function zH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",8)(1,"div",9)(2,"div",10),t.ɵɵtext(3,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",11)(5,"mat-form-field",12)(6,"mat-select",13)(7,"mat-select-trigger")(8,"div",14),t.ɵɵelement(9,"mat-icon",15),t.ɵɵelementStart(10,"span"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(13,BH,5,5,"mat-option",16),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(14,"div",17)(15,"div",10),t.ɵɵtext(16,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-form-field",18),t.ɵɵelementContainerStart(18,19),t.ɵɵtemplate(19,NH,2,3,"input",20)(20,LH,2,3,"input",21)(21,VH,2,3,"input",22)(22,qH,5,2,"mat-select",23),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(23,GH,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"button",25),t.ɵɵpipe(25,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext();return t.ɵɵresetView(i.removeArgument(n))})),t.ɵɵelementStart(26,"mat-icon"),t.ɵɵtext(27,"delete"),t.ɵɵelementEnd()()()}if(2&e){let e,i;const a=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("formGroup",a),t.ɵɵadvance(9),t.ɵɵproperty("svgIcon",null==(e=r.valueTypes.get(a.get("type").value))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,11,null==(i=r.valueTypes.get(a.get("type").value))?null:i.name)),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",r.valueTypeKeys),t.ɵɵadvance(5),t.ɵɵproperty("ngSwitch",a.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.STRING),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.INTEGER),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.DOUBLE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",r.MappingValueType.BOOLEAN),t.ɵɵadvance(),t.ɵɵproperty("ngIf",a.get(a.get("type").value+"Value").hasError("required")&&a.get(a.get("type").value+"Value").touched),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(25,13,"gateway.rpc.remove"))}}class UH{constructor(e,t){this.fb=e,this.cdr=t,this.valueTypeKeys=Object.values(Yt),this.MappingValueType=Yt,this.valueTypes=Xt,this.onChange=e=>{},this.onTouched=()=>{},this.destroy$=new te,this.rpcParametersFormGroup=this.fb.group({method:[null,[$.required,$.pattern(an)]],arguments:this.fb.array([])}),this.observeValueChanges()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.rpcParametersFormGroup.valid?null:{rpcParametersFormGroup:{valid:!1}}}writeValue(e){this.clearArguments(),e.arguments?.map((({type:e,value:t})=>({type:e,[e+"Value"]:t}))).forEach((e=>this.addArgument(e))),this.cdr.markForCheck(),this.rpcParametersFormGroup.get("method").patchValue(e.method)}observeValueChanges(){this.rpcParametersFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{const t=e.arguments.map((({type:e,...t})=>({type:e,value:t[e+"Value"]})));this.onChange({method:e.method,arguments:t}),this.onTouched()}))}removeArgument(e){this.rpcParametersFormGroup.get("arguments").removeAt(e)}addArgument(e={}){const t=this.fb.group({type:[e.type??Yt.STRING],stringValue:[e.stringValue??{value:"",disabled:!(Se(e,{})||e.stringValue)},[$.required,$.pattern(an)]],integerValue:[{value:e.integerValue??0,disabled:!ke(e.integerValue)},[$.required,$.pattern(rn)]],doubleValue:[{value:e.doubleValue??0,disabled:!ke(e.doubleValue)},[$.required]],booleanValue:[{value:e.booleanValue??!1,disabled:!ke(e.booleanValue)},[$.required]]});this.observeTypeChange(t),this.rpcParametersFormGroup.get("arguments").push(t,{emitEvent:!1})}clearArguments(){const e=this.rpcParametersFormGroup.get("arguments");for(;0!==e.length;)e.removeAt(0)}observeTypeChange(e){e.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((t=>{e.disable({emitEvent:!1}),e.get("type").enable({emitEvent:!1}),e.get(t+"Value").enable({emitEvent:!1})}))}static{this.ɵfac=function(e){return new(e||UH)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:UH,selectors:[["tb-gateway-opc-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>UH)),multi:!0},{provide:K,useExisting:c((()=>UH)),multi:!0}]),t.ɵɵStandaloneFeature],decls:18,vars:14,consts:[[3,"formGroup"],[1,"tb-form-hint","tb-primary-fill","tb-flex","no-padding-top","hint-container"],[1,"tb-flex"],["matInput","","formControlName","method","placeholder","multiply"],["formArrayName","arguments",1,"tb-form-panel","stroked","arguments-container"],[1,"fields-label"],["class","flex flex-1 items-center justify-center gap-2.5",3,"formGroup",4,"ngFor","ngForOf"],["mat-raised-button","",1,"self-start",3,"click"],[1,"flex","flex-1","items-center","justify-center","gap-2.5",3,"formGroup"],[1,"tb-form-row","column-xs","type-container","items-center","justify-between"],["translate","",1,"tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","type"],[1,"tb-flex","align-center"],[1,"tb-mat-18",3,"svgIcon"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-row","column-xs","value-container","item-center","justify-between"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","tb-suffix-absolute","flex"],[3,"ngSwitch"],["matInput","","required","","formControlName","stringValue",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder",4,"ngSwitchCase"],["formControlName","booleanValue",4,"ngSwitchCase"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["mat-icon-button","","matTooltipPosition","above",1,"tb-box-button",3,"click","matTooltip"],[3,"value"],[1,"tb-mat-20",3,"svgIcon"],["matInput","","required","","formControlName","stringValue",3,"placeholder"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder"],["formControlName","booleanValue"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",2)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",3),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"fieldset",4)(10,"strong")(11,"span",5),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(14,zH,28,15,"div",6),t.ɵɵelementStart(15,"button",7),t.ɵɵlistener("click",(function(){return n.addArgument()})),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e&&(t.ɵɵproperty("formGroup",n.rpcParametersFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,6,"gateway.rpc.hint.opc-method")," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,8,"gateway.rpc.method")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,10,"gateway.rpc.arguments")),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.rpcParametersFormGroup.get("arguments").controls),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,12,"gateway.rpc.add-argument")," "))},dependencies:t.ɵɵgetComponentDepsFactory(UH,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .arguments-container[_ngcontent-%COMP%]{margin-bottom:10px}[_nghost-%COMP%] .type-container[_ngcontent-%COMP%]{width:40%}[_nghost-%COMP%] .value-container[_ngcontent-%COMP%]{width:50%}[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{margin-bottom:12px}'],changeDetection:d.OnPush})}}function jH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SocketMethodProcessingsTranslates.get(e))," ")}}function HH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}class WH extends Ra{constructor(){super(...arguments),this.SocketMethodProcessingsTranslates=Cj,this.socketMethodProcessings=Object.values(Sj),this.socketEncoding=Object.values(mt)}initFormGroup(){return this.fb.group({methodRPC:[null,[$.required,$.pattern(an)]],methodProcessing:[Sj.WRITE,[$.required]],encoding:[Ij.UTF_8,[$.required,$.pattern(an)]],withResponse:[!1,[]]})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(WH)))(n||WH)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:WH,selectors:[["tb-gateway-socket-rpc-parameters"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>WH)),multi:!0},{provide:K,useExisting:c((()=>WH)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:21,vars:15,consts:[[3,"formGroup"],[1,"w-full"],["matInput","","formControlName","methodRPC","placeholder","rpcMethod1"],[1,"mat-block"],["formControlName","methodProcessing"],[3,"value",4,"ngFor","ngForOf"],["formControlName","encoding"],["formControlName","withResponse",1,"mat-slide","margin"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementContainerStart(0,0),t.ɵɵelementStart(1,"mat-form-field",1)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",2),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",3)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-select",4),t.ɵɵtemplate(11,jH,3,4,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",3)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-select",6),t.ɵɵtemplate(17,HH,2,2,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"mat-slide-toggle",7),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e&&(t.ɵɵproperty("formGroup",n.formGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,7,"gateway.rpc.methodRPC")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,9,"gateway.rpc.methodProcessing")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.socketMethodProcessings),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,11,"gateway.encoding")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,13,"gateway.rpc.withResponse")," "))},dependencies:t.ɵɵgetComponentDepsFactory(WH,[j,_]),encapsulation:2,changeDetection:d.OnPush})}}const $H=e=>({border:e}),KH=e=>({type:e});function YH(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",15),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function XH(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-min")," "))}function ZH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"mat-form-field")(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",9),t.ɵɵtemplate(6,YH,2,2,"mat-option",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",11)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",12),t.ɵɵtemplate(12,XH,3,3,"mat-error",13),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"button",14),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.sendCommand())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,6,"gateway.statistics.command")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.RPCCommands),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,8,"gateway.statistics.timeout")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.commandForm.get("time").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("disabled",e.commandForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,10,"gateway.rpc-command-send")," ")}}function QH(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-service-rpc-connector",17),t.ɵɵlistener("sendCommand",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.sendCommand())}))("saveTemplate",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.saveTemplate())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("connectorType",e.connectorType)}}function JH(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-modbus-rpc-parameters",24)}function eW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-mqtt-rpc-parameters",24)}function tW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-opc-rpc-parameters",24)}function nW(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-socket-rpc-parameters",24)}function iW(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",18)(1,"div",19),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(4,20),t.ɵɵtemplate(5,JH,1,0,"tb-gateway-modbus-rpc-parameters",21)(6,eW,1,0,"tb-gateway-mqtt-rpc-parameters",21)(7,tW,1,0,"tb-gateway-opc-rpc-parameters",21)(8,nW,1,0,"tb-gateway-socket-rpc-parameters",21),t.ɵɵelementContainerEnd(),t.ɵɵelementStart(9,"div",22)(10,"button",23),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.saveTemplate())})),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"button",14),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.sendCommand())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(3,10,"gateway.rpc.title",t.ɵɵpureFunction1(17,KH,e.gatewayConnectorDefaultTypesTranslates.get(e.connectorType)))),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",e.connectorType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MODBUS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MQTT),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.OPCUA),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.SOCKET),t.ɵɵadvance(2),t.ɵɵproperty("disabled",e.commandForm.get("params").invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,13,"gateway.rpc-command-save-template")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",e.commandForm.get("params").invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,15,"gateway.rpc-command-send")," ")}}function aW(e,n){if(1&e&&t.ɵɵtemplate(0,QH,1,1,"tb-gateway-service-rpc-connector",16)(1,iW,16,19,"ng-template",null,1,t.ɵɵtemplateRefExtractor),2&e){const e=t.ɵɵreference(2),n=t.ɵɵnextContext();t.ɵɵproperty("ngIf",!n.typesWithUpdatedParams.has(n.connectorType))("ngIfElse",e)}}function rW(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",25)(1,"mat-icon",26),t.ɵɵtext(2,"schedule"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"span"),t.ɵɵtext(4),t.ɵɵpipe(5,"date"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind2(5,1,e.resultTime,"yyyy/MM/dd HH:mm:ss"))}}function oW(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-service-rpc-connector-templates",27),t.ɵɵlistener("useTemplate",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.useTemplate(n))})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("rpcTemplates",e.templates)("ctx",e.ctx)("connectorType",e.connectorType)}}class sW{constructor(e,t,n,i,a){this.fb=e,this.dialog=t,this.utils=n,this.cd=i,this.attributeService=a,this.contentTypes=V,this.RPCCommands=["Ping","Stats","Devices","Update","Version","Restart","Reboot"],this.templates=[],this.ConnectorType=ct,this.gatewayConnectorDefaultTypesTranslates=dt,this.typesWithUpdatedParams=new Set([ct.MQTT,ct.OPCUA,ct.MODBUS,ct.SOCKET]),this.modbusReadFunctionCodes=[1,2,3,4],this.subscriptionOptions={callbacks:{onDataUpdated:()=>this.ctx.ngZone.run((()=>{this.updateTemplates()})),onDataUpdateError:(e,t)=>this.ctx.ngZone.run((()=>{this.onDataUpdateError(t)})),dataLoading:()=>{}}},this.commandForm=this.fb.group({command:[null,[$.required]],time:[60,[$.required,$.min(1)]],params:["{}",[st]],result:[null]})}ngOnInit(){if(this.isConnector=this.ctx.settings.isConnector,this.isConnector){this.connectorType=this.ctx.stateController.getStateParams().connector_rpc.value.type;const e=[{type:N.entity,entityType:L.DEVICE,entityId:this.ctx.defaultSubscription.targetDeviceId,entityName:"Connector",attributes:[{name:`${this.connectorType}_template`}]}];this.ctx.subscriptionApi.createSubscriptionFromInfo(R.latest,e,this.subscriptionOptions,!1,!0).subscribe((e=>{this.subscription=e}))}else this.commandForm.get("command").setValue(this.RPCCommands[0])}sendCommand(e){this.resultTime=null;const t=e||this.commandForm.value,n=this.isConnector?`${this.connectorType}_`:"gateway_",i=this.isConnector?this.getCommandFromParamsByType(t.params):t.command.toLowerCase(),a=this.ctx.stateController.getStateParams().connector_rpc?.value.configurationJson.id,r=a?{...t.params,connectorId:a}:t.params;this.ctx.controlApi.sendTwoWayCommand(n+i,r,t.time).subscribe({next:e=>{this.resultTime=(new Date).getTime(),this.commandForm.get("result").setValue(JSON.stringify(e))},error:e=>{this.resultTime=(new Date).getTime(),console.error(e),this.commandForm.get("result").setValue(JSON.stringify(e.error))}})}getCommandFromParamsByType(e){switch(this.connectorType){case ct.MQTT:case ct.FTP:case ct.SNMP:case ct.REST:case ct.REQUEST:return e.methodFilter;case ct.MODBUS:return this.modbusReadFunctionCodes.includes(e.functionCode)?"get":"set";case ct.BACNET:case ct.CAN:case ct.OPCUA:return e.method;case ct.BLE:case ct.OCPP:case ct.SOCKET:case ct.XMPP:return e.methodRPC;default:return e.command}}saveTemplate(){this.dialog.open(Yj,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{config:this.commandForm.value.params,templates:this.templates}}).afterClosed().subscribe((e=>{if(e){const t={name:e,config:this.commandForm.value.params,type:this.connectorType},n=this.templates,i=n.findIndex((e=>e.name==t.name));i>-1&&n.splice(i,1),n.push(t);const a=`${this.connectorType}_template`;this.attributeService.saveEntityAttributes({id:this.ctx.defaultSubscription.targetDeviceId,entityType:L.DEVICE},D.SERVER_SCOPE,[{key:a,value:n}]).subscribe((()=>{this.cd.detectChanges()}))}}))}useTemplate(e){this.commandForm.get("params").patchValue(e.config)}updateTemplates(){this.templates=this.subscription.data[0].data[0][1].length?JSON.parse(this.subscription.data[0].data[0][1]):[],this.cd.detectChanges()}onDataUpdateError(e){const t=this.utils.parseException(e);let n=t.name;t.message&&(n+=": "+t.message),console.error(n)}static{this.ɵfac=function(e){return new(e||sW)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(_e.UtilsService),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(_e.AttributeService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:sW,selectors:[["tb-gateway-service-rpc"]],inputs:{ctx:"ctx",dialogRef:"dialogRef"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:12,vars:14,consts:[["connectorForm",""],["updatedParameters",""],[1,"flex","flex-1","flex-col"],[1,"command-form","flex","flex-row","gap-2.5","lt-sm:flex-col",3,"formGroup"],[4,"ngIf","ngIfElse"],[1,"result-block",3,"formGroup"],["class","result-time flex flex-1 flex-row items-center justify-center",4,"ngIf"],["readonly","true","formControlName","result",3,"contentType"],["class","border",3,"rpcTemplates","ctx","connectorType","useTemplate",4,"ngIf"],["formControlName","command"],[3,"value",4,"ngFor","ngForOf"],[1,"flex-1"],["matInput","","formControlName","time","type","number","min","1"],[4,"ngIf"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["formControlName","params",3,"connectorType","sendCommand","saveTemplate",4,"ngIf","ngIfElse"],["formControlName","params",3,"sendCommand","saveTemplate","connectorType"],[1,"rpc-parameters","flex","flex-col"],[1,"mat-subtitle-1","tb-form-panel-title"],[3,"ngSwitch"],["formControlName","params",4,"ngSwitchCase"],[1,"fex-row","template-actions","flex","flex-1","items-center","justify-end","gap-2.5"],["mat-raised-button","",3,"click","disabled"],["formControlName","params"],[1,"result-time","flex","flex-1","flex-row","items-center","justify-center"],[1,"material-icons"],[1,"border",3,"useTemplate","rpcTemplates","ctx","connectorType"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",2)(1,"div",3),t.ɵɵtemplate(2,ZH,16,12,"ng-container",4)(3,aW,3,2,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"section",5)(6,"span"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,rW,6,4,"div",6),t.ɵɵelementEnd(),t.ɵɵelement(10,"tb-json-content",7),t.ɵɵelementEnd()(),t.ɵɵtemplate(11,oW,1,3,"tb-gateway-service-rpc-connector-templates",8)),2&e){const e=t.ɵɵreference(4);t.ɵɵclassMap(t.ɵɵpureFunction1(12,$H,n.isConnector)),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.commandForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.isConnector)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵproperty("formGroup",n.commandForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(8,10,"gateway.rpc-command-result")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.resultTime),t.ɵɵadvance(),t.ɵɵproperty("contentType",n.contentTypes.JSON),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isConnector)}},dependencies:t.ɵɵgetComponentDepsFactory(sW,[j,_,IH,OH,RH,UH,Wj,WH]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;overflow:auto;display:flex;flex-direction:row;padding:0 5px}[_nghost-%COMP%] > *[_ngcontent-%COMP%]{height:100%;overflow:auto}[_nghost-%COMP%] > tb-gateway-service-rpc-connector-templates[_ngcontent-%COMP%]:last-child{margin-left:10px}[_nghost-%COMP%] tb-gateway-service-rpc-connector-templates[_ngcontent-%COMP%]{flex:1 1 30%;max-width:30%}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%]{flex-wrap:nowrap;padding:0 5px 5px}[_nghost-%COMP%] .command-form[_ngcontent-%COMP%] > button[_ngcontent-%COMP%]{margin-top:10px}[_nghost-%COMP%] .rpc-parameters[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%]{padding:0 5px;display:flex;flex-direction:column;flex:1}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] > span[_ngcontent-%COMP%]{font-weight:600;position:relative;font-size:14px;margin-bottom:10px}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] .result-time[_ngcontent-%COMP%]{font-weight:400;font-size:14px;line-height:32px;position:absolute;left:0;top:25px;z-index:5;color:#0000008a}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] .result-time[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{padding-left:10px}[_nghost-%COMP%] .result-block[_ngcontent-%COMP%] tb-json-content[_ngcontent-%COMP%]{flex:1}[_nghost-%COMP%] .border[_ngcontent-%COMP%]{padding:16px;box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;border:solid 1px #e0e0e0;border-radius:4px}']})}}function lW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.configuration-delete-dialog-input-required")," "))}e("GatewayServiceRPCComponent",sW);class pW extends A{constructor(e,t,n,i,a){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.gatewayName=this.data.gatewayName,this.gatewayControl=this.fb.control("")}close(){this.dialogRef.close()}turnOff(){this.dialogRef.close(!0)}static{this.ɵfac=function(e){return new(e||pW)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:pW,selectors:[["tb-gateway-remote-configuration-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:24,vars:14,consts:[["color","warn"],["translate",""],[1,"flex-1"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"mat-content","flex-col",2,"max-width","600px"],[3,"innerHTML"],[1,"mat-block","tb-value-type",2,"flex-grow","0"],["matInput","","required","",3,"formControl"],[4,"ngIf"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","warn","type","button","cdkFocusInitial","",3,"click"],["mat-button","","color","warn","type","button",3,"click","disabled"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-toolbar",0)(1,"mat-icon"),t.ɵɵtext(2,"warning"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"h2",1),t.ɵɵtext(4,"gateway.configuration-delete-dialog-header"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",2),t.ɵɵelementStart(6,"button",3),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵelementStart(7,"mat-icon",4),t.ɵɵtext(8,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",5),t.ɵɵelement(10,"span",6),t.ɵɵpipe(11,"translate"),t.ɵɵelementStart(12,"mat-form-field",7)(13,"mat-label",1),t.ɵɵtext(14,"gateway.configuration-delete-dialog-input"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",8),t.ɵɵtemplate(16,lW,3,3,"mat-error",9),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",10)(18,"button",11),t.ɵɵlistener("click",(function(){return n.close()})),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"button",12),t.ɵɵlistener("click",(function(){return n.turnOff()})),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(10),t.ɵɵpropertyInterpolate2("innerHTML","",t.ɵɵpipeBind1(11,8,"gateway.configuration-delete-dialog-body")," ",n.gatewayName,"",t.ɵɵsanitizeHtml),t.ɵɵadvance(5),t.ɵɵproperty("formControl",n.gatewayControl),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayControl.hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,10,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.gatewayControl.value!==n.gatewayName),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(23,12,"gateway.configuration-delete-dialog-confirm")," "))},dependencies:t.ɵɵgetComponentDepsFactory(pW,[j,_]),encapsulation:2})}}var cW;e("GatewayRemoteConfigurationDialogComponent",pW),function(e){e.tls="tls",e.accessToken="accessToken"}(cW||(cW={}));const dW="configuration_drafts",uW="RemoteLoggingLevel",mW=new Map([[cW.tls,"gateway.security-types.tls"],[cW.accessToken,"gateway.security-types.access-token"]]);var hW,gW;!function(e){e.none="NONE",e.critical="CRITICAL",e.error="ERROR",e.warning="WARNING",e.info="INFO",e.debug="DEBUG"}(hW||(hW={})),function(e){e.memory="memory",e.file="file"}(gW||(gW={}));const fW=new Map([[gW.memory,"gateway.storage-types.memory-storage"],[gW.file,"gateway.storage-types.file-storage"]]);var yW;!function(e){e.mqtt="MQTT",e.modbus="Modbus",e.opcua="OPC-UA",e.ble="BLE",e.request="Request",e.can="CAN",e.bacnet="BACnet",e.custom="Custom"}(yW||(yW={}));const vW={config:{},name:"",configType:null,enabled:!1};function xW(e){return JSON.stringify(e.value)===JSON.stringify({})?{validJSON:!0}:null}function bW(e){return e.replace("_","").replace("-","").replace(/^\s+|\s+/g,"").toLowerCase()+".json"}function wW(e,t){return'[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=converterHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"'.replace(/{ERROR}/g,e).replace(/{.\/logs\/}/g,t)}function SW(e){return{id:e,entityType:L.DEVICE}}function CW(e){const t={};return Object.prototype.hasOwnProperty.call(e,"thingsboard")&&(t.host=e.thingsboard.host,t.port=e.thingsboard.port,t.remoteConfiguration=e.thingsboard.remoteConfiguration,Object.prototype.hasOwnProperty.call(e.thingsboard.security,cW.accessToken)?(t.securityType=cW.accessToken,t.accessToken=e.thingsboard.security.accessToken):(t.securityType=cW.tls,t.caCertPath=e.thingsboard.security.caCert,t.privateKeyPath=e.thingsboard.security.privateKey,t.certPath=e.thingsboard.security.cert)),Object.prototype.hasOwnProperty.call(e,"storage")&&Object.prototype.hasOwnProperty.call(e.storage,"type")&&(e.storage.type===gW.memory?(t.storageType=gW.memory,t.readRecordsCount=e.storage.read_records_count,t.maxRecordsCount=e.storage.max_records_count):e.storage.type===gW.file&&(t.storageType=gW.file,t.dataFolderPath=e.storage.data_folder_path,t.maxFilesCount=e.storage.max_file_count,t.readRecordsCount=e.storage.read_records_count,t.maxRecordsCount=e.storage.max_records_count)),t}function _W(e){const t={};for(const n of e)n.enabled||(t[n.name]={connector:n.configType,config:n.config});return t}function TW(e){const t={thingsboard:IW(e)};return function(e,t){for(const n of t)if(n.enabled){const t=n.configType;Array.isArray(e[t])||(e[t]=[]);const i={name:n.name,config:n.config};e[t].push(i)}}(t,e.connectors),t}function IW(e){let t;t=e.securityType===cW.accessToken?{accessToken:e.accessToken}:{caCert:e.caCertPath,privateKey:e.privateKeyPath,cert:e.certPath};const n={host:e.host,remoteConfiguration:e.remoteConfiguration,port:e.port,security:t};let i;i=e.storageType===gW.memory?{type:gW.memory,read_records_count:e.readRecordsCount,max_records_count:e.maxRecordsCount}:{type:gW.file,data_folder_path:e.dataFolderPath,max_file_count:e.maxFilesCount,max_read_records_count:e.readRecordsCount,max_records_per_file:e.maxRecordsCount};const a=[];for(const t of e.connectors)if(t.enabled){const e={configuration:bW(t.name),name:t.name,type:t.configType};a.push(e)}return{thingsboard:n,connectors:a,storage:i,logs:window.btoa(wW(e.remoteLoggingLevel,e.remoteLoggingPathToLogs))}}const EW=["formContainer"],MW=(e,t,n)=>({"gap-1.25":e,"flex-row":t,"flex-col":n}),kW=(e,t,n)=>({"gap-1.25":e,"flex-row justify-end item-center":t,"flex-col justify-evenly item-center":n});function PW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e.value.toString())," ")}}function DW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-host-required "),t.ɵɵelementEnd())}function OW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-required "),t.ɵɵelementEnd())}function AW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-min "),t.ɵɵelementEnd())}function FW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-max "),t.ɵɵelementEnd())}function RW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.thingsboard-port-pattern "),t.ɵɵelementEnd())}function BW(e,n){1&e&&(t.ɵɵelementStart(0,"div",16)(1,"mat-form-field")(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",30),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field")(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",31),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field")(12,"mat-label"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",32),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.tls-path-ca-certificate")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,5,"gateway.tls-path-private-key")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,7,"gateway.tls-path-client-certificate")))}function NW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function LW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.path-logs-required "),t.ɵɵelementEnd())}function VW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e.value.toString())," ")}}function qW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-pack-size-required "),t.ɵɵelementEnd())}function GW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-pack-size-min "),t.ɵɵelementEnd())}function zW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-pack-size-pattern "),t.ɵɵelementEnd())}function UW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-records-required "),t.ɵɵelementEnd())}function jW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-records-min "),t.ɵɵelementEnd())}function HW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-records-pattern "),t.ɵɵelementEnd())}function WW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-files-required "),t.ɵɵelementEnd())}function $W(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-files-min "),t.ɵɵelementEnd())}function KW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-max-files-pattern "),t.ɵɵelementEnd())}function YW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.storage-path-required "),t.ɵɵelementEnd())}function XW(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",5)(1,"mat-form-field",8)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",33),t.ɵɵtemplate(6,WW,2,0,"mat-error",10)(7,$W,2,0,"mat-error",10)(8,KW,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",8)(10,"mat-label"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",34),t.ɵɵtemplate(14,YW,2,0,"mat-error",10),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵclassMap(t.ɵɵpureFunction3(12,MW,e.layoutGap,e.alignment,!e.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,8,"gateway.storage-max-files")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("maxFilesCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("maxFilesCount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("maxFilesCount").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,10,"gateway.storage-path")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.gatewayConfigurationGroup.get("dataFolderPath").hasError("required"))}}function ZW(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function QW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.connector-type-required "),t.ɵɵelementEnd())}function JW(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",29),t.ɵɵtext(1," gateway.connector-name-required "),t.ɵɵelementEnd())}function e$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",35)(1,"div",36)(2,"div",37),t.ɵɵelement(3,"mat-slide-toggle",38),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",39)(5,"mat-form-field",8)(6,"mat-label"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",40),t.ɵɵlistener("selectionChange",(function(){const n=t.ɵɵrestoreView(e).$implicit,i=t.ɵɵnextContext();return t.ɵɵresetView(i.changeConnectorType(n))})),t.ɵɵtemplate(10,ZW,2,2,"mat-option",7),t.ɵɵelementEnd(),t.ɵɵtemplate(11,QW,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",8)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"input",41),t.ɵɵlistener("blur",(function(){const n=t.ɵɵrestoreView(e),i=n.$implicit,a=n.index,r=t.ɵɵnextContext();return t.ɵɵresetView(r.changeConnectorName(i,a))})),t.ɵɵelementEnd(),t.ɵɵtemplate(17,JW,2,0,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",42)(19,"button",43),t.ɵɵpipe(20,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e),a=i.$implicit,r=i.index,o=t.ɵɵnextContext();return t.ɵɵresetView(o.openConfigDialog(n,r,a.get("config").value,a.get("name").value))})),t.ɵɵelementStart(21,"mat-icon"),t.ɵɵtext(22,"more_horiz"),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"button",43),t.ɵɵpipe(24,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,i=t.ɵɵnextContext();return t.ɵɵresetView(i.removeConnector(n))})),t.ɵɵelementStart(25,"mat-icon"),t.ɵɵtext(26,"close"),t.ɵɵelementEnd()()()()()}if(2&e){const e=n.$implicit,i=n.index,a=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("formGroupName",i),t.ɵɵadvance(3),t.ɵɵclassMap(t.ɵɵpureFunction3(24,MW,a.layoutGap,a.alignment,!a.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,16,"gateway.connector-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",a.connectorTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("configType").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,18,"gateway.connector-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.get("name").hasError("required")),t.ɵɵadvance(),t.ɵɵclassMap(t.ɵɵpureFunction3(28,kW,a.layoutGap,a.alignment,!a.alignment)),t.ɵɵadvance(),t.ɵɵclassProp("mat-warn",e.get("config").invalid),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(20,20,"gateway.update-config")),t.ɵɵproperty("disabled",a.isReadOnlyForm),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(24,22,"gateway.delete")),t.ɵɵproperty("disabled",a.isReadOnlyForm)}}function t$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",44),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.exportConfig())})),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,3,"gateway.download-tip")),t.ɵɵproperty("disabled",!e.gatewayConfigurationGroup.dirty||e.gatewayConfigurationGroup.invalid),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"action.download")," ")}}function n$(e,n){if(1&e&&(t.ɵɵelementStart(0,"button",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,3,"gateway.save-tip")),t.ɵɵproperty("disabled",!e.gatewayConfigurationGroup.dirty||e.gatewayConfigurationGroup.invalid),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"action.save")," ")}}class i$ extends q{constructor(e,t,n,i,a,r,o,s,l,p,c){super(e),this.store=e,this.elementRef=t,this.utils=n,this.ngZone=i,this.fb=a,this.window=r,this.dialog=o,this.translate=s,this.deviceService=l,this.attributeService=p,this.importExport=c,this.alignment=!0,this.layoutGap=!0,this.securityTypes=mW,this.gatewayLogLevels=Object.keys(hW).map((e=>hW[e])),this.connectorTypes=Object.keys(yW),this.storageTypes=fW,this.toastTargetId="gateway-configuration-widget"+this.utils.guid(),this.isReadOnlyForm=!1}get connectors(){return this.gatewayConfigurationGroup.get("connectors")}ngOnInit(){this.initWidgetSettings(this.ctx.settings),this.ctx.datasources&&this.ctx.datasources.length&&(this.deviceNameForm=this.ctx.datasources[0].name),this.buildForm(),this.ctx.updateWidgetParams(),this.formResize$=new ResizeObserver((()=>{this.resize()})),this.formResize$.observe(this.formContainerRef.nativeElement)}ngOnDestroy(){this.formResize$&&this.formResize$.disconnect(),this.subscribeGateway$.unsubscribe(),this.subscribeStorageType$.unsubscribe()}initWidgetSettings(e){let t;t=e.gatewayTitle&&e.gatewayTitle.length?this.utils.customTranslation(e.gatewayTitle,e.gatewayTitle):this.translate.instant("gateway.gateway"),this.ctx.widgetTitle=t,this.isReadOnlyForm=!!e.readOnly&&e.readOnly,this.archiveFileName=e.archiveFileName?.length?e.archiveFileName:"gatewayConfiguration",this.gatewayType=e.gatewayType?.length?e.gatewayType:"Gateway",this.gatewayNameExists=this.utils.customTranslation(e.gatewayNameExists,e.gatewayNameExists)||this.translate.instant("gateway.gateway-exists"),this.successfulSaved=this.utils.customTranslation(e.successfulSave,e.successfulSave)||this.translate.instant("gateway.gateway-saved"),this.updateWidgetDisplaying()}resize(){this.ngZone.run((()=>{this.updateWidgetDisplaying(),this.ctx.detectChanges()}))}updateWidgetDisplaying(){this.ctx.$container&&this.ctx.$container[0].offsetWidth<=425?(this.layoutGap=!1,this.alignment=!1):(this.layoutGap=!0,this.alignment=!0)}saveAttribute(e,t,n){const i=this.gatewayConfigurationGroup.get("gateway").value,a={key:e,value:t};return this.attributeService.saveEntityAttributes(SW(i),n,[a])}createConnector(e=vW){this.connectors.push(this.fb.group({enabled:[e.enabled],configType:[e.configType,[$.required]],name:[e.name,[$.required]],config:[e.config,[$.nullValidator,xW]]}))}getFormField(e){return this.gatewayConfigurationGroup.get(e)}buildForm(){this.gatewayConfigurationGroup=this.fb.group({gateway:[null,[]],accessToken:[null,[$.required]],securityType:[cW.accessToken],host:[this.window.location.hostname,[$.required]],port:[1883,[$.required,$.min(1),$.max(65535),$.pattern(/^-?[0-9]+$/)]],remoteConfiguration:[!0],caCertPath:["/etc/thingsboard-gateway/ca.pem"],privateKeyPath:["/etc/thingsboard-gateway/privateKey.pem"],certPath:["/etc/thingsboard-gateway/certificate.pem"],remoteLoggingLevel:[hW.debug],remoteLoggingPathToLogs:["./logs/",[$.required]],storageType:[gW.memory],readRecordsCount:[100,[$.required,$.min(1),$.pattern(/^-?[0-9]+$/)]],maxRecordsCount:[1e4,[$.required,$.min(1),$.pattern(/^-?[0-9]+$/)]],maxFilesCount:[5,[$.required,$.min(1),$.pattern(/^-?[0-9]+$/)]],dataFolderPath:["./data/",[$.required]],connectors:this.fb.array([])}),this.isReadOnlyForm&&this.gatewayConfigurationGroup.disable({emitEvent:!1}),this.subscribeStorageType$=this.getFormField("storageType").valueChanges.subscribe((e=>{e===gW.memory?(this.getFormField("maxFilesCount").disable(),this.getFormField("dataFolderPath").disable()):(this.getFormField("maxFilesCount").enable(),this.getFormField("dataFolderPath").enable())})),this.subscribeGateway$=this.getFormField("gateway").valueChanges.subscribe((e=>{null!==e?oe([this.deviceService.getDeviceCredentials(e).pipe(me((e=>{this.getFormField("accessToken").patchValue(e.credentialsId)}))),...this.getAttributes(e)]).subscribe((()=>{this.gatewayConfigurationGroup.markAsPristine(),this.ctx.detectChanges()})):this.getFormField("accessToken").patchValue("")}))}gatewayExist(){this.ctx.showErrorToast(this.gatewayNameExists,"top","left",this.toastTargetId)}exportConfig(){const e=this.gatewayConfigurationGroup.value,t={};var n,i,a;t["tb_gateway.yaml"]=function(e){let t;t="thingsboard:\n",t+=" host: "+e.host+"\n",t+=" remoteConfiguration: "+e.remoteConfiguration+"\n",t+=" port: "+e.port+"\n",t+=" security:\n",e.securityType===cW.accessToken?t+=" access-token: "+e.accessToken+"\n":(t+=" ca_cert: "+e.caCertPath+"\n",t+=" privateKey: "+e.privateKeyPath+"\n",t+=" cert: "+e.certPath+"\n"),t+="storage:\n",e.storageType===gW.memory?(t+=" type: memory\n",t+=" read_records_count: "+e.readRecordsCount+"\n",t+=" max_records_count: "+e.maxRecordsCount+"\n"):(t+=" type: file\n",t+=" data_folder_path: "+e.dataFolderPath+"\n",t+=" max_file_count: "+e.maxFilesCount+"\n",t+=" max_read_records_count: "+e.readRecordsCount+"\n",t+=" max_records_per_file: "+e.maxRecordsCount+"\n"),t+="connectors:\n";for(const n of e.connectors)n.enabled&&(t+=" -\n",t+=" name: "+n.name+"\n",t+=" type: "+n.configType+"\n",t+=" configuration: "+bW(n.name)+"\n");return t}(e),function(e,t){for(const n of t)n.enabled&&(e[bW(n.name)]=JSON.stringify(n.config))}(t,e.connectors),n=t,i=e.remoteLoggingLevel,a=e.remoteLoggingPathToLogs,n["logs.conf"]=wW(i,a),this.importExport.exportJSZip(t,this.archiveFileName),this.saveAttribute(uW,this.gatewayConfigurationGroup.value.remoteLoggingLevel.toUpperCase(),D.SHARED_SCOPE)}addNewConnector(){this.createConnector()}removeConnector(e){e>-1&&(this.connectors.removeAt(e),this.connectors.markAsDirty())}openConfigDialog(e,t,n,i){e&&(e.stopPropagation(),e.preventDefault()),this.dialog.open(tt,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{jsonValue:n,required:!0,title:this.translate.instant("gateway.title-connectors-json",{typeName:i})}}).afterClosed().subscribe((e=>{e&&(this.connectors.at(t).get("config").patchValue(e),this.ctx.detectChanges())}))}createConnectorName(e,t,n=0){const i=n?t+n:t;return-1===e.findIndex((e=>e.name===i))?i:this.createConnectorName(e,t,++n)}validateConnectorName(e,t,n,i=0){for(let a=0;a{this.ctx.showSuccessToast(this.successfulSaved,2e3,"top","left",this.toastTargetId),this.gatewayConfigurationGroup.markAsPristine()}))}getAttributes(e){const t=[];return t.push(oe([this.getAttribute("current_configuration",D.CLIENT_SCOPE,e),this.getAttribute(dW,D.SERVER_SCOPE,e)]).pipe(me((([e,t])=>{this.setFormGatewaySettings(e),this.setFormConnectorsDraft(t),this.isReadOnlyForm&&this.gatewayConfigurationGroup.disable({emitEvent:!1})})))),t.push(this.getAttribute(uW,D.SHARED_SCOPE,e).pipe(me((e=>this.processLoggingLevel(e))))),t}getAttribute(e,t,n){return this.attributeService.getEntityAttributes(SW(n),t,[e])}setFormGatewaySettings(e){if(this.connectors.clear(),e.length>0){const t=JSON.parse(window.atob(e[0].value));for(const e of Object.keys(t)){const n=t[e];if("thingsboard"===e)null!==n&&Object.keys(n).length>0&&this.gatewayConfigurationGroup.patchValue(CW(n));else for(const t of Object.keys(n)){let i="No name";Object.prototype.hasOwnProperty.call(n[t],"name")&&(i=n[t].name);const a={enabled:!0,configType:e,config:n[t].config,name:i};this.createConnector(a)}}}}setFormConnectorsDraft(e){if(e.length>0){const t=JSON.parse(window.atob(e[0].value));for(const e of Object.keys(t)){const n={enabled:!1,configType:t[e].connector,config:t[e].config,name:e};this.createConnector(n)}}}processLoggingLevel(e){let t=hW.debug;e.length>0&&hW[e[0].value.toLowerCase()]&&(t=hW[e[0].value.toLowerCase()]),this.getFormField("remoteLoggingLevel").patchValue(t)}static{this.ɵfac=function(e){return new(e||i$)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(t.ElementRef),t.ɵɵdirectiveInject(_e.UtilsService),t.ɵɵdirectiveInject(t.NgZone),t.ɵɵdirectiveInject(H.UntypedFormBuilder),t.ɵɵdirectiveInject(Ce),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(_e.DeviceService),t.ɵɵdirectiveInject(_e.AttributeService),t.ɵɵdirectiveInject(nt.ImportExportService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:i$,selectors:[["tb-gateway-form"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(EW,7),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.formContainerRef=e.first)}},inputs:{ctx:"ctx",isStateForm:"isStateForm"},standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:104,vars:104,consts:[["formContainer",""],["tb-toast","",1,"gateway-form",3,"ngSubmit","formGroup","toastTarget"],["multi","true",1,"mat-body-2"],[1,"tb-panel-title"],["formControlName","gateway","required","",3,"gatewayNameExist","deviceName","isStateForm","newGatewayType"],[1,"flex"],["formControlName","securityType"],[3,"value",4,"ngFor","ngForOf"],[1,"flex-1"],["matInput","","type","text","formControlName","host"],["translate","",4,"ngIf"],["matInput","","type","number","formControlName","port"],["class","flex flex-col",4,"ngIf"],["formControlName","remoteConfiguration"],["formControlName","remoteLoggingLevel"],["matInput","","type","text","formControlName","remoteLoggingPathToLogs"],[1,"flex","flex-col"],["formControlName","storageType"],["matInput","","type","number","formControlName","readRecordsCount"],["matInput","","type","number","formControlName","maxRecordsCount"],["class","flex",3,"class",4,"ngIf"],[1,"gateway-config","flex","flex-col"],["formArrayName","connectors",4,"ngFor","ngForOf"],[1,"no-data-found","items-center","justify-center"],["mat-raised-button","","type","button","matTooltipPosition","above",3,"click","matTooltip"],[1,"form-action-buttons","flex","flex-row","items-center","justify-end"],["mat-raised-button","","color","primary","type","button",3,"disabled","matTooltip","click",4,"ngIf"],["mat-raised-button","","color","primary","type","submit",3,"disabled","matTooltip",4,"ngIf"],[3,"value"],["translate",""],["matInput","","type","text","formControlName","caCertPath"],["matInput","","type","text","formControlName","privateKeyPath"],["matInput","","type","text","formControlName","certPath"],["matInput","","type","number","formControlName","maxFilesCount"],["matInput","","type","text","formControlName","dataFolderPath"],["formArrayName","connectors"],[1,"flex","flex-row","items-stretch","justify-between","gap-2",3,"formGroupName"],[1,"flex","flex-col","justify-center"],["formControlName","enabled"],[1,"flex-full","flex"],["formControlName","configType",3,"selectionChange"],["matInput","","type","text","formControlName","name",3,"blur"],[1,"action-buttons","flex"],["mat-icon-button","","matTooltipPosition","above",3,"click","disabled","matTooltip"],["mat-raised-button","","color","primary","type","button",3,"click","disabled","matTooltip"],["mat-raised-button","","color","primary","type","submit",3,"disabled","matTooltip"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"form",1,0),t.ɵɵlistener("ngSubmit",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.save())})),t.ɵɵelementStart(2,"mat-accordion",2)(3,"mat-expansion-panel")(4,"mat-expansion-panel-header")(5,"mat-panel-title")(6,"div",3),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"uppercase"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"tb-entity-gateway-select",4),t.ɵɵlistener("gatewayNameExist",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.gatewayExist())})),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",5)(12,"mat-label"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-select",6),t.ɵɵtemplate(16,PW,3,4,"mat-option",7),t.ɵɵpipe(17,"keyvalue"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",5)(19,"mat-form-field",8)(20,"mat-label"),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(23,"input",9),t.ɵɵtemplate(24,DW,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",8)(26,"mat-label"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(29,"input",11),t.ɵɵtemplate(30,OW,2,0,"mat-error",10)(31,AW,2,0,"mat-error",10)(32,FW,2,0,"mat-error",10)(33,RW,2,0,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵtemplate(34,BW,16,9,"div",12),t.ɵɵelementStart(35,"mat-checkbox",13),t.ɵɵtext(36),t.ɵɵpipe(37,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"div",5)(39,"mat-form-field",8)(40,"mat-label"),t.ɵɵtext(41),t.ɵɵpipe(42,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(43,"mat-select",14),t.ɵɵtemplate(44,NW,2,2,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"mat-form-field",8)(46,"mat-label"),t.ɵɵtext(47),t.ɵɵpipe(48,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(49,"input",15),t.ɵɵtemplate(50,LW,2,0,"mat-error",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(51,"mat-expansion-panel")(52,"mat-expansion-panel-header")(53,"mat-panel-title")(54,"div",3),t.ɵɵtext(55),t.ɵɵpipe(56,"translate"),t.ɵɵpipe(57,"uppercase"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(58,"div",16)(59,"mat-form-field")(60,"mat-label"),t.ɵɵtext(61),t.ɵɵpipe(62,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(63,"mat-select",17),t.ɵɵtemplate(64,VW,3,4,"mat-option",7),t.ɵɵpipe(65,"keyvalue"),t.ɵɵelementEnd()(),t.ɵɵelementStart(66,"div",5)(67,"mat-form-field",8)(68,"mat-label"),t.ɵɵtext(69),t.ɵɵpipe(70,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(71,"input",18),t.ɵɵtemplate(72,qW,2,0,"mat-error",10)(73,GW,2,0,"mat-error",10)(74,zW,2,0,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(75,"mat-form-field",8)(76,"mat-label"),t.ɵɵtext(77),t.ɵɵpipe(78,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(79,"input",19),t.ɵɵtemplate(80,UW,2,0,"mat-error",10)(81,jW,2,0,"mat-error",10)(82,HW,2,0,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵtemplate(83,XW,15,16,"div",20),t.ɵɵelementEnd()(),t.ɵɵelementStart(84,"mat-expansion-panel")(85,"mat-expansion-panel-header")(86,"mat-panel-title")(87,"div",3),t.ɵɵtext(88),t.ɵɵpipe(89,"translate"),t.ɵɵpipe(90,"uppercase"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(91,"div",21),t.ɵɵtemplate(92,e$,27,32,"section",22),t.ɵɵelementStart(93,"span",23),t.ɵɵtext(94),t.ɵɵpipe(95,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(96,"div")(97,"button",24),t.ɵɵpipe(98,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addNewConnector())})),t.ɵɵtext(99),t.ɵɵpipe(100,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(101,"section",25),t.ɵɵtemplate(102,t$,4,7,"button",26)(103,n$,4,7,"button",27),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("toastTarget",n.toastTargetId),t.ɵɵproperty("formGroup",n.gatewayConfigurationGroup),t.ɵɵadvance(7),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,54,t.ɵɵpipeBind1(8,52,"gateway.thingsboard"))),t.ɵɵadvance(3),t.ɵɵproperty("deviceName",n.deviceNameForm)("isStateForm",n.isStateForm)("newGatewayType",n.gatewayType),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,56,"gateway.security-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(17,58,n.securityTypes)),t.ɵɵadvance(2),t.ɵɵclassMap(t.ɵɵpureFunction3(92,MW,n.layoutGap,n.alignment,!n.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,60,"gateway.thingsboard-host")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("host").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,62,"gateway.thingsboard-port")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("max")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("port").hasError("pattern")),t.ɵɵadvance(),t.ɵɵproperty("ngIf","tls"===n.gatewayConfigurationGroup.get("securityType").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(37,64,"gateway.remote")),t.ɵɵadvance(2),t.ɵɵclassMap(t.ɵɵpureFunction3(96,MW,n.layoutGap,n.alignment,!n.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(42,66,"gateway.remote-logging-level")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.gatewayLogLevels),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(48,68,"gateway.path-logs")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("remoteLoggingPathToLogs").hasError("required")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(57,72,t.ɵɵpipeBind1(56,70,"gateway.storage"))),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(62,74,"gateway.storage-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(65,76,n.storageTypes)),t.ɵɵadvance(2),t.ɵɵclassMap(t.ɵɵpureFunction3(100,MW,n.layoutGap,n.alignment,!n.alignment)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(70,78,"gateway.storage-pack-size")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("readRecordsCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("readRecordsCount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("readRecordsCount").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(78,80,"file"!==n.gatewayConfigurationGroup.get("storageType").value?"gateway.storage-max-records":"gateway.storage-max-file-records")," "),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("maxRecordsCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("maxRecordsCount").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("maxRecordsCount").hasError("pattern")),t.ɵɵadvance(),t.ɵɵproperty("ngIf","file"===n.gatewayConfigurationGroup.get("storageType").value),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(90,84,t.ɵɵpipeBind1(89,82,"gateway.connectors-config"))),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",n.connectors.controls),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.connectors.length),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(95,86,"gateway.no-connectors")),t.ɵɵadvance(3),t.ɵɵclassProp("!hidden",n.isReadOnlyForm),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(98,88,"gateway.connector-add")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(100,90,"action.add")," "),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.isReadOnlyForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.gatewayConfigurationGroup.get("remoteConfiguration").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.gatewayConfigurationGroup.get("remoteConfiguration").value))},dependencies:t.ɵɵgetComponentDepsFactory(i$,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .gateway-form[_ngcontent-%COMP%]{height:100%;padding:5px;background-color:transparent;overflow-y:auto;overflow-x:hidden}[_nghost-%COMP%] .gateway-form[_ngcontent-%COMP%] .form-action-buttons[_ngcontent-%COMP%]{padding-top:8px}[_nghost-%COMP%] .gateway-form[_ngcontent-%COMP%] .gateway-config[_ngcontent-%COMP%] .no-data-found[_ngcontent-%COMP%]{position:relative;display:flex;height:40px}']})}}e("GatewayFormComponent",i$);class a${transform(e,t){return za.parseVersion(e)>=za.parseVersion(Ei.get(t))}static{this.ɵfac=function(e){return new(e||a$)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"isLatestVersionConfig",type:a$,pure:!0,standalone:!0})}}class r${constructor(e){this.translate=e}transform(e){return e.hasError("required")?this.translate.instant("gateway.port-required"):e.hasError("min")||e.hasError("max")?this.translate.instant("gateway.port-limits-error",{min:Ii.MIN,max:Ii.MAX}):""}static{this.ɵfac=function(e){return new(e||r$)(t.ɵɵdirectiveInject(He.TranslateService,16))}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"getGatewayPortTooltip",type:r$,pure:!0,standalone:!0})}}class o${transform(e,t,n,i){switch(e){case ct.OPCUA:return this.getOpcConnectorHelpLink(t,n);case ct.MQTT:return this.getMqttConnectorHelpLink(t,n,i);case ct.BACNET:return this.getBacnetConnectorHelpLink(t,n);case ct.REST:return this.getRestConnectorHelpLink(n)}}getOpcConnectorHelpLink(e,t){if(t!==Mi.CONST)return`widget/lib/gateway/${e}-${t}_fn`}getMqttConnectorHelpLink(e,t,n){if(t!==ei.CONST)return n?e!==Ni.ATTRIBUTES&&e!==Ni.TIMESERIES||n!==Jn.JSON?`widget/lib/gateway/mqtt-${n}-expression_fn`:"widget/lib/gateway/mqtt-json-key-expression_fn":"widget/lib/gateway/mqtt-expression_fn"}getBacnetConnectorHelpLink(e,t){if(t!==Mi.CONST)return`widget/lib/gateway/bacnet-device-${e}-${t}_fn`}getRestConnectorHelpLink(e){if(e!==fi.CONST)return"widget/lib/gateway/rest-json_fn"}static{this.ɵfac=function(e){return new(e||o$)}}static{this.ɵpipe=t.ɵɵdefinePipe({name:"getConnectorMappingHelpLink",type:o$,pure:!0,standalone:!0})}}function s$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.gatewayConnectorDefaultTypesTranslatesMap.get(e)," ")}}function l$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",22),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.connectorForm.get("name").hasError("duplicateName")?"gateway.connector-duplicate-name":"gateway.name-required"))}}function p$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"div",9),t.ɵɵtext(2,"gateway.connectors-table-class"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",10),t.ɵɵelement(4,"input",23),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,1,"gateway.set")))}function c$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"div",9),t.ɵɵtext(2,"gateway.connectors-table-key"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",10),t.ɵɵelement(4,"input",24),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,1,"gateway.set")))}function d$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function u$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"mat-slide-toggle",25)(2,"mat-label",26),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.fill-connector-defaults-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.fill-connector-defaults")," "))}function m$(e,n){1&e&&(t.ɵɵelementStart(0,"div",8)(1,"mat-slide-toggle",27)(2,"mat-label",26),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.send-change-data-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.send-change-data")," "))}class h$ extends A{constructor(e,t,n,i,a,r){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.isLatestVersionConfig=r,this.connectorType=ct,this.gatewayConnectorDefaultTypesTranslatesMap=dt,this.gatewayLogLevel=Object.values(lt),this.submitted=!1,this.destroy$=new te,this.updateConnectorTypesByVersion(),this.connectorForm=this.fb.group({type:[ct.MQTT,[]],name:["",[$.required,this.uniqNameRequired(),$.pattern(an)]],logLevel:[lt.INFO,[]],useDefaults:[!0,[]],sendDataOnlyOnChange:[!1,[]],class:["",[]],key:["auto",[]]})}ngOnInit(){this.observeTypeChange()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}helpLinkId(){return O+"/docs/iot-gateway/configuration/"}cancel(){this.dialogRef.close(null)}add(){this.submitted=!0;const e=this.connectorForm.getRawValue();if(e.useDefaults){const t=Ht(e.type),n=this.data.gatewayVersion;n&&(e.configVersion=n),e.configurationJson=(this.isLatestVersionConfig.transform(n,e.type)?t[Ei.get(e.type)]:t[pt.Legacy])??t,this.connectorForm.valid&&this.dialogRef.close(e)}else this.connectorForm.valid&&this.dialogRef.close(e)}uniqNameRequired(){return e=>{const t=e.value.trim().toLowerCase();return this.data.dataSourceData.some((({value:{name:e}})=>e.toLowerCase()===t))?{duplicateName:{valid:!1}}:null}}updateConnectorTypesByVersion(){const e=ut.keys();for(let t of e)if(t===pt.Legacy||za.parseVersion(this.data.gatewayVersion)>=za.parseVersion(t)){this.connectorTypes=ut.get(t);break}}observeTypeChange(){this.connectorForm.get("type").valueChanges.pipe(me((e=>{const t=this.connectorForm.get("useDefaults");e===ct.GRPC||e===ct.CUSTOM?t.setValue(!1):t.value||t.setValue(!0)})),le(this.destroy$)).subscribe()}static{this.ɵfac=function(e){return new(e||h$)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(a$))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:h$,selectors:[["tb-add-connector-dialog"]],standalone:!0,features:[t.ɵɵProvidersFeature([a$]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:43,vars:25,consts:[[1,"add-connector",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","autocomplete","off","name","value","formControlName","name",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf"],["formControlName","logLevel"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","class",3,"placeholder"],["matInput","","name","value","formControlName","key",3,"placeholder"],["formControlName","useDefaults",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"mat-toolbar",1)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",2)(6,"div",3),t.ɵɵelementStart(7,"button",4),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",5),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",6)(11,"div",7)(12,"div",8)(13,"div",9),t.ɵɵtext(14,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-form-field",10)(16,"mat-select",11),t.ɵɵtemplate(17,s$,2,2,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(18,"div",8)(19,"div",13),t.ɵɵtext(20,"gateway.name"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",10),t.ɵɵelement(22,"input",14),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,l$,3,3,"mat-icon",15),t.ɵɵelementEnd()(),t.ɵɵtemplate(25,p$,6,3,"div",16)(26,c$,6,3,"div",16),t.ɵɵelementStart(27,"div",8)(28,"div",9),t.ɵɵtext(29,"gateway.remote-logging-level"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",10)(31,"mat-select",17),t.ɵɵtemplate(32,d$,2,2,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵtemplate(33,u$,6,6,"div",16)(34,m$,6,6,"div",16),t.ɵɵpipe(35,"withReportStrategy"),t.ɵɵelementEnd()(),t.ɵɵelementStart(36,"div",18)(37,"button",19),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(38),t.ɵɵpipe(39,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(40,"button",20),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(41),t.ɵɵpipe(42,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.connectorForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,14,"gateway.add-connector")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.helpLinkId()),t.ɵɵadvance(11),t.ɵɵproperty("ngForOf",n.connectorTypes),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,16,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.connectorForm.get("name").hasError("required")&&n.connectorForm.get("name").touched||n.connectorForm.get("name").hasError("duplicateName")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value===n.connectorType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value===n.connectorType.GRPC),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.gatewayLogLevel),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value!==n.connectorType.GRPC&&n.connectorForm.get("type").value!==n.connectorType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.connectorForm.get("type").value===n.connectorType.MQTT&&!t.ɵɵpipeBind2(35,18,n.data.gatewayVersion,n.connectorType.MQTT)),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(39,21,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.connectorForm.invalid||!n.connectorForm.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(42,23,"action.add")," "))},dependencies:t.ɵɵgetComponentDepsFactory(h$,[j,_,Ka]),styles:['@charset "UTF-8";[_nghost-%COMP%] .add-connector[_ngcontent-%COMP%]{min-width:400px;width:500px}']})}}e("AddConnectorDialogComponent",h$);const g$=()=>({maxWidth:"970px"});function f$(e,n){1&e&&(t.ɵɵelementStart(0,"div",4),t.ɵɵtext(1,"gateway.device-info.source"),t.ɵɵelementEnd())}function y$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function v$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"mat-form-field",18)(2,"mat-select",19),t.ɵɵtemplate(3,y$,3,4,"mat-option",20),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.sourceTypes)}}function x$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",22),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-info.device-name-expression-required"))}function b$(e,n){if(1&e&&(t.ɵɵelement(0,"div",23),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,e.connectorType,"name-field",e.mappingFormGroup.get("deviceNameExpressionSource").value,e.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,g$))}}function w$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function S$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"mat-form-field",18)(2,"mat-select",26),t.ɵɵtemplate(3,w$,3,4,"mat-option",20),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.sourceTypes)}}function C$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",22),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-info.device-profile-expression-required"))}function _$(e,n){if(1&e&&(t.ɵɵelement(0,"div",23),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,e.connectorType,"profile-name",e.mappingFormGroup.get("deviceProfileExpressionSource").value,e.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,g$))}}function T$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",24)(1,"div",9),t.ɵɵtext(2,"gateway.device-info.profile-name"),t.ɵɵelementEnd(),t.ɵɵtemplate(3,S$,4,1,"div",10),t.ɵɵelementStart(4,"div",11)(5,"mat-form-field",12),t.ɵɵelement(6,"input",25),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,C$,3,3,"mat-icon",14)(9,_$,2,8,"div",15),t.ɵɵpipe(10,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.useSource),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,4,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingFormGroup.get("deviceProfileExpression").hasError("required")&&e.mappingFormGroup.get("deviceProfileExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(10,6,e.connectorType,"profile-name",e.mappingFormGroup.get("deviceProfileExpressionSource").value,e.convertorType))}}class I$ extends q{get deviceInfoType(){return this.deviceInfoTypeValue}set deviceInfoType(e){this.deviceInfoTypeValue!==e&&(this.deviceInfoTypeValue=e)}constructor(e,t,n,i){super(e),this.store=e,this.translate=t,this.dialog=n,this.fb=i,this.SourceTypeTranslationsMap=Bi,this.DeviceInfoType=ga,this.useSource=!0,this.required=!1,this.connectorType=ct.MQTT,this.sourceTypes=Object.values(ei),this.destroy$=new te,this.propagateChange=e=>{}}ngOnInit(){this.mappingFormGroup=this.fb.group({deviceNameExpression:["",this.required?[$.required,$.pattern(an)]:[$.pattern(an)]]}),this.useSource&&this.mappingFormGroup.addControl("deviceNameExpressionSource",this.fb.control(this.sourceTypes[0],[])),this.deviceInfoType===ga.FULL&&(this.useSource&&this.mappingFormGroup.addControl("deviceProfileExpressionSource",this.fb.control(this.sourceTypes[0],[])),this.mappingFormGroup.addControl("deviceProfileExpression",this.fb.control("",this.required?[$.required,$.pattern(an)]:[$.pattern(an)]))),this.mappingFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.updateView(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}writeValue(e){this.mappingFormGroup.patchValue(e,{emitEvent:!1})}validate(){return this.mappingFormGroup.valid?null:{mappingForm:{valid:!1}}}updateView(e){this.propagateChange(e)}static{this.ɵfac=function(e){return new(e||I$)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:I$,selectors:[["tb-device-info-table"]],inputs:{useSource:"useSource",required:"required",connectorType:"connectorType",convertorType:"convertorType",sourceTypes:"sourceTypes",deviceInfoType:"deviceInfoType"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>I$)),multi:!0},{provide:K,useExisting:c((()=>I$)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:23,vars:18,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-table","no-padding","no-gap"],[1,"tb-form-table-header"],["translate","",1,"tb-form-table-header-cell","w-1/5"],["class","tb-form-table-header-cell w-1/5","translate","",4,"ngIf"],["translate","",1,"tb-form-table-header-cell","w-1/2"],[1,"tb-form-table-body","no-gap"],[1,"tb-form-table-row","tb-form-row","no-border","same-padding","pt-4"],["translate","",1,"tb-required","w-1/5"],["class","no-gap w-1/5",4,"ngIf"],[1,"tb-form-table-row-cell","tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","deviceNameExpression",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style",4,"ngIf"],["class","tb-form-table-row tb-form-row no-border same-padding pb-4",4,"ngIf"],[1,"no-gap","w-1/5"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","!w-full"],["formControlName","deviceNameExpressionSource"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"tb-form-table-row","tb-form-row","no-border","same-padding","pb-4"],["matInput","","name","value","formControlName","deviceProfileExpression",3,"placeholder"],["formControlName","deviceProfileExpressionSource"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2,"device.device"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2)(4,"div",3)(5,"div",4),t.ɵɵtext(6,"gateway.device-info.entity-field"),t.ɵɵelementEnd(),t.ɵɵtemplate(7,f$,2,0,"div",5),t.ɵɵelementStart(8,"div",6),t.ɵɵtext(9," gateway.device-info.expression "),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",7)(11,"div",8)(12,"div",9),t.ɵɵtext(13,"gateway.device-info.name"),t.ɵɵelementEnd(),t.ɵɵtemplate(14,v$,4,1,"div",10),t.ɵɵelementStart(15,"div",11)(16,"mat-form-field",12),t.ɵɵelement(17,"input",13),t.ɵɵpipe(18,"translate"),t.ɵɵtemplate(19,x$,3,3,"mat-icon",14)(20,b$,2,8,"div",15),t.ɵɵpipe(21,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(22,T$,11,11,"div",16),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingFormGroup),t.ɵɵadvance(),t.ɵɵclassProp("tb-required",n.required),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.useSource),t.ɵɵadvance(4),t.ɵɵclassProp("pb-4",n.deviceInfoType!==n.DeviceInfoType.FULL),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.useSource),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(18,11,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.mappingFormGroup.get("deviceNameExpression").hasError("required")&&n.mappingFormGroup.get("deviceNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(21,13,n.connectorType,"name-field",n.mappingFormGroup.get("deviceNameExpressionSource").value,n.convertorType)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceInfoType===n.DeviceInfoType.FULL))},dependencies:t.ɵɵgetComponentDepsFactory(I$,[j,_,o$]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}'],changeDetection:d.OnPush})}}Ge([I()],I$.prototype,"useSource",void 0),Ge([I()],I$.prototype,"required",void 0);const E$=()=>({maxWidth:"970px"});function M$(e,n){if(1&e&&t.ɵɵelement(0,"mat-icon",19),2&e){let e;const n=t.ɵɵnextContext();t.ɵɵproperty("svgIcon",null==(e=n.valueTypes.get(n.valueTypeFormGroup.get("type").value))?null:e.icon)}}function k$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵelement(1,"mat-icon",22),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){let e,i;const a=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",a),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("svgIcon",null==(e=r.valueTypes.get(a))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,null==(i=r.valueTypes.get(a))?null:i.name))}}function P$(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,k$,5,5,"mat-option",20),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.valueTypeKeys)}}function D$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-option",23)(1,"span"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.raw")))}function O$(e,n){1&e&&(t.ɵɵelement(0,"input",24),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function A$(e,n){1&e&&(t.ɵɵelement(0,"input",25),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function F$(e,n){1&e&&(t.ɵɵelement(0,"input",26),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function R$(e,n){1&e&&(t.ɵɵelement(0,"input",27),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(1,1,"gateway.set"))}function B$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-select",28)(1,"mat-option",21),t.ɵɵtext(2,"true"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-option",21),t.ɵɵtext(4,"false"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵproperty("value",!0),t.ɵɵadvance(2),t.ɵɵproperty("value",!1))}function N$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",29),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function L$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",30),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("tb-help-popup",e.helpLink)("tb-help-popup-style",t.ɵɵpureFunction0(2,E$))}}class V${constructor(e){this.fb=e,this.valueTypeKeys=Object.values(Yt),this.valueTypes=Xt,this.MappingValueType=Yt,this.destroy$=new te,this.onChange=e=>{},this.valueTypeFormGroup=this.fb.group({type:[Yt.STRING],stringValue:[{value:"",disabled:this.rawData},[$.required,$.pattern(an)]],integerValue:[{value:0,disabled:!0},[$.required,$.pattern(rn)]],doubleValue:[{value:0,disabled:!0},[$.required]],booleanValue:[{value:!1,disabled:!0},[$.required]],rawValue:[{value:"",disabled:!this.rawData},[$.required,$.pattern(an)]]}),this.valueTypeFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((({type:e,...t})=>{this.onChange({type:e,value:t[e+"Value"]})})),this.observeTypeChange()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}observeTypeChange(){this.valueTypeFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.toggleTypeInputs(e)))}toggleTypeInputs(e){this.valueTypeFormGroup.disable({emitEvent:!1}),this.valueTypeFormGroup.get("type").enable({emitEvent:!1}),this.valueTypeFormGroup.get(e+"Value").enable({emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){const t=this.getValueType(e?.value),n={stringValue:"",rawValue:"",integerValue:0,doubleValue:0,booleanValue:!1,type:t};n[t+"Value"]=e?.value,this.toggleTypeInputs(t),this.valueTypeFormGroup.patchValue(n,{emitEvent:!1})}validate(){return this.valueTypeFormGroup.valid?null:{valueTypeFormGroup:{valid:!1}}}getValueType(e){if(this.rawData)return"raw";switch(typeof e){case"boolean":return Yt.BOOLEAN;case"number":return Number.isInteger(e)?Yt.INTEGER:Yt.DOUBLE;default:return Yt.STRING}}static{this.ɵfac=function(e){return new(e||V$)(t.ɵɵdirectiveInject(H.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:V$,selectors:[["tb-type-value-field"]],inputs:{rawData:"rawData",helpLink:"helpLink"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>V$)),multi:!0},{provide:K,useExisting:c((()=>V$)),multi:!0}]),t.ɵɵStandaloneFeature],decls:29,vars:17,consts:[["raw",""],[3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","type"],[1,"tb-flex","align-center"],["class","tb-mat-18",3,"svgIcon",4,"ngIf"],[4,"ngIf","ngIfElse"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","flex","tb-suffix-absolute"],[3,"ngSwitch"],["matInput","","required","","formControlName","stringValue",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder",4,"ngSwitchCase"],["matInput","","required","","formControlName","rawValue",3,"placeholder",4,"ngSwitchCase"],["formControlName","booleanValue",4,"ngSwitchCase"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style","click",4,"ngIf"],[1,"tb-mat-18",3,"svgIcon"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],[1,"tb-mat-20",3,"svgIcon"],["value","raw"],["matInput","","required","","formControlName","stringValue",3,"placeholder"],["matInput","","required","","formControlName","integerValue","type","number",3,"placeholder"],["matInput","","required","","formControlName","doubleValue","type","number",3,"placeholder"],["matInput","","required","","formControlName","rawValue",3,"placeholder"],["formControlName","booleanValue"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"click","tb-help-popup","tb-help-popup-style"]],template:function(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,1),t.ɵɵelementStart(1,"div",2)(2,"div",3),t.ɵɵtext(3,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",4)(5,"mat-form-field",5)(6,"mat-select",6)(7,"mat-select-trigger")(8,"div",7),t.ɵɵtemplate(9,M$,1,1,"mat-icon",8),t.ɵɵelementStart(10,"span"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(14,P$,2,1,"ng-container",9)(15,D$,4,3,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(17,"div",2)(18,"div",3),t.ɵɵtext(19,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"mat-form-field",10),t.ɵɵelementContainerStart(21,11),t.ɵɵtemplate(22,O$,2,3,"input",12)(23,A$,2,3,"input",13)(24,F$,2,3,"input",14)(25,R$,2,3,"input",15)(26,B$,5,2,"mat-select",16),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(27,N$,3,3,"mat-icon",17)(28,L$,1,3,"div",18),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e){let e,i;const a=t.ɵɵreference(16);t.ɵɵproperty("formGroup",n.valueTypeFormGroup),t.ɵɵadvance(9),t.ɵɵproperty("ngIf",!n.rawData),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,13,null==(e=n.valueTypes.get(n.valueTypeFormGroup.get("type").value))?null:e.name)||t.ɵɵpipeBind1(13,15,"gateway.raw")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",!n.rawData)("ngIfElse",a),t.ɵɵadvance(7),t.ɵɵproperty("ngSwitch",n.valueTypeFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.STRING),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.INTEGER),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.DOUBLE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase","raw"),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingValueType.BOOLEAN),t.ɵɵadvance(),t.ɵɵproperty("ngIf",(null==(i=n.valueTypeFormGroup.get(n.valueTypeFormGroup.get("type").value))?null:i.hasError("required"))&&n.valueTypeFormGroup.get(n.valueTypeFormGroup.get("type").value).touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.helpLink)}},dependencies:t.ɵɵgetComponentDepsFactory(V$,[_,j]),styles:['@charset "UTF-8";[_nghost-%COMP%]{gap:16px;display:grid;width:100%}']})}}function q$(e,n){if(1&e&&t.ɵɵelement(0,"tb-type-value-field",14),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("helpLink",e.helpLink)}}function G$(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",6)(1,"div",7),t.ɵɵelementContainerStart(2,8),t.ɵɵelementStart(3,"mat-expansion-panel",9)(4,"mat-expansion-panel-header",10)(5,"mat-panel-title")(6,"div",11),t.ɵɵtext(7),t.ɵɵelementEnd()()(),t.ɵɵtemplate(8,q$,1,1,"ng-template",12),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",13),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"delete"),t.ɵɵelementEnd()()()}if(2&e){let e;const i=n.$implicit,a=n.last;t.ɵɵadvance(2),t.ɵɵproperty("formGroup",i),t.ɵɵadvance(),t.ɵɵproperty("expanded",a),t.ɵɵadvance(4),t.ɵɵtextInterpolate(null!==(e=null==(e=i.get("typeValue").value)?null:e.value)&&void 0!==e?e:""),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(10,4,"gateway.delete-argument"))}}function z$(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4),t.ɵɵtemplate(1,G$,13,6,"div",5),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.valueListFormArray.controls)("ngForTrackBy",e.trackByKey)}}function U$(e,n){1&e&&(t.ɵɵelementStart(0,"div",15)(1,"span",16),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate("gateway.no-value"))}Ge([I()],V$.prototype,"rawData",void 0);class j${constructor(e){this.fb=e,this.destroy$=new te,this.onChange=e=>{}}ngOnInit(){this.valueListFormArray=this.fb.array([]),this.valueListFormArray.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e.map((({typeValue:e})=>({...e}))))}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}trackByKey(e,t){return t}addKey(){const e=this.fb.group({typeValue:[]});this.valueListFormArray.push(e)}deleteKey(e,t){e&&e.stopPropagation(),this.valueListFormArray.removeAt(t),this.valueListFormArray.markAsDirty()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){for(const t of e){const e={typeValue:[t]},n=this.fb.group(e);this.valueListFormArray.push(n)}}validate(){return this.valueListFormArray.valid?null:{valueListForm:{valid:!1}}}static{this.ɵfac=function(e){return new(e||j$)(t.ɵɵdirectiveInject(H.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:j$,selectors:[["tb-type-value-panel"]],inputs:{helpLink:"helpLink"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>j$)),multi:!0},{provide:K,useExisting:c((()=>j$)),multi:!0}]),t.ɵɵStandaloneFeature],decls:8,vars:5,consts:[["noKeys",""],[1,"tb-form-panel","no-border","no-padding"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],["tbTruncateWithTooltip","",1,"title-container"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],["formControlName","typeValue",3,"helpLink"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",1),t.ɵɵtemplate(1,z$,2,2,"div",2),t.ɵɵelementStart(2,"div")(3,"button",3),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(6,U$,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor)}if(2&e){const e=t.ɵɵreference(7);t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.valueListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,3,"gateway.add-value")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(j$,[j,_,V$]),styles:['@charset "UTF-8";[_nghost-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw}[_nghost-%COMP%] .key-panel[_ngcontent-%COMP%]{height:250px;overflow:auto}[_nghost-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}']})}}const H$=()=>({maxWidth:"970px"});function W$(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",19),t.ɵɵtext(2),t.ɵɵelementEnd(),t.ɵɵtext(3),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",e.get("key").value," "),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",":","  ")}}function $$(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function K$(e,n){if(1&e&&(t.ɵɵelement(0,"div",41),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext(3).$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,n.connectorType,n.keysType,e.get("type").value,n.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,H$))}}function Y$(e,n){if(1&e&&t.ɵɵelement(0,"mat-icon",44),2&e){let e;const n=t.ɵɵnextContext(4).$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("svgIcon",null==(e=i.valueTypes.get(n.get("type").value))?null:e.icon)}}function X$(e,n){if(1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){let e;const n=t.ɵɵnextContext(4).$implicit,i=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,null!==(e=null==(e=i.valueTypes.get(n.get("type").value))?null:e.name)&&void 0!==e?e:i.valueTypes.get(n.get("type").value))," ")}}function Z$(e,n){1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.raw")))}function Q$(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-select-trigger")(1,"div",42),t.ɵɵtemplate(2,Y$,1,1,"mat-icon",43)(3,X$,3,3,"span",36)(4,Z$,3,3,"ng-template",null,2,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()),2&e){let e;const n=t.ɵɵreference(5),i=t.ɵɵnextContext(3).$implicit,a=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("ngIf",null==(e=a.valueTypes.get(i.get("type").value))?null:e.icon),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!a.rawData)("ngIfElse",n)}}function J$(e,n){if(1&e&&t.ɵɵelement(0,"mat-icon",48),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(6);t.ɵɵpropertyInterpolate("svgIcon",n.valueTypes.get(e).icon)}}function eK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",46),t.ɵɵtemplate(1,J$,1,1,"mat-icon",47),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){let e,i;const a=n.$implicit,r=t.ɵɵnextContext(6);t.ɵɵproperty("value",a),t.ɵɵadvance(),t.ɵɵproperty("ngIf",null==(e=r.valueTypes.get(a))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,3,null!==(i=null==(i=r.valueTypes.get(a))?null:i.name)&&void 0!==i?i:r.valueTypes.get(a))," ")}}function tK(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,eK,5,5,"mat-option",45),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(5);t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.valueTypeKeys)}}function nK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-option",46)(1,"span"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("value","raw"),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,2,"gateway.raw")))}function iK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function aK(e,n){if(1&e&&(t.ɵɵelement(0,"div",41),t.ɵɵpipe(1,"getConnectorMappingHelpLink")),2&e){const e=t.ɵɵnextContext(3).$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind4(1,2,n.connectorType,n.keysType,e.get("type").value,n.convertorType))("tb-help-popup-style",t.ɵɵpureFunction0(7,H$))}}function rK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"div",24)(2,"div",25),t.ɵɵtext(3,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",26)(5,"div",27),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",28),t.ɵɵelement(10,"input",29),t.ɵɵpipe(11,"translate"),t.ɵɵtemplate(12,$$,3,3,"mat-icon",30)(13,K$,2,8,"div",31),t.ɵɵpipe(14,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",24)(16,"div",25),t.ɵɵtext(17,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"div",32)(19,"div",33),t.ɵɵtext(20,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",34)(22,"mat-select",35),t.ɵɵtemplate(23,Q$,6,3,"mat-select-trigger",18)(24,tK,2,1,"ng-container",36)(25,nK,4,4,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()(),t.ɵɵelementStart(27,"div",37)(28,"div",27),t.ɵɵpipe(29,"translate"),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(32,"mat-form-field",38),t.ɵɵelement(33,"input",39),t.ɵɵpipe(34,"translate"),t.ɵɵtemplate(35,iK,3,3,"mat-icon",30)(36,aK,2,8,"div",31),t.ɵɵpipe(37,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵreference(26),n=t.ɵɵnextContext(2).$implicit,i=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,13,"gateway.JSONPath-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,15,"gateway.key")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(11,17,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.get("key").hasError("required")&&n.get("key").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(14,19,i.connectorType,i.keysType,n.get("type").value,i.convertorType)),t.ɵɵadvance(10),t.ɵɵproperty("ngIf",!i.rawData),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!i.rawData)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(29,24,"gateway.JSONPath-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(31,26,"gateway.value")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(34,28,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.get("value").hasError("required")&&n.get("value").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind4(37,30,i.connectorType,i.keysType,n.get("type").value,i.convertorType))}}function oK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function sK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function lK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"div",26)(2,"div",33),t.ɵɵtext(3,"gateway.key"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",28),t.ɵɵelement(5,"input",29),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,oK,3,3,"mat-icon",30),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",26)(9,"div",33),t.ɵɵtext(10,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",49),t.ɵɵelement(12,"input",39),t.ɵɵpipe(13,"translate"),t.ɵɵtemplate(14,sK,3,3,"mat-icon",30),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,4,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("key").hasError("required")&&e.get("key").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(13,6,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("value").hasError("required")&&e.get("value").touched)}}function pK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-required"))}function cK(e,n){1&e&&t.ɵɵelement(0,"tb-type-value-panel",54)}function dK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"div",26)(2,"div",27),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",28),t.ɵɵelement(7,"input",50),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,pK,3,3,"mat-icon",30),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",14)(11,"mat-expansion-panel",51)(12,"mat-expansion-panel-header",52)(13,"mat-panel-title")(14,"div",53),t.ɵɵpipe(15,"translate"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(18,cK,1,0,"ng-template",20),t.ɵɵelementEnd()()()),2&e){let e;const n=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,7,"gateway.hints.method-name")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,9,"gateway.method-name")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,11,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.get("method").hasError("required")&&n.get("method").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(15,13,"gateway.hints.arguments")),t.ɵɵadvance(2),t.ɵɵtextInterpolate2(" ",t.ɵɵpipeBind1(17,15,"gateway.arguments"),""," ("+(null==(e=n.get("arguments").value)?null:e.length)+")"," ")}}function uK(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",55),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Key)}}function mK(e,n){if(1&e&&t.ɵɵtemplate(0,rK,38,35,"div",22)(1,lK,15,8,"div",22)(2,dK,19,17,"div",22)(3,uK,1,2,"tb-report-strategy",23),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("ngIf",e.keysType!==e.MappingKeysType.CUSTOM&&e.keysType!==e.MappingKeysType.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.MappingKeysType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.MappingKeysType.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.withReportStrategy&&(e.keysType===e.MappingKeysType.ATTRIBUTES||e.keysType===e.MappingKeysType.TIMESERIES))}}function hK(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",13)(1,"div",14),t.ɵɵelementContainerStart(2,15),t.ɵɵelementStart(3,"mat-expansion-panel",16)(4,"mat-expansion-panel-header",17)(5,"mat-panel-title"),t.ɵɵtemplate(6,W$,4,2,"ng-container",18),t.ɵɵelementStart(7,"div",19),t.ɵɵtext(8),t.ɵɵelementEnd()()(),t.ɵɵtemplate(9,mK,4,4,"ng-template",20),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",21),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.last,a=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",a.keysType!==a.MappingKeysType.RPC_METHODS),t.ɵɵadvance(2),t.ɵɵtextInterpolate(a.valueTitle(e)),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,a.deleteKeyTitle))}}function gK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",11),t.ɵɵtemplate(1,hK,14,7,"div",12),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.keysListFormArray.controls)("ngForTrackBy",e.trackByKey)}}function fK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",56)(1,"span",57),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class yK extends q{constructor(e,t,n){super(n),this.fb=e,this.popover=t,this.store=n,this.valueTypeEnum=Yt,this.valueTypes=Xt,this.valueTypeKeys=Object.values(Yt),this.rawData=!1,this.withReportStrategy=!0,this.keysDataApplied=new u,this.MappingKeysType=Ni,this.ReportStrategyDefaultValue=en,this.ConnectorType=ct,this.errorText=""}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}trackByKey(e,t){return t}addKey(){let e;e=this.keysType===Ni.RPC_METHODS?this.fb.group({method:["",[$.required]],arguments:[[],[]]}):this.keysType===Ni.CUSTOM?this.fb.group({key:["",[$.required,$.pattern(an)]],value:["",[$.required,$.pattern(an)]]}):this.fb.group({key:["",[$.required,$.pattern(an)]],type:[this.rawData?"raw":this.valueTypeKeys[0]],value:["",[$.required,$.pattern(an)]],reportStrategy:[{value:null,disabled:this.isReportStrategyDisabled()}]}),this.keysListFormArray.push(e)}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){let e=this.keysListFormArray.value.map((({reportStrategy:e,...t})=>({...t,...e&&{reportStrategy:e}})));if(this.keysType===Ni.CUSTOM){e={};for(let t of this.keysListFormArray.value)e[t.key]=t.value}this.keysDataApplied.emit(e)}prepareKeysFormArray(e){const t=[];return e&&(this.keysType===Ni.CUSTOM&&(e=Object.keys(e).map((t=>({key:t,value:e[t],type:""})))),e.forEach((e=>{let n;if(this.keysType===Ni.RPC_METHODS)n=this.fb.group({method:[e.method,[$.required]],arguments:[[...e.arguments],[]]});else if(this.keysType===Ni.CUSTOM){const{key:t,value:i}=e;n=this.fb.group({key:[t,[$.required,$.pattern(an)]],value:[i,[$.required,$.pattern(an)]]})}else{const{key:t,value:i,type:a,reportStrategy:r}=e;n=this.fb.group({key:[t,[$.required,$.pattern(an)]],type:[a],value:[i,[$.required,$.pattern(an)]],reportStrategy:[{value:r,disabled:this.isReportStrategyDisabled()}]})}t.push(n)}))),this.fb.array(t)}valueTitle(e){const t=this.keysType===Ni.RPC_METHODS?e.get("method").value:e.get("value").value;return ke(t)?"object"==typeof t?JSON.stringify(t):t:""}isReportStrategyDisabled(){return!(this.withReportStrategy&&(this.keysType===Ni.ATTRIBUTES||this.keysType===Ni.TIMESERIES))}static{this.ɵfac=function(e){return new(e||yK)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(it.TbPopoverComponent),t.ɵɵdirectiveInject(Ye.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:yK,selectors:[["tb-mapping-data-keys-panel"]],inputs:{panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keys:"keys",keysType:"keysType",connectorType:"connectorType",convertorType:"convertorType",sourceType:"sourceType",valueTypeEnum:"valueTypeEnum",valueTypes:"valueTypes",valueTypeKeys:"valueTypeKeys",rawData:"rawData",withReportStrategy:"withReportStrategy"},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:16,consts:[["noKeys",""],["rawOption",""],["rawText",""],[1,"tb-mapping-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex","flex-row","flex-wrap"],[4,"ngIf"],["tbTruncateWithTooltip","",1,"title-container"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],["class","tb-form-panel no-border no-padding",4,"ngIf"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue",4,"ngIf"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style",4,"ngIf"],[1,"tb-form-row"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","type"],[4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs","flex","items-center","justify-between"],["appearance","outline","subscriptSizing","dynamic",1,"no-gap","flex","flex-1"],["matInput","","required","","formControlName","value",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"tb-flex","align-center"],["class","tb-mat-18",3,"svgIcon",4,"ngIf"],[1,"tb-mat-18",3,"svgIcon"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["class","tb-mat-20",3,"svgIcon",4,"ngIf"],[1,"tb-mat-20",3,"svgIcon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","tb-suffix-absolute","flex"],["matInput","","name","value","formControlName","method",3,"placeholder"],[1,"tb-settings"],[1,"flex","flex-wrap"],[1,"title-container",3,"tb-hint-tooltip-icon"],["formControlName","arguments"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"div",5),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,gK,2,2,"div",6),t.ɵɵelementStart(6,"div")(7,"button",7),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(10,fK,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",8)(13,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.applyKeysData())})),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(11);t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,8,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")",""),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.keysListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,n.addKeyTitle)," "),t.ɵɵadvance(6),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,14,"action.apply")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(yK,[j,_,Xn,j$,o$]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] tb-value-input[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .tb-mapping-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}']})}}Ge([I()],yK.prototype,"rawData",void 0),Ge([I()],yK.prototype,"withReportStrategy",void 0);const vK=()=>({maxWidth:"970px"}),xK=(e,t)=>[e,t];function bK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.topic-required"))}function wK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.QualityTranslationsMap.get(e))," ")}}function SK(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.ConvertorTypeTranslationsMap.get(e))," ")}}function CK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",41),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("convertorType",e.ConvertorTypeEnum.JSON)("deviceInfoType",e.DeviceInfoType.FULL)}}function _K(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",42),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("deviceInfoType",e.DeviceInfoType.FULL)("convertorType",e.ConvertorTypeEnum.BYTES)("sourceTypes",t.ɵɵpureFunction2(3,xK,e.sourceTypesEnum.MSG,e.sourceTypesEnum.CONST))}}function TK(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",37),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function IK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function EK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function MK(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",14)(1,"div",31)(2,"div",32),t.ɵɵtext(3,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",43)(5,"mat-chip-listbox",44),t.ɵɵtemplate(6,IK,2,1,"mat-chip",45),t.ɵɵelementStart(7,"mat-chip",46),t.ɵɵelement(8,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",48,0),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(10),a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES))})),t.ɵɵelementStart(12,"tb-icon",49),t.ɵɵtext(13,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(14,"div",31)(15,"div",32),t.ɵɵtext(16,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"div",43)(18,"mat-chip-listbox",44),t.ɵɵtemplate(19,EK,2,1,"mat-chip",45),t.ɵɵelementStart(20,"mat-chip",46),t.ɵɵelement(21,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"button",48,1),t.ɵɵpipe(24,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(23),a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.TIMESERIES))})),t.ɵɵelementStart(25,"tb-icon",49),t.ɵɵtext(26,"edit"),t.ɵɵelementEnd()()()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",e.converterAttributes),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.converterAttributes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,6,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.converterTelemetry),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.converterTelemetry),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(24,8,"action.edit"))}}function kK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.extension-required"))}function PK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function DK(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",14)(1,"div",21)(2,"div",50),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",23),t.ɵɵelement(7,"input",51),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,kK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",52)(11,"div",35),t.ɵɵtext(12,"gateway.extension-configuration"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",15),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",31)(17,"div",32),t.ɵɵtext(18,"gateway.keys"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",43)(20,"mat-chip-listbox",44),t.ɵɵtemplate(21,PK,2,1,"mat-chip",45),t.ɵɵelementStart(22,"mat-chip",46),t.ɵɵelement(23,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"button",48,2),t.ɵɵpipe(26,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(25),a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.CUSTOM))})),t.ɵɵelementStart(27,"tb-icon",49),t.ɵɵtext(28,"edit"),t.ɵɵelementEnd()()()()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,8,"gateway.extension-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,10,"gateway.extension")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,12,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("converter.custom.extension").hasError("required")&&e.mappingForm.get("converter.custom.extension").touched),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,14,"gateway.extension-configuration-hint")),t.ɵɵadvance(6),t.ɵɵproperty("tbEllipsisChipList",e.customKeys),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.customKeys),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(26,16,"action.edit"))}}function OK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",22),t.ɵɵtext(2,"gateway.topic-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",23),t.ɵɵelement(4,"input",24),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,bK,3,3,"mat-icon",25),t.ɵɵelement(7,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",21)(9,"div",27),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",23)(14,"mat-select",28),t.ɵɵtemplate(15,wK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementContainerStart(16,30),t.ɵɵelementStart(17,"div",31)(18,"div",32),t.ɵɵtext(19,"gateway.payload-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"tb-toggle-select",33),t.ɵɵtemplate(21,SK,3,4,"tb-toggle-option",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"div",34)(23,"div",35),t.ɵɵtext(24,"gateway.data-conversion"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"div",15),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(28,36),t.ɵɵtemplate(29,CK,1,2,"ng-template",17)(30,_K,1,6,"ng-template",17)(31,TK,1,2,"tb-report-strategy",37)(32,MK,27,10,"div",38)(33,DK,29,18,"div",38),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,16,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("topicFilter").hasError("required")&&e.mappingForm.get("topicFilter").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/topic-filter_fn")("tb-help-popup-style",t.ɵɵpureFunction0(24,vK)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,18,"gateway.response-topic-Qos-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,20,"gateway.mqtt-qos")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.qualityTypes),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",e.convertorTypes),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(27,22,e.DataConversionTranslationsMap.get(e.converterType))," "),t.ɵɵadvance(2),t.ɵɵproperty("formGroupName",e.converterType)("ngSwitch",e.converterType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConvertorTypeEnum.JSON),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConvertorTypeEnum.BYTES),t.ɵɵadvance(),t.ɵɵconditional(e.data.withReportStrategy?31:-1),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.converterType===e.ConvertorTypeEnum.BYTES||e.converterType===e.ConvertorTypeEnum.JSON),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.converterType===e.ConvertorTypeEnum.CUSTOM)}}function AK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.RequestTypesTranslationsMap.get(e))," ")}}function FK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.topic-required"))}function RK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",22),t.ɵɵtext(2,"gateway.topic-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",23),t.ɵɵelement(4,"input",57),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,FK,3,3,"mat-icon",25),t.ɵɵelement(7,"div",26),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,5,"gateway.set")),t.ɵɵproperty("formControl",e.mappingForm.get("requestValue").get(e.requestMappingType).get("topicFilter")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue").get(e.requestMappingType).get("topicFilter").hasError("required")&&e.mappingForm.get("requestValue").get(e.requestMappingType).get("topicFilter").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/topic-filter_fn")("tb-help-popup-style",t.ɵɵpureFunction0(7,vK))}}function BK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",58),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("deviceInfoType",e.DeviceInfoType.FULL)}}function NK(e,n){if(1&e&&t.ɵɵelement(0,"tb-device-info-table",58),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("deviceInfoType",e.DeviceInfoType.PARTIAL)}}function LK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function VK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-info.device-name-expression-required"))}function qK(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function GK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.attribute-name-expression-required"))}function zK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-value-expression-required"))}function UK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-topic-expression-required"))}function jK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",34)(1,"div",59),t.ɵɵtext(2,"gateway.from-device-request-settings"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",60),t.ɵɵtext(4," gateway.from-device-request-settings-hint "),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",61)(6,"div",62)(7,"div",63),t.ɵɵtext(8,"gateway.device-info.device-name-expression"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",64)(10,"div",65)(11,"mat-form-field",23)(12,"mat-select",66),t.ɵɵtemplate(13,LK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(14,"mat-form-field",23),t.ɵɵelement(15,"input",67),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,VK,3,3,"mat-icon",25),t.ɵɵelement(18,"div",26),t.ɵɵelementEnd()()(),t.ɵɵelementStart(19,"div",21)(20,"div",22),t.ɵɵtext(21,"gateway.attribute-name-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"div",64)(23,"div",65)(24,"mat-form-field",23)(25,"mat-select",68),t.ɵɵtemplate(26,qK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(27,"mat-form-field",23),t.ɵɵelement(28,"input",69),t.ɵɵpipe(29,"translate"),t.ɵɵtemplate(30,GK,3,3,"mat-icon",25),t.ɵɵelement(31,"div",26),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(32,"div",34)(33,"div",59),t.ɵɵtext(34,"gateway.to-device-response-settings"),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"div",60),t.ɵɵtext(36," gateway.to-device-response-settings-hint "),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"div",21)(38,"div",22),t.ɵɵtext(39,"gateway.response-value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(40,"mat-form-field",23),t.ɵɵelement(41,"input",70),t.ɵɵpipe(42,"translate"),t.ɵɵtemplate(43,zK,3,3,"mat-icon",25),t.ɵɵelement(44,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"div",21)(46,"div",22),t.ɵɵtext(47,"gateway.response-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(48,"mat-form-field",23),t.ɵɵelement(49,"input",71),t.ɵɵpipe(50,"translate"),t.ɵɵtemplate(51,UK,3,3,"mat-icon",25),t.ɵɵelement(52,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(53,"div",72)(54,"mat-slide-toggle",73)(55,"mat-label",74),t.ɵɵpipe(56,"translate"),t.ɵɵtext(57),t.ɵɵpipe(58,"translate"),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(13),t.ɵɵproperty("ngForOf",e.sourceTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.deviceInfo.deviceNameExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.deviceInfo.deviceNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(32,vK)),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",e.sourceTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(29,22,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.attributeNameExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.attributeNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(33,vK)),t.ɵɵadvance(10),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(42,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.valueExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.valueExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(34,vK)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(50,26,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeRequests.topicExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeRequests.topicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(35,vK)),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(56,28,"gateway.retain-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(58,30,"gateway.retain")," ")}}function HK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-filter-required"))}function WK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.attribute-filter-required"))}function $K(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-value-expression-required"))}function KK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-topic-expression-required"))}function YK(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",50),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",23),t.ɵɵelement(6,"input",75),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,HK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",21)(10,"div",50),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-form-field",23),t.ɵɵelement(15,"input",76),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,WK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",21)(19,"div",22),t.ɵɵtext(20,"gateway.response-value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",23),t.ɵɵelement(22,"input",70),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,$K,3,3,"mat-icon",25),t.ɵɵelement(25,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"div",21)(27,"div",22),t.ɵɵtext(28,"gateway.response-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-form-field",23),t.ɵɵelement(30,"input",71),t.ɵɵpipe(31,"translate"),t.ɵɵtemplate(32,KK,3,3,"mat-icon",25),t.ɵɵelement(33,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"div",72)(35,"mat-slide-toggle",73)(36,"mat-label",74),t.ɵɵpipe(37,"translate"),t.ɵɵtext(38),t.ɵɵpipe(39,"translate"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,18,"gateway.device-name-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,20,"gateway.device-name-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,22,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.deviceNameFilter").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.deviceNameFilter").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,24,"gateway.attribute-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,26,"gateway.attribute-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,28,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.attributeFilter").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.attributeFilter").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,30,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.valueExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.valueExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(38,vK)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(31,32,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.attributeUpdates.topicExpression").hasError("required")&&e.mappingForm.get("requestValue.attributeUpdates.topicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(39,vK)),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(37,34,"gateway.retain-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(39,36,"gateway.retain")," ")}}function XK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-filter-required"))}function ZK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-filter-required"))}function QK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.request-topic-expression-required"))}function JK(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-expression-required"))}function eY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.response-topic-expression-required"))}function tY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(4);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.QualityTranslationsMap.get(e))," ")}}function nY(e,n){if(1&e&&t.ɵɵelement(0,"tb-error-icon",84),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("tooltipText",e.responseTimeoutErrorTooltip)}}function iY(e,n){1&e&&(t.ɵɵelementStart(0,"span",85),t.ɵɵtext(1,"gateway.suffix.ms"),t.ɵɵelementEnd())}function aY(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",21)(2,"div",22),t.ɵɵtext(3,"gateway.response-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",23),t.ɵɵelement(5,"input",81),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,eY,3,3,"mat-icon",25),t.ɵɵelement(8,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",21)(10,"div",27),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-form-field",23)(15,"mat-select",82),t.ɵɵtemplate(16,tY,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div",21)(18,"div",22),t.ɵɵtext(19,"gateway.response-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"mat-form-field",23),t.ɵɵelement(21,"input",83),t.ɵɵpipe(22,"translate"),t.ɵɵtemplate(23,nY,1,1,"tb-error-icon",84)(24,iY,2,0,"span",85),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.responseTopicExpression").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.responseTopicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(17,vK)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,11,"gateway.response-topic-Qos-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(13,13,"gateway.response-topic-Qos")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.qualityTypes),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(22,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((e.mappingForm.get("requestValue.serverSideRpc.responseTimeout").hasError("required")||e.mappingForm.get("requestValue.serverSideRpc.responseTimeout").hasError("min"))&&e.mappingForm.get("requestValue.serverSideRpc.responseTimeout").touched?23:24)}}function rY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",77)(1,"tb-toggle-select",33)(2,"tb-toggle-option",40),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-option",40),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",21)(9,"div",50),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",23),t.ɵɵelement(14,"input",75),t.ɵɵpipe(15,"translate"),t.ɵɵtemplate(16,XK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"div",21)(18,"div",50),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",23),t.ɵɵelement(23,"input",78),t.ɵɵpipe(24,"translate"),t.ɵɵtemplate(25,ZK,3,3,"mat-icon",25),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"div",21)(27,"div",22),t.ɵɵtext(28,"gateway.request-topic-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-form-field",23),t.ɵɵelement(30,"input",79),t.ɵɵpipe(31,"translate"),t.ɵɵtemplate(32,QK,3,3,"mat-icon",25),t.ɵɵelement(33,"div",26),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"div",21)(35,"div",22),t.ɵɵtext(36,"gateway.value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"mat-form-field",23),t.ɵɵelement(38,"input",70),t.ɵɵpipe(39,"translate"),t.ɵɵtemplate(40,JK,3,3,"mat-icon",25),t.ɵɵelement(41,"div",26),t.ɵɵelementEnd()(),t.ɵɵtemplate(42,aY,25,18,"ng-container",80)),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("value",e.ServerSideRPCType.TWO_WAY),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,21,"gateway.with-response")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",e.ServerSideRPCType.ONE_WAY),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,23,"gateway.without-response")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,25,"gateway.device-name-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,27,"gateway.device-name-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,29,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.deviceNameFilter").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.deviceNameFilter").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(19,31,"gateway.method-filter-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(21,33,"gateway.method-filter")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(24,35,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.methodFilter").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.methodFilter").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(31,37,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.requestTopicExpression").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.requestTopicExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(41,vK)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(39,39,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.valueExpression").hasError("required")&&e.mappingForm.get("requestValue.serverSideRpc.valueExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/mqtt-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(42,vK)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.mappingForm.get("requestValue.serverSideRpc.type").value===e.ServerSideRPCType.TWO_WAY)}}function oY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",32),t.ɵɵtext(2,"gateway.request-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",23)(4,"mat-select",53),t.ɵɵtemplate(5,AK,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementContainerStart(6,54)(7,55),t.ɵɵtemplate(8,RK,8,8,"div",56)(9,BK,1,1,"ng-template",17)(10,NK,1,1,"ng-template",17)(11,jK,59,36,"ng-template",17)(12,YK,40,40,"ng-template",17)(13,rY,43,43,"ng-template",17),t.ɵɵelementContainerEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.requestTypes),t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e.mappingForm.get("requestValue").get(e.requestMappingType))("ngSwitch",e.requestMappingType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.requestMappingType===e.RequestTypeEnum.ATTRIBUTE_REQUEST||e.requestMappingType===e.RequestTypeEnum.CONNECT_REQUEST||e.requestMappingType===e.RequestTypeEnum.DISCONNECT_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.CONNECT_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.DISCONNECT_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.ATTRIBUTE_REQUEST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.ATTRIBUTE_UPDATE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.RequestTypeEnum.SERVER_SIDE_RPC)}}function sY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",40),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SourceTypeTranslationsMap.get(e))," ")}}function lY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",39),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-node-required"))}function pY(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",37),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function cY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function dY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function uY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function mY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function hY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",86)(1,"div",87)(2,"div",88),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"div",89)(7,"mat-form-field",90)(8,"mat-select",91),t.ɵɵtemplate(9,sY,3,4,"mat-option",29),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"mat-form-field",90),t.ɵɵelement(11,"input",92),t.ɵɵpipe(12,"translate"),t.ɵɵtemplate(13,lY,3,3,"mat-icon",25),t.ɵɵelement(14,"div",26),t.ɵɵpipe(15,"getConnectorMappingHelpLink"),t.ɵɵelementEnd()(),t.ɵɵelement(16,"tb-device-info-table",93),t.ɵɵtemplate(17,pY,1,2,"tb-report-strategy",37),t.ɵɵelementStart(18,"div",31)(19,"div",32),t.ɵɵtext(20,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",43)(22,"mat-chip-listbox",44),t.ɵɵtemplate(23,cY,2,1,"mat-chip",45),t.ɵɵelementStart(24,"mat-chip",46),t.ɵɵelement(25,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"button",48,3),t.ɵɵpipe(28,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(27),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES))})),t.ɵɵelementStart(29,"tb-icon",49),t.ɵɵtext(30,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(31,"div",31)(32,"div",32),t.ɵɵtext(33,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"div",43)(35,"mat-chip-listbox",44),t.ɵɵtemplate(36,dY,2,1,"mat-chip",45),t.ɵɵelementStart(37,"mat-chip",46),t.ɵɵelement(38,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"button",48,4),t.ɵɵpipe(41,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(40),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.TIMESERIES))})),t.ɵɵelementStart(42,"tb-icon",49),t.ɵɵtext(43,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(44,"div",31)(45,"div",32),t.ɵɵtext(46,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(47,"div",43)(48,"mat-chip-listbox",44),t.ɵɵtemplate(49,uY,2,1,"mat-chip",45),t.ɵɵelementStart(50,"mat-chip",46),t.ɵɵelement(51,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(52,"button",48,5),t.ɵɵpipe(54,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(53),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES_UPDATES))})),t.ɵɵelementStart(55,"tb-icon",49),t.ɵɵtext(56,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(57,"div",31)(58,"div",32),t.ɵɵtext(59,"gateway.rpc-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(60,"div",43)(61,"mat-chip-listbox",44),t.ɵɵtemplate(62,mY,2,1,"mat-chip",45),t.ɵɵelementStart(63,"mat-chip",46),t.ɵɵelement(64,"label",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(65,"button",48,6),t.ɵɵpipe(67,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(66),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.RPC_METHODS))})),t.ɵɵelementStart(68,"tb-icon",49),t.ɵɵtext(69,"edit"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,23,"gateway.device-node-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,25,"gateway.device-node")," "),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",t.ɵɵpureFunction2(41,xK,e.OPCUaSourceTypesEnum.PATH,e.OPCUaSourceTypesEnum.IDENTIFIER)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(12,27,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mappingForm.get("deviceNodePattern").hasError("required")&&e.mappingForm.get("deviceNodePattern").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup",t.ɵɵpipeBind3(15,29,e.ConnectorType.OPCUA,"device-node",e.mappingForm.get("deviceNodeSource").value))("tb-help-popup-style",t.ɵɵpureFunction0(44,vK)),t.ɵɵadvance(2),t.ɵɵproperty("connectorType",e.ConnectorType.OPCUA)("sourceTypes",e.OPCUaSourceTypes)("deviceInfoType",e.DeviceInfoType.FULL),t.ɵɵadvance(),t.ɵɵconditional(e.data.withReportStrategy?17:-1),t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",e.opcAttributes),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcAttributes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(28,33,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.opcTelemetry),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcTelemetry),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(41,35,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.opcAttributesUpdates),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcAttributesUpdates),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(54,37,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.opcRpcMethods),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.opcRpcMethods),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(67,39,"action.edit"))}}class gY extends A{constructor(e,t,n,i,a,r,o,s,l){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.translate=l,this.MappingType=Di,this.qualityTypes=zi,this.QualityTranslationsMap=ni,this.convertorTypes=Object.values(Jn),this.ConvertorTypeEnum=Jn,this.ConvertorTypeTranslationsMap=ii,this.sourceTypes=Object.values(ei),this.OPCUaSourceTypes=Object.values(Mi),this.OPCUaSourceTypesEnum=Mi,this.sourceTypesEnum=ei,this.SourceTypeTranslationsMap=Bi,this.requestTypes=Object.values(ai),this.RequestTypeEnum=ai,this.RequestTypesTranslationsMap=ri,this.DeviceInfoType=ga,this.ServerSideRPCType=Wi,this.MappingKeysType=Ni,this.MappingHintTranslationsMap=Hi,this.MappingTypeTranslationsMap=Oi,this.DataConversionTranslationsMap=oi,this.HelpLinkByMappingTypeMap=ji,this.ConnectorType=ct,this.ReportStrategyDefaultValue=en,this.keysPopupClosed=!0,this.destroy$=new te,this.createMappingForm()}get converterAttributes(){if(this.converterType)return this.mappingForm.get("converter").get(this.converterType).value.attributes.map((e=>e.key))}get converterTelemetry(){if(this.converterType)return this.mappingForm.get("converter").get(this.converterType).value.timeseries.map((e=>e.key))}get opcAttributes(){return this.mappingForm.get("attributes").value?.map((e=>e.key))||[]}get opcTelemetry(){return this.mappingForm.get("timeseries").value?.map((e=>e.key))||[]}get opcRpcMethods(){return this.mappingForm.get("rpc_methods").value?.map((e=>e.method))||[]}get opcAttributesUpdates(){return this.mappingForm.get("attributes_updates")?.value?.map((e=>e.key))||[]}get converterType(){return this.mappingForm.get("converter")?.get("type").value}get customKeys(){return Object.keys(this.mappingForm.get("converter").get("custom").value.extensionConfig)}get requestMappingType(){return this.mappingForm.get("requestType").value}get responseTimeoutErrorTooltip(){const e=this.mappingForm.get("requestValue.serverSideRpc.responseTimeout");return e.hasError("required")?this.translate.instant("gateway.response-timeout-required"):e.hasError("min")?this.translate.instant("gateway.response-timeout-limits-error",{min:1}):""}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}createMappingForm(){switch(this.data.mappingType){case Di.DATA:this.mappingForm=this.fb.group({}),this.createDataMappingForm();break;case Di.REQUESTS:this.mappingForm=this.fb.group({}),this.createRequestMappingForm();break;case Di.OPCUA:this.createOPCUAMappingForm()}}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){this.mappingForm.valid&&this.dialogRef.close(this.prepareMappingData())}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))this.popoverService.hidePopover(i);else{const e=(this.data.mappingType!==Di.OPCUA?this.mappingForm.get("converter").get(this.converterType):this.mappingForm).get(n),t={keys:e.value,keysType:n,rawData:this.mappingForm.get("converter.type")?.value===Jn.BYTES,panelTitle:Li.get(n),addKeyTitle:Vi.get(n),deleteKeyTitle:qi.get(n),noKeysText:Gi.get(n),withReportStrategy:this.data.withReportStrategy,connectorType:this.data.mappingType===Di.OPCUA?ct.OPCUA:ct.MQTT,convertorType:this.converterType};this.data.mappingType===Di.OPCUA&&(t.valueTypeKeys=Object.values(Mi),t.valueTypeEnum=Mi,t.valueTypes=Bi,t.sourceType=this.mappingForm.get("deviceNodeSource").value),this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,yK,"leftBottom",!1,null,t,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(le(this.destroy$)).subscribe((t=>{this.popoverComponent.hide(),e.patchValue(t),e.markAsDirty()})),this.popoverComponent.tbHideStart.pipe(le(this.destroy$)).subscribe((()=>{this.keysPopupClosed=!0}))}}prepareMappingData(){const e=this.mappingForm.value;switch(Te(e),this.data.mappingType){case Di.DATA:const{converter:t,topicFilter:n,subscriptionQos:i}=e,{reportStrategy:a,...r}=t[t.type];return{topicFilter:n,subscriptionQos:i,...a?{reportStrategy:a}:{},converter:{type:t.type,...r}};case Di.REQUESTS:return{requestType:e.requestType,requestValue:e.requestValue[e.requestType]};default:return e}}getFormValueData(){if(this.data.value&&Object.keys(this.data.value).length)switch(this.data.mappingType){case Di.DATA:const{converter:e,topicFilter:t,subscriptionQos:n,reportStrategy:i}=this.data.value;return{topicFilter:t,subscriptionQos:n,converter:{type:e.type,[e.type]:{...e,...i?{reportStrategy:i}:{}}}};case Di.REQUESTS:return{requestType:this.data.value.requestType,requestValue:{[this.data.value.requestType]:this.data.value.requestValue}};default:return this.data.value}}createDataMappingForm(){this.mappingForm.addControl("topicFilter",this.fb.control("",[$.required,$.pattern(an)])),this.mappingForm.addControl("subscriptionQos",this.fb.control(0)),this.mappingForm.addControl("converter",this.fb.group({type:[Jn.JSON,[]],json:this.fb.group({deviceInfo:[{},[]],attributes:[[],[]],timeseries:[[],[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),bytes:this.fb.group({deviceInfo:[{},[]],attributes:[[],[]],timeseries:[[],[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),custom:this.fb.group({extension:["",[$.required,$.pattern(an)]],extensionConfig:[{},[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]})})),this.mappingForm.patchValue(this.getFormValueData()),this.mappingForm.get("converter.type").valueChanges.pipe(be(this.mappingForm.get("converter.type").value),le(this.destroy$)).subscribe((e=>{const t=this.mappingForm.get("converter");t.get("json").disable({emitEvent:!1}),t.get("bytes").disable({emitEvent:!1}),t.get("custom").disable({emitEvent:!1}),t.get(e).enable({emitEvent:!1})}))}createRequestMappingForm(){this.mappingForm.addControl("requestType",this.fb.control(ai.CONNECT_REQUEST,[])),this.mappingForm.addControl("requestValue",this.fb.group({connectRequests:this.fb.group({topicFilter:["",[$.required,$.pattern(an)]],deviceInfo:[{},[]]}),disconnectRequests:this.fb.group({topicFilter:["",[$.required,$.pattern(an)]],deviceInfo:[{},[]]}),attributeRequests:this.fb.group({topicFilter:["",[$.required,$.pattern(an)]],deviceInfo:this.fb.group({deviceNameExpressionSource:[ei.MSG,[]],deviceNameExpression:["",[$.required]]}),attributeNameExpressionSource:[ei.MSG,[]],attributeNameExpression:["",[$.required,$.pattern(an)]],topicExpression:["",[$.required,$.pattern(an)]],valueExpression:["",[$.required,$.pattern(an)]],retain:[!1,[]]}),attributeUpdates:this.fb.group({deviceNameFilter:["",[$.required,$.pattern(an)]],attributeFilter:["",[$.required,$.pattern(an)]],topicExpression:["",[$.required,$.pattern(an)]],valueExpression:["",[$.required,$.pattern(an)]],retain:[!0,[]]}),serverSideRpc:this.fb.group({type:[Wi.TWO_WAY,[]],deviceNameFilter:["",[$.required,$.pattern(an)]],methodFilter:["",[$.required,$.pattern(an)]],requestTopicExpression:["",[$.required,$.pattern(an)]],responseTopicExpression:["",[$.required,$.pattern(an)]],valueExpression:["",[$.required,$.pattern(an)]],responseTopicQoS:[0,[]],responseTimeout:[1e4,[$.required,$.min(1)]]}),reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]})),this.mappingForm.get("requestType").valueChanges.pipe(be(this.mappingForm.get("requestType").value),le(this.destroy$)).subscribe((e=>{const t=this.mappingForm.get("requestValue");t.get("connectRequests").disable({emitEvent:!1}),t.get("disconnectRequests").disable({emitEvent:!1}),t.get("attributeRequests").disable({emitEvent:!1}),t.get("attributeUpdates").disable({emitEvent:!1}),t.get("serverSideRpc").disable({emitEvent:!1}),t.get(e).enable()})),this.mappingForm.get("requestValue.serverSideRpc.type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{const t=this.mappingForm.get("requestValue.serverSideRpc");e===Wi.ONE_WAY?(t.get("responseTopicExpression").disable({emitEvent:!1}),t.get("responseTopicQoS").disable({emitEvent:!1}),t.get("responseTimeout").disable({emitEvent:!1})):(t.get("responseTopicExpression").enable({emitEvent:!1}),t.get("responseTopicQoS").enable({emitEvent:!1}),t.get("responseTimeout").enable({emitEvent:!1}))})),this.mappingForm.patchValue(this.getFormValueData())}createOPCUAMappingForm(){this.mappingForm=this.fb.group({deviceNodeSource:[Mi.PATH,[]],deviceNodePattern:["",[$.required]],deviceInfo:[{},[]],attributes:[[],[]],timeseries:[[],[]],rpc_methods:[[],[]],attributes_updates:[[],[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),this.mappingForm.patchValue(this.getFormValueData())}static{this.ɵfac=function(e){return new(e||gY)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(He.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:gY,selectors:[["tb-mapping-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:26,vars:19,consts:[["attributesButton",""],["telemetryButton",""],["keysButton",""],["opcAttributesButton",""],["opcTelemetryButton",""],["attributesUpdatesButton",""],["rpcMethodsButton",""],[1,"key-mapping",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-hint","tb-primary-fill"],[3,"ngSwitch"],[3,"ngSwitchCase"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex"],["matInput","","name","value","formControlName","topicFilter",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","subscriptionQos"],[3,"value",4,"ngFor","ngForOf"],["formGroupName","converter"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[3,"formGroupName","ngSwitch"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],["class","tb-form-panel no-border no-padding",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["formControlName","deviceInfo","required","true",3,"convertorType","deviceInfoType"],["formControlName","deviceInfo","required","true",3,"deviceInfoType","convertorType","sourceTypes"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[4,"ngFor","ngForOf"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","name","value","formControlName","extension",3,"placeholder"],[1,"tb-form-row","space-between","same-padding","tb-flex","column"],["formControlName","requestType"],["formGroupName","requestValue"],[3,"formGroup","ngSwitch"],["class","tb-form-row column-xs",4,"ngIf"],["matInput","","name","value",3,"formControl","placeholder"],["formControlName","deviceInfo","required","true",3,"deviceInfoType"],["translate","",1,"tb-form-panel-title","tb-required"],["translate","",1,"tb-form-hint","tb-primary-fill"],["formGroupName","deviceInfo",1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-flex","no-flex","align-center"],["translate","",1,"tb-required"],[1,"flex","flex-1","gap-2"],[1,"flex","w-1/4"],["formControlName","deviceNameExpressionSource"],["matInput","","name","value","formControlName","deviceNameExpression",3,"placeholder"],["formControlName","attributeNameExpressionSource"],["matInput","","name","value","formControlName","attributeNameExpression",3,"placeholder"],["matInput","","name","value","formControlName","valueExpression",3,"placeholder"],["matInput","","name","value","formControlName","topicExpression",3,"placeholder"],[1,"tb-form-row"],["formControlName","retain",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["matInput","","name","value","formControlName","deviceNameFilter",3,"placeholder"],["matInput","","name","value","formControlName","attributeFilter",3,"placeholder"],[1,"tb-flex","row","center","align-center","no-gap","fill-width"],["matInput","","name","value","formControlName","methodFilter",3,"placeholder"],["matInput","","name","value","formControlName","requestTopicExpression",3,"placeholder"],[4,"ngIf"],["matInput","","name","value","formControlName","responseTopicExpression",3,"placeholder"],["formControlName","responseTopicQoS"],["matInput","","name","value","type","number","min","1","formControlName","responseTimeout",3,"placeholder"],["matSuffix","",3,"tooltipText"],["translate","","matSuffix","",1,"block","pr-2"],[1,"device-node-row","tb-form-row","column-xs","pr-4"],["translate","",1,"tb-flex","no-flex","align-center","w-1/5"],[1,"tb-required",3,"tb-hint-tooltip-icon"],[1,"flex","w-1/5"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","deviceNodeSource"],["matInput","","name","value","formControlName","deviceNodePattern",3,"placeholder"],["formControlName","deviceInfo","required","true",3,"connectorType","sourceTypes","deviceInfoType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",7)(1,"mat-toolbar",8)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",9)(6,"div",10),t.ɵɵelementStart(7,"button",11),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",12),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",13)(11,"div",14)(12,"div",15),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(15,16),t.ɵɵtemplate(16,OK,34,25,"ng-template",17)(17,oY,14,9,"ng-template",17)(18,hY,70,45,"ng-template",17),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"div",18)(20,"button",19),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"button",20),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,11,n.MappingTypeTranslationsMap.get(null==n.data?null:n.data.mappingType))),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.HelpLinkByMappingTypeMap.get(n.data.mappingType)),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,13,n.MappingHintTranslationsMap.get(null==n.data?null:n.data.mappingType))," "),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",n.data.mappingType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingType.DATA),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingType.REQUESTS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.MappingType.OPCUA),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(22,15,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.mappingForm.invalid||!n.mappingForm.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,17,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(gY,[j,_,o$,Gn,I$,Qn,Xn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:grid;height:100%}[_nghost-%COMP%] .key-mapping[_ngcontent-%COMP%]{max-width:900px;display:flex;flex-direction:column}[_nghost-%COMP%] .key-mapping[_ngcontent-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{min-height:64px}[_nghost-%COMP%] .key-mapping[_ngcontent-%COMP%] tb-toggle-select[_ngcontent-%COMP%]{padding:4px 0}[_nghost-%COMP%] .mat-mdc-dialog-content[_ngcontent-%COMP%]{height:670px}[_nghost-%COMP%] .ellipsis-chips-container[_ngcontent-%COMP%]{max-width:70%}[_nghost-%COMP%] .key-mapping .mat-mdc-chip-listbox .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center;flex-wrap:nowrap}[_nghost-%COMP%] .tb-form-row .fixed-title-width{min-width:40px;width:35%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-form-row .mat-mdc-form-field{width:0}[_nghost-%COMP%] .device-node-row.tb-form-row{gap:12px}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}']})}}e("MappingDialogComponent",gY);const fY=["searchInput"],yY=()=>({minWidth:"96px",textAlign:"center"});function vY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",24)(2,"span",25),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",9),t.ɵɵelementStart(6,"button",11),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageMapping(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",11),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,e.mappingTypeTranslationsMap.get(e.mappingType))),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search"))}}function xY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-header-cell",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext();t.ɵɵclassProp("request-column",n.mappingType===n.mappingTypeEnum.REQUESTS),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,3,e.title)," ")}}function bY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext().$implicit,a=t.ɵɵnextContext();t.ɵɵclassProp("request-column",a.mappingType===a.mappingTypeEnum.REQUESTS),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e[i.def]," ")}}function wY(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,26),t.ɵɵtemplate(1,xY,3,5,"mat-header-cell",27)(2,bY,2,3,"mat-cell",28),t.ɵɵelementContainerEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("matColumnDef",e.def)}}function SY(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",31)}function CY(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",11),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageMapping(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",11),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteMapping(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function _Y(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,CY,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",32),t.ɵɵelementContainer(4,33),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",34)(6,"button",35),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",36),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",37,2),t.ɵɵelementContainer(11,33),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,yY)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function TY(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",38)}function IY(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class EY{set mappingType(e){this.mappingTypeValue!==e&&(this.mappingTypeValue=e)}get mappingType(){return this.mappingTypeValue}constructor(e,t,n,i){this.translate=e,this.dialog=t,this.dialogService=n,this.fb=i,this.required=!1,this.withReportStrategy=!0,this.mappingTypeTranslationsMap=Oi,this.mappingTypeEnum=Di,this.displayedColumns=[],this.mappingColumns=[],this.textSearchMode=!1,this.hidePageSize=!1,this.activeValue=!1,this.dirtyValue=!1,this.textSearch=this.fb.control("",{nonNullable:!0}),this.onChange=()=>{},this.onTouched=()=>{},this.destroy$=new te,this.mappingFormGroup=this.fb.array([]),this.dirtyValue=!this.activeValue,this.dataSource=new MY}ngOnInit(){this.setMappingColumns(),this.displayedColumns.push(...this.mappingColumns.map((e=>e.def)),"actions"),this.mappingFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.updateTableData(e),this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(de(150),ce(((e,t)=>(e??"")===t.trim())),le(this.destroy$)).subscribe((e=>{const t=e.trim();this.updateTableData(this.mappingFormGroup.value,t.trim())}))}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){this.mappingFormGroup.clear(),this.pushDataAsFormArrays(e)}validate(){return!this.required||this.mappingFormGroup.controls.length?null:{mappingFormGroup:{valid:!1}}}enterFilterMode(){this.textSearchMode=!0,setTimeout((()=>{this.searchInputField.nativeElement.focus(),this.searchInputField.nativeElement.setSelectionRange(0,0)}),10)}exitFilterMode(){this.updateTableData(this.mappingFormGroup.value),this.textSearchMode=!1,this.textSearch.reset()}manageMapping(e,t){e&&e.stopPropagation();const n=ke(t)?this.mappingFormGroup.at(t).value:{};this.dialog.open(gY,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{mappingType:this.mappingType,value:n,buttonTitle:Ae(t)?"action.add":"action.apply",withReportStrategy:this.withReportStrategy}}).afterClosed().pipe(ye(1),le(this.destroy$)).subscribe((e=>{e&&(ke(t)?this.mappingFormGroup.at(t).patchValue(e):this.pushDataAsFormArrays([e]),this.mappingFormGroup.markAsDirty())}))}updateTableData(e,t){let n=e.map((e=>this.getMappingValue(e)));t&&(n=n.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(n)}deleteMapping(e,t){e&&e.stopPropagation();const n=this.mappingFormGroup.controls[t].value,i=n.deviceInfo?.deviceNameExpression??n.topicFilter??this.getRequestDetails(n);this.dialogService.confirm(this.translate.instant("gateway.delete-mapping-title",{name:i}),this.translate.instant("gateway.delete-mapping-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).subscribe((e=>{e&&(this.mappingFormGroup.removeAt(t),this.mappingFormGroup.markAsDirty())}))}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.mappingFormGroup.push(this.fb.control(e))))}getMappingValue(e){switch(this.mappingType){case Di.DATA:const t=ii.get(e.converter?.type);return{topicFilter:e.topicFilter,QoS:e.subscriptionQos,converter:t?this.translate.instant(t):""};case Di.REQUESTS:return{requestType:e.requestType,type:this.translate.instant(ri.get(e.requestType)),details:this.getRequestDetails(e)};case Di.OPCUA:const n=e.deviceInfo?.deviceNameExpression,i=e.deviceInfo?.deviceProfileExpression,{deviceNodePattern:a}=e;return{deviceNodePattern:a,deviceNamePattern:n,deviceProfileExpression:i};default:return{}}}getRequestDetails(e){let t;return t=e.requestType===ai.ATTRIBUTE_UPDATE?e.requestValue.attributeFilter:e.requestType===ai.SERVER_SIDE_RPC?e.requestValue.methodFilter:e.requestValue.topicFilter,t}setMappingColumns(){switch(this.mappingType){case Di.DATA:this.mappingColumns.push({def:"topicFilter",title:"gateway.topic-filter"},{def:"QoS",title:"gateway.mqtt-qos"},{def:"converter",title:"gateway.payload-type"});break;case Di.REQUESTS:this.mappingColumns.push({def:"type",title:"gateway.type"},{def:"details",title:"gateway.details"});break;case Di.OPCUA:this.mappingColumns.push({def:"deviceNodePattern",title:"gateway.device-node"},{def:"deviceNamePattern",title:"gateway.device-name"},{def:"deviceProfileExpression",title:"gateway.device-profile"})}}static{this.ɵfac=function(e){return new(e||EY)(t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(_e.DialogService),t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:EY,selectors:[["tb-mapping-table"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(fY,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{required:"required",withReportStrategy:"withReportStrategy",mappingType:"mappingType"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>EY)),multi:!0},{provide:K,useExisting:c((()=>EY)),multi:!0}]),t.ɵɵStandaloneFeature],decls:40,vars:33,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-mapping-table","tb-absolute-fill"],[1,"tb-mapping-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef",4,"ngFor","ngForOf"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-mapping-table-title"],[3,"matColumnDef"],["class","table-value-column",3,"request-column",4,"matHeaderCellDef"],["tbTruncateWithTooltip","","class","table-value-column",3,"request-column",4,"matCellDef"],[1,"table-value-column"],["tbTruncateWithTooltip","",1,"table-value-column"],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,vY,14,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"div",7)(7,"button",8),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",9)(12,"mat-label"),t.ɵɵtext(13," "),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10,0),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵpipe(18,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(19,"mat-icon"),t.ɵɵtext(20,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(21,"div",12)(22,"table",13),t.ɵɵtemplate(23,wY,3,1,"ng-container",14),t.ɵɵelementContainerStart(24,15),t.ɵɵtemplate(25,SY,1,0,"mat-header-cell",16)(26,_Y,12,6,"mat-cell",17),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(27,TY,1,0,"mat-header-row",18)(28,IY,1,0,"mat-row",19),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"section",20),t.ɵɵpipe(30,"async"),t.ɵɵelementStart(31,"button",21),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageMapping(i))})),t.ɵɵelementStart(32,"mat-icon",22),t.ɵɵtext(33,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"span"),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(37,"span",23),t.ɵɵpipe(38,"async"),t.ɵɵtext(39," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(4,19,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,21,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,23,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,25,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.mappingColumns),t.ɵɵadvance(4),t.ɵɵproperty("matHeaderRowDef",n.displayedColumns)("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",n.displayedColumns),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(30,27,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,29,"gateway.add-mapping")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(38,31,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(EY,[j,_,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%]{width:100%;height:100%;background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content.tb-outlined-border[_ngcontent-%COMP%]{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;border:solid 1px #e0e0e0;border-radius:4px}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .tb-mapping-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:21%}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column.request-column[_ngcontent-%COMP%]{width:35%}[_nghost-%COMP%] .tb-mapping-table[_ngcontent-%COMP%] .tb-mapping-table-content[_ngcontent-%COMP%] .ellipsis[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-mapping-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}e("MappingTableComponent",EY),Ge([I()],EY.prototype,"required",void 0),Ge([I()],EY.prototype,"withReportStrategy",void 0);let MY=class extends G{constructor(){super()}};function kY(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",7),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SecurityTypeTranslationsMap.get(e))," ")}}function PY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",18),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.username-required"))}function DY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",8)(1,"div",9),t.ɵɵtext(2,"gateway.username"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",10)(4,"mat-form-field",11),t.ɵɵelement(5,"input",12),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,PY,3,3,"mat-icon",13),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",8)(9,"div",14),t.ɵɵtext(10,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",10)(12,"mat-form-field",11),t.ɵɵelement(13,"input",15),t.ɵɵpipe(14,"translate"),t.ɵɵelementStart(15,"div",16),t.ɵɵelement(16,"tb-toggle-password",17),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,3,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("username").hasError("required")&&e.securityFormGroup.get("username").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,5,"gateway.set"))}}function OY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",7),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function AY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",18),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.username-required"))}function FY(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",8)(2,"div",14),t.ɵɵtext(3,"gateway.mode"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",10)(5,"mat-form-field",11)(6,"mat-select",25),t.ɵɵtemplate(7,OY,2,2,"mat-option",4),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(8,"div",8)(9,"div",14),t.ɵɵtext(10,"gateway.username"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",10)(12,"mat-form-field",11),t.ɵɵelement(13,"input",12),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,AY,3,3,"mat-icon",13),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",8)(17,"div",14),t.ɵɵtext(18,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",10)(20,"mat-form-field",11),t.ɵɵelement(21,"input",15),t.ɵɵpipe(22,"translate"),t.ɵɵelementStart(23,"div",16),t.ɵɵelement(24,"tb-toggle-password",17),t.ɵɵelementEnd()()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",e.modeTypes),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,4,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.securityFormGroup.get("username").hasError("required")&&e.securityFormGroup.get("username").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(22,6,"gateway.set"))}}function RY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",19),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",8)(4,"div",20),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",10)(8,"mat-form-field",11),t.ɵɵelement(9,"input",21),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",8)(12,"div",20),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"div",10)(16,"mat-form-field",11),t.ɵɵelement(17,"input",22),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(19,"div",8)(20,"div",20),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"div",10)(24,"mat-form-field",11),t.ɵɵelement(25,"input",23),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(27,FY,25,8,"ng-container",24)),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,8,"gateway.path-hint")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,10,"gateway.CA-certificate-path")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(10,12,"gateway.set")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,14,"gateway.private-key-path")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(18,16,"gateway.set")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,18,"gateway.client-cert-path")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(26,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.mode()===e.SecurityMode.extendedCertificates)}}e("MappingDatasource",MY);class BY{constructor(e,t){this.fb=e,this.cdr=t,this.title="gateway.security",this.mode=l($i.certificates),this.BrokerSecurityType=ki,this.SecurityMode=$i,this.securityTypes=Object.values(ki),this.modeTypes=Object.values(Pi),this.SecurityTypeTranslationsMap=Ri,this.destroy$=new te,o((()=>{this.mode()===$i.basic&&(this.securityTypes=this.securityTypes.filter((e=>e!==ki.CERTIFICATES)))}))}ngOnInit(){this.securityFormGroup=this.fb.group({type:[ki.ANONYMOUS,[]],username:["",[$.required,$.pattern(an)]],password:["",[$.pattern(an)]],pathToCACert:["",[$.pattern(an)]],pathToPrivateKey:["",[$.pattern(an)]],pathToClientCert:["",[$.pattern(an)]]}),this.mode()===$i.extendedCertificates&&this.securityFormGroup.addControl("mode",this.fb.control(Pi.NONE,[])),this.securityFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{Te(e),this.onChange(e),this.onTouched()})),this.securityFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.updateValidators(e)))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){if(e)e.type||(e.type=ki.ANONYMOUS),this.updateValidators(e.type),this.securityFormGroup.reset(e,{emitEvent:!1});else{const e={type:ki.ANONYMOUS};this.securityFormGroup.reset(e,{emitEvent:!1})}this.cdr.markForCheck()}validate(){return this.securityFormGroup.get("type").value!==ki.BASIC||this.securityFormGroup.valid?null:{securityForm:{valid:!1}}}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}updateValidators(e){if(e)if(this.securityFormGroup.get("username").disable({emitEvent:!1}),this.securityFormGroup.get("password").disable({emitEvent:!1}),this.securityFormGroup.get("pathToCACert").disable({emitEvent:!1}),this.securityFormGroup.get("pathToPrivateKey").disable({emitEvent:!1}),this.securityFormGroup.get("pathToClientCert").disable({emitEvent:!1}),this.securityFormGroup.get("mode")?.disable({emitEvent:!1}),e===ki.BASIC)this.securityFormGroup.get("username").enable({emitEvent:!1}),this.securityFormGroup.get("password").enable({emitEvent:!1});else if(e===ki.CERTIFICATES&&(this.securityFormGroup.get("pathToCACert").enable({emitEvent:!1}),this.securityFormGroup.get("pathToPrivateKey").enable({emitEvent:!1}),this.securityFormGroup.get("pathToClientCert").enable({emitEvent:!1}),this.mode()===$i.extendedCertificates)){const e=this.securityFormGroup.get("mode");e&&!e.value&&e.setValue(Pi.NONE,{emitEvent:!1}),e?.enable({emitEvent:!1}),this.securityFormGroup.get("username").enable({emitEvent:!1}),this.securityFormGroup.get("password").enable({emitEvent:!1})}}static{this.ɵfac=function(e){return new(e||BY)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:BY,selectors:[["tb-security-config"]],inputs:{title:"title",mode:[1,"mode"]},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>BY)),multi:!0},{provide:K,useExisting:c((()=>BY)),multi:!0}]),t.ɵɵStandaloneFeature],decls:10,vars:8,consts:[[1,"tb-form-row","space-between","same-padding","tb-flex","column",3,"formGroup"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],[1,"fixed-title-width","tb-required"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[3,"ngSwitch"],[3,"ngSwitchCase"],[3,"value"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","username",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width"],["matInput","","type","password","name","value","formControlName","password",3,"placeholder"],["matSuffix","",1,"tb-flex","no-gap","align-center","fill-height"],[1,"tb-flex","align-center","fill-height"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[1,"tb-form-hint","tb-primary-fill"],["tbTruncateWithTooltip","",1,"fixed-title-width"],["matInput","","name","value","formControlName","pathToCACert",3,"placeholder"],["matInput","","name","value","formControlName","pathToPrivateKey",3,"placeholder"],["matInput","","name","value","formControlName","pathToClientCert",3,"placeholder"],[4,"ngIf"],["formControlName","mode"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",3),t.ɵɵtemplate(6,kY,3,4,"tb-toggle-option",4),t.ɵɵelementEnd()(),t.ɵɵelementContainerStart(7,5),t.ɵɵtemplate(8,DY,17,7,"ng-template",6)(9,RY,28,22,"ng-template",6),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.securityFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,6,n.title)),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.securityTypes),t.ɵɵadvance(),t.ɵɵproperty("ngSwitch",n.securityFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.BrokerSecurityType.BASIC),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.BrokerSecurityType.CERTIFICATES))},dependencies:t.ɵɵgetComponentDepsFactory(BY,[j,_,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}'],changeDetection:d.OnPush})}}e("SecurityConfigComponent",BY);const NY=()=>({min:1e3}),LY=()=>({min:50}),VY=()=>({min:100});function qY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.server-url-required"))}function GY(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",9),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.timeout-error",t.ɵɵpureFunction0(4,NY)))}function zY(e,n){1&e&&(t.ɵɵelementStart(0,"span",10),t.ɵɵtext(1,"gateway.suffix.ms"),t.ɵɵelementEnd())}function UY(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name)}}function jY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind2(1,1,"gateway.scan-period-error",t.ɵɵpureFunction0(4,NY)))}function HY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind2(1,1,"gateway.poll-period-error",t.ɵɵpureFunction0(4,LY)))}function WY(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"div",6),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"div",7),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"mat-form-field",3),t.ɵɵelement(7,"input",23),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,HY,3,5,"mat-icon",5),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,4,"gateway.hints.poll-period")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,6,"gateway.poll-period")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.serverConfigFormGroup.get("pollPeriodInMillis").hasError("required")||e.serverConfigFormGroup.get("pollPeriodInMillis").hasError("min"))&&e.serverConfigFormGroup.get("pollPeriodInMillis").touched)}}function $Y(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind2(1,1,"gateway.sub-check-period-error",t.ɵɵpureFunction0(4,VY)))}class KY{constructor(e){this.fb=e,this.hideNewFields=!1,this.securityPolicyTypes=Fi,this.SecurityMode=$i,this.destroy$=new te,this.serverConfigFormGroup=this.fb.group({url:["",[$.required,$.pattern(an)]],timeoutInMillis:[1e3,[$.required,$.min(1e3)]],scanPeriodInMillis:[z,[$.required,$.min(1e3)]],pollPeriodInMillis:[5e3,[$.required,$.min(50)]],enableSubscriptions:[!0,[]],subCheckPeriodInMillis:[100,[$.required,$.min(100)]],showMap:[!1,[]],security:[Ai.BASIC128,[]],identity:[]}),this.serverConfigFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}ngAfterViewInit(){this.hideNewFields&&this.serverConfigFormGroup.get("pollPeriodInMillis").disable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.serverConfigFormGroup.valid?null:{serverConfigFormGroup:{valid:!1}}}writeValue(e){const{timeoutInMillis:t=1e3,scanPeriodInMillis:n=z,pollPeriodInMillis:i=5e3,enableSubscriptions:a=!0,subCheckPeriodInMillis:r=100,showMap:o=!1,security:s=Ai.BASIC128,identity:l={}}=e;this.serverConfigFormGroup.reset({...e,timeoutInMillis:t,scanPeriodInMillis:n,pollPeriodInMillis:i,enableSubscriptions:a,subCheckPeriodInMillis:r,showMap:o,security:s,identity:l},{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||KY)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:KY,selectors:[["tb-opc-server-config"]],inputs:{hideNewFields:"hideNewFields"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>KY)),multi:!0},{provide:K,useExisting:c((()=>KY)),multi:!0}]),t.ɵɵStandaloneFeature],decls:63,vars:56,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],["tbTruncateWithTooltip","","translate","",1,"fixed-title-width"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","url",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],[1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip",""],["matInput","","type","number","min","1000","name","value","formControlName","timeoutInMillis",3,"placeholder"],["matSuffix","",3,"tooltipText"],["translate","","matSuffix","",1,"block","pr-2"],["formControlName","security"],[3,"value",4,"ngFor","ngForOf"],["matInput","","type","number","min","1000","name","value","formControlName","scanPeriodInMillis",3,"placeholder"],["class","tb-form-row column-xs",4,"ngIf"],["matInput","","type","number","min","100","name","value","formControlName","subCheckPeriodInMillis",3,"placeholder"],[1,"tb-form-row"],["formControlName","enableSubscriptions",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","showMap",1,"mat-slide"],["formControlName","identity",3,"mode"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["matInput","","type","number","min","50","name","value","formControlName","pollPeriodInMillis",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.server-url"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",3),t.ɵɵelement(5,"input",4),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,qY,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",1)(9,"div",6),t.ɵɵpipe(10,"translate"),t.ɵɵelementStart(11,"div",7),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-form-field",3),t.ɵɵelement(15,"input",8),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,GY,2,5,"tb-error-icon",9)(18,zY,2,0,"span",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"div",1)(20,"div",6),t.ɵɵpipe(21,"translate"),t.ɵɵelementStart(22,"div",7),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(25,"mat-form-field",3)(26,"mat-select",11),t.ɵɵtemplate(27,UY,2,2,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(28,"div",1)(29,"div",6),t.ɵɵpipe(30,"translate"),t.ɵɵelementStart(31,"div",7),t.ɵɵtext(32),t.ɵɵpipe(33,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"mat-form-field",3),t.ɵɵelement(35,"input",13),t.ɵɵpipe(36,"translate"),t.ɵɵtemplate(37,jY,3,5,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵtemplate(38,WY,10,10,"div",14),t.ɵɵelementStart(39,"div",1)(40,"div",6),t.ɵɵpipe(41,"translate"),t.ɵɵelementStart(42,"div",7),t.ɵɵtext(43),t.ɵɵpipe(44,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"mat-form-field",3),t.ɵɵelement(46,"input",15),t.ɵɵpipe(47,"translate"),t.ɵɵtemplate(48,$Y,3,5,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(49,"div",16)(50,"mat-slide-toggle",17)(51,"mat-label",18),t.ɵɵpipe(52,"translate"),t.ɵɵelementStart(53,"div",7),t.ɵɵtext(54),t.ɵɵpipe(55,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(56,"div",16)(57,"mat-slide-toggle",19)(58,"mat-label",18),t.ɵɵpipe(59,"translate"),t.ɵɵtext(60),t.ɵɵpipe(61,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelement(62,"tb-security-config",20),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.serverConfigFormGroup),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.serverConfigFormGroup.get("url").hasError("required")&&n.serverConfigFormGroup.get("url").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,26,"gateway.hints.opc-timeout")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,28,"gateway.timeout")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,30,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((n.serverConfigFormGroup.get("timeoutInMillis").hasError("required")||n.serverConfigFormGroup.get("timeoutInMillis").hasError("min"))&&n.serverConfigFormGroup.get("timeoutInMillis").touched?17:18),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,32,"gateway.hints.security-policy")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(24,34,"gateway.security-policy")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",n.securityPolicyTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(30,36,"gateway.hints.scan-period")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(33,38,"gateway.scan-period")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,40,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.serverConfigFormGroup.get("scanPeriodInMillis").hasError("required")||n.serverConfigFormGroup.get("scanPeriodInMillis").hasError("min"))&&n.serverConfigFormGroup.get("scanPeriodInMillis").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.hideNewFields),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(41,42,"gateway.hints.sub-check-period")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(44,44,"gateway.sub-check-period")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(47,46,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.serverConfigFormGroup.get("subCheckPeriodInMillis").hasError("required")||n.serverConfigFormGroup.get("subCheckPeriodInMillis").hasError("min"))&&n.serverConfigFormGroup.get("subCheckPeriodInMillis").touched),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(52,48,"gateway.hints.enable-subscription")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(55,50,"gateway.enable-subscription")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(59,52,"gateway.hints.show-map")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(61,54,"gateway.show-map")," "),t.ɵɵadvance(2),t.ɵɵproperty("mode",n.SecurityMode.extendedCertificates))},dependencies:t.ɵɵgetComponentDepsFactory(KY,[j,_,BY,qn,Qn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}'],changeDetection:d.OnPush})}}e("OpcServerConfigComponent",KY),Ge([I()],KY.prototype,"hideNewFields",void 0);class YY extends Ba{constructor(){super(...arguments),this.withReportStrategy=!0,this.mappingTypes=Di,this.isLegacy=!0}initBasicFormGroup(){return this.fb.group({mapping:[],server:[]})}mapConfigToFormValue(e){return{server:e.server?ja.mapServerToUpgradedVersion(e.server):{},mapping:e.server?.mapping?ja.mapMappingToUpgradedVersion(e.server.mapping):[]}}getMappedValue(e){return{server:ja.mapServerToDowngradedVersion(e)}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(YY)))(n||YY)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:YY,selectors:[["tb-opc-ua-legacy-basic-config"]],inputs:{withReportStrategy:"withReportStrategy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>YY)),multi:!0},{provide:K,useExisting:c((()=>YY)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server",3,"hideNewFields"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"required","withReportStrategy","mappingType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-opc-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,11,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,13,"gateway.server"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("hideNewFields",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,15,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("required",!0)("withReportStrategy",n.withReportStrategy)("mappingType",n.mappingTypes.OPCUA))},dependencies:t.ɵɵgetComponentDepsFactory(YY,[j,_,BY,EY,KY]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("OpcUaLegacyBasicConfigComponent",YY),Ge([I()],YY.prototype,"withReportStrategy",void 0);class XY extends Ba{constructor(){super(...arguments),this.withReportStrategy=!0,this.mappingTypes=Di,this.isLegacy=!1}initBasicFormGroup(){return this.fb.group({mapping:[],server:[]})}mapConfigToFormValue(e){return{server:e.server??{},mapping:e.mapping??[]}}getMappedValue(e){return{server:e.server,mapping:e.mapping}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(XY)))(n||XY)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:XY,selectors:[["tb-opc-ua-basic-config"]],inputs:{withReportStrategy:"withReportStrategy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>XY)),multi:!0},{provide:K,useExisting:c((()=>XY)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server",3,"hideNewFields"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"required","withReportStrategy","mappingType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-opc-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,11,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,13,"gateway.server"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("hideNewFields",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,15,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("required",!0)("withReportStrategy",n.withReportStrategy)("mappingType",n.mappingTypes.OPCUA))},dependencies:t.ɵɵgetComponentDepsFactory(XY,[j,_,BY,EY,KY]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("OpcUaBasicConfigComponent",XY),Ge([I()],XY.prototype,"withReportStrategy",void 0);class ZY extends Ba{constructor(){super(...arguments),this.withReportStrategy=!0,this.MappingType=Di}initBasicFormGroup(){return this.fb.group({mapping:[],requestsMapping:[],broker:[],workers:[]})}getRequestDataArray(e){const t=[];return Fe(e)&&Object.keys(e).forEach((n=>{for(const i of e[n])t.push({requestType:n,requestValue:i})})),t}getRequestDataObject(e){return e.reduce(((e,{requestType:t,requestValue:n})=>(e[t].push(n),e)),{connectRequests:[],disconnectRequests:[],attributeRequests:[],attributeUpdates:[],serverSideRpc:[]})}getBrokerMappedValue(e,t){return{...e,maxNumberOfWorkers:t.maxNumberOfWorkers??100,maxMessageNumberPerWorker:t.maxMessageNumberPerWorker??10}}writeValue(e){this.basicFormGroup.setValue(this.mapConfigToFormValue(e),{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(ZY)))(n||ZY)}})()}static{this.ɵdir=t.ɵɵdefineDirective({type:ZY,inputs:{withReportStrategy:"withReportStrategy"},features:[t.ɵɵInheritDefinitionFeature]})}}function QY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",8),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.max-number-of-workers-required"))}function JY(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",8),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.max-messages-queue-for-worker-required"))}e("MqttBasicConfigDirective",ZY),Ge([I()],ZY.prototype,"withReportStrategy",void 0);class eX{constructor(e){this.fb=e,this.destroy$=new te,this.workersConfigFormGroup=this.fb.group({maxNumberOfWorkers:[100,[$.required,$.min(1)]],maxMessageNumberPerWorker:[10,[$.required,$.min(1)]]}),this.workersConfigFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){const{maxNumberOfWorkers:t,maxMessageNumberPerWorker:n}=e;this.workersConfigFormGroup.reset({maxNumberOfWorkers:t||100,maxMessageNumberPerWorker:n||10},{emitEvent:!1})}validate(){return this.workersConfigFormGroup.valid?null:{workersConfigFormGroup:{valid:!1}}}static{this.ɵfac=function(e){return new(e||eX)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:eX,selectors:[["tb-workers-config-control"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>eX)),multi:!0},{provide:K,useExisting:c((()=>eX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:21,vars:21,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",2,"width","50%",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip",""],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","type","number","min","1","formControlName","maxNumberOfWorkers",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","name","value","type","number","min","1","formControlName","maxMessageNumberPerWorker",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",4),t.ɵɵelement(8,"input",5),t.ɵɵpipe(9,"translate"),t.ɵɵtemplate(10,QY,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",1)(12,"div",2),t.ɵɵpipe(13,"translate"),t.ɵɵelementStart(14,"div",3),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"mat-form-field",4),t.ɵɵelement(18,"input",7),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,JY,3,3,"mat-icon",6),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.workersConfigFormGroup),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,9,"gateway.max-number-of-workers-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,11,"gateway.max-number-of-workers")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(9,13,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.workersConfigFormGroup.get("maxNumberOfWorkers").hasError("min")||n.workersConfigFormGroup.get("maxNumberOfWorkers").hasError("required")&&n.workersConfigFormGroup.get("maxNumberOfWorkers").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(13,15,"gateway.max-messages-queue-for-worker-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,17,"gateway.max-messages-queue-for-worker")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,19,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.workersConfigFormGroup.get("maxMessageNumberPerWorker").hasError("min")||n.workersConfigFormGroup.get("maxMessageNumberPerWorker").hasError("required")&&n.workersConfigFormGroup.get("maxMessageNumberPerWorker").touched))},dependencies:t.ɵɵgetComponentDepsFactory(eX,[j,_,qn]),encapsulation:2,changeDetection:d.OnPush})}}function tX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",13),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function nX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",13),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.brokerConfigFormGroup.get("port")))}}function iX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",14),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name)}}function aX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",15),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("clientId"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.generate-client-id"))}e("WorkersConfigControlComponent",eX);class rX{constructor(e,t){this.fb=e,this.cdr=t,this.mqttVersions=ti,this.portLimits=Ii,this.destroy$=new te,this.brokerConfigFormGroup=this.fb.group({host:["",[$.required,$.pattern(an)]],port:[null,[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],version:[5,[]],clientId:["tb_gw_"+Re(5),[$.pattern(an)]],security:[]}),this.brokerConfigFormGroup.valueChanges.subscribe((e=>{this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}generate(e){this.brokerConfigFormGroup.get(e)?.patchValue("tb_gw_"+Re(5))}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){const{version:t=5,clientId:n=`tb_gw_${Re(5)}`,security:i={}}=e;this.brokerConfigFormGroup.reset({...e,version:t,clientId:n,security:i},{emitEvent:!1}),this.cdr.markForCheck()}validate(){return this.brokerConfigFormGroup.valid?null:{brokerConfigFormGroup:{valid:!1}}}static{this.ɵfac=function(e){return new(e||rX)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:rX,selectors:[["tb-broker-config-control"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>rX)),multi:!0},{provide:K,useExisting:c((()=>rX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:29,vars:16,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["translate","",1,"fixed-title-width"],["formControlName","version"],[3,"value",4,"ngFor","ngForOf"],["matInput","","name","value","formControlName","clientId",3,"placeholder"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"matTooltip","click",4,"ngIf"],["formControlName","security"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"click","matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",3),t.ɵɵelement(5,"input",4),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,tX,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",1)(9,"div",2),t.ɵɵtext(10,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",3),t.ɵɵelement(12,"input",6),t.ɵɵpipe(13,"translate"),t.ɵɵtemplate(14,nX,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"div",1)(16,"div",7),t.ɵɵtext(17,"gateway.mqtt-version"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"mat-form-field",3)(19,"mat-select",8),t.ɵɵtemplate(20,iX,2,2,"mat-option",9),t.ɵɵelementEnd()()(),t.ɵɵelementStart(21,"div",1)(22,"div",7),t.ɵɵtext(23,"gateway.client-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(24,"mat-form-field",3),t.ɵɵelement(25,"input",10),t.ɵɵpipe(26,"translate"),t.ɵɵtemplate(27,aX,4,3,"button",11),t.ɵɵelementEnd()(),t.ɵɵelement(28,"tb-security-config",12),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.brokerConfigFormGroup),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,10,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.brokerConfigFormGroup.get("host").hasError("required")&&n.brokerConfigFormGroup.get("host").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(13,12,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.brokerConfigFormGroup.get("port").hasError("required")||n.brokerConfigFormGroup.get("port").hasError("min")||n.brokerConfigFormGroup.get("port").hasError("max"))&&n.brokerConfigFormGroup.get("port").touched),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.mqttVersions),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(26,14,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!n.brokerConfigFormGroup.get("clientId").value))},dependencies:t.ɵɵgetComponentDepsFactory(rX,[j,_,BY,r$]),encapsulation:2,changeDetection:d.OnPush})}}e("BrokerConfigControlComponent",rX);class oX extends ZY{mapConfigToFormValue(e){const{broker:t,mapping:n=[],requestsMapping:i}=e;return{workers:t&&(t.maxNumberOfWorkers||t.maxMessageNumberPerWorker)?{maxNumberOfWorkers:t.maxNumberOfWorkers,maxMessageNumberPerWorker:t.maxMessageNumberPerWorker}:{},mapping:n??[],broker:t??{},requestsMapping:this.getRequestDataArray(i)}}getMappedValue(e){const{broker:t,workers:n,mapping:i,requestsMapping:a}=e||{};return{broker:this.getBrokerMappedValue(t,n),mapping:i,requestsMapping:a?.length?this.getRequestDataObject(a):{}}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(oX)))(n||oX)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:oX,selectors:[["tb-mqtt-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>oX)),multi:!0},{provide:K,useExisting:c((()=>oX)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:24,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","broker"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"withReportStrategy","required","mappingType"],["formControlName","requestsMapping",3,"withReportStrategy","mappingType"],[1,"tb-form-panel","no-border","no-padding"],["formControlName","workers"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-broker-config-control",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-tab",1),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"div",4),t.ɵɵelement(14,"tb-mapping-table",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-tab",1),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"div",7),t.ɵɵelement(18,"tb-workers-config-control",8),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,14,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,16,"gateway.broker.connection"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,18,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("required",!0)("mappingType",n.MappingType.DATA),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,20,"gateway.requests-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("mappingType",n.MappingType.REQUESTS),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(16,22,"gateway.workers-settings")))},dependencies:t.ɵɵgetComponentDepsFactory(oX,[j,_,eX,rX,EY]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("MqttBasicConfigComponent",oX);class sX extends ZY{mapConfigToFormValue(e){const{broker:t,mapping:n=[],connectRequests:i=[],disconnectRequests:a=[],attributeRequests:r=[],attributeUpdates:o=[],serverSideRpc:s=[]}=e,l=Pa.mapRequestsToUpgradedVersion({connectRequests:i,disconnectRequests:a,attributeRequests:r,attributeUpdates:o,serverSideRpc:s});return{workers:t&&(t.maxNumberOfWorkers||t.maxMessageNumberPerWorker)?{maxNumberOfWorkers:t.maxNumberOfWorkers,maxMessageNumberPerWorker:t.maxMessageNumberPerWorker}:{},mapping:Pa.mapMappingToUpgradedVersion(n)||[],broker:t||{},requestsMapping:this.getRequestDataArray(l)}}getMappedValue(e){const{broker:t,workers:n,mapping:i,requestsMapping:a}=e||{},r=a?.length?this.getRequestDataObject(a):{};return{broker:this.getBrokerMappedValue(t,n),mapping:Pa.mapMappingToDowngradedVersion(i),...Pa.mapRequestsToDowngradedVersion(r)}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(sX)))(n||sX)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:sX,selectors:[["tb-mqtt-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>sX)),multi:!0},{provide:K,useExisting:c((()=>sX)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:24,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","broker"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","mapping",3,"withReportStrategy","required","mappingType"],["formControlName","requestsMapping",3,"withReportStrategy","mappingType"],[1,"tb-form-panel","no-border","no-padding"],["formControlName","workers"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-broker-config-control",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-mapping-table",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-tab",1),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"div",4),t.ɵɵelement(14,"tb-mapping-table",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-tab",1),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"div",7),t.ɵɵelement(18,"tb-workers-config-control",8),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,14,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,16,"gateway.broker.connection"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,18,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("required",!0)("mappingType",n.MappingType.DATA),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,20,"gateway.requests-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("withReportStrategy",n.withReportStrategy)("mappingType",n.MappingType.REQUESTS),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(16,22,"gateway.workers-settings")))},dependencies:t.ɵɵgetComponentDepsFactory(sX,[j,_,eX,rX,EY]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("MqttLegacyBasicConfigComponent",sX);class lX extends A{constructor(e,t,n,i,a){super(t,n,a),this.fb=e,this.store=t,this.router=n,this.data=i,this.dialogRef=a,this.portLimits=Ii,this.modbusProtocolTypes=Object.values(Ki),this.modbusMethodTypes=Object.values(Yi),this.modbusSerialMethodTypes=Object.values(Xi),this.modbusParities=Object.values(Zi),this.modbusByteSizes=na,this.modbusBaudrates=ta,this.modbusOrderType=Object.values(Qi),this.ModbusProtocolType=Ki,this.ModbusParityLabelsMap=la,this.ModbusProtocolLabelsMap=sa,this.ModbusMethodLabelsMap=oa,this.ReportStrategyDefaultValue=en,this.modbusHelpLink=O+"/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters",this.serialSpecificControlKeys=["serialPort","baudrate","stopbits","bytesize","parity","strict"],this.tcpUdpSpecificControlKeys=["port","security","host"],this.destroy$=new te,this.showSecurityControl=this.fb.control(!1),this.initializeSlaveFormGroup(),this.updateSlaveFormGroup(),this.updateControlsEnabling(this.data.value.type),this.observeTypeChange(),this.observeShowSecurity(),this.showSecurityControl.patchValue(!!this.data.value.security&&!Se(this.data.value.security,{}))}get protocolType(){return this.slaveConfigFormGroup.get("type").value}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}cancel(){this.dialogRef.close(null)}add(){this.slaveConfigFormGroup.valid&&this.dialogRef.close(this.getSlaveResultData())}initializeSlaveFormGroup(){this.slaveConfigFormGroup=this.fb.group({type:[Ki.TCP],host:["",[$.required,$.pattern(an)]],port:[null,[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],serialPort:["",[$.required,$.pattern(an)]],method:[Yi.SOCKET,[$.required]],baudrate:[this.modbusBaudrates[0]],stopbits:[1],bytesize:[na[0]],parity:[Zi.None],strict:[!0],unitId:[null,[$.required]],deviceName:["",[$.required,$.pattern(an)]],deviceType:["",[$.required,$.pattern(an)]],timeout:[35],byteOrder:[Qi.BIG],wordOrder:[Qi.BIG],retries:[!0],retryOnEmpty:[!0],retryOnInvalid:[!0],pollPeriod:[1e3,[$.required]],connectAttemptTimeMs:[500,[$.required]],connectAttemptCount:[5,[$.required]],waitAfterFailedAttemptsMs:[3e4,[$.required]],values:[{}],security:[{}]}),this.addFieldsToFormGroup()}updateSlaveFormGroup(){this.slaveConfigFormGroup.patchValue({...this.data.value,port:this.data.value.type===Ki.Serial?null:this.data.value.port,serialPort:this.data.value.type===Ki.Serial?this.data.value.port:"",values:{attributes:this.data.value.attributes??[],timeseries:this.data.value.timeseries??[],attributeUpdates:this.data.value.attributeUpdates??[],rpc:this.data.value.rpc??[]}})}observeTypeChange(){this.slaveConfigFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.updateControlsEnabling(e),this.updateMethodType(e)}))}updateMethodType(e){this.slaveConfigFormGroup.get("method").value!==Yi.RTU&&this.slaveConfigFormGroup.get("method").patchValue(e===Ki.Serial?Xi.ASCII:Yi.SOCKET,{emitEvent:!1})}updateControlsEnabling(e){const[t,n]=e===Ki.Serial?[this.serialSpecificControlKeys,this.tcpUdpSpecificControlKeys]:[this.tcpUdpSpecificControlKeys,this.serialSpecificControlKeys];t.forEach((e=>this.slaveConfigFormGroup.get(e)?.enable({emitEvent:!1}))),n.forEach((e=>this.slaveConfigFormGroup.get(e)?.disable({emitEvent:!1}))),this.updateSecurityEnabling(this.showSecurityControl.value)}observeShowSecurity(){this.showSecurityControl.valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.updateSecurityEnabling(e)))}updateSecurityEnabling(e){e&&this.protocolType!==Ki.Serial?this.slaveConfigFormGroup.get("security").enable({emitEvent:!1}):this.slaveConfigFormGroup.get("security").disable({emitEvent:!1})}static{this.ɵfac=function(e){return new(e||lX)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef))}}static{this.ɵdir=t.ɵɵdefineDirective({type:lX,features:[t.ɵɵInheritDefinitionFeature]})}}e("ModbusSlaveDialogAbstract",lX);const pX=()=>({maxWidth:"970px"});function cX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",20),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate3(" ",e.get("tag").value,"",": ","",e.get("value").value," ")}}function dX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21)(1,"div",22),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"span",23),t.ɵɵtext(5),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"div",24),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"span",25),t.ɵɵtext(10),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",24),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementStart(14,"span",25),t.ɵɵtext(15),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(3,6,n.keysType===n.ModbusValueKey.RPC_REQUESTS?"gateway.method":"gateway.key"),": "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(e.get("tag").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(8,8,"gateway.address"),": "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(e.get("address").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(13,10,"gateway.type"),": "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(e.get("type").value)}}function uX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.keysType===e.ModbusValueKey.RPC_REQUESTS?"gateway.method-required":"gateway.key-required"))}}function mX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",46),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function hX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",46),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(5);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.ModbusFunctionCodeTranslationsMap.get(e))," ")}}function gX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",30)(1,"div",35),t.ɵɵtext(2,"gateway.function-code"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",32)(4,"mat-select",47),t.ɵɵtemplate(5,hX,3,4,"mat-option",37),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.functionCodesMap.get(e.get("id").value)||n.defaultFunctionCodes)}}function fX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.objects-count-required"))}function yX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.modbus.max-bit"))}function vX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",30)(1,"div",48),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.bit"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",49),t.ɵɵelement(5,"input",50),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,yX,3,3,"mat-icon",34),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(3).$implicit;t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.bit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("bit").hasError("max")&&e.get("bit").touched)}}function xX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",46),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(6);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.BitTargetTypeTranslationMap.get(e)))}}function bX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",30)(1,"div",48),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.bit-target-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",51)(5,"mat-form-field",52)(6,"mat-select",53),t.ɵɵtemplate(7,xX,3,4,"mat-option",37),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(5);t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,2,"gateway.hints.modbus.bit-target-type")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",e.bitTargetTypes)}}function wX(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,vX,8,7,"div",38)(2,bX,8,4,"div",38),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("objectsCount").value>1),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.hideNewFields)}}function SX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-required"))}function CX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",46),t.ɵɵelement(1,"mat-icon",62),t.ɵɵelementStart(2,"span"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(5);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("svgIcon",i.ModifierTypesMap.get(e).icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,i.ModifierTypesMap.get(e).name))}}function _X(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.modifier-invalid"))}function TX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",54)(1,"mat-expansion-panel",15)(2,"mat-expansion-panel-header",16)(3,"mat-panel-title")(4,"mat-slide-toggle",55),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label",56),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(9,"div",51)(10,"div",57)(11,"div",35),t.ɵɵtext(12,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",52)(14,"mat-select",58)(15,"mat-select-trigger")(16,"div",59),t.ɵɵelement(17,"mat-icon",60),t.ɵɵelementStart(18,"span"),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(21,CX,5,5,"mat-option",37),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(22,"div",30)(23,"div",35),t.ɵɵtext(24,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",49),t.ɵɵelement(26,"input",61),t.ɵɵpipe(27,"translate"),t.ɵɵtemplate(28,_X,3,3,"mat-icon",34),t.ɵɵelementEnd()()()()}if(2&e){let e,n;const i=t.ɵɵnextContext(2).$implicit,a=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("expanded",a.enableModifiersControlMap.get(i.get("id").value).value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",a.enableModifiersControlMap.get(i.get("id").value)),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,9,"gateway.hints.modbus.modifier")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,11,"gateway.modifier")," "),t.ɵɵadvance(10),t.ɵɵproperty("svgIcon",null==(e=a.ModifierTypesMap.get(i.get("modifierType").value))?null:e.icon),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,13,null==(n=a.ModifierTypesMap.get(i.get("modifierType").value))?null:n.name)),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",a.modifierTypes),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(27,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",i.get("modifierValue").hasError("pattern")&&i.get("modifierValue").touched)}}function IX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",45),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function EX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",30)(1,"div",63),t.ɵɵtext(2,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",32),t.ɵɵelement(4,"input",64),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,IX,3,3,"mat-icon",34),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,2,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("value").hasError("required")&&e.get("value").touched)}}function MX(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",65),2&e){const e=t.ɵɵnextContext(4);t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Key)("isExpansionMode",!0)}}function kX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",26),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelement(3,"div",27),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",28)(5,"div",29),t.ɵɵtext(6,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",30)(8,"div",31),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",32),t.ɵɵelement(13,"input",33),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,uX,3,3,"mat-icon",34),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",28)(17,"div",29),t.ɵɵtext(18,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",30)(20,"div",35),t.ɵɵtext(21," gateway.type "),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",32)(23,"mat-select",36),t.ɵɵtemplate(24,mX,2,2,"mat-option",37),t.ɵɵelementEnd()()(),t.ɵɵtemplate(25,gX,6,1,"div",38),t.ɵɵelementStart(26,"div",30)(27,"div",39),t.ɵɵpipe(28,"translate"),t.ɵɵtext(29,"gateway.objects-count"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",32),t.ɵɵelement(31,"input",40),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,fX,3,3,"mat-icon",34),t.ɵɵelementEnd()(),t.ɵɵtemplate(34,wX,3,2,"ng-container",41),t.ɵɵelementStart(35,"div",30)(36,"div",39),t.ɵɵpipe(37,"translate"),t.ɵɵtext(38,"gateway.address"),t.ɵɵelementEnd(),t.ɵɵelementStart(39,"mat-form-field",32),t.ɵɵelement(40,"input",42),t.ɵɵpipe(41,"translate"),t.ɵɵtemplate(42,SX,3,3,"mat-icon",34),t.ɵɵelementEnd()(),t.ɵɵtemplate(43,TX,29,17,"div",43)(44,EX,7,4,"div",38)(45,MX,1,2,"tb-report-strategy",44),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,20,"gateway.hints.modbus.data-keys")," "),t.ɵɵadvance(2),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/modbus-functions-data-types_fn")("tb-help-popup-style",t.ɵɵpureFunction0(36,pX)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(9,22,n.keysType===n.ModbusValueKey.RPC_REQUESTS?"gateway.hints.modbus.method":"gateway.hints.modbus.key")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,24,n.keysType===n.ModbusValueKey.RPC_REQUESTS?"gateway.method-name":"gateway.key")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,26,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("tag").hasError("required")&&e.get("tag").touched),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",n.modbusDataTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.withFunctionCode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(28,28,"gateway.hints.modbus.objects-count")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,30,"gateway.set")),t.ɵɵproperty("readonly",!n.ModbusEditableDataTypes.includes(e.get("type").value)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("objectsCount").hasError("required")&&e.get("objectsCount").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("type").value===n.ModbusDataType.BITS),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(37,32,"gateway.hints.modbus.address")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(41,34,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("address").hasError("required")&&e.get("address").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.showModifiersMap.get(e.get("id").value)),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isMaster),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.withReportStrategy)}}function PX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",12)(1,"div",13),t.ɵɵelementContainerStart(2,14),t.ɵɵelementStart(3,"mat-expansion-panel",15)(4,"mat-expansion-panel-header",16)(5,"mat-panel-title"),t.ɵɵtemplate(6,cX,2,3,"div",17)(7,dX,16,12,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()(),t.ɵɵtemplate(9,kX,46,37,"ng-template",18),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",19),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.last,a=t.ɵɵreference(8),r=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",r.isMaster)("ngIfElse",a),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,r.deleteKeyTitle))}}function DX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10),t.ɵɵtemplate(1,PX,14,7,"div",11),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.keysListFormArray.controls)("ngForTrackBy",e.trackByControlId)}}function OX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",66)(1,"span",67),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class AX{constructor(e,t){this.fb=e,this.popover=t,this.isMaster=!1,this.hideNewFields=!1,this.keysDataApplied=new u,this.withFunctionCode=!0,this.withReportStrategy=!0,this.enableModifiersControlMap=new Map,this.showModifiersMap=new Map,this.functionCodesMap=new Map,this.defaultFunctionCodes=[],this.modbusDataTypes=Object.values(Wt),this.modifierTypes=Object.values(ma),this.bitTargetTypes=Object.values(aa),this.BitTargetTypeTranslationMap=ra,this.ModbusEditableDataTypes=$t,this.ModbusFunctionCodeTranslationsMap=Zt,this.ModifierTypesMap=ha,this.ReportStrategyDefaultValue=en,this.ModbusDataType=Wt,this.ModbusValueKey=ea,this.destroy$=new te,this.defaultReadFunctionCodes=[3,4],this.bitsReadFunctionCodes=[1,2],this.defaultWriteFunctionCodes=[6,16],this.bitsWriteFunctionCodes=[5,15]}ngOnInit(){this.withFunctionCode=!this.isMaster||this.keysType!==ea.ATTRIBUTES&&this.keysType!==ea.TIMESERIES,this.withReportStrategy=!(this.isMaster||this.keysType!==ea.ATTRIBUTES&&this.keysType!==ea.TIMESERIES||this.hideNewFields),this.keysListFormArray=this.prepareKeysFormArray(this.values),this.defaultFunctionCodes=this.getDefaultFunctionCodes()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}trackByControlId(e,t){return t.value.id}addKey(){const e=Re(5),t=this.fb.group({tag:["",[$.required,$.pattern(an)]],value:[{value:"",disabled:!this.isMaster},[$.required,$.pattern(an)]],type:[Wt.BYTES,[$.required]],address:[null,[$.required]],objectsCount:[1,[$.required]],functionCode:[{value:this.getDefaultFunctionCodes()[0],disabled:!this.withFunctionCode},[$.required]],reportStrategy:[{value:null,disabled:!this.withReportStrategy}],modifierType:[{value:ma.MULTIPLIER,disabled:!0}],modifierValue:[{value:1,disabled:!0},[$.pattern(on)]],bit:[{value:null,disabled:!0}],bitTargetType:[{value:aa.IntegerType,disabled:!0}],id:[{value:e,disabled:!0}]});this.showModifiersMap.set(e,!1),this.enableModifiersControlMap.set(e,this.fb.control(!1)),this.observeKeyDataType(t),this.observeObjectsCount(t),this.observeEnableModifier(t),this.keysListFormArray.push(t)}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover.hide()}applyKeysData(){this.keysDataApplied.emit(this.getFormValue())}getFormValue(){return this.mapKeysWithModifier(this.withReportStrategy?this.cleanUpEmptyStrategies(this.keysListFormArray.value):this.keysListFormArray.value)}cleanUpEmptyStrategies(e){return e.map((e=>{const{reportStrategy:t,...n}=e;return t?e:n}))}mapKeysWithModifier(e){return e.map(((e,t)=>{if(this.showModifiersMap.get(this.keysListFormArray.controls[t].get("id").value)){const{modifierType:t,modifierValue:n,...i}=e;return t?{...i,[t]:n}:i}return e}))}prepareKeysFormArray(e){const t=[];return e&&e.forEach((e=>{const n=this.createDataKeyFormGroup(e);this.observeKeyDataType(n),this.observeObjectsCount(n),this.observeEnableModifier(n),this.functionCodesMap.set(n.get("id").value,this.getFunctionCodes(e.type)),t.push(n)})),this.fb.array(t)}createDataKeyFormGroup(e){const{tag:t,value:n,type:i,address:a,objectsCount:r,functionCode:o,multiplier:s,divider:l,reportStrategy:p,bit:c,bitTargetType:d}=e,u=Re(5),m=this.shouldShowModifier(i);return this.showModifiersMap.set(u,m),this.enableModifiersControlMap.set(u,this.fb.control((s||l)&&m)),this.fb.group({tag:[t,[$.required,$.pattern(an)]],value:[{value:n,disabled:!this.isMaster},[$.required,$.pattern(an)]],type:[i,[$.required]],address:[a,[$.required]],objectsCount:[r,[$.required]],functionCode:[{value:o,disabled:!this.withFunctionCode},[$.required]],modifierType:[{value:l?ma.DIVIDER:ma.MULTIPLIER,disabled:!this.enableModifiersControlMap.get(u).value}],bit:[{value:c,disabled:i!==Wt.BITS||r<2},[$.max(r-1)]],bitTargetType:[{value:d??aa.IntegerType,disabled:i!==Wt.BITS||this.hideNewFields}],modifierValue:[{value:s??l??1,disabled:!this.enableModifiersControlMap.get(u).value},[$.pattern(on)]],id:[{value:u,disabled:!0}],reportStrategy:[{value:p,disabled:!this.withReportStrategy}]})}shouldShowModifier(e){return!(this.isMaster||this.keysType!==ea.ATTRIBUTES&&this.keysType!==ea.TIMESERIES||this.ModbusEditableDataTypes.includes(e))}observeKeyDataType(e){e.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((t=>{this.ModbusEditableDataTypes.includes(t)||e.get("objectsCount").patchValue(Kt[t],{emitEvent:!1}),this.toggleBitsFields(e);const n=this.shouldShowModifier(t);this.showModifiersMap.set(e.get("id").value,n),this.updateFunctionCodes(e,t)}))}observeObjectsCount(e){e.get("objectsCount").valueChanges.pipe(ue((()=>e.get("type").value===Wt.BITS)),le(this.destroy$)).subscribe((()=>this.toggleBitsFields(e)))}toggleBitsFields(e){const{objectsCount:t,type:n,bit:i,bitTargetType:a}=e.controls,r=n.value===Wt.BITS,o=t.value>1;r&&o?(i.enable({emitEvent:!1}),i.setValidators($.max(t.value-1))):i.disable({emitEvent:!1}),i.updateValueAndValidity({emitEvent:!1}),a[r?"enable":"disable"]({emitEvent:!1})}observeEnableModifier(e){this.enableModifiersControlMap.get(e.get("id").value).valueChanges.pipe(le(this.destroy$)).subscribe((t=>this.toggleModifierControls(e,t)))}toggleModifierControls(e,t){const n=e.get("modifierType"),i=e.get("modifierValue");n[t?"enable":"disable"]({emitEvent:!1}),i[t?"enable":"disable"]({emitEvent:!1}),n.markAsDirty(),i.markAsDirty()}updateFunctionCodes(e,t){const n=this.getFunctionCodes(t);this.functionCodesMap.set(e.get("id").value,n),n.includes(e.get("functionCode").value)||e.get("functionCode").patchValue(n[0],{emitEvent:!1})}getFunctionCodes(e){const t=[...e===Wt.BITS?this.bitsWriteFunctionCodes:[],...this.defaultWriteFunctionCodes];if(this.keysType===ea.ATTRIBUTES_UPDATES)return t.sort(((e,t)=>e-t));const n=[...this.defaultReadFunctionCodes];return e===Wt.BITS&&n.push(...this.bitsReadFunctionCodes),this.keysType===ea.RPC_REQUESTS&&n.push(...t),n.sort(((e,t)=>e-t))}getDefaultFunctionCodes(){return this.keysType===ea.ATTRIBUTES_UPDATES?this.defaultWriteFunctionCodes:this.keysType===ea.RPC_REQUESTS?[...this.defaultReadFunctionCodes,...this.defaultWriteFunctionCodes]:this.defaultReadFunctionCodes}static{this.ɵfac=function(e){return new(e||AX)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(it.TbPopoverComponent))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:AX,selectors:[["tb-modbus-data-keys-panel"]],inputs:{isMaster:"isMaster",hideNewFields:"hideNewFields",panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keysType:"keysType",values:"values"},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:19,vars:16,consts:[["noKeys",""],["tagName",""],[1,"tb-modbus-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],["class","title-container","tbTruncateWithTooltip","",4,"ngIf","ngIfElse"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],["tbTruncateWithTooltip","",1,"title-container"],[1,"tb-flex"],[1,"title-container","tb-flex"],["tbTruncateWithTooltip","",1,"key-label"],[1,"title-container"],[1,"key-label"],[1,"tb-form-hint","tb-primary-fill","tb-flex","align-center"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","tag",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width"],["formControlName","type"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","1","max","50000","name","value","formControlName","objectsCount",3,"placeholder","readonly"],[4,"ngIf"],["matInput","","type","number","min","0","max","50000","name","value","formControlName","address",3,"placeholder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],["class","stroked tb-form-panel","formControlName","reportStrategy",3,"defaultValue","isExpansionMode",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"],["formControlName","functionCode"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","tb-suffix-absolute","flex"],["matInput","","formControlName","bit","step","1","type","number","min","0",3,"placeholder"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","fill-width"],["formControlName","bitTargetType"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"mat-slide",3,"click","formControl"],[3,"tb-hint-tooltip-icon"],[1,"tb-form-row","column-xs","w-full"],["formControlName","modifierType"],[1,"tb-flex","align-center"],[1,"tb-mat-18",3,"svgIcon"],["matInput","","required","","formControlName","modifierValue","step","0.1","type","number",3,"placeholder"],[1,"tb-mat-20",3,"svgIcon"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","value",3,"placeholder"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"defaultValue","isExpansionMode"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"div",3)(2,"div",4),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,DX,2,2,"div",5),t.ɵɵelementStart(6,"div")(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(10,OX,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",7)(13,"button",8),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.applyKeysData())})),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(11);t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,8,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")",""),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.keysListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,n.addKeyTitle)," "),t.ɵɵadvance(6),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,14,"action.apply")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(AX,[j,_,Xn,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{width:180px}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .key-label[_ngcontent-%COMP%]{font-weight:400}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-modbus-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}']})}}e("ModbusDataKeysPanelComponent",AX),Ge([I()],AX.prototype,"isMaster",void 0),Ge([I()],AX.prototype,"hideNewFields",void 0);const FX=()=>({$implicit:null}),RX=e=>({$implicit:e});function BX(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",7),t.ɵɵelementContainer(2,8),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(),n=t.ɵɵreference(4);t.ɵɵadvance(),t.ɵɵproperty("formGroup",e.valuesFormGroup),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",n)("ngTemplateOutletContext",t.ɵɵpureFunction0(3,FX))}}function NX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-tab",11),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"div",7),t.ɵɵelementContainer(3,8),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2),a=t.ɵɵreference(4);t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(1,4,i.ModbusValuesTranslationsMap.get(e))),t.ɵɵadvance(2),t.ɵɵproperty("formGroup",i.valuesFormGroup.get(e)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",a)("ngTemplateOutletContext",t.ɵɵpureFunction1(6,RX,e))}}function LX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-tab-group",9),t.ɵɵtemplate(1,NX,4,8,"mat-tab",10),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("formGroup",e.valuesFormGroup),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.modbusRegisterTypes)}}function VX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function qX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function GX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function zX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.tag," ")}}function UX(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",12)(1,"div",13),t.ɵɵtext(2,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",14)(4,"mat-chip-listbox",15),t.ɵɵtemplate(5,VX,2,1,"mat-chip",16),t.ɵɵelementStart(6,"mat-chip",17),t.ɵɵelement(7,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"button",19,2),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(9),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.ATTRIBUTES,i))})),t.ɵɵelementStart(11,"tb-icon",20),t.ɵɵtext(12,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(13,"div",12)(14,"div",13),t.ɵɵtext(15,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",14)(17,"mat-chip-listbox",15),t.ɵɵtemplate(18,qX,2,1,"mat-chip",16),t.ɵɵelementStart(19,"mat-chip",17),t.ɵɵelement(20,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(21,"button",19,3),t.ɵɵpipe(23,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(22),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.TIMESERIES,i))})),t.ɵɵelementStart(24,"tb-icon",20),t.ɵɵtext(25,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(26,"div",12)(27,"div",13),t.ɵɵtext(28,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"div",14)(30,"mat-chip-listbox",15),t.ɵɵtemplate(31,GX,2,1,"mat-chip",16),t.ɵɵelementStart(32,"mat-chip",17),t.ɵɵelement(33,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"button",19,4),t.ɵɵpipe(36,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(35),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.ATTRIBUTES_UPDATES,i))})),t.ɵɵelementStart(37,"tb-icon",20),t.ɵɵtext(38,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(39,"div",12)(40,"div",13),t.ɵɵtext(41,"gateway.rpc-requests"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"div",14)(43,"mat-chip-listbox",15),t.ɵɵtemplate(44,zX,2,1,"mat-chip",16),t.ɵɵelementStart(45,"mat-chip",17),t.ɵɵelement(46,"label",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(47,"button",19,5),t.ɵɵpipe(49,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵreference(48),r=t.ɵɵnextContext();return t.ɵɵresetView(r.manageKeys(n,a,r.ModbusValueKey.RPC_REQUESTS,i))})),t.ɵɵelementStart(50,"tb-icon",20),t.ɵɵtext(51,"edit"),t.ɵɵelementEnd()()()()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,16,"action.edit")),t.ɵɵproperty("disabled",i.disabled),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.TIMESERIES,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.TIMESERIES,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(23,18,"action.edit")),t.ɵɵproperty("disabled",i.disabled),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES_UPDATES,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.ATTRIBUTES_UPDATES,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(36,20,"action.edit")),t.ɵɵproperty("disabled",i.disabled),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",i.getValueGroup(i.ModbusValueKey.RPC_REQUESTS,e).value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",i.getValueGroup(i.ModbusValueKey.RPC_REQUESTS,e).value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(49,22,"action.edit")),t.ɵɵproperty("disabled",i.disabled)}}class jX{constructor(e,t,n,i,a,r){this.fb=e,this.popoverService=t,this.renderer=n,this.viewContainerRef=i,this.cdr=a,this.destroyRef=r,this.singleMode=!1,this.hideNewFields=!1,this.disabled=!1,this.modbusRegisterTypes=Object.values(Ji),this.modbusValueKeys=Object.values(ea),this.ModbusValuesTranslationsMap=ia,this.ModbusValueKey=ea}ngOnInit(){this.initializeValuesFormGroup(),this.observeValuesChanges()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){if(this.singleMode)this.valuesFormGroup.setValue(this.getSingleRegisterState(e),{emitEvent:!1});else{const{holding_registers:t,coils_initializer:n,input_registers:i,discrete_inputs:a}=e;this.valuesFormGroup.setValue({holding_registers:this.getSingleRegisterState(t),coils_initializer:this.getSingleRegisterState(n),input_registers:this.getSingleRegisterState(i),discrete_inputs:this.getSingleRegisterState(a)},{emitEvent:!1})}this.cdr.markForCheck()}validate(){return this.valuesFormGroup.valid?null:{valuesFormGroup:{valid:!1}}}setDisabledState(e){this.disabled=e,this.cdr.markForCheck()}getValueGroup(e,t){return t?this.valuesFormGroup.get(t).get(e):this.valuesFormGroup.get(e)}manageKeys(e,t,n,i){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const a=t._elementRef.nativeElement;if(this.popoverService.hasPopover(a))return void this.popoverService.hidePopover(a);const r=this.getValueGroup(n,i),o={values:r.value,isMaster:!this.singleMode,keysType:n,panelTitle:pa.get(n),addKeyTitle:ca.get(n),deleteKeyTitle:da.get(n),noKeysText:ua.get(n),hideNewFields:this.hideNewFields};this.popoverComponent=this.popoverService.displayPopover(a,this.renderer,this.viewContainerRef,AX,"leftBottom",!1,null,o,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(bn(this.destroyRef)).subscribe((e=>{this.popoverComponent.hide(),r.patchValue(e),r.markAsDirty(),this.cdr.markForCheck()}))}initializeValuesFormGroup(){const e=()=>this.fb.group(this.modbusValueKeys.reduce(((e,t)=>(e[t]=this.fb.control([[],[]]),e)),{}));this.singleMode?this.valuesFormGroup=e():this.valuesFormGroup=this.fb.group(this.modbusRegisterTypes.reduce(((t,n)=>(t[n]=e(),t)),{}))}observeValuesChanges(){this.valuesFormGroup.valueChanges.pipe(bn(this.destroyRef)).subscribe((e=>{this.onChange(e),this.onTouched()}))}getSingleRegisterState(e){return{attributes:e?.attributes??[],timeseries:e?.timeseries??[],attributeUpdates:e?.attributeUpdates??[],rpc:e?.rpc??[]}}static{this.ɵfac=function(e){return new(e||jX)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:jX,selectors:[["tb-modbus-values"]],inputs:{singleMode:"singleMode",hideNewFields:"hideNewFields"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>jX)),multi:!0},{provide:K,useExisting:c((()=>jX)),multi:!0}]),t.ɵɵStandaloneFeature],decls:5,vars:2,consts:[["multipleView",""],["singleView",""],["attributesButton",""],["telemetryButton",""],["attributesUpdatesButton",""],["rpcRequestsButton",""],[4,"ngIf","ngIfElse"],[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[3,"ngTemplateOutlet","ngTemplateOutletContext"],[3,"formGroup"],[3,"label",4,"ngFor","ngForOf"],[3,"label"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[4,"ngFor","ngForOf"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","disabled","matTooltip"],["matButtonIcon",""]],template:function(e,n){if(1&e&&t.ɵɵtemplate(0,BX,3,4,"ng-container",6)(1,LX,2,2,"ng-template",null,0,t.ɵɵtemplateRefExtractor)(3,UX,52,24,"ng-template",null,1,t.ɵɵtemplateRefExtractor),2&e){const e=t.ɵɵreference(2);t.ɵɵproperty("ngIf",n.singleMode)("ngIfElse",e)}},dependencies:t.ɵɵgetComponentDepsFactory(jX,[j,_,Gn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .mat-mdc-tab-body-wrapper{min-height:320px} .mdc-evolution-chip-set__chips{align-items:center}'],changeDetection:d.OnPush})}}function HX(e,n){1&e&&(t.ɵɵelementStart(0,"div",2)(1,"div",10),t.ɵɵtext(2,"gateway.server-hostname"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",5)(4,"mat-form-field",6),t.ɵɵelement(5,"input",16),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,1,"gateway.set")))}function WX(e,n){1&e&&(t.ɵɵelementStart(0,"div",17)(1,"mat-slide-toggle",18)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.request-client-certificate")," "))}e("ModbusValuesComponent",jX),Ge([I()],jX.prototype,"singleMode",void 0),Ge([I()],jX.prototype,"hideNewFields",void 0);class $X{constructor(e,t){this.fb=e,this.cdr=t,this.isMaster=!1,this.disabled=!1,this.destroy$=new te,this.securityConfigFormGroup=this.fb.group({certfile:["",[$.pattern(an)]],keyfile:["",[$.pattern(an)]],password:["",[$.pattern(an)]],server_hostname:["",[$.pattern(an)]],reqclicert:[{value:!1,disabled:!0}]}),this.observeValueChanges()}ngOnChanges(){this.updateMasterEnabling()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){this.disabled=e,this.disabled?this.securityConfigFormGroup.disable({emitEvent:!1}):this.securityConfigFormGroup.enable({emitEvent:!1}),this.updateMasterEnabling(),this.cdr.markForCheck()}validate(){return this.securityConfigFormGroup.valid?null:{securityConfigFormGroup:{valid:!1}}}writeValue(e){const{certfile:t,password:n,keyfile:i,server_hostname:a}=e,r={certfile:t??"",password:n??"",keyfile:i??"",server_hostname:a??"",reqclicert:!!e.reqclicert};this.securityConfigFormGroup.reset(r,{emitEvent:!1})}updateMasterEnabling(){this.isMaster?(this.disabled||this.securityConfigFormGroup.get("reqclicert").enable({emitEvent:!1}),this.securityConfigFormGroup.get("server_hostname").disable({emitEvent:!1})):(this.disabled||this.securityConfigFormGroup.get("server_hostname").enable({emitEvent:!1}),this.securityConfigFormGroup.get("reqclicert").disable({emitEvent:!1}))}observeValueChanges(){this.securityConfigFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}static{this.ɵfac=function(e){return new(e||$X)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:$X,selectors:[["tb-modbus-security-config"]],inputs:{isMaster:"isMaster"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>$X)),multi:!0},{provide:K,useExisting:c((()=>$X)),multi:!0}]),t.ɵɵNgOnChangesFeature,t.ɵɵStandaloneFeature],decls:33,vars:21,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-hint","tb-primary-fill"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["tbTruncateWithTooltip","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","certfile",3,"placeholder"],[1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["matInput","","name","value","formControlName","keyfile",3,"placeholder"],["translate","",1,"fixed-title-width"],["matInput","","type","password","name","value","formControlName","password",3,"placeholder"],["matSuffix","",1,"tb-flex","no-gap","align-center","fill-height"],[1,"tb-flex","align-center","fill-height"],["class","tb-form-row space-between tb-flex fill-width",4,"ngIf"],["class","tb-form-row",4,"ngIf"],["matInput","","name","value","formControlName","server_hostname",3,"placeholder"],[1,"tb-form-row"],["formControlName","reqclicert",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",2)(5,"div",3),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"span",4),t.ɵɵtext(8,"gateway.client-cert-path"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",5)(10,"mat-form-field",6),t.ɵɵelement(11,"input",7),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(13,"div",2)(14,"div",8),t.ɵɵpipe(15,"translate"),t.ɵɵelementStart(16,"span",4),t.ɵɵtext(17,"gateway.private-key-path"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",5)(19,"mat-form-field",6),t.ɵɵelement(20,"input",9),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"div",2)(23,"div",10),t.ɵɵtext(24,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"div",5)(26,"mat-form-field",6),t.ɵɵelement(27,"input",11),t.ɵɵpipe(28,"translate"),t.ɵɵelementStart(29,"div",12),t.ɵɵelement(30,"tb-toggle-password",13),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(31,HX,7,3,"div",14)(32,WX,5,3,"div",15),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.securityConfigFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,9,"gateway.hints.path-in-os")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,11,"gateway.hints.ca-cert")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(12,13,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(15,15,"gateway.private-key-path")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,17,"gateway.set")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,19,"gateway.set")),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",!n.isMaster),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.isMaster))},dependencies:t.ɵɵgetComponentDepsFactory($X,[j,_,qn]),encapsulation:2,changeDetection:d.OnPush})}}function KX(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusProtocolLabelsMap.get(e))}}function YX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function XX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",53),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,YX,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("host").hasError("required")&&e.slaveConfigFormGroup.get("host").touched)}}function ZX(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.slaveConfigFormGroup.get("port")))}}function QX(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",55),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,ZX,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.hints.modbus.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",e.portLimits.MIN),t.ɵɵpropertyInterpolate("max",e.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.slaveConfigFormGroup.get("port").hasError("required")||e.slaveConfigFormGroup.get("port").hasError("min")||e.slaveConfigFormGroup.get("port").hasError("max"))&&e.slaveConfigFormGroup.get("port").touched)}}function JX(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function eZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",56),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,JX,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.serial-port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("serialPort").hasError("required")&&e.slaveConfigFormGroup.get("serialPort").touched)}}function tZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusMethodLabelsMap.get(e))}}function nZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function iZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function aZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusParityLabelsMap.get(e))}}function rZ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",17)(2,"div",18),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4,"gateway.baudrate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",19)(6,"mat-select",57),t.ɵɵtemplate(7,nZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",17)(9,"div",18),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"gateway.bytesize"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",19)(13,"mat-select",58),t.ɵɵtemplate(14,iZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",17)(16,"div",18),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"gateway.stopbits"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"mat-form-field",19),t.ɵɵelement(20,"input",59),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"div",17)(23,"div",18),t.ɵɵpipe(24,"translate"),t.ɵɵtext(25,"gateway.parity"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",19)(27,"mat-select",60),t.ɵɵtemplate(28,aZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(29,"div",36)(30,"mat-slide-toggle",61)(31,"mat-label",38),t.ɵɵpipe(32,"translate"),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.modbus.baudrate")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusBaudrates),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,12,"gateway.hints.modbus.bytesize")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusByteSizes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(17,14,"gateway.hints.modbus.stopbits")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,16,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(24,18,"gateway.hints.modbus.parity")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusParities),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(32,20,"gateway.hints.modbus.strict")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(34,22,"gateway.strict")," ")}}function oZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.unit-id-required"))}function sZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function lZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function pZ(e,n){1&e&&(t.ɵɵelementStart(0,"div",36)(1,"mat-slide-toggle",62)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.send-data-on-change")," "))}function cZ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",63),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Device)("isExpansionMode",!0)}}function dZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function uZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function mZ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",64)(1,"mat-expansion-panel",65)(2,"mat-expansion-panel-header",66)(3,"mat-panel-title")(4,"mat-slide-toggle",67),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(8,"tb-modbus-security-config",68),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("expanded",e.showSecurityControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showSecurityControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,3,"gateway.tls-connection")," ")}}e("ModbusSecurityConfigComponent",$X),Ge([I()],$X.prototype,"isMaster",void 0);class hZ extends lX{constructor(e,t,n,i,a){super(e,t,n,i,a),this.fb=e,this.store=t,this.router=n,this.data=i,this.dialogRef=a}getSlaveResultData(){const{values:e,type:t,serialPort:n,...i}=this.slaveConfigFormGroup.value,a={...i,type:t,...e};return t===Ki.Serial&&(a.port=n),a.reportStrategy||delete a.reportStrategy,Te(a),a}addFieldsToFormGroup(){this.slaveConfigFormGroup.addControl("reportStrategy",this.fb.control(null))}static{this.ɵfac=function(e){return new(e||hZ)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:hZ,selectors:[["tb-modbus-slave-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:141,vars:97,consts:[["serialPort",""],["reportStrategy",""],[1,"slaves-config-container"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"tb-form-panel",3,"formGroup"],[1,"stroked","tb-form-panel"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","method"],[4,"ngIf"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","unitId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],["class","tb-form-row",4,"ngIf","ngIfElse"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["formControlName","byteOrder"],["formControlName","wordOrder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],[1,"tb-form-row"],["formControlName","retries",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","retryOnEmpty",1,"mat-slide"],["formControlName","retryOnInvalid",1,"mat-slide"],[1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","100","name","value","formControlName","pollPeriod",3,"placeholder"],["translate","",1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","500","name","value","formControlName","connectAttemptTimeMs",3,"placeholder"],["matInput","","type","number","min","1","name","value","formControlName","connectAttemptCount",3,"placeholder"],["matInput","","type","number","min","30000","name","value","formControlName","waitAfterFailedAttemptsMs",3,"placeholder"],["formControlName","values",3,"singleMode","hideNewFields"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matInput","","name","value","formControlName","serialPort",3,"placeholder"],["formControlName","baudrate"],["formControlName","bytesize"],["matInput","","type","number","min","0","name","value","formControlName","stopbits",3,"placeholder"],["formControlName","parity"],["formControlName","strict",1,"mat-slide"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"defaultValue","isExpansionMode"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide","justify-start",3,"click","formControl"],["formControlName","security",1,"security-config"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"mat-toolbar",3)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",4)(6,"div",5),t.ɵɵelementStart(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",7),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",8)(11,"div",9)(12,"div",10)(13,"div",11)(14,"div",12),t.ɵɵtext(15,"gateway.server-connection"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"tb-toggle-select",13),t.ɵɵtemplate(17,KX,2,2,"tb-toggle-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",10),t.ɵɵtemplate(19,XX,8,7,"div",15)(20,QX,8,9,"div",16)(21,eZ,8,7,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(23,"div",17)(24,"div",18),t.ɵɵpipe(25,"translate"),t.ɵɵtext(26," gateway.method "),t.ɵɵelementEnd(),t.ɵɵelementStart(27,"mat-form-field",19)(28,"mat-select",20),t.ɵɵtemplate(29,tZ,2,2,"mat-option",14),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(30,rZ,35,24,"ng-container",21),t.ɵɵelementStart(31,"div",17)(32,"div",22),t.ɵɵpipe(33,"translate"),t.ɵɵtext(34,"gateway.unit-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"mat-form-field",19),t.ɵɵelement(36,"input",23),t.ɵɵpipe(37,"translate"),t.ɵɵtemplate(38,oZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"div",17)(40,"div",25),t.ɵɵtext(41,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"mat-form-field",19),t.ɵɵelement(43,"input",26),t.ɵɵpipe(44,"translate"),t.ɵɵtemplate(45,sZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"div",17)(47,"div",25),t.ɵɵtext(48,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(49,"mat-form-field",19),t.ɵɵelement(50,"input",27),t.ɵɵpipe(51,"translate"),t.ɵɵtemplate(52,lZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵtemplate(53,pZ,5,3,"div",28)(54,cZ,1,2,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(56,"div",29)(57,"mat-expansion-panel",30)(58,"mat-expansion-panel-header")(59,"mat-panel-title")(60,"div",31),t.ɵɵtext(61,"gateway.advanced-connection-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(62,"div",10)(63,"div",17)(64,"div",18),t.ɵɵpipe(65,"translate"),t.ɵɵtext(66,"gateway.connection-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(67,"mat-form-field",19),t.ɵɵelement(68,"input",32),t.ɵɵpipe(69,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(70,"div",17)(71,"div",18),t.ɵɵpipe(72,"translate"),t.ɵɵtext(73,"gateway.byte-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(74,"mat-form-field",19)(75,"mat-select",33),t.ɵɵtemplate(76,dZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(77,"div",17)(78,"div",18),t.ɵɵpipe(79,"translate"),t.ɵɵtext(80,"gateway.word-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(81,"mat-form-field",19)(82,"mat-select",34),t.ɵɵtemplate(83,uZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵtemplate(84,mZ,9,5,"div",35),t.ɵɵelementStart(85,"div",36)(86,"mat-slide-toggle",37)(87,"mat-label",38),t.ɵɵpipe(88,"translate"),t.ɵɵtext(89),t.ɵɵpipe(90,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(91,"div",36)(92,"mat-slide-toggle",39)(93,"mat-label",38),t.ɵɵpipe(94,"translate"),t.ɵɵtext(95),t.ɵɵpipe(96,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(97,"div",36)(98,"mat-slide-toggle",40)(99,"mat-label",38),t.ɵɵpipe(100,"translate"),t.ɵɵtext(101),t.ɵɵpipe(102,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(103,"div",17)(104,"div",41),t.ɵɵpipe(105,"translate"),t.ɵɵelementStart(106,"span",42),t.ɵɵtext(107," gateway.poll-period "),t.ɵɵelementEnd()(),t.ɵɵelementStart(108,"mat-form-field",19),t.ɵɵelement(109,"input",43),t.ɵɵpipe(110,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(111,"div",17)(112,"div",44),t.ɵɵpipe(113,"translate"),t.ɵɵtext(114,"gateway.connect-attempt-time"),t.ɵɵelementEnd(),t.ɵɵelementStart(115,"mat-form-field",19),t.ɵɵelement(116,"input",45),t.ɵɵpipe(117,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(118,"div",17)(119,"div",44),t.ɵɵpipe(120,"translate"),t.ɵɵtext(121,"gateway.connect-attempt-count"),t.ɵɵelementEnd(),t.ɵɵelementStart(122,"mat-form-field",19),t.ɵɵelement(123,"input",46),t.ɵɵpipe(124,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(125,"div",17)(126,"div",44),t.ɵɵpipe(127,"translate"),t.ɵɵtext(128,"gateway.wait-after-failed-attempts"),t.ɵɵelementEnd(),t.ɵɵelementStart(129,"mat-form-field",19),t.ɵɵelement(130,"input",47),t.ɵɵpipe(131,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(132,"div",29),t.ɵɵelement(133,"tb-modbus-values",48),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(134,"div",49)(135,"button",50),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(136),t.ɵɵpipe(137,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(138,"button",51),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(139),t.ɵɵpipe(140,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(22),i=t.ɵɵreference(55);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,45,"gateway.server-slave")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.modbusHelpLink),t.ɵɵadvance(4),t.ɵɵproperty("formGroup",n.slaveConfigFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.modbusProtocolTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(25,47,"gateway.hints.modbus.framer-type")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.protocolType===n.ModbusProtocolType.Serial?n.modbusSerialMethodTypes:n.modbusMethodTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType===n.ModbusProtocolType.Serial),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(33,49,"gateway.hints.modbus.unit-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(37,51,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("unitId").hasError("required")&&n.slaveConfigFormGroup.get("unitId").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(44,53,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceName").hasError("required")&&n.slaveConfigFormGroup.get("deviceName").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(51,55,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceType").hasError("required")&&n.slaveConfigFormGroup.get("deviceType").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.data.hideNewFields)("ngIfElse",i),t.ɵɵadvance(11),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(65,57,"gateway.hints.modbus.connection-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(69,59,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(72,61,"gateway.hints.modbus.byte-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(79,63,"gateway.hints.modbus.word-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(88,65,"gateway.hints.modbus.retries")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(90,67,"gateway.retries")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(94,69,"gateway.hints.modbus.retries-on-empty")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(96,71,"gateway.retries-on-empty")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(100,73,"gateway.hints.modbus.retries-on-invalid")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(102,75,"gateway.retries-on-invalid")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(105,77,"gateway.hints.modbus.poll-period")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(110,79,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(113,81,"gateway.hints.modbus.connect-attempt-time")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(117,83,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(120,85,"gateway.hints.modbus.connect-attempt-count")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(124,87,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(127,89,"gateway.hints.modbus.wait-after-failed-attempts")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(131,91,"gateway.set")),t.ɵɵadvance(3),t.ɵɵproperty("singleMode",!0)("hideNewFields",n.data.hideNewFields),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(137,93,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.slaveConfigFormGroup.invalid||!n.slaveConfigFormGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(140,95,n.data.buttonTitle)," ")}},dependencies:t.ɵɵgetComponentDepsFactory(hZ,[j,_,jX,$X,r$,Xn,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .slaves-config-container[_ngcontent-%COMP%]{width:80vw;max-width:900px}[_nghost-%COMP%] .slave-name-label[_ngcontent-%COMP%]{margin-right:16px;color:#000000de}[_nghost-%COMP%] .fixed-title-width-260[_ngcontent-%COMP%]{min-width:260px}[_nghost-%COMP%] .security-config .fixed-title-width{min-width:230px}'],changeDetection:d.OnPush})}}function gZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusProtocolLabelsMap.get(e))}}function fZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function yZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",53),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,fZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("host").hasError("required")&&e.slaveConfigFormGroup.get("host").touched)}}function vZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.slaveConfigFormGroup.get("port")))}}function xZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",55),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,vZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.hints.modbus.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",e.portLimits.MIN),t.ɵɵpropertyInterpolate("max",e.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.slaveConfigFormGroup.get("port").hasError("required")||e.slaveConfigFormGroup.get("port").hasError("min")||e.slaveConfigFormGroup.get("port").hasError("max"))&&e.slaveConfigFormGroup.get("port").touched)}}function bZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function wZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",22),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",19),t.ɵɵelement(5,"input",56),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,bZ,3,3,"mat-icon",24),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.serial-port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("serialPort").hasError("required")&&e.slaveConfigFormGroup.get("serialPort").touched)}}function SZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusMethodLabelsMap.get(e))}}function CZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function _Z(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function TZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusParityLabelsMap.get(e))}}function IZ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵelementStart(1,"div",17)(2,"div",18),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4,"gateway.baudrate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",19)(6,"mat-select",57),t.ɵɵtemplate(7,CZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(8,"div",17)(9,"div",18),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"gateway.bytesize"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",19)(13,"mat-select",58),t.ɵɵtemplate(14,_Z,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",17)(16,"div",18),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"gateway.stopbits"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"mat-form-field",19),t.ɵɵelement(20,"input",59),t.ɵɵpipe(21,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"div",17)(23,"div",18),t.ɵɵpipe(24,"translate"),t.ɵɵtext(25,"gateway.parity"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",19)(27,"mat-select",60),t.ɵɵtemplate(28,TZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(29,"div",36)(30,"mat-slide-toggle",61)(31,"mat-label",38),t.ɵɵpipe(32,"translate"),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.modbus.baudrate")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusBaudrates),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(10,12,"gateway.hints.modbus.bytesize")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusByteSizes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(17,14,"gateway.hints.modbus.stopbits")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,16,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(24,18,"gateway.hints.modbus.parity")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusParities),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(32,20,"gateway.hints.modbus.strict")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(34,22,"gateway.strict")," ")}}function EZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.unit-id-required"))}function MZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function kZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",54),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function PZ(e,n){1&e&&(t.ɵɵelementStart(0,"div",36)(1,"mat-slide-toggle",62)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.send-data-on-change")," "))}function DZ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",63),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Device)("isExpansionMode",!0)}}function OZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function AZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",52),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function FZ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",64)(1,"mat-expansion-panel",65)(2,"mat-expansion-panel-header",66)(3,"mat-panel-title")(4,"mat-slide-toggle",67),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(8,"tb-modbus-security-config",68),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("expanded",e.showSecurityControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showSecurityControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,3,"gateway.tls-connection")," ")}}e("ModbusSlaveDialogComponent",hZ);class RZ extends lX{constructor(e,t,n,i,a){super(e,t,n,i,a),this.fb=e,this.store=t,this.router=n,this.data=i,this.dialogRef=a}getSlaveResultData(){const{values:e,type:t,serialPort:n,...i}=this.slaveConfigFormGroup.value,a={...i,type:t,...e};return t===Ki.Serial&&(a.port=n),Te(a),a}addFieldsToFormGroup(){this.slaveConfigFormGroup.addControl("sendDataOnlyOnChange",this.fb.control(!1))}static{this.ɵfac=function(e){return new(e||RZ)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:RZ,selectors:[["tb-modbus-legacy-slave-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:141,vars:97,consts:[["serialPort",""],["reportStrategy",""],[1,"slaves-config-container"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content","",1,"tb-form-panel",3,"formGroup"],[1,"stroked","tb-form-panel"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-row column-xs",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","method"],[4,"ngIf"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","unitId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],["class","tb-form-row",4,"ngIf","ngIfElse"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["formControlName","byteOrder"],["formControlName","wordOrder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],[1,"tb-form-row"],["formControlName","retries",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","retryOnEmpty",1,"mat-slide"],["formControlName","retryOnInvalid",1,"mat-slide"],[1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","100","name","value","formControlName","pollPeriod",3,"placeholder"],["translate","",1,"fixed-title-width-260","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","500","name","value","formControlName","connectAttemptTimeMs",3,"placeholder"],["matInput","","type","number","min","1","name","value","formControlName","connectAttemptCount",3,"placeholder"],["matInput","","type","number","min","30000","name","value","formControlName","waitAfterFailedAttemptsMs",3,"placeholder"],["formControlName","values",3,"singleMode","hideNewFields"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[3,"value"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matInput","","name","value","formControlName","serialPort",3,"placeholder"],["formControlName","baudrate"],["formControlName","bytesize"],["matInput","","type","number","min","0","name","value","formControlName","stopbits",3,"placeholder"],["formControlName","parity"],["formControlName","strict",1,"mat-slide"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"defaultValue","isExpansionMode"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide","justify-start",3,"click","formControl"],["formControlName","security",1,"security-config"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"mat-toolbar",3)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",4)(6,"div",5),t.ɵɵelementStart(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",7),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",8)(11,"div",9)(12,"div",10)(13,"div",11)(14,"div",12),t.ɵɵtext(15,"gateway.server-connection"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"tb-toggle-select",13),t.ɵɵtemplate(17,gZ,2,2,"tb-toggle-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",10),t.ɵɵtemplate(19,yZ,8,7,"div",15)(20,xZ,8,9,"div",16)(21,wZ,8,7,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(23,"div",17)(24,"div",18),t.ɵɵpipe(25,"translate"),t.ɵɵtext(26," gateway.method "),t.ɵɵelementEnd(),t.ɵɵelementStart(27,"mat-form-field",19)(28,"mat-select",20),t.ɵɵtemplate(29,SZ,2,2,"mat-option",14),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(30,IZ,35,24,"ng-container",21),t.ɵɵelementStart(31,"div",17)(32,"div",22),t.ɵɵpipe(33,"translate"),t.ɵɵtext(34,"gateway.unit-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"mat-form-field",19),t.ɵɵelement(36,"input",23),t.ɵɵpipe(37,"translate"),t.ɵɵtemplate(38,EZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"div",17)(40,"div",25),t.ɵɵtext(41,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"mat-form-field",19),t.ɵɵelement(43,"input",26),t.ɵɵpipe(44,"translate"),t.ɵɵtemplate(45,MZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"div",17)(47,"div",25),t.ɵɵtext(48,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(49,"mat-form-field",19),t.ɵɵelement(50,"input",27),t.ɵɵpipe(51,"translate"),t.ɵɵtemplate(52,kZ,3,3,"mat-icon",24),t.ɵɵelementEnd()(),t.ɵɵtemplate(53,PZ,5,3,"div",28)(54,DZ,1,2,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(56,"div",29)(57,"mat-expansion-panel",30)(58,"mat-expansion-panel-header")(59,"mat-panel-title")(60,"div",31),t.ɵɵtext(61,"gateway.advanced-connection-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(62,"div",10)(63,"div",17)(64,"div",18),t.ɵɵpipe(65,"translate"),t.ɵɵtext(66,"gateway.connection-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(67,"mat-form-field",19),t.ɵɵelement(68,"input",32),t.ɵɵpipe(69,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(70,"div",17)(71,"div",18),t.ɵɵpipe(72,"translate"),t.ɵɵtext(73,"gateway.byte-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(74,"mat-form-field",19)(75,"mat-select",33),t.ɵɵtemplate(76,OZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(77,"div",17)(78,"div",18),t.ɵɵpipe(79,"translate"),t.ɵɵtext(80,"gateway.word-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(81,"mat-form-field",19)(82,"mat-select",34),t.ɵɵtemplate(83,AZ,2,2,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵtemplate(84,FZ,9,5,"div",35),t.ɵɵelementStart(85,"div",36)(86,"mat-slide-toggle",37)(87,"mat-label",38),t.ɵɵpipe(88,"translate"),t.ɵɵtext(89),t.ɵɵpipe(90,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(91,"div",36)(92,"mat-slide-toggle",39)(93,"mat-label",38),t.ɵɵpipe(94,"translate"),t.ɵɵtext(95),t.ɵɵpipe(96,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(97,"div",36)(98,"mat-slide-toggle",40)(99,"mat-label",38),t.ɵɵpipe(100,"translate"),t.ɵɵtext(101),t.ɵɵpipe(102,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(103,"div",17)(104,"div",41),t.ɵɵpipe(105,"translate"),t.ɵɵelementStart(106,"span",42),t.ɵɵtext(107," gateway.poll-period "),t.ɵɵelementEnd()(),t.ɵɵelementStart(108,"mat-form-field",19),t.ɵɵelement(109,"input",43),t.ɵɵpipe(110,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(111,"div",17)(112,"div",44),t.ɵɵpipe(113,"translate"),t.ɵɵtext(114,"gateway.connect-attempt-time"),t.ɵɵelementEnd(),t.ɵɵelementStart(115,"mat-form-field",19),t.ɵɵelement(116,"input",45),t.ɵɵpipe(117,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(118,"div",17)(119,"div",44),t.ɵɵpipe(120,"translate"),t.ɵɵtext(121,"gateway.connect-attempt-count"),t.ɵɵelementEnd(),t.ɵɵelementStart(122,"mat-form-field",19),t.ɵɵelement(123,"input",46),t.ɵɵpipe(124,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(125,"div",17)(126,"div",44),t.ɵɵpipe(127,"translate"),t.ɵɵtext(128,"gateway.wait-after-failed-attempts"),t.ɵɵelementEnd(),t.ɵɵelementStart(129,"mat-form-field",19),t.ɵɵelement(130,"input",47),t.ɵɵpipe(131,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(132,"div",29),t.ɵɵelement(133,"tb-modbus-values",48),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(134,"div",49)(135,"button",50),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(136),t.ɵɵpipe(137,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(138,"button",51),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(139),t.ɵɵpipe(140,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(22),i=t.ɵɵreference(55);t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,45,"gateway.server-slave")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.modbusHelpLink),t.ɵɵadvance(4),t.ɵɵproperty("formGroup",n.slaveConfigFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.modbusProtocolTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(25,47,"gateway.hints.modbus.framer-type")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.protocolType===n.ModbusProtocolType.Serial?n.modbusSerialMethodTypes:n.modbusMethodTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType===n.ModbusProtocolType.Serial),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(33,49,"gateway.hints.modbus.unit-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(37,51,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("unitId").hasError("required")&&n.slaveConfigFormGroup.get("unitId").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(44,53,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceName").hasError("required")&&n.slaveConfigFormGroup.get("deviceName").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(51,55,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceType").hasError("required")&&n.slaveConfigFormGroup.get("deviceType").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.data.hideNewFields)("ngIfElse",i),t.ɵɵadvance(11),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(65,57,"gateway.hints.modbus.connection-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(69,59,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(72,61,"gateway.hints.modbus.byte-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(79,63,"gateway.hints.modbus.word-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(88,65,"gateway.hints.modbus.retries")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(90,67,"gateway.retries")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(94,69,"gateway.hints.modbus.retries-on-empty")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(96,71,"gateway.retries-on-empty")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(100,73,"gateway.hints.modbus.retries-on-invalid")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(102,75,"gateway.retries-on-invalid")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(105,77,"gateway.hints.modbus.poll-period")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(110,79,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(113,81,"gateway.hints.modbus.connect-attempt-time")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(117,83,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(120,85,"gateway.hints.modbus.connect-attempt-count")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(124,87,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(127,89,"gateway.hints.modbus.wait-after-failed-attempts")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(131,91,"gateway.set")),t.ɵɵadvance(3),t.ɵɵproperty("singleMode",!0)("hideNewFields",n.data.hideNewFields),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(137,93,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.slaveConfigFormGroup.invalid||!n.slaveConfigFormGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(140,95,n.data.buttonTitle)," ")}},dependencies:t.ɵɵgetComponentDepsFactory(RZ,[j,_,jX,$X,r$,Xn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .slaves-config-container[_ngcontent-%COMP%]{width:80vw;max-width:900px}[_nghost-%COMP%] .slave-name-label[_ngcontent-%COMP%]{margin-right:16px;color:#000000de}[_nghost-%COMP%] .fixed-title-width-260[_ngcontent-%COMP%]{min-width:260px}[_nghost-%COMP%] .security-config .fixed-title-width{min-width:230px}'],changeDetection:d.OnPush})}}function BZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusProtocolLabelsMap.get(e))}}function NZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function LZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",14),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",39),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,NZ,3,3,"mat-icon",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("host").hasError("required")&&e.slaveConfigFormGroup.get("host").touched)}}function VZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.slaveConfigFormGroup.get("port")))}}function qZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",14),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",41),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,VZ,3,3,"mat-icon",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.hints.modbus.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",e.portLimits.MIN),t.ɵɵpropertyInterpolate("max",e.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(e.slaveConfigFormGroup.get("port").hasError("required")||e.slaveConfigFormGroup.get("port").hasError("min")||e.slaveConfigFormGroup.get("port").hasError("max"))&&e.slaveConfigFormGroup.get("port").touched)}}function GZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.port-required"))}function zZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",14),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12),t.ɵɵelement(5,"input",42),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,GZ,3,3,"mat-icon",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.modbus.serial-port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.slaveConfigFormGroup.get("port").hasError("required")&&e.slaveConfigFormGroup.get("port").touched)}}function UZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.ModbusMethodLabelsMap.get(e))}}function jZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.unit-id-required"))}function HZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function WZ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function $Z(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function KZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10)(1,"div",11),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.baudrate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",12)(5,"mat-select",43),t.ɵɵtemplate(6,$Z,2,2,"mat-option",6),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,2,"gateway.hints.modbus.baudrate")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.modbusBaudrates)}}function YZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function XZ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",38),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function ZZ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",44)(1,"mat-expansion-panel",45)(2,"mat-expansion-panel-header",46)(3,"mat-panel-title")(4,"mat-slide-toggle",47),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(8,"tb-modbus-security-config",48),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("expanded",e.showSecurityControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.showSecurityControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,3,"gateway.tls-connection")," ")}}e("ModbusLegacySlaveDialogComponent",RZ);class QZ{constructor(e){this.fb=e,this.ModbusProtocolLabelsMap=sa,this.ModbusMethodLabelsMap=oa,this.portLimits=Ii,this.modbusProtocolTypes=Object.values(Ki),this.modbusMethodTypes=Object.values(Yi),this.modbusSerialMethodTypes=Object.values(Xi),this.modbusOrderType=Object.values(Qi),this.ModbusProtocolType=Ki,this.modbusBaudrates=ta,this.isSlaveEnabled=!1,this.serialSpecificControlKeys=["serialPort","baudrate"],this.tcpUdpSpecificControlKeys=["port","security","host"],this.destroy$=new te,this.showSecurityControl=this.fb.control(!1),this.slaveConfigFormGroup=this.fb.group({type:[Ki.TCP],host:["",[$.required,$.pattern(an)]],port:[null,[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],serialPort:["",[$.required,$.pattern(an)]],method:[Yi.SOCKET],unitId:[null,[$.required]],baudrate:[this.modbusBaudrates[0]],deviceName:["",[$.required,$.pattern(an)]],deviceType:["",[$.required,$.pattern(an)]],pollPeriod:[1e3,[$.required]],sendDataToThingsBoard:[!1],byteOrder:[Qi.BIG],wordOrder:[Qi.BIG],security:[],identity:this.fb.group({vendorName:["",[$.pattern(an)]],productCode:["",[$.pattern(an)]],vendorUrl:["",[$.pattern(an)]],productName:["",[$.pattern(an)]],modelName:["",[$.pattern(an)]]}),values:[]}),this.observeValueChanges(),this.observeTypeChange(),this.observeShowSecurity()}get protocolType(){return this.slaveConfigFormGroup.get("type").value}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}validate(){return this.slaveConfigFormGroup.valid?null:{slaveConfigFormGroup:{valid:!1}}}writeValue(e){this.showSecurityControl.patchValue(!!e.security&&!Se(e.security,{})),this.updateSlaveConfig(e)}setDisabledState(e){this.isSlaveEnabled=!e,this.updateFormEnableState()}observeValueChanges(){this.slaveConfigFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{e.type===Ki.Serial&&(e.port=e.serialPort,delete e.serialPort),this.onChange(e),this.onTouched()}))}observeTypeChange(){this.slaveConfigFormGroup.get("type").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.updateFormEnableState(),this.updateMethodType(e)}))}updateMethodType(e){this.slaveConfigFormGroup.get("method").value!==Yi.RTU&&this.slaveConfigFormGroup.get("method").patchValue(e===Ki.Serial?Xi.ASCII:Yi.SOCKET,{emitEvent:!1})}updateFormEnableState(){this.isSlaveEnabled?(this.slaveConfigFormGroup.enable({emitEvent:!1}),this.showSecurityControl.enable({emitEvent:!1})):(this.slaveConfigFormGroup.disable({emitEvent:!1}),this.showSecurityControl.disable({emitEvent:!1})),this.updateEnablingByProtocol(),this.updateSecurityEnable(this.showSecurityControl.value)}observeShowSecurity(){this.showSecurityControl.valueChanges.pipe(le(this.destroy$)).subscribe((e=>this.updateSecurityEnable(e)))}updateSecurityEnable(e){e&&this.isSlaveEnabled&&this.protocolType!==Ki.Serial?this.slaveConfigFormGroup.get("security").enable({emitEvent:!1}):this.slaveConfigFormGroup.get("security").disable({emitEvent:!1})}updateEnablingByProtocol(){const e=this.protocolType===Ki.Serial,t=e?this.serialSpecificControlKeys:this.tcpUdpSpecificControlKeys,n=e?this.tcpUdpSpecificControlKeys:this.serialSpecificControlKeys;this.isSlaveEnabled&&t.forEach((e=>this.slaveConfigFormGroup.get(e)?.enable({emitEvent:!1}))),n.forEach((e=>this.slaveConfigFormGroup.get(e)?.disable({emitEvent:!1})))}updateSlaveConfig(e){const{vendorName:t="",productCode:n="",vendorUrl:i="",productName:a="",modelName:r=""}=e.identity??{},o={vendorName:t,productCode:n,vendorUrl:i,productName:a,modelName:r},{type:s=Ki.TCP,method:l=Yi.RTU,unitId:p=0,deviceName:c="",deviceType:d="",pollPeriod:u=1e3,sendDataToThingsBoard:m=!1,byteOrder:h=Qi.BIG,wordOrder:g=Qi.BIG,security:f={},values:y={},baudrate:v=this.modbusBaudrates[0],host:x="",port:b=null}=e,w={type:s,method:l,unitId:p,deviceName:c,deviceType:d,pollPeriod:u,sendDataToThingsBoard:!!m,byteOrder:h,wordOrder:g,security:f,identity:o,values:y,baudrate:v,host:s===Ki.Serial?"":x,port:s===Ki.Serial?null:b,serialPort:s===Ki.Serial?b:""};this.slaveConfigFormGroup.setValue(w,{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||QZ)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:QZ,selectors:[["tb-modbus-slave-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>QZ)),multi:!0},{provide:K,useExisting:c((()=>QZ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:112,vars:59,consts:[["serialPort",""],[1,"slave-container",3,"formGroup"],[1,"slave-content","tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["translate","",1,"fixed-title-width"],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-panel","no-border","no-padding","padding-top"],["class","tb-form-row column-xs",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf","ngIfElse"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","method"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","unitId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","100","name","value","formControlName","pollPeriod",3,"placeholder"],[1,"tb-form-row"],["formControlName","sendDataToThingsBoard",1,"mat-slide"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],["formControlName","byteOrder"],["formControlName","wordOrder"],["class","tb-form-panel stroked tb-slide-toggle",4,"ngIf"],[3,"formGroup"],["matInput","","name","value","formControlName","vendorName",3,"placeholder"],["matInput","","name","value","formControlName","productCode",3,"placeholder"],["matInput","","name","value","formControlName","vendorUrl",3,"placeholder"],["matInput","","name","value","formControlName","productName",3,"placeholder"],["matInput","","name","value","formControlName","modelName",3,"placeholder"],["formControlName","values"],[3,"value"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matInput","","name","value","formControlName","serialPort",3,"placeholder"],["formControlName","baudrate"],[1,"tb-form-panel","stroked","tb-slide-toggle"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide",3,"click","formControl"],["formControlName","security"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",1)(1,"div",2)(2,"div",3)(3,"div",4),t.ɵɵtext(4,"gateway.server-slave-config"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",5),t.ɵɵtemplate(6,BZ,2,2,"tb-toggle-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",7),t.ɵɵtemplate(8,LZ,8,7,"div",8)(9,qZ,8,9,"div",9)(10,zZ,8,7,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",10)(13,"div",11),t.ɵɵpipe(14,"translate"),t.ɵɵtext(15," gateway.method "),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-form-field",12)(17,"mat-select",13),t.ɵɵtemplate(18,UZ,2,2,"mat-option",6),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(19,"div",10)(20,"div",14),t.ɵɵpipe(21,"translate"),t.ɵɵtext(22,"gateway.unit-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",12),t.ɵɵelement(24,"input",15),t.ɵɵpipe(25,"translate"),t.ɵɵtemplate(26,jZ,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(27,"div",10)(28,"div",17),t.ɵɵtext(29,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",12),t.ɵɵelement(31,"input",18),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,HZ,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(34,"div",10)(35,"div",17),t.ɵɵtext(36,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"mat-form-field",12),t.ɵɵelement(38,"input",19),t.ɵɵpipe(39,"translate"),t.ɵɵtemplate(40,WZ,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(41,"div",10)(42,"div",20),t.ɵɵpipe(43,"translate"),t.ɵɵelementStart(44,"span",21),t.ɵɵtext(45," gateway.poll-period "),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"mat-form-field",12),t.ɵɵelement(47,"input",22),t.ɵɵpipe(48,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(49,KZ,7,4,"div",8),t.ɵɵelementStart(50,"div",23)(51,"mat-slide-toggle",24)(52,"mat-label"),t.ɵɵtext(53),t.ɵɵpipe(54,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(55,"div",25)(56,"mat-expansion-panel",26)(57,"mat-expansion-panel-header")(58,"mat-panel-title")(59,"div",27),t.ɵɵtext(60,"gateway.advanced-connection-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(61,"div",7)(62,"div",10)(63,"div",11),t.ɵɵpipe(64,"translate"),t.ɵɵtext(65,"gateway.byte-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(66,"mat-form-field",12)(67,"mat-select",28),t.ɵɵtemplate(68,YZ,2,2,"mat-option",6),t.ɵɵelementEnd()()(),t.ɵɵelementStart(69,"div",10)(70,"div",11),t.ɵɵpipe(71,"translate"),t.ɵɵtext(72,"gateway.word-order"),t.ɵɵelementEnd(),t.ɵɵelementStart(73,"mat-form-field",12)(74,"mat-select",29),t.ɵɵtemplate(75,XZ,2,2,"mat-option",6),t.ɵɵelementEnd()()(),t.ɵɵtemplate(76,ZZ,9,5,"div",30),t.ɵɵelementContainerStart(77,31),t.ɵɵelementStart(78,"div",10)(79,"div",4),t.ɵɵtext(80,"gateway.vendor-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(81,"mat-form-field",12),t.ɵɵelement(82,"input",32),t.ɵɵpipe(83,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(84,"div",10)(85,"div",4),t.ɵɵtext(86,"gateway.product-code"),t.ɵɵelementEnd(),t.ɵɵelementStart(87,"mat-form-field",12),t.ɵɵelement(88,"input",33),t.ɵɵpipe(89,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(90,"div",10)(91,"div",4),t.ɵɵtext(92,"gateway.vendor-url"),t.ɵɵelementEnd(),t.ɵɵelementStart(93,"mat-form-field",12),t.ɵɵelement(94,"input",34),t.ɵɵpipe(95,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(96,"div",10)(97,"div",4),t.ɵɵtext(98,"gateway.product-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(99,"mat-form-field",12),t.ɵɵelement(100,"input",35),t.ɵɵpipe(101,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(102,"div",10)(103,"div",4),t.ɵɵtext(104,"gateway.model-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(105,"mat-form-field",12),t.ɵɵelement(106,"input",36),t.ɵɵpipe(107,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()(),t.ɵɵelementStart(108,"div",25)(109,"div",27),t.ɵɵtext(110,"gateway.values"),t.ɵɵelementEnd(),t.ɵɵelement(111,"tb-modbus-values",37),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵreference(11);t.ɵɵproperty("formGroup",n.slaveConfigFormGroup),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.modbusProtocolTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial)("ngIfElse",e),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(14,29,"gateway.hints.modbus.framer-type")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.protocolType===n.ModbusProtocolType.Serial?n.modbusSerialMethodTypes:n.modbusMethodTypes),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,31,"gateway.hints.modbus.unit-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(25,33,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("unitId").hasError("required")&&n.slaveConfigFormGroup.get("unitId").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,35,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceName").hasError("required")&&n.slaveConfigFormGroup.get("deviceName").touched),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(39,37,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.slaveConfigFormGroup.get("deviceType").hasError("required")&&n.slaveConfigFormGroup.get("deviceType").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(43,39,"gateway.hints.modbus.poll-period")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(48,41,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.protocolType===n.ModbusProtocolType.Serial),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(54,43,"gateway.send-data-to-platform")," "),t.ɵɵadvance(10),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(64,45,"gateway.hints.modbus.byte-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(71,47,"gateway.hints.modbus.word-order")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.modbusOrderType),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.protocolType!==n.ModbusProtocolType.Serial),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.slaveConfigFormGroup.get("identity")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(83,49,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(89,51,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(95,53,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(101,55,"gateway.set")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(107,57,"gateway.set"))}},dependencies:t.ɵɵgetComponentDepsFactory(QZ,[j,_,jX,$X,r$,qn]),encapsulation:2,changeDetection:d.OnPush})}}e("ModbusSlaveConfigComponent",QZ);const JZ=["searchInput"],eQ=()=>["deviceName","info","unitId","type","actions"],tQ=()=>({minWidth:"96px",textAlign:"center"});function nQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",9)(1,"div",28)(2,"span",29),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",11),t.ɵɵelementStart(6,"button",13),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageSlave(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",13),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.servers-slaves")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search")))}function iQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.device-name")))}function aQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.deviceName)}}function rQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.info")," "))}function oQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){let e;const i=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(null!==(e=i.host)&&void 0!==e?e:i.port)}}function sQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.unit-id")," "))}function lQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.unitId)}}function pQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30)(1,"div"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.type")))}function cQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",30),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.ModbusProtocolLabelsMap.get(e.type)," ")}}function dQ(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",32)}function uQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageSlave(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",13),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteSlave(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function mQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,uQ,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",33),t.ɵɵelementContainer(4,34),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",35)(6,"button",36),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",37),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",38,2),t.ɵɵelementContainer(11,34),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,tQ)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function hQ(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",39)}function gQ(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class fQ{constructor(e,t,n,i,a){this.translate=e,this.dialog=t,this.dialogService=n,this.fb=i,this.cdr=a,this.isLegacy=!1,this.textSearchMode=!1,this.textSearch=this.fb.control("",{nonNullable:!0}),this.ModbusProtocolLabelsMap=sa,this.onChange=()=>{},this.onTouched=()=>{},this.destroy$=new te,this.masterFormGroup=this.fb.group({slaves:this.fb.array([])}),this.dataSource=new yQ}get slaves(){return this.masterFormGroup.get("slaves")}ngOnInit(){this.masterFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.updateTableData(e.slaves),this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(de(150),ce(((e,t)=>(e??"")===t.trim())),le(this.destroy$)).subscribe((e=>this.updateTableData(this.slaves.value,e.trim())))}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){this.slaves.clear(),this.pushDataAsFormArrays(e.slaves)}enterFilterMode(){this.textSearchMode=!0,this.cdr.detectChanges();const e=this.searchInputField.nativeElement;e.focus(),e.setSelectionRange(0,0)}exitFilterMode(){this.updateTableData(this.slaves.value),this.textSearchMode=!1,this.textSearch.reset()}manageSlave(e,t){e&&e.stopPropagation();const n=ke(t),i=n?this.slaves.at(t).value:{};this.getSlaveDialog(i,n?"action.apply":"action.add").afterClosed().pipe(ye(1),le(this.destroy$)).subscribe((e=>{e&&(n?this.slaves.at(t).patchValue(e):this.slaves.push(this.fb.control(e)),this.masterFormGroup.markAsDirty())}))}getSlaveDialog(e,t){return this.isLegacy?this.dialog.open(RZ,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,hideNewFields:!0,buttonTitle:t}}):this.dialog.open(hZ,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,hideNewFields:!1}})}deleteSlave(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-slave-title",{name:this.slaves.controls[t].value.deviceName}),this.translate.instant("gateway.delete-slave-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(ye(1),le(this.destroy$)).subscribe((e=>{e&&(this.slaves.removeAt(t),this.masterFormGroup.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.slaves.push(this.fb.control(e))))}static{this.ɵfac=function(e){return new(e||fQ)(t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(_e.DialogService),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:fQ,selectors:[["tb-modbus-master-table"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(JZ,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{isLegacy:"isLegacy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>fQ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:55,vars:41,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-master-table","tb-absolute-fill"],[1,"tb-form-panel","no-border","no-padding","padding-top","hint-container"],["tbTruncateWithTooltip","",1,"tb-form-hint","tb-primary-fill","tb-flex"],[1,"tb-master-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","table-value-column",4,"matHeaderCellDef"],["class","table-value-column",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-master-table-title"],[1,"table-value-column"],["tbTruncateWithTooltip",""],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"div",5),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"div",6)(6,"mat-toolbar",7),t.ɵɵtemplate(7,nQ,14,9,"div",8),t.ɵɵpipe(8,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-toolbar",7)(10,"div",9)(11,"button",10),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"mat-icon"),t.ɵɵtext(14,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-form-field",11)(16,"mat-label"),t.ɵɵtext(17," "),t.ɵɵelementEnd(),t.ɵɵelement(18,"input",12,0),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"button",13),t.ɵɵpipe(22,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(23,"mat-icon"),t.ɵɵtext(24,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(25,"div",14)(26,"table",15),t.ɵɵelementContainerStart(27,16),t.ɵɵtemplate(28,iQ,4,3,"mat-header-cell",17)(29,aQ,3,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(30,16),t.ɵɵtemplate(31,rQ,3,3,"mat-header-cell",17)(32,oQ,3,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(33,16),t.ɵɵtemplate(34,sQ,3,3,"mat-header-cell",17)(35,lQ,3,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(36,16),t.ɵɵtemplate(37,pQ,4,3,"mat-header-cell",17)(38,cQ,2,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(39,19),t.ɵɵtemplate(40,dQ,1,0,"mat-header-cell",20)(41,mQ,12,6,"mat-cell",21),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(42,hQ,1,0,"mat-header-row",22)(43,gQ,1,0,"mat-row",23),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"section",24),t.ɵɵpipe(45,"async"),t.ɵɵelementStart(46,"button",25),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageSlave(i))})),t.ɵɵelementStart(47,"mat-icon",26),t.ɵɵtext(48,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(49,"span"),t.ɵɵtext(50),t.ɵɵpipe(51,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(52,"span",27),t.ɵɵpipe(53,"async"),t.ɵɵtext(54," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,23,"gateway.hints.modbus-master")),t.ɵɵadvance(3),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(8,25,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(12,27,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(20,29,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(22,31,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","deviceName"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","info"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","unitId"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","type"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(39,eQ))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(40,eQ)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(45,33,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(51,35,"gateway.add-slave")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(53,37,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(fQ,[j,_,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%]{width:100%;height:calc(100% - 60px);background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .tb-master-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-master-table[_ngcontent-%COMP%] .tb-master-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:15%}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-master-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{z-index:1000}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}e("ModbusMasterTableComponent",fQ),Ge([rt()],fQ.prototype,"isLegacy",void 0);class yQ extends G{constructor(){super()}}e("SlavesDatasource",yQ);class vQ extends Ba{constructor(){super(),this.enableSlaveControl=new X(!1),this.enableSlaveControl.valueChanges.pipe(bn()).subscribe((e=>{this.updateSlaveEnabling(e),this.basicFormGroup.get("slave").updateValueAndValidity({emitEvent:!!this.onChange})}))}writeValue(e){super.writeValue(e),this.onEnableSlaveControl(e)}validate(){const{master:e,slave:t}=this.basicFormGroup.value,n=!e?.slaves?.length&&(Se(t,{})||!t);return!this.basicFormGroup.valid||n?{basicFormGroup:{valid:!1}}:null}initBasicFormGroup(){return this.fb.group({master:[],slave:[]})}updateSlaveEnabling(e){e?this.basicFormGroup.get("slave").enable({emitEvent:!1}):this.basicFormGroup.get("slave").disable({emitEvent:!1})}onEnableSlaveControl(e){this.enableSlaveControl.setValue(!!e.slave&&!Se(e.slave,{}))}static{this.ɵfac=function(e){return new(e||vQ)}}static{this.ɵdir=t.ɵɵdefineDirective({type:vQ,features:[t.ɵɵInheritDefinitionFeature]})}}e("ModbusBasicConfigDirective",vQ);class xQ extends vQ{constructor(){super(...arguments),this.isLegacy=!1}mapConfigToFormValue({master:e,slave:t}){return{master:e?.slaves?e:{slaves:[]},slave:t??{}}}getMappedValue(e){return{master:e.master,slave:e.slave}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(xQ)))(n||xQ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:xQ,selectors:[["tb-modbus-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>xQ)),multi:!0},{provide:K,useExisting:c((()=>xQ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:19,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","master",3,"isLegacy"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-form-hint","tb-primary-fill","tb-flex","center"],[1,"tb-form-row"],[1,"mat-slide",3,"formControl"],["formControlName","slave"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-modbus-master-table",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4)(10,"div",5),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",6)(14,"mat-slide-toggle",7)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(18,"tb-modbus-slave-config",8),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(5,11,"gateway.master-connections")),t.ɵɵadvance(2),t.ɵɵproperty("isLegacy",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(8,13,"gateway.server-config")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,15,"gateway.hints.modbus-server")),t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.enableSlaveControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,17,"gateway.enable")," "))},dependencies:t.ɵɵgetComponentDepsFactory(xQ,[j,_,QZ,fQ,Gn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}'],changeDetection:d.OnPush})}}e("ModbusBasicConfigComponent",xQ);class bQ extends vQ{constructor(){super(...arguments),this.isLegacy=!0}mapConfigToFormValue(e){return{master:e.master?.slaves?e.master:{slaves:[]},slave:e.slave?Ua.mapSlaveToUpgradedVersion(e.slave):{}}}getMappedValue(e){return{master:e.master,slave:e.slave?Ua.mapSlaveToDowngradedVersion(e.slave):{}}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(bQ)))(n||bQ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:bQ,selectors:[["tb-modbus-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>bQ)),multi:!0},{provide:K,useExisting:c((()=>bQ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:19,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","master",3,"isLegacy"],[1,"tb-form-panel","no-border","no-padding","padding-top"],[1,"tb-form-hint","tb-primary-fill","tb-flex","center"],[1,"tb-form-row"],[1,"mat-slide",3,"formControl"],["formControlName","slave"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-modbus-master-table",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4)(10,"div",5),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",6)(14,"mat-slide-toggle",7)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelement(18,"tb-modbus-slave-config",8),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(5,11,"gateway.master-connections")),t.ɵɵadvance(2),t.ɵɵproperty("isLegacy",n.isLegacy),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(8,13,"gateway.server-config")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(12,15,"gateway.hints.modbus-server")),t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.enableSlaveControl),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,17,"gateway.enable")," "))},dependencies:t.ɵɵgetComponentDepsFactory(bQ,[j,_,QZ,fQ,Gn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}'],changeDetection:d.OnPush})}}e("ModbusLegacyBasicConfigComponent",bQ);const wQ=()=>({maxWidth:"970px"});function SQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",20),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.get("key").value," ")}}function CQ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate(e.get("methodRPC").value)}}function _Q(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate(e.get("attributeOnThingsBoard").value)}}function TQ(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2).$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate2(" ",e.get("requestExpression").value+" - ","",e.get("attributeNameExpression").value," ")}}function IQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",21),t.ɵɵtemplate(1,CQ,2,1,"ng-container",22)(2,_Q,2,1,"ng-container",22)(3,TQ,2,2,"ng-container",22),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("ngSwitch",e.keysType),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.SocketValueKey.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.SocketValueKey.ATTRIBUTES_UPDATES),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.SocketValueKey.ATTRIBUTES_REQUESTS)}}function EQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function MQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function kQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function PQ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",42),2&e){const e=t.ɵɵnextContext(5);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Key)}}function DQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",24)(2,"div",25),t.ɵɵtext(3,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",26)(5,"div",27),t.ɵɵpipe(6,"translate"),t.ɵɵpipe(7,"translate"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"div",28)(11,"mat-form-field",29),t.ɵɵelement(12,"input",30),t.ɵɵpipe(13,"translate"),t.ɵɵtemplate(14,EQ,3,3,"mat-icon",31),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(15,"div",24)(16,"div",25),t.ɵɵtext(17,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"div",26)(19,"div",32)(20,"span",33),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(23,"div",34),t.ɵɵelementEnd(),t.ɵɵelementStart(24,"label",35),t.ɵɵtext(25,"from"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",36),t.ɵɵelement(27,"input",37),t.ɵɵpipe(28,"translate"),t.ɵɵtemplate(29,MQ,3,3,"mat-icon",31),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"label",38),t.ɵɵtext(31,"to"),t.ɵɵelementEnd(),t.ɵɵelementStart(32,"mat-form-field",36),t.ɵɵelement(33,"input",39),t.ɵɵpipe(34,"translate"),t.ɵɵtemplate(35,kQ,3,3,"mat-icon",31),t.ɵɵelementEnd()(),t.ɵɵtemplate(36,PQ,1,2,"tb-report-strategy",40),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",n.keysType===n.SocketValueKey.ATTRIBUTES?t.ɵɵpipeBind1(6,12,"gateway.hints.socket.key-attribute"):t.ɵɵpipeBind1(7,14,"gateway.hints.socket.key-telemetry")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,16,"gateway.key")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(13,18,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("key").hasError("required")&&e.get("key").touched),t.ɵɵadvance(7),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,20,"gateway.byte")),t.ɵɵadvance(2),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/byte_fn")("tb-help-popup-style",t.ɵɵpureFunction0(26,wQ)),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,22,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("byteFrom").hasError("required")&&e.get("byteFrom").touched),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(34,24,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("byteTo").hasError("required")&&e.get("byteTo").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.withReportStrategy&&(n.keysType===n.SocketValueKey.ATTRIBUTES||n.keysType===n.SocketValueKey.TIMESERIES))}}function OQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-required"))}function AQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}function FQ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",26)(2,"div",43),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",28)(6,"mat-form-field",29),t.ɵɵelement(7,"input",44),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,OQ,3,3,"mat-icon",31),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",26)(11,"div",45),t.ɵɵpipe(12,"translate"),t.ɵɵtext(13," gateway.encoding "),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",28)(15,"mat-form-field",29)(16,"mat-select",46),t.ɵɵtemplate(17,AQ,2,2,"mat-option",47),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(18,"div",48)(19,"mat-slide-toggle",49),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(20,"mat-label",50),t.ɵɵpipe(21,"translate"),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,7,"gateway.method-name")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("methodRPC").hasError("required")&&e.get("methodRPC").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(12,11,"gateway.hints.encoding")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,13,"gateway.hints.socket.with-response")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(23,15,"gateway.rpc.withResponse")," ")}}function RQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e)," ")}}function BQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e)," ")}}function NQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.request-expression-required"))}function LQ(e,n){1&e&&t.ɵɵelement(0,"div",34),2&e&&t.ɵɵproperty("tb-help-popup","widget/lib/gateway/request-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(2,wQ))}function VQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e)," ")}}function qQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.attribute-name-expression-required"))}function GQ(e,n){1&e&&t.ɵɵelement(0,"div",34),2&e&&t.ɵɵproperty("tb-help-popup","widget/lib/gateway/attribute-name-expression_fn")("tb-help-popup-style",t.ɵɵpureFunction0(2,wQ))}function zQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",26)(2,"div",45),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4," gateway.type "),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",28)(6,"mat-form-field",29)(7,"mat-select",52),t.ɵɵtemplate(8,RQ,3,4,"mat-option",47),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(9,"div",26)(10,"div",43),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",53)(14,"mat-form-field",29)(15,"mat-select",54),t.ɵɵtemplate(16,BQ,3,4,"mat-option",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"mat-form-field",29),t.ɵɵelement(18,"input",55),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,NQ,3,3,"mat-icon",31)(21,LQ,1,3,"div",56),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"div",26)(23,"div",43),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"div",53)(27,"mat-form-field",29)(28,"mat-select",57),t.ɵɵtemplate(29,VQ,3,4,"mat-option",47),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"mat-form-field",29),t.ɵɵelement(31,"input",58),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,qQ,3,3,"mat-icon",31)(34,GQ,1,3,"div",56),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,12,"gateway.hints.socket.attribute-requests-type")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.requestsType),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,14,"gateway.request-expression")," "),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.expressionType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,16,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("requestExpression").hasError("required")&&e.get("requestExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("requestExpressionSource").value===n.ExpressionType.Expression),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,18,"gateway.attribute-name-expression")," "),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.expressionType),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("attributeNameExpression").hasError("required")&&e.get("attributeNameExpression").touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("attributeNameExpressionSource").value===n.ExpressionType.Expression)}}function UQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",51),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}function jQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",41),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.socket.attribute-on-platform-required"))}function HQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",26)(2,"div",45),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4," gateway.encoding "),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",28)(6,"mat-form-field",29)(7,"mat-select",46),t.ɵɵtemplate(8,UQ,2,2,"mat-option",47),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(9,"div",26)(10,"div",43),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"div",28)(14,"mat-form-field",29),t.ɵɵelement(15,"input",59),t.ɵɵpipe(16,"translate"),t.ɵɵtemplate(17,jQ,3,3,"mat-icon",31),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext(2).$implicit,n=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,5,"gateway.hints.encoding")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,7,"gateway.attribute-on-platform")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("attributeOnThingsBoard").hasError("required")&&e.get("attributeOnThingsBoard").touched)}}function WQ(e,n){if(1&e&&t.ɵɵtemplate(0,DQ,37,27,"div",23)(1,FQ,24,17,"div",23)(2,zQ,35,22,"div",23)(3,HQ,18,11,"div",23),2&e){const e=t.ɵɵnextContext(3);t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.TIMESERIES||e.keysType===e.SocketValueKey.ATTRIBUTES),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.RPC_METHODS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.ATTRIBUTES_REQUESTS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.keysType===e.SocketValueKey.ATTRIBUTES_UPDATES)}}function $Q(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",12)(1,"div",13),t.ɵɵelementContainerStart(2,14),t.ɵɵelementStart(3,"mat-expansion-panel",15)(4,"mat-expansion-panel-header",16)(5,"mat-panel-title"),t.ɵɵtemplate(6,SQ,2,1,"div",17)(7,IQ,4,4,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()(),t.ɵɵtemplate(9,WQ,4,4,"ng-template",18),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",19),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.last,a=t.ɵɵreference(8),r=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",r.keysType===r.SocketValueKey.TIMESERIES||r.keysType===r.SocketValueKey.ATTRIBUTES)("ngIfElse",a),t.ɵɵadvance(4),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,r.deleteKeyTitle))}}function KQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10),t.ɵɵtemplate(1,$Q,14,7,"div",11),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.keysListFormArray.controls)("ngForTrackBy",e.trackByKey)}}function YQ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",60)(1,"span",61),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class XQ extends q{constructor(e,t,n){super(n),this.fb=e,this.popover=t,this.store=n,this.withReportStrategy=!0,this.keysDataApplied=new u,this.SocketValueKey=li,this.socketEncoding=Object.values(mt),this.requestsType=Object.values(ci),this.expressionType=Object.values(di),this.ExpressionType=di,this.ReportStrategyDefaultValue=en}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}trackByKey(e,t){return t}addKey(){let e;e=this.keysType===li.RPC_METHODS?this.fb.group({methodRPC:["",[$.required]],encoding:[mt.UTF16,[$.required]],withResponse:[!0]}):this.keysType===li.ATTRIBUTES_UPDATES?this.fb.group({encoding:[mt.UTF16,[$.required]],attributeOnThingsBoard:["",[$.required]]}):this.keysType===li.ATTRIBUTES_REQUESTS?this.fb.group({type:[ci.Shared],requestExpressionSource:[di.Constant],attributeNameExpressionSource:[di.Constant],requestExpression:["",[$.required]],attributeNameExpression:["",[$.required]]}):this.fb.group({key:["",[$.required,$.pattern(an)]],byteFrom:[0,[$.required]],byteTo:[0,[$.required]],reportStrategy:[{value:null,disabled:this.isReportStrategyDisabled()}]}),this.keysListFormArray.push(e)}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){let e=this.keysListFormArray.value.map((({reportStrategy:e,...t})=>({...t,...e&&{reportStrategy:e}})));this.keysDataApplied.emit(e)}prepareKeysFormArray(e){const t=[];return e?.forEach((e=>{let n;if(this.keysType===li.RPC_METHODS){const t=e;n=this.fb.group({methodRPC:[t.methodRPC,[$.required]],encoding:[t.encoding,[$.required]],withResponse:[t.withResponse]})}else if(this.keysType===li.ATTRIBUTES_REQUESTS){const t=e;n=this.fb.group({type:[t.type??ci.Shared],requestExpressionSource:[t.requestExpressionSource??di.Constant],attributeNameExpressionSource:[t.attributeNameExpressionSource??di.Constant],requestExpression:[t.requestExpression,[$.required]],attributeNameExpression:[t.attributeNameExpression,[$.required]]})}else if(this.keysType===li.ATTRIBUTES_UPDATES)n=this.fb.group({encoding:[e.encoding??mt.UTF16],attributeOnThingsBoard:[e.attributeOnThingsBoard,[$.required]]});else{const{key:t,byteFrom:i,byteTo:a,reportStrategy:r}=e;n=this.fb.group({key:[t,[$.required,$.pattern(an)]],byteFrom:[i??0,[$.required]],byteTo:[a??0,[$.required]],reportStrategy:[{value:r,disabled:this.isReportStrategyDisabled()}]})}t.push(n)})),this.fb.array(t)}isReportStrategyDisabled(){return!(this.withReportStrategy&&(this.keysType===li.ATTRIBUTES||this.keysType===li.TIMESERIES))}static{this.ɵfac=function(e){return new(e||XQ)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(it.TbPopoverComponent),t.ɵɵdirectiveInject(Ye.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:XQ,selectors:[["tb-device-data-keys-panel"]],inputs:{panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keys:"keys",keysType:"keysType",withReportStrategy:"withReportStrategy"},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:19,vars:16,consts:[["noKeys",""],["valueTitle",""],[1,"tb-device-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],["class","tb-form-panel no-border no-padding key-panel",4,"ngIf","ngIfElse"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","key-panel"],["class","tb-form-panel no-border no-padding tb-flex no-flex row center fill-width",4,"ngFor","ngForOf","ngForTrackBy"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],["class","title-container",4,"ngIf","ngIfElse"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"title-container"],[1,"title-container",3,"ngSwitch"],[4,"ngSwitchCase"],["class","tb-form-panel no-border no-padding",4,"ngIf"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],[1,"fixed-title-width","tb-flex","align-center"],[1,"tb-required"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["for","byteFrom",1,"tb-small-label"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap","flex-1"],["matInput","","required","","formControlName","byteFrom","type","number","id","byteFrom",3,"placeholder"],["for","byteTo",1,"tb-small-label"],["matInput","","required","","formControlName","byteTo","type","number","id","byteTo",3,"placeholder"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue"],[1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","methodRPC",3,"placeholder"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","encoding"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-row"],["formControlName","withResponse",1,"mat-slide",3,"click"],[3,"tb-hint-tooltip-icon"],[3,"value"],["formControlName","type"],[1,"tb-flex"],["formControlName","requestExpressionSource"],["matInput","","name","value","formControlName","requestExpression",3,"placeholder"],["matSuffix","","class","see-example p-1","tb-help-popup-placement","left",3,"tb-help-popup","tb-help-popup-style",4,"ngIf"],["formControlName","attributeNameExpressionSource"],["matInput","","name","value","formControlName","attributeNameExpression",3,"placeholder"],["matInput","","name","value","formControlName","attributeOnThingsBoard",3,"placeholder"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["translate","",1,"tb-prompt"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",2)(1,"div",3)(2,"div",4),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,KQ,2,2,"div",5),t.ɵɵelementStart(6,"div")(7,"button",6),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey())})),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()(),t.ɵɵtemplate(10,YQ,3,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(12,"div",7)(13,"button",8),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.applyKeysData())})),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(11);t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,8,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")"," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.keysListFormArray.controls.length)("ngIfElse",e),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,n.addKeyTitle)," "),t.ɵɵadvance(6),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,14,"action.apply")," ")}},dependencies:t.ɵɵgetComponentDepsFactory(XQ,[j,_,Xn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-small-label[_ngcontent-%COMP%]{font-size:16px;padding-right:0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}'],changeDetection:d.OnPush})}}e("DeviceDataKeysPanelComponent",XQ),Ge([I()],XQ.prototype,"withReportStrategy",void 0);const ZQ=()=>({maxWidth:"970px"});function QQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-filter-required"))}function JQ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-name-required"))}function eJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",40),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.device-profile-required"))}function tJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",41),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.toUpperCase()," ")}}function nJ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",27),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function iJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function aJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function rJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.type," ")}}function oJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.encoding," ")}}function sJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.methodRPC," ")}}class lJ extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.destroyRef=l,this.cdr=p,this.deviceFormGroup=this.fb.group({address:["",[$.required,$.pattern(an)]],deviceName:["",[$.required,$.pattern(an)]],deviceType:["",[$.required,$.pattern(an)]],encoding:[mt.UTF8],telemetry:[[]],attributes:[[]],attributeRequests:[[]],attributeUpdates:[[]],serverSideRpc:[[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),this.keysPopupClosed=!0,this.SocketValueKey=li,this.socketDeviceHelpLink=O+"/docs/iot-gateway/config/socket/#device-subsection",this.socketEncoding=Object.values(mt),this.ReportStrategyDefaultValue=en,this.deviceFormGroup.patchValue(this.data.value,{emitEvent:!1})}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.deviceFormGroup.valid){const e=this.deviceFormGroup.value;Te(e),this.dialogRef.close(e)}}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))return void this.popoverService.hidePopover(i);const a=this.deviceFormGroup.get(n),r={keys:a.value,keysType:n,panelTitle:pi.get(n),addKeyTitle:ui.get(n),deleteKeyTitle:mi.get(n),noKeysText:hi.get(n),withReportStrategy:this.data.withReportStrategy};this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,XQ,"leftBottom",!1,null,r,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(bn(this.destroyRef)).subscribe((e=>{this.popoverComponent.hide(),a.patchValue(e),a.markAsDirty(),this.cdr.markForCheck()})),this.popoverComponent.tbHideStart.pipe(bn(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}static{this.ɵfac=function(e){return new(e||lJ)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:lJ,selectors:[["tb-device-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:119,vars:57,consts:[["socketTelemetryButton",""],["attributesButton",""],["attributeRequestsButton",""],["attributesUpdatesButton",""],["rpcMethodsButton",""],[1,"dialog-mapping",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width"],["translate","",1,"tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","address",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","name","value","formControlName","deviceName",3,"placeholder"],["matInput","","name","value","formControlName","deviceType",3,"placeholder"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","encoding"],[3,"value",4,"ngFor","ngForOf"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[4,"ngFor","ngForOf"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[3,"value"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",5)(1,"mat-toolbar",6)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",7)(6,"div",8),t.ɵɵelementStart(7,"button",9),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",10),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",11)(11,"div",12)(12,"div",13)(13,"div",14)(14,"div",15),t.ɵɵtext(15," gateway.address-filter "),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"div",16)(17,"mat-form-field",17),t.ɵɵelement(18,"input",18),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,QQ,3,3,"mat-icon",19),t.ɵɵelement(21,"div",20),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"div",13)(23,"div",21),t.ɵɵtext(24,"gateway.device-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"div",16)(26,"mat-form-field",17),t.ɵɵelement(27,"input",22),t.ɵɵpipe(28,"translate"),t.ɵɵtemplate(29,JQ,3,3,"mat-icon",19),t.ɵɵelementEnd()()(),t.ɵɵelementStart(30,"div",13)(31,"div",21),t.ɵɵtext(32,"gateway.device-profile"),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"div",16)(34,"mat-form-field",17),t.ɵɵelement(35,"input",23),t.ɵɵpipe(36,"translate"),t.ɵɵtemplate(37,eJ,3,3,"mat-icon",19),t.ɵɵelementEnd()()(),t.ɵɵelementStart(38,"div",13)(39,"div",24),t.ɵɵpipe(40,"translate"),t.ɵɵtext(41," gateway.encoding "),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"div",16)(43,"mat-form-field",17)(44,"mat-select",25),t.ɵɵtemplate(45,tJ,2,2,"mat-option",26),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(46,nJ,1,2,"tb-report-strategy",27),t.ɵɵelementStart(47,"div",28)(48,"div",29),t.ɵɵtext(49,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(50,"div",30)(51,"mat-chip-listbox",31),t.ɵɵtemplate(52,iJ,2,1,"mat-chip",32),t.ɵɵelementStart(53,"mat-chip",33),t.ɵɵelement(54,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(55,"button",35,0),t.ɵɵpipe(57,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(56);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.TIMESERIES))})),t.ɵɵelementStart(58,"tb-icon",36),t.ɵɵtext(59,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(60,"div",28)(61,"div",29),t.ɵɵtext(62,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(63,"div",30)(64,"mat-chip-listbox",31),t.ɵɵtemplate(65,aJ,2,1,"mat-chip",32),t.ɵɵelementStart(66,"mat-chip",33),t.ɵɵelement(67,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(68,"button",35,1),t.ɵɵpipe(70,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(69);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.ATTRIBUTES))})),t.ɵɵelementStart(71,"tb-icon",36),t.ɵɵtext(72,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(73,"div",28)(74,"div",29),t.ɵɵtext(75,"gateway.attribute-requests"),t.ɵɵelementEnd(),t.ɵɵelementStart(76,"div",30)(77,"mat-chip-listbox",31),t.ɵɵtemplate(78,rJ,2,1,"mat-chip",32),t.ɵɵelementStart(79,"mat-chip",33),t.ɵɵelement(80,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(81,"button",35,2),t.ɵɵpipe(83,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(82);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.ATTRIBUTES_REQUESTS))})),t.ɵɵelementStart(84,"tb-icon",36),t.ɵɵtext(85,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(86,"div",28)(87,"div",29),t.ɵɵtext(88,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(89,"div",30)(90,"mat-chip-listbox",31),t.ɵɵtemplate(91,oJ,2,1,"mat-chip",32),t.ɵɵelementStart(92,"mat-chip",33),t.ɵɵelement(93,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(94,"button",35,3),t.ɵɵpipe(96,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(95);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.ATTRIBUTES_UPDATES))})),t.ɵɵelementStart(97,"tb-icon",36),t.ɵɵtext(98,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(99,"div",28)(100,"div",29),t.ɵɵtext(101,"gateway.rpc-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(102,"div",30)(103,"mat-chip-listbox",31),t.ɵɵtemplate(104,sJ,2,1,"mat-chip",32),t.ɵɵelementStart(105,"mat-chip",33),t.ɵɵelement(106,"label",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(107,"button",35,4),t.ɵɵpipe(109,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(108);return t.ɵɵresetView(n.manageKeys(i,a,n.SocketValueKey.RPC_METHODS))})),t.ɵɵelementStart(110,"tb-icon",36),t.ɵɵtext(111,"edit"),t.ɵɵelementEnd()()()()()(),t.ɵɵelementStart(112,"div",37)(113,"button",38),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(114),t.ɵɵpipe(115,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(116,"button",39),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(117),t.ɵɵpipe(118,"translate"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵproperty("formGroup",n.deviceFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,32,"gateway.device")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.socketDeviceHelpLink),t.ɵɵadvance(12),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,34,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceFormGroup.get("address").hasError("required")&&n.deviceFormGroup.get("address").touched),t.ɵɵadvance(),t.ɵɵproperty("tb-help-popup","widget/lib/gateway/address-filter_fn")("tb-help-popup-style",t.ɵɵpureFunction0(56,ZQ)),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,36,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceFormGroup.get("deviceName").hasError("required")&&n.deviceFormGroup.get("deviceName").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,38,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceFormGroup.get("deviceType").hasError("required")&&n.deviceFormGroup.get("deviceType").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(40,40,"gateway.hints.encoding")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.socketEncoding),t.ɵɵadvance(),t.ɵɵconditional(n.data.withReportStrategy?46:-1),t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("telemetry").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("telemetry").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(57,42,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(70,44,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributeRequests").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("attributeRequests").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(83,46,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(96,48,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(109,50,"action.edit")),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(115,52,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.deviceFormGroup.invalid||!n.deviceFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(118,54,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(lJ,[j,_,Gn,Xn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:grid;height:100%}[_nghost-%COMP%] .tb-form-panel[_ngcontent-%COMP%]{width:77vw;max-width:800px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%]{max-width:900px;display:flex;flex-direction:column}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{min-height:64px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] tb-toggle-select[_ngcontent-%COMP%]{padding:4px 0}[_nghost-%COMP%] .mat-mdc-dialog-content[_ngcontent-%COMP%]{height:670px}[_nghost-%COMP%] .ellipsis-chips-container[_ngcontent-%COMP%]{max-width:70%}[_nghost-%COMP%] .dialog-mapping .mat-mdc-chip-listbox .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center}[_nghost-%COMP%] .tb-form-row .fixed-title-width{min-width:40px;width:35%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center}[_nghost-%COMP%] .tb-form-row .mat-mdc-form-field{width:0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}[_nghost-%COMP%] .device-config{gap:12px;padding-left:10px;padding-right:10px}[_nghost-%COMP%] .device-node-pattern-field{flex-basis:3%}'],changeDetection:d.OnPush})}}e("DeviceDialogComponent",lJ);const pJ=["searchInput"],cJ=()=>["address","deviceName","actions"],dJ=()=>({minWidth:"96px",textAlign:"center"});function uJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",26)(2,"span",27),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",9),t.ɵɵelementStart(6,"button",11),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageDevices(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",11),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.devices")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search")))}function mJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.address-filter")," "))}function hJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.address)}}function gJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.device-name")))}function fJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.deviceName)}}function yJ(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",30)}function vJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",11),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageDevices(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",11),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteDevice(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function xJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,vJ,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",31),t.ɵɵelementContainer(4,32),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",33)(6,"button",34),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",35),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",36,2),t.ɵɵelementContainer(11,32),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,dJ)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function bJ(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",37)}function wJ(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class SJ{constructor(e,t,n,i,a){this.translate=e,this.dialog=t,this.dialogService=n,this.fb=i,this.cdr=a,this.withReportStrategy=!0,this.textSearchMode=!1,this.textSearch=this.fb.control("",{nonNullable:!0}),this.onChange=()=>{},this.destroy$=new te,this.devicesFormGroup=this.fb.array([]),this.dataSource=new CJ}ngOnInit(){this.devicesFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.updateTableData(e),this.onChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}ngAfterViewInit(){this.textSearch.valueChanges.pipe(de(150),ce(((e,t)=>(e??"")===t.trim())),le(this.destroy$)).subscribe((e=>this.updateTableData(this.devicesFormGroup.value,e.trim())))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){this.devicesFormGroup.clear(),this.pushDataAsFormArrays(e)}enterFilterMode(){this.textSearchMode=!0,this.cdr.detectChanges();const e=this.searchInputField.nativeElement;e.focus(),e.setSelectionRange(0,0)}exitFilterMode(){this.updateTableData(this.devicesFormGroup.value),this.textSearchMode=!1,this.textSearch.reset()}manageDevices(e,t){e&&e.stopPropagation();const n=ke(t),i=n?this.devicesFormGroup.at(t).value:{};this.getDeviceDialog(i,n?"action.apply":"action.add").afterClosed().pipe(ye(1),le(this.destroy$)).subscribe((e=>{e&&(n?this.devicesFormGroup.at(t).patchValue(e):this.devicesFormGroup.push(this.fb.control(e)),this.devicesFormGroup.markAsDirty())}))}validate(){return this.devicesFormGroup.controls.length?null:{devicesFormGroup:{valid:!1}}}getDeviceDialog(e,t){return this.dialog.open(lJ,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,withReportStrategy:this.withReportStrategy}})}deleteDevice(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-device-title",{name:this.devicesFormGroup.controls[t].value.deviceName}),this.translate.instant("gateway.delete-device-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(ye(1),le(this.destroy$)).subscribe((e=>{e&&(this.devicesFormGroup.removeAt(t),this.devicesFormGroup.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}pushDataAsFormArrays(e){e?.length&&e.forEach((e=>this.devicesFormGroup.push(this.fb.control(e))))}static{this.ɵfac=function(e){return new(e||SJ)(t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(_e.DialogService),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:SJ,selectors:[["tb-devices-config-table"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(pJ,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.searchInputField=e.first)}},inputs:{withReportStrategy:"withReportStrategy"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>SJ)),multi:!0},{provide:K,useExisting:c((()=>SJ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:45,vars:36,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-device-table","tb-absolute-fill"],[1,"tb-device-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","table-value-column",4,"matHeaderCellDef"],["class","table-value-column",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-device-table-title"],[1,"table-value-column"],["tbTruncateWithTooltip",""],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,uJ,14,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"div",7)(7,"button",8),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",9)(12,"mat-label"),t.ɵɵtext(13," "),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10,0),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵpipe(18,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(19,"mat-icon"),t.ɵɵtext(20,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(21,"div",12)(22,"table",13),t.ɵɵelementContainerStart(23,14),t.ɵɵtemplate(24,mJ,3,3,"mat-header-cell",15)(25,hJ,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(26,14),t.ɵɵtemplate(27,gJ,4,3,"mat-header-cell",15)(28,fJ,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(29,17),t.ɵɵtemplate(30,yJ,1,0,"mat-header-cell",18)(31,xJ,12,6,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(32,bJ,1,0,"mat-header-row",20)(33,wJ,1,0,"mat-row",21),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"section",22),t.ɵɵpipe(35,"async"),t.ɵɵelementStart(36,"button",23),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageDevices(i))})),t.ɵɵelementStart(37,"mat-icon",24),t.ɵɵtext(38,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(39,"span"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(42,"span",25),t.ɵɵpipe(43,"async"),t.ɵɵtext(44," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(4,20,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,22,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,24,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,26,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","address"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","deviceName"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(34,cJ))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(35,cJ)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(35,28,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,30,"gateway.add-device")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(43,32,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(SJ,[j,_,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%]{width:100%;height:100%;background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:35%}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}e("DevicesConfigTableComponent",SJ),Ge([I()],SJ.prototype,"withReportStrategy",void 0);let CJ=class extends G{constructor(){super()}};function _J(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",14),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function TJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",15),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.address-required"))}function IJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",15),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.socketConfigFormGroup.get("port")))}}function EJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",15),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.socketConfigFormGroup.get("bufferSize").hasError("min")?"gateway.buffer-size-range":"gateway.buffer-size-required"))}}e("DevicesDatasource",CJ);class MJ{constructor(e){this.fb=e,this.portLimits=Ii,this.socketTypes=Object.values(si),this.onChange=e=>{},this.destroy$=new te,this.socketConfigFormGroup=this.fb.group({address:["",[$.required,$.pattern(an)]],type:[si.TCP],port:[5e4,[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],bufferSize:[1024,[$.required,$.min(1),$.pattern(an)]]}),this.socketConfigFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){const{address:t="",type:n=si.TCP,port:i=5e4,bufferSize:a=1024}=e??{};this.socketConfigFormGroup.reset({address:t,type:n,port:i,bufferSize:a})}validate(){return this.socketConfigFormGroup.valid?null:{socketConfigFormGroup:{valid:!1}}}static{this.ɵfac=function(e){return new(e||MJ)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:MJ,selectors:[["tb-socket-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>MJ)),multi:!0},{provide:K,useExisting:c((()=>MJ)),multi:!0}]),t.ɵɵStandaloneFeature],decls:34,vars:25,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],[1,"fixed-title-width"],["tbTruncateWithTooltip",""],["formControlName","type","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","address",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","name","value","min","1","formControlName","bufferSize",3,"placeholder"],[3,"value"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2)(3,"div",3),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"tb-toggle-select",4),t.ɵɵtemplate(7,_J,2,2,"tb-toggle-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",1)(9,"div",6),t.ɵɵtext(10,"gateway.address"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",7)(12,"mat-form-field",8),t.ɵɵelement(13,"input",9),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,TJ,3,3,"mat-icon",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",1)(17,"div",6),t.ɵɵtext(18,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",7)(20,"mat-form-field",8),t.ɵɵelement(21,"input",11),t.ɵɵpipe(22,"translate"),t.ɵɵtemplate(23,IJ,3,3,"mat-icon",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(24,"div",1)(25,"div",12),t.ɵɵpipe(26,"translate"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"div",7)(30,"mat-form-field",8),t.ɵɵelement(31,"input",13),t.ɵɵpipe(32,"translate"),t.ɵɵtemplate(33,EJ,3,3,"mat-icon",10),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.socketConfigFormGroup),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,13,"gateway.connection-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.socketTypes),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.socketConfigFormGroup.get("address").hasError("required")&&n.socketConfigFormGroup.get("address").touched),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(22,17,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",(n.socketConfigFormGroup.get("port").hasError("required")||n.socketConfigFormGroup.get("port").hasError("min")||n.socketConfigFormGroup.get("port").hasError("max"))&&n.socketConfigFormGroup.get("port").touched),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(26,19,"gateway.hints.buffer-size")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(28,21,"gateway.buffer-size")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(32,23,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.socketConfigFormGroup.get("bufferSize").hasError("required")||n.socketConfigFormGroup.get("bufferSize").hasError("min")&&n.socketConfigFormGroup.get("bufferSize").touched))},dependencies:t.ɵɵgetComponentDepsFactory(MJ,[j,_,r$,qn]),encapsulation:2,changeDetection:d.OnPush})}}e("SocketConfigComponent",MJ);class kJ extends Ba{constructor(){super(...arguments),this.isLegacy=!1}getMappedValue(e){return e}initBasicFormGroup(){return this.fb.group({socket:[],devices:[]})}mapConfigToFormValue(e){return{socket:e.socket??{},devices:e.devices??[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(kJ)))(n||kJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:kJ,selectors:[["tb-socket-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>kJ)),multi:!0},{provide:K,useExisting:c((()=>kJ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:10,vars:14,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","socket"],["formControlName","devices",3,"withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-socket-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-devices-config-table",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,8,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,10,"gateway.socket"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,12,"gateway.devices"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("withReportStrategy",!n.isLegacy))},dependencies:t.ɵɵgetComponentDepsFactory(kJ,[j,_,MJ,SJ]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}e("SocketBasicConfigComponent",kJ);class PJ extends Ba{constructor(){super(...arguments),this.isLegacy=!0}getMappedValue(e){return Ha.mapSocketToDowngradedVersion(e)}initBasicFormGroup(){return this.fb.group({socket:[],devices:[]})}mapConfigToFormValue(e){return{socket:Ha.mapSocketToUpgradedVersion(e),devices:e?.devices?Ha.mapDevicesToUpgradedVersion(e.devices):[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(PJ)))(n||PJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:PJ,selectors:[["tb-socket-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>PJ)),multi:!0},{provide:K,useExisting:c((()=>PJ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:10,vars:14,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","socket"],["formControlName","devices",3,"withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-socket-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-devices-config-table",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,8,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,10,"gateway.socket"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,12,"gateway.devices"),"*"),t.ɵɵadvance(2),t.ɵɵproperty("withReportStrategy",!n.isLegacy))},dependencies:t.ɵɵgetComponentDepsFactory(PJ,[j,_,MJ,SJ]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}function DJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.name-required"))}function OJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function AJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.applicationConfigFormGroup.get("port")))}}function FJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.object-id-required"))}function RJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",6),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.vendor-id-required"))}function BJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.SegmentationTypeTranslationsMap.get(e))," ")}}e("SocketLegacyBasicConfigComponent",PJ);class NJ extends Ra{constructor(){super(...arguments),this.segmentationTypes=Object.values(fa),this.SegmentationTypeTranslationsMap=ya,this.portLimits=Ii}get applicationConfigFormGroup(){return this.formGroup}initFormGroup(){return this.fb.group({objectName:["",[$.required,$.pattern(an)]],host:["",[$.required,$.pattern(an)]],port:[null,[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],mask:[""],objectIdentifier:[null,[$.required]],vendorIdentifier:[null,[$.required]],maxApduLengthAccepted:[],segmentationSupported:[fa.BOTH],networkNumber:[],deviceDiscoveryTimeoutInSec:[]})}mapOnChangeValue(e){return Te(e),e}onWriteValue(e){const{maxApduLengthAccepted:t=1476,segmentationSupported:n=fa.BOTH,networkNumber:i=3,deviceDiscoveryTimeoutInSec:a=5,...r}=e;this.formGroup.reset({...r,maxApduLengthAccepted:t,segmentationSupported:n,networkNumber:i,deviceDiscoveryTimeoutInSec:a},{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(NJ)))(n||NJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:NJ,selectors:[["tb-bacnet-application-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>NJ)),multi:!0},{provide:K,useExisting:c((()=>NJ)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:83,vars:53,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","autocomplete","off","name","value","formControlName","objectName",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["translate","",1,"fixed-title-width"],["matInput","","name","value","formControlName","mask",3,"placeholder"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["matInput","","type","number","min","0","name","value","formControlName","objectIdentifier",3,"placeholder"],["matInput","","type","number","min","0","name","value","formControlName","vendorIdentifier",3,"placeholder"],[1,"tb-form-panel","stroked"],[1,"tb-settings"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-panel","no-border","no-padding","padding-top"],["matInput","","type","number","min","0","name","value","formControlName","maxApduLengthAccepted",3,"placeholder"],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","segmentationSupported"],[3,"value"],["matInput","","type","number","min","0","name","value","formControlName","networkNumber",3,"placeholder"],["matInput","","type","number","min","0","name","value","formControlName","deviceDiscoveryTimeoutInSec",3,"placeholder"],["translate","","matSuffix","",1,"block","pr-2"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.bacnet.object-name"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4),t.ɵɵelement(6,"input",5),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,DJ,3,3,"mat-icon",6),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",1)(10,"div",2),t.ɵɵtext(11,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",4),t.ɵɵelement(13,"input",7),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,OJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"div",1)(17,"div",2),t.ɵɵtext(18,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"mat-form-field",4),t.ɵɵelement(20,"input",8),t.ɵɵpipe(21,"translate"),t.ɵɵtemplate(22,AJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"div",1)(24,"div",9),t.ɵɵtext(25,"gateway.network-mask"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",4),t.ɵɵelement(27,"input",10),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(29,"div",1)(30,"div",11),t.ɵɵpipe(31,"translate"),t.ɵɵtext(32,"gateway.object-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"mat-form-field",4),t.ɵɵelement(34,"input",12),t.ɵɵpipe(35,"translate"),t.ɵɵtemplate(36,FJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(37,"div",1)(38,"div",11),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40,"gateway.vendor-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"mat-form-field",4),t.ɵɵelement(42,"input",13),t.ɵɵpipe(43,"translate"),t.ɵɵtemplate(44,RJ,3,3,"mat-icon",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(45,"div",14)(46,"mat-expansion-panel",15)(47,"mat-expansion-panel-header")(48,"mat-panel-title")(49,"div",16),t.ɵɵtext(50,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(51,"div",17)(52,"div",1)(53,"div",11),t.ɵɵpipe(54,"translate"),t.ɵɵtext(55,"gateway.bacnet.apdu-length"),t.ɵɵelementEnd(),t.ɵɵelementStart(56,"mat-form-field",4),t.ɵɵelement(57,"input",18),t.ɵɵpipe(58,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(59,"div",1)(60,"div",19),t.ɵɵpipe(61,"translate"),t.ɵɵtext(62,"gateway.bacnet.segmentation.label"),t.ɵɵelementEnd(),t.ɵɵelementStart(63,"mat-form-field",4)(64,"mat-select",20),t.ɵɵrepeaterCreate(65,BJ,3,4,"mat-option",21,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()(),t.ɵɵelementStart(67,"div",1)(68,"div",19),t.ɵɵpipe(69,"translate"),t.ɵɵtext(70,"gateway.bacnet.network-number"),t.ɵɵelementEnd(),t.ɵɵelementStart(71,"mat-form-field",4),t.ɵɵelement(72,"input",22),t.ɵɵpipe(73,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(74,"div",1)(75,"div",19),t.ɵɵpipe(76,"translate"),t.ɵɵtext(77,"gateway.bacnet.device-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(78,"mat-form-field",4),t.ɵɵelement(79,"input",23),t.ɵɵpipe(80,"translate"),t.ɵɵelementStart(81,"span",24),t.ɵɵtext(82,"gateway.suffix.s"),t.ɵɵelementEnd()()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.applicationConfigFormGroup),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,23,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("objectName").hasError("required")&&n.applicationConfigFormGroup.get("objectName").touched?8:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,25,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("host").hasError("required")&&n.applicationConfigFormGroup.get("host").touched?15:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(21,27,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((n.applicationConfigFormGroup.get("port").hasError("required")||n.applicationConfigFormGroup.get("port").hasError("min")||n.applicationConfigFormGroup.get("port").hasError("max"))&&n.applicationConfigFormGroup.get("port").touched?22:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(28,29,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(31,31,"gateway.hints.bacnet.object-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(35,33,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("objectIdentifier").hasError("required")&&n.applicationConfigFormGroup.get("objectIdentifier").touched?36:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(39,35,"gateway.hints.bacnet.vendor-id")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(43,37,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.applicationConfigFormGroup.get("vendorIdentifier").hasError("required")&&n.applicationConfigFormGroup.get("vendorIdentifier").touched?44:-1),t.ɵɵadvance(9),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(54,39,"gateway.hints.bacnet.apdu-length")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(58,41,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(61,43,"gateway.hints.bacnet.segmentation")),t.ɵɵadvance(5),t.ɵɵrepeater(n.segmentationTypes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(69,45,"gateway.hints.bacnet.network-number")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(73,47,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(76,49,"gateway.hints.bacnet.device-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(80,51,"gateway.set")))},dependencies:t.ɵɵgetComponentDepsFactory(NJ,[j,_,r$]),encapsulation:2,changeDetection:d.OnPush})}}function LJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",11),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function VJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",14),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",15)(6,"mat-form-field",6),t.ɵɵelement(7,"input",16),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,LJ,3,3,"mat-icon",11),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",e.keyType===e.BacnetDeviceKeysType.TIMESERIES?t.ɵɵpipeBind1(1,4,"gateway.hints.socket.key-telemetry"):t.ɵɵpipeBind1(2,6,"gateway.hints.socket.key-attribute")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,8,"gateway.key")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,10,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.formGroup.get("key").hasError("required")&&e.formGroup.get("key").touched?9:-1)}}function qJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",11),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.method-required"))}function GJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",14),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",15)(5,"mat-form-field",6),t.ɵɵelement(6,"input",17),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,qJ,3,3,"mat-icon",11),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,4,"gateway.hints.method")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,6,"gateway.method")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.formGroup.get("method").hasError("required")&&e.formGroup.get("method").touched?8:-1)}}function zJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.BacnetRequestTypeTranslationsMap.get(e))," ")}}function UJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",18)(2,"div",14),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-toggle-select",19),t.ɵɵrepeaterCreate(7,zJ,3,4,"tb-toggle-option",8,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.hints.bacnet.request-type")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,4,"gateway.bacnet.request-type.label")),t.ɵɵadvance(3),t.ɵɵrepeater(e.requestTypes)}}function jJ(e,n){1&e&&(t.ɵɵelementStart(0,"div",3)(1,"div",4),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.request-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",6),t.ɵɵelement(5,"input",20),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,2,"gateway.hints.bacnet.request-timeout")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,4,"gateway.set")))}function HJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.BacnetKeyObjectTypeTranslationsMap.get(e))," ")}}function WJ(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",11),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.object-id-required"))}function $J(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.BacnetPropertyIdTranslationsMap.get(e))," ")}}function KJ(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",13),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Key)}}class YJ extends Ra{constructor(){super(...arguments),this.withReportStrategy=!0,this.propertyIds=Ia.get(Ca.analogOutput),this.objectTypes=Object.values(Ca),this.requestTypes=Object.values(Ma),this.ReportStrategyDefaultValue=en,this.BacnetDeviceKeysType=va,this.BacnetKeyObjectTypeTranslationsMap=_a,this.BacnetPropertyIdTranslationsMap=Ea,this.BacnetRequestTypeTranslationsMap=ka}ngOnInit(){this.formGroup=this.initKeyFormGroup(),this.observeValueChanges(),this.observeObjectType()}isReportStrategyDisabled(){return!(this.withReportStrategy&&(this.keyType===va.ATTRIBUTES||this.keyType===va.TIMESERIES))}initKeyFormGroup(){return this.fb.group({key:[{value:"",disabled:this.keyType===va.RPC_METHODS},[$.required,$.pattern(an)]],method:[{value:"",disabled:this.keyType!==va.RPC_METHODS},[$.required,$.pattern(an)]],objectType:[Ca.analogOutput],objectId:[0,[$.required]],propertyId:[Ta.presentValue],requestTimeout:[{value:0,disabled:this.keyType!==va.RPC_METHODS}],requestType:[{value:Ma.Write,disabled:this.keyType!==va.RPC_METHODS}],reportStrategy:[{value:null,disabled:this.isReportStrategyDisabled()}]})}observeObjectType(){this.formGroup.get("objectType").valueChanges.pipe(bn(this.destroyRef)).subscribe((e=>{this.propertyIds=Ia.get(e),this.propertyIds.includes(this.formGroup.get("propertyId").value)||this.formGroup.get("propertyId").patchValue(this.propertyIds[0],{emitEvent:!1})}))}initFormGroup(){return this.fb.group({})}mapOnChangeValue({reportStrategy:e,...t}){return e?{...t,reportStrategy:e}:t}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(YJ)))(n||YJ)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:YJ,selectors:[["tb-bacnet-device-data-key"]],inputs:{keyType:"keyType",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>YJ)),multi:!0},{provide:K,useExisting:c((()=>YJ)),multi:!0}]),t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:35,vars:15,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap","raw-value-option"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","objectType"],[3,"value"],[1,"tb-form-table-row-cell","tb-flex","no-gap"],["matInput","","type","number","min","0","name","value","formControlName","objectId",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["formControlName","propertyId"],["formControlName","reportStrategy",3,"isExpansionMode","defaultValue"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matInput","","name","value","formControlName","method",3,"placeholder"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],["formControlName","requestType","appearance","fill"],["matInput","","type","number","min","0","name","value","formControlName","requestTimeout",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.platform-side"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtemplate(5,VJ,10,12)(6,GJ,9,10),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",1)(8,"div",2),t.ɵɵtext(9,"gateway.connector-side"),t.ɵɵelementEnd(),t.ɵɵtemplate(10,UJ,9,6,"div",3)(11,jJ,7,6,"div",3),t.ɵɵelementStart(12,"div",3)(13,"div",4),t.ɵɵpipe(14,"translate"),t.ɵɵtext(15,"gateway.object-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"div",5)(17,"mat-form-field",6)(18,"mat-select",7),t.ɵɵrepeaterCreate(19,HJ,3,4,"mat-option",8,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()()(),t.ɵɵelementStart(21,"div",9)(22,"mat-form-field",6),t.ɵɵelement(23,"input",10),t.ɵɵpipe(24,"translate"),t.ɵɵtemplate(25,WJ,3,3,"mat-icon",11),t.ɵɵelementEnd()()(),t.ɵɵelementStart(26,"div",3)(27,"div",4),t.ɵɵpipe(28,"translate"),t.ɵɵtext(29,"gateway.property-id"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",6)(31,"mat-select",12),t.ɵɵrepeaterCreate(32,$J,3,4,"mat-option",8,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()()(),t.ɵɵtemplate(34,KJ,1,2,"tb-report-strategy",13),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.formGroup),t.ɵɵadvance(5),t.ɵɵconditional(n.keyType!==n.BacnetDeviceKeysType.RPC_METHODS?5:6),t.ɵɵadvance(5),t.ɵɵconditional(n.keyType===n.BacnetDeviceKeysType.RPC_METHODS?10:-1),t.ɵɵadvance(),t.ɵɵconditional(n.keyType===n.BacnetDeviceKeysType.RPC_METHODS?11:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(14,9,"gateway.hints.bacnet.key-object-id")),t.ɵɵadvance(6),t.ɵɵrepeater(n.objectTypes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(24,11,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.formGroup.get("objectId").hasError("required")&&n.formGroup.get("objectId").touched?25:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(28,13,"gateway.hints.bacnet.property-id")),t.ɵɵadvance(5),t.ɵɵrepeater(n.propertyIds),t.ɵɵadvance(2),t.ɵɵconditional(n.isReportStrategyDisabled()?-1:34))},dependencies:t.ɵɵgetComponentDepsFactory(YJ,[j,_,Xn]),encapsulation:2,changeDetection:d.OnPush})}}function XJ(e,n){if(1&e&&t.ɵɵelement(0,"tb-bacnet-device-data-key",17),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext(2);t.ɵɵproperty("formControl",e)("keyType",n.keysType)("withReportStrategy",n.withReportStrategy)}}function ZJ(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵelementContainerStart(2,11),t.ɵɵelementStart(3,"mat-expansion-panel",12)(4,"mat-expansion-panel-header",13)(5,"mat-panel-title")(6,"div",14),t.ɵɵtext(7),t.ɵɵelementEnd()()(),t.ɵɵtemplate(8,XJ,1,3,"ng-template",15),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",16),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"delete"),t.ɵɵelementEnd()()()}if(2&e){let e;const i=n.$implicit,a=n.$index,r=n.$count,o=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵproperty("formGroup",i),t.ɵɵadvance(),t.ɵɵproperty("expanded",a===r-1),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",null!==(e=null==i.value?null:i.value.key)&&void 0!==e?e:null==i.value?null:i.value.method," "),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(10,4,o.deleteKeyTitle))}}function QJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3),t.ɵɵrepeaterCreate(1,ZJ,13,6,"div",9,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵrepeater(e.keysListFormArray.controls)}}function JJ(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",4)(1,"span",18),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.noKeysText)}}class e1 extends q{constructor(e,t,n){super(n),this.fb=e,this.popover=t,this.store=n,this.withReportStrategy=!0,this.keysDataApplied=p(),this.ReportStrategyDefaultValue=en}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}addKey(){this.keysListFormArray.push(this.fb.control({}))}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){this.keysDataApplied.emit(this.keysListFormArray.value)}prepareKeysFormArray(e){const t=[];return e?.forEach((e=>{t.push(this.fb.control(e))})),this.fb.array(t)}static{this.ɵfac=function(e){return new(e||e1)(t.ɵɵdirectiveInject(H.UntypedFormBuilder),t.ɵɵdirectiveInject(it.TbPopoverComponent),t.ɵɵdirectiveInject(Ye.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:e1,selectors:[["tb-bacnet-device-data-keys-panel"]],inputs:{panelTitle:"panelTitle",addKeyTitle:"addKeyTitle",deleteKeyTitle:"deleteKeyTitle",noKeysText:"noKeysText",keys:"keys",keysType:"keysType",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:18,vars:15,consts:[[1,"tb-device-keys-panel"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],[1,"tb-form-panel","no-border","no-padding","key-panel"],[1,"tb-flex","no-flex","center","align-center","key-panel"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"title-container"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[3,"formControl","keyType","withReportStrategy"],["translate","",1,"tb-prompt"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,QJ,3,0,"div",3)(6,JJ,3,1,"div",4),t.ɵɵelementStart(7,"div")(8,"button",5),t.ɵɵlistener("click",(function(){return n.addKey()})),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",6)(12,"button",7),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"button",8),t.ɵɵlistener("click",(function(){return n.applyKeysData()})),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,7,n.panelTitle),""," ("+n.keysListFormArray.controls.length+")"," "),t.ɵɵadvance(2),t.ɵɵconditional(n.keysListFormArray.controls.length?5:6),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(10,9,n.addKeyTitle)," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,11,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,13,"action.apply")," "))},dependencies:t.ɵɵgetComponentDepsFactory(e1,[j,_,YJ]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%]{width:77vw;max-width:700px}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{max-width:11vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .key-panel[_ngcontent-%COMP%]{height:500px;overflow:auto}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-form-panel[_ngcontent-%COMP%] .mat-mdc-icon-button[_ngcontent-%COMP%]{width:56px;height:56px;padding:16px;color:#0000008a}[_nghost-%COMP%] .tb-device-keys-panel[_ngcontent-%COMP%] .tb-small-label[_ngcontent-%COMP%]{font-size:16px;padding-right:0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}'],changeDetection:d.OnPush})}}function t1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function n1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.deviceFormGroup.get("port")))}}function i1(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",12)(1,"mat-expansion-panel",34)(2,"mat-expansion-panel-header")(3,"mat-panel-title")(4,"div",35),t.ɵɵtext(5,"gateway.advanced-configuration-settings"),t.ɵɵelementEnd()()(),t.ɵɵelement(6,"tb-string-items-list",36),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()),2&e){let e;const n=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,2,"gateway.bacnet.alt-responses-address")),t.ɵɵpropertyInterpolate("placeholder",null!=(e=n.deviceFormGroup.get("altResponsesAddresses").value)&&e.length?"":t.ɵɵpipeBind1(8,4,"gateway.address"))}}function a1(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",19),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function r1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",16),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.hints.poll-period-required"))}function o1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function s1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function l1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function p1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.method," ")}}class c1 extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.cdr=l,this.destroyRef=p,this.deviceFormGroup=this.fb.group({host:["",[$.required,$.pattern(an)]],port:["",[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],deviceInfo:[],altResponsesAddresses:[{value:[],disabled:this.data.hideNewFields}],pollPeriod:[1e4,[$.required,$.min(0)]],timeseries:[[]],attributes:[[]],attributeUpdates:[[]],serverSideRpc:[[]],reportStrategy:[{value:null,disabled:!this.data.withReportStrategy}]}),this.keysPopupClosed=!0,this.BacnetDeviceKeysType=va,this.DeviceInfoType=ga,this.portLimits=Ii,this.deviceHelpLink=O+"/docs/iot-gateway/config/bacnet/#device-object-settings",this.sourceTypes=Object.values(di),this.ConnectorType=ct,this.ReportStrategyDefaultValue=en,this.deviceFormGroup.patchValue(this.data.value,{emitEvent:!1})}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.deviceFormGroup.valid){const{altResponsesAddresses:e,reportStrategy:t,...n}=this.deviceFormGroup.value;this.dialogRef.close({altResponsesAddresses:e??[],...t?{reportStrategy:t}:{},...n})}}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))return void this.popoverService.hidePopover(i);const a=this.deviceFormGroup.get(n),r={keys:a.value,keysType:n,panelTitle:xa.get(n),addKeyTitle:ba.get(n),deleteKeyTitle:wa.get(n),noKeysText:Sa.get(n),withReportStrategy:this.data.withReportStrategy};this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,e1,"leftBottom",!1,null,r,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.subscribe((e=>{this.popoverComponent.hide(),a.patchValue(e),a.markAsDirty(),this.cdr.markForCheck()})),this.popoverComponent.tbHideStart.pipe(bn(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}static{this.ɵfac=function(e){return new(e||c1)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:c1,selectors:[["tb-bacnet-device-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:100,vars:47,consts:[["attributesButton",""],["socketTelemetryButton",""],["attributesUpdatesButton",""],["rpcMethodsButton",""],[1,"dialog-mapping",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["formControlName","deviceInfo","required","true",3,"deviceInfoType","sourceTypes","connectorType"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],[1,"fixed-title-width","tb-required"],["tbTruncateWithTooltip","","translate",""],["matInput","","type","number","min","0","name","value","formControlName","pollPeriod",3,"placeholder"],[1,"tb-form-row","space-between","tb-flex"],["translate","",1,"fixed-title-width"],[1,"tb-flex","ellipsis-chips-container"],[1,"tb-flex",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[1,"tb-settings","chips-panel","w-full"],["translate","",1,"tb-form-panel-title"],["editable","","floatLabel","always","formControlName","altResponsesAddresses",1,"chips-list",3,"label","placeholder"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",4)(1,"mat-toolbar",5)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",6)(6,"div",7),t.ɵɵelementStart(7,"button",8),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(8,"mat-icon",9),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",10)(11,"div",11)(12,"div",12)(13,"div",13),t.ɵɵtext(14,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-form-field",14),t.ɵɵelement(16,"input",15),t.ɵɵpipe(17,"translate"),t.ɵɵtemplate(18,t1,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"div",12)(20,"div",13),t.ɵɵtext(21,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",14),t.ɵɵelement(23,"input",17),t.ɵɵpipe(24,"translate"),t.ɵɵtemplate(25,n1,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵtemplate(26,i1,9,6,"div",12),t.ɵɵelement(27,"tb-device-info-table",18),t.ɵɵtemplate(28,a1,1,2,"tb-report-strategy",19),t.ɵɵelementStart(29,"div",12)(30,"div",20)(31,"span",21),t.ɵɵtext(32,"gateway.poll-period"),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"mat-form-field",14),t.ɵɵelement(34,"input",22),t.ɵɵpipe(35,"translate"),t.ɵɵtemplate(36,r1,3,3,"mat-icon",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(37,"div",23)(38,"div",24),t.ɵɵtext(39,"gateway.attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(40,"div",25)(41,"mat-chip-listbox",26),t.ɵɵrepeaterCreate(42,o1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(44,"mat-chip",27),t.ɵɵelement(45,"label",28),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"button",29,0),t.ɵɵpipe(48,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(47);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.ATTRIBUTES))})),t.ɵɵelementStart(49,"tb-icon",30),t.ɵɵtext(50,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(51,"div",23)(52,"div",24),t.ɵɵtext(53,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(54,"div",25)(55,"mat-chip-listbox",26),t.ɵɵrepeaterCreate(56,s1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(58,"mat-chip",27),t.ɵɵelement(59,"label",28),t.ɵɵelementEnd()(),t.ɵɵelementStart(60,"button",29,1),t.ɵɵpipe(62,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(61);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.TIMESERIES))})),t.ɵɵelementStart(63,"tb-icon",30),t.ɵɵtext(64,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(65,"div",23)(66,"div",24),t.ɵɵtext(67,"gateway.attribute-updates"),t.ɵɵelementEnd(),t.ɵɵelementStart(68,"div",25)(69,"mat-chip-listbox",26),t.ɵɵrepeaterCreate(70,l1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(72,"mat-chip",27),t.ɵɵelement(73,"label",28),t.ɵɵelementEnd()(),t.ɵɵelementStart(74,"button",29,2),t.ɵɵpipe(76,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(75);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.ATTRIBUTES_UPDATES))})),t.ɵɵelementStart(77,"tb-icon",30),t.ɵɵtext(78,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(79,"div",23)(80,"div",24),t.ɵɵtext(81,"gateway.rpc-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(82,"div",25)(83,"mat-chip-listbox",26),t.ɵɵrepeaterCreate(84,p1,2,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(86,"mat-chip",27),t.ɵɵelement(87,"label",28),t.ɵɵelementEnd()(),t.ɵɵelementStart(88,"button",29,3),t.ɵɵpipe(90,"translate"),t.ɵɵlistener("click",(function(i){t.ɵɵrestoreView(e);const a=t.ɵɵreference(89);return t.ɵɵresetView(n.manageKeys(i,a,n.BacnetDeviceKeysType.RPC_METHODS))})),t.ɵɵelementStart(91,"tb-icon",30),t.ɵɵtext(92,"edit"),t.ɵɵelementEnd()()()()()(),t.ɵɵelementStart(93,"div",31)(94,"button",32),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.cancel())})),t.ɵɵtext(95),t.ɵɵpipe(96,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(97,"button",33),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add())})),t.ɵɵtext(98),t.ɵɵpipe(99,"translate"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵproperty("formGroup",n.deviceFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,27,"gateway.device")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.deviceHelpLink),t.ɵɵadvance(10),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(17,29,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.deviceFormGroup.get("host").hasError("required")&&n.deviceFormGroup.get("host").touched?18:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(24,31,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional((n.deviceFormGroup.get("port").hasError("required")||n.deviceFormGroup.get("port").hasError("min")||n.deviceFormGroup.get("port").hasError("max"))&&n.deviceFormGroup.get("port").touched?25:-1),t.ɵɵadvance(),t.ɵɵconditional(n.data.hideNewFields?-1:26),t.ɵɵadvance(),t.ɵɵproperty("deviceInfoType",n.DeviceInfoType.FULL)("sourceTypes",n.sourceTypes)("connectorType",n.ConnectorType.BACNET),t.ɵɵadvance(),t.ɵɵconditional(n.data.withReportStrategy?28:-1),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(35,33,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.deviceFormGroup.get("pollPeriod").hasError("required")&&n.deviceFormGroup.get("pollPeriod").touched?36:-1),t.ɵɵadvance(5),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("attributes").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(48,35,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("timeseries").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("timeseries").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(62,37,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("attributeUpdates").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(76,39,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(),t.ɵɵrepeater(n.deviceFormGroup.get("serverSideRpc").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(90,41,"action.edit")),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(96,43,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.deviceFormGroup.invalid||!n.deviceFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(99,45,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(c1,[j,_,Gn,qn,r$,I$,Xn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{display:grid;height:100%}[_nghost-%COMP%] .tb-form-panel[_ngcontent-%COMP%]{width:77vw;max-width:800px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%]{max-width:900px;display:flex;flex-direction:column}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{min-height:64px}[_nghost-%COMP%] .dialog-mapping[_ngcontent-%COMP%] tb-toggle-select[_ngcontent-%COMP%]{padding:4px 0}[_nghost-%COMP%] .mat-mdc-dialog-content[_ngcontent-%COMP%]{height:670px}[_nghost-%COMP%] .ellipsis-chips-container[_ngcontent-%COMP%]{max-width:70%}[_nghost-%COMP%] .chips-panel[_ngcontent-%COMP%]{padding:6px 6px 6px 0}[_nghost-%COMP%] .dialog-mapping .mat-mdc-chip-listbox .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center}[_nghost-%COMP%] .tb-form-row .fixed-title-width{min-width:40px;width:35%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center}[_nghost-%COMP%] .tb-form-row .mat-mdc-form-field{width:0}[_nghost-%COMP%] .tb-form-row .chips-list .mat-mdc-form-field{width:100%}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex}[_nghost-%COMP%] .device-config{gap:12px;padding-left:10px;padding-right:10px}[_nghost-%COMP%] .device-node-pattern-field{flex-basis:3%}'],changeDetection:d.OnPush})}}const d1=()=>["deviceName","host","port","actions"],u1=()=>({minWidth:"96px",textAlign:"center"});function m1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",26)(2,"span",27),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"span",9),t.ɵɵelementStart(6,"button",11),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageDevices(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"button",11),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.devices")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,7,"action.search")))}function h1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.device-name")))}function g1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(null==e.deviceInfo?null:e.deviceInfo.deviceNameExpression)}}function f1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.host")," "))}function y1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.host)}}function v1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.port")," "))}function x1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",28)(1,"div",29),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.port)}}function b1(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",30)}function w1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",11),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageDevices(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",11),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteDevice(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function S1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,w1,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",31),t.ɵɵelementContainer(4,32),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",33)(6,"button",34),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",35),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",36,2),t.ɵɵelementContainer(11,32),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(3),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,u1)),t.ɵɵadvance(),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function C1(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",37)}function _1(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class T1 extends qa{constructor(){super(...arguments),this.hideNewFields=!1}getDatasource(){return new I1}manageDevices(e,t){e&&e.stopPropagation();const n=ke(t),i=n?this.entityFormArray.at(t).value:{};this.getDeviceDialog(i,n?"action.apply":"action.add").afterClosed().pipe(ye(1),bn(this.destroyRef)).subscribe((e=>{e&&(n?this.entityFormArray.at(t).patchValue(e):this.entityFormArray.push(this.fb.control(e)),this.entityFormArray.markAsDirty())}))}deleteDevice(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-device-title",{name:this.entityFormArray.controls[t].value.deviceInfo?.deviceNameExpression}),this.translate.instant("gateway.delete-device-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(ye(1),bn(this.destroyRef)).subscribe((e=>{e&&(this.entityFormArray.removeAt(t),this.entityFormArray.markAsDirty())}))}getDeviceDialog(e,t){return this.dialog.open(c1,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,withReportStrategy:this.withReportStrategy,hideNewFields:this.hideNewFields}})}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())||e.deviceNameExpression?.toString().toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(T1)))(n||T1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:T1,selectors:[["tb-bacnet-devices-config-table"]],inputs:{hideNewFields:[2,"hideNewFields","hideNewFields",m]},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>T1)),multi:!0},{provide:K,useExisting:c((()=>T1)),multi:!0}]),t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:48,vars:37,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-device-table","tb-absolute-fill"],[1,"tb-device-table-content","flex","flex-col"],[1,"mat-mdc-table-toolbar"],["class","mat-toolbar-tools",4,"ngIf"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"table-container"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","table-value-column",4,"matHeaderCellDef"],["class","table-value-column",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"tb-device-table-title"],[1,"table-value-column"],["tbTruncateWithTooltip",""],[1,"w-12"],[1,"lt-lg:!hidden","flex","flex-1","flex-row","items-stretch","justify-end"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,m1,14,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"div",7)(7,"button",8),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",9)(12,"mat-label"),t.ɵɵtext(13," "),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10,0),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"button",11),t.ɵɵpipe(18,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(19,"mat-icon"),t.ɵɵtext(20,"close"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(21,"div",12)(22,"table",13),t.ɵɵelementContainerStart(23,14),t.ɵɵtemplate(24,h1,4,3,"mat-header-cell",15)(25,g1,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(26,14),t.ɵɵtemplate(27,f1,3,3,"mat-header-cell",15)(28,y1,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(29,14),t.ɵɵtemplate(30,v1,3,3,"mat-header-cell",15)(31,x1,3,1,"mat-cell",16),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(32,17),t.ɵɵtemplate(33,b1,1,0,"mat-header-cell",18)(34,S1,12,6,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(35,C1,1,0,"mat-header-row",20)(36,_1,1,0,"mat-row",21),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"section",22),t.ɵɵpipe(38,"async"),t.ɵɵelementStart(39,"button",23),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageDevices(i))})),t.ɵɵelementStart(40,"mat-icon",24),t.ɵɵtext(41,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"span"),t.ɵɵtext(43),t.ɵɵpipe(44,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(45,"span",25),t.ɵɵpipe(46,"async"),t.ɵɵtext(47," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===t.ɵɵpipeBind1(4,21,n.dataSource.isEmpty())),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,23,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(16,25,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,27,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","deviceName"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","host"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","port"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(35,d1))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(36,d1)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(38,29,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(44,31,"gateway.add-device")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(46,33,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(T1,[j,_,qn]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%]{width:100%;height:100%;background:#fff;overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .mat-toolbar-tools[_ngcontent-%COMP%]{min-height:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .title-container[_ngcontent-%COMP%]{overflow:hidden}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-right:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%]{table-layout:fixed;min-width:450px}[_nghost-%COMP%] .tb-device-table[_ngcontent-%COMP%] .tb-device-table-content[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%] .mat-mdc-table[_ngcontent-%COMP%] .table-value-column[_ngcontent-%COMP%]{padding:0 12px;width:21%}[_nghost-%COMP%] .no-data-found[_ngcontent-%COMP%]{height:calc(100% - 120px)}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{height:auto;min-height:100px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%] .tb-device-table-title[_ngcontent-%COMP%]{padding-bottom:5px;width:100%}}[_nghost-%COMP%] mat-cell.tb-value-cell{cursor:pointer}[_nghost-%COMP%] mat-cell.tb-value-cell .mat-icon{height:24px;width:24px;font-size:24px;color:#757575}'],changeDetection:d.OnPush})}}class I1 extends G{constructor(){super()}}class E1 extends Ba{initBasicFormGroup(){return this.fb.group({application:[],devices:[]})}mapConfigToFormValue(e){return{application:e.application??{},devices:e.devices??[]}}getMappedValue(e){return{application:e.application,devices:e.devices??[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(E1)))(n||E1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:E1,selectors:[["tb-bacnet-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>E1)),multi:!0},{provide:K,useExisting:c((()=>E1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:15,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","application"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","devices",3,"hideNewFields","withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-bacnet-application-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-bacnet-devices-config-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.application"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.devices"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("hideNewFields",n.isLegacy)("withReportStrategy",n.withReportStrategy))},dependencies:t.ɵɵgetComponentDepsFactory(E1,[j,_,NJ,T1]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}class M1 extends Ba{constructor(){super(...arguments),this.isLegacy=!0}initBasicFormGroup(){return this.fb.group({application:[],devices:[]})}mapConfigToFormValue(e){return{application:e.general?Wa.mapApplicationToUpgradedVersion(e.general):{},devices:e.devices?.length?Wa.mapDevicesToUpgradedVersion(e.devices):[]}}getMappedValue(e){return{general:e.application?Wa.mapApplicationToDowngradedVersion(e.application):{},devices:e.devices?.length?Wa.mapDevicesToDowngradedVersion(e.devices):[]}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(M1)))(n||M1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:M1,selectors:[["tb-bacnet-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>M1)),multi:!0},{provide:K,useExisting:c((()=>M1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:15,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","application"],[1,"tb-form-panel","no-border","no-padding","padding-top","tb-flex","fill-height"],["formControlName","devices",3,"hideNewFields","withReportStrategy"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-bacnet-application-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"div",4),t.ɵɵelement(10,"tb-bacnet-devices-config-table",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.application"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.devices"),"*"),t.ɵɵadvance(3),t.ɵɵproperty("hideNewFields",n.isLegacy)("withReportStrategy",n.withReportStrategy))},dependencies:t.ɵɵgetComponentDepsFactory(M1,[j,_,NJ,T1]),styles:['@charset "UTF-8";[_nghost-%COMP%]{height:100%}[_nghost-%COMP%] .mat-mdc-tab-group, [_nghost-%COMP%] .mat-mdc-tab-body-wrapper{height:100%}'],changeDetection:d.OnPush})}}function k1(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",5),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.host-required"))}function P1(e,n){if(1&e&&(t.ɵɵelement(0,"tb-error-icon",8),t.ɵɵpipe(1,"getGatewayPortTooltip"),t.ɵɵpipe(2,"translate")),2&e){t.ɵɵnextContext();const e=t.ɵɵreadContextLet(16);t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(2,3,t.ɵɵpipeBind1(1,1,e)))}}class D1 extends Ra{constructor(){super(...arguments),this.portLimits=Ii}get serverConfigFormGroup(){return this.formGroup}initFormGroup(){return this.fb.group({host:["",[$.required,$.pattern(an)]],port:[null,[$.required,$.min(Ii.MIN),$.max(Ii.MAX)]],SSL:[!1],security:this.fb.group({cert:[""],key:[""]})})}mapOnChangeValue(e){return Te(e),e}onWriteValue(e){this.formGroup.reset(e,{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(D1)))(n||D1)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:D1,selectors:[["tb-rest-server-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>D1)),multi:!0},{provide:K,useExisting:c((()=>D1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:46,vars:43,consts:[[1,"tb-form-panel","no-border","no-padding","padding-top",3,"formGroup"],[1,"tb-form-row"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","host",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],[1,"tb-form-row","column-xs"],["matInput","","type","number","name","value","formControlName","port",3,"min","max","placeholder"],["matSuffix","",3,"tooltipText"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","space-between","tb-flex","fill-width"],["formControlName","SSL",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],[3,"formGroup"],["tbTruncateWithTooltip","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],[1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","cert",3,"placeholder"],["matInput","","name","value","formControlName","key",3,"placeholder"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4,"gateway.host"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",3),t.ɵɵelement(6,"input",4),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,k1,3,3,"mat-icon",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",6)(10,"div",2),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"gateway.port"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",3),t.ɵɵelement(14,"input",7),t.ɵɵpipe(15,"translate"),t.ɵɵdeclareLet(16),t.ɵɵtemplate(17,P1,3,5,"tb-error-icon",8),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",9)(19,"div",10),t.ɵɵtext(20,"gateway.security"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",11)(22,"mat-slide-toggle",12)(23,"mat-label",13),t.ɵɵpipe(24,"translate"),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerStart(27,14),t.ɵɵelementStart(28,"div",11)(29,"div",15),t.ɵɵpipe(30,"translate"),t.ɵɵtext(31),t.ɵɵpipe(32,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"div",16)(34,"mat-form-field",3),t.ɵɵelement(35,"input",17),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(37,"div",11)(38,"div",15),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(42,"div",16)(43,"mat-form-field",3),t.ɵɵelement(44,"input",18),t.ɵɵpipe(45,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()),2&e){t.ɵɵproperty("formGroup",n.serverConfigFormGroup),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,18,"gateway.hints.rest.host")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,20,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.serverConfigFormGroup.get("host").hasError("required")&&n.serverConfigFormGroup.get("host").touched?8:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,22,"gateway.hints.rest.port")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("min",n.portLimits.MIN),t.ɵɵpropertyInterpolate("max",n.portLimits.MAX),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,24,"gateway.set")),t.ɵɵadvance(2);const e=t.ɵɵstoreLet(n.serverConfigFormGroup.get("port"));t.ɵɵadvance(),t.ɵɵconditional((e.hasError("required")||e.hasError("min")||e.hasError("max"))&&e.touched?17:-1),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(24,27,"gateway.hints.rest.ssl-verify")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(26,29,"gateway.rest.ssl-verify")," "),t.ɵɵadvance(2),t.ɵɵproperty("formGroup",n.serverConfigFormGroup.get("security")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(30,31,"gateway.hints.rest.cert")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(32,33,"gateway.certificate")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,35,"gateway.set")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(39,37,"gateway.hints.rest.key")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,39,"gateway.key")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(45,41,"gateway.set"))}},dependencies:t.ɵɵgetComponentDepsFactory(D1,[j,_,r$,qn,Qn]),encapsulation:2,changeDetection:d.OnPush})}}const O1=()=>({min:1});function A1(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",4),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.ResponseTypeTranslationsMap.get(e)))}}function F1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",4),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function R1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",4),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function B1(e,n){if(1&e&&(t.ɵɵelementContainerStart(0,7),t.ɵɵelementStart(1,"div",8)(2,"div",9),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",10)(6,"mat-form-field",11)(7,"mat-select",12),t.ɵɵrepeaterCreate(8,F1,2,2,"mat-option",4,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(10,"div",8)(11,"div",9),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",10)(15,"mat-form-field",11)(16,"mat-select",13),t.ɵɵrepeaterCreate(17,R1,2,2,"mat-option",4,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("formGroup",e.responseConfigFormGroup.get(e.ResponseType.CONST)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.rest.on-success")),t.ɵɵadvance(5),t.ɵɵrepeater(e.responseStatuses),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,5,"gateway.rest.on-error")),t.ɵɵadvance(5),t.ɵɵrepeater(e.responseStatuses)}}function N1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.response-timeout-required"))}function L1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,O1)))}function V1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function q1(e,n){1&e&&(t.ɵɵelementStart(0,"span",18),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function G1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.response-attribute-required"))}function z1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",17),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.response-attribute-pattern"))}function U1(e,n){if(1&e&&(t.ɵɵdeclareLet(0),t.ɵɵelementContainerStart(1,7),t.ɵɵelementStart(2,"div",8)(3,"mat-slide-toggle",14)(4,"mat-label"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(7,"div",8)(8,"div",15),t.ɵɵtext(9,"gateway.timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-form-field",11),t.ɵɵelement(11,"input",16),t.ɵɵpipe(12,"translate"),t.ɵɵdeclareLet(13),t.ɵɵtemplate(14,N1,2,3,"tb-error-icon",17)(15,L1,2,5,"tb-error-icon",17)(16,V1,2,3,"tb-error-icon",17)(17,q1,2,0,"span",18),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",8)(19,"div",15),t.ɵɵtext(20,"gateway.rest.response-attribute"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",11),t.ɵɵelement(22,"input",19),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,G1,2,3,"tb-error-icon",17)(25,z1,2,3,"tb-error-icon",17),t.ɵɵelementEnd()(),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(),n=e.responseConfigFormGroup.get(e.ResponseType.ADVANCED);t.ɵɵadvance(),t.ɵɵproperty("formGroup",n),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,6,"gateway.rest.response-expected")," "),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(12,8,"gateway.set"));const i=n.get("timeout").touched;t.ɵɵadvance(3),t.ɵɵconditional(n.get("timeout").hasError("required")&&i?14:n.get("timeout").hasError("min")&&i?15:n.get("timeout").hasError("pattern")&&i?16:17),t.ɵɵadvance(8),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,10,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.get("responseAttribute").hasError("required")&&n.get("responseAttribute").touched?24:n.get("responseAttribute").hasError("pattern")&&n.get("responseAttribute").touched?25:-1)}}class j1 extends Ra{get responseConfigFormGroup(){return this.formGroup}constructor(){super(),this.ResponseTypeTranslationsMap=xi,this.ResponseType=yi,this.responseTypes=Object.values(yi),this.responseStatuses=Object.values(vi),this.observeIsExpected()}initFormGroup(){return this.fb.group({type:[yi.DEFAULT],[yi.CONST]:this.fb.group({successResponse:[vi.OK],unsuccessfulResponse:[vi.ERROR]}),[yi.ADVANCED]:this.fb.group({responseExpected:[!0],timeout:[null,[$.required,$.min(.001),$.pattern(nn)]],responseAttribute:["",[$.required,$.pattern(an)]]})})}mapOnChangeValue({type:e,...t}){return{type:e,...t[e]??{}}}onWriteValue(e){const{type:t=yi.DEFAULT,...n}=e??{};this.toggleIsExpected(n.responseExpected,t),this.responseConfigFormGroup.patchValue({type:t,[t]:n},{emitEvent:!1})}toggleIsExpected(e,t){const n=e&&t===yi.ADVANCED;this.responseConfigFormGroup.get(yi.ADVANCED).get("timeout")[n?"enable":"disable"]({emitEvent:!1}),this.responseConfigFormGroup.get(yi.ADVANCED).get("responseAttribute")[n?"enable":"disable"]({emitEvent:!1})}observeIsExpected(){se(this.responseConfigFormGroup.get("type").valueChanges,this.responseConfigFormGroup.get(yi.ADVANCED).get("responseExpected").valueChanges).pipe(bn()).subscribe((()=>this.toggleIsExpected(this.responseConfigFormGroup.get(yi.ADVANCED).get("responseExpected").value,this.responseConfigFormGroup.get("type").value)))}static{this.ɵfac=function(e){return new(e||j1)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:j1,selectors:[["tb-rest-response-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>j1)),multi:!0},{provide:K,useExisting:c((()=>j1)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:11,vars:7,consts:[[1,"tb-form-row","space-between","same-padding","tb-flex","column","size-full",3,"formGroup"],[1,"tb-flex","row","space-between","align-center","no-gap","fill-width"],[1,"fixed-title-width","tb-required"],["formControlName","type","appearance","fill"],[3,"value"],[3,"ngSwitch"],[3,"ngSwitchCase"],[3,"formGroup"],[1,"tb-form-row"],[1,"fixed-title-width"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["formControlName","successResponse"],["formControlName","unsuccessfulResponse"],["formControlName","responseExpected",1,"mat-slide"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["matSuffix","",3,"tooltipText"],["translate","","matSuffix","",1,"block","pr-2"],["matInput","","type","text","name","value","formControlName","responseAttribute",3,"placeholder"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",3),t.ɵɵrepeaterCreate(6,A1,3,4,"tb-toggle-option",4,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()(),t.ɵɵelementContainerStart(8,5),t.ɵɵtemplate(9,B1,19,7,"ng-template",6)(10,U1,26,12,"ng-template",6),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.responseConfigFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,5,"gateway.response")),t.ɵɵadvance(3),t.ɵɵrepeater(n.responseTypes),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",n.responseConfigFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.ResponseType.CONST),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.ResponseType.ADVANCED))},dependencies:t.ɵɵgetComponentDepsFactory(j1,[j,_,Qn]),encapsulation:2,changeDetection:d.OnPush})}}function H1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",15),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.endpoint-required"))}function W1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",17),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function $1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",15),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.http-methods-required"))}function K1(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",17),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.RestConvertorTypeTranslationsMap.get(e)))}}function Y1(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",27),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Device)}}function X1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",40),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function Z1(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",40),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function Q1(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",28)(1,"div",20)(2,"div",21),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",33)(6,"mat-chip-listbox",34),t.ɵɵrepeaterCreate(7,X1,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(9,"mat-chip",35),t.ɵɵelement(10,"label",36),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"button",37,0),t.ɵɵpipe(13,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(12),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.ATTRIBUTES))})),t.ɵɵelementStart(14,"tb-icon",38),t.ɵɵtext(15,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(16,"div",20)(17,"div",39),t.ɵɵtext(18,"gateway.timeseries"),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",33)(20,"mat-chip-listbox",34),t.ɵɵrepeaterCreate(21,Z1,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(23,"mat-chip",35),t.ɵɵelement(24,"label",36),t.ɵɵelementEnd()(),t.ɵɵelementStart(25,"button",37,1),t.ɵɵpipe(27,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(26),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.TIMESERIES))})),t.ɵɵelementStart(28,"tb-icon",38),t.ɵɵtext(29,"edit"),t.ɵɵelementEnd()()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,5,"gateway.attributes")),t.ɵɵadvance(3),t.ɵɵproperty("tbEllipsisChipList",e.converterAttributes),t.ɵɵadvance(),t.ɵɵrepeater(e.converterAttributes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(13,7,"action.edit")),t.ɵɵadvance(9),t.ɵɵproperty("tbEllipsisChipList",e.converterTelemetry),t.ɵɵadvance(),t.ɵɵrepeater(e.converterTelemetry),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(27,9,"action.edit"))}}function J1(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",15),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.extension-required"))}function e0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",40),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function t0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",28)(1,"div",11)(2,"div",12),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",41),t.ɵɵelement(7,"input",42),t.ɵɵpipe(8,"translate"),t.ɵɵtemplate(9,J1,2,3,"tb-error-icon",15),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"div",43)(11,"div",24),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",25),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"div",20)(18,"div",21),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",44)(22,"mat-chip-listbox",34),t.ɵɵtemplate(23,e0,3,1,"mat-chip",45),t.ɵɵelementStart(24,"mat-chip",35),t.ɵɵelement(25,"label",36),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"button",37,2),t.ɵɵpipe(28,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(27),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i,a.MappingKeysType.CUSTOM))})),t.ɵɵelementStart(29,"tb-icon",38),t.ɵɵtext(30,"edit"),t.ɵɵelementEnd()()()()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.rest.extension")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,12,"gateway.extension")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,14,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("converter.extension").hasError("required")&&e.mappingFormGroup.get("converter.extension").touched?9:-1),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,16,"gateway.extension-configuration")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,18,"gateway.extension-configuration-hint")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,20,"gateway.keys")),t.ɵɵadvance(3),t.ɵɵproperty("tbEllipsisChipList",e.customKeys),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",e.customKeys),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(28,22,"action.edit"))}}class n0 extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.destroyRef=l,this.cd=p,this.mappingFormGroup=this.fb.group({endpoint:["",[$.required,$.pattern(an)]],HTTPMethods:[[ht.POST],[$.required]],security:[{type:ki.ANONYMOUS}],converter:this.fb.group({type:[gi.JSON,[]],deviceInfo:[],reportStrategy:[],attributes:[[]],timeseries:[[]],extension:["",[$.required,$.pattern(an)]],extensionConfig:[]}),response:[]}),this.keysPopupClosed=!0,this.helpLink=`${O}/docs/iot-gateway/config/rest/#mapping-section`,this.httpMethods=Object.values(ht),this.converterTypes=Object.values(gi),this.restSourceTypes=Object.values(fi),this.SecurityMode=$i,this.MappingKeysType=Ni,this.DeviceInfoType=ga,this.RestConvertorTypeTranslationsMap=_i,this.RestDataConversionTranslationsMap=Ti,this.RestConverterType=gi,this.ConnectorType=ct,this.ReportStrategyDefaultValue=en,this.setInitialFormValue(),this.observeConverterTypeChange(),this.toggleConverterFieldsByType(this.converterType)}get converterType(){return this.mappingFormGroup.get("converter")?.get("type").value}get converterAttributes(){if(this.converterType)return this.mappingFormGroup.get("converter").value.attributes.map((e=>e.key))}get converterTelemetry(){if(this.converterType)return this.mappingFormGroup.get("converter").value.timeseries.map((e=>e.key))}get customKeys(){return Object.keys(this.mappingFormGroup.get("converter").value.extensionConfig??{})}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.mappingFormGroup.valid){const{converter:e,...t}=this.mappingFormGroup.value,{reportStrategy:n,...i}=e,a={...t,reportStrategy:n,converter:i};Te(a),this.dialogRef.close(a)}}manageKeys(e,t,n){e?.stopPropagation(),this.popoverComponent&&!this.popoverComponent.tbHidden&&this.popoverComponent.hide();const i=t._elementRef.nativeElement;if(this.popoverService.hasPopover(i))this.popoverService.hidePopover(i);else{const e=this.mappingFormGroup.get("converter").get(n),t={keys:e.value,keysType:n,panelTitle:Li.get(n),addKeyTitle:Vi.get(n),deleteKeyTitle:qi.get(n),noKeysText:Gi.get(n),withReportStrategy:this.data.withReportStrategy,convertorType:this.converterType,connectorType:ct.REST};this.keysPopupClosed=!1,this.popoverComponent=this.popoverService.displayPopover(i,this.renderer,this.viewContainerRef,yK,"leftBottom",!1,null,t,{},{},{},!0),this.popoverComponent.tbComponentRef.instance.keysDataApplied.pipe(bn(this.destroyRef)).subscribe((t=>{this.popoverComponent.hide(),e.patchValue(t),e.markAsDirty(),this.cd.markForCheck()})),this.popoverComponent.tbHideStart.pipe(bn(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}}setInitialFormValue(){const{converter:e,reportStrategy:t,...n}=this.data.value;this.mappingFormGroup.patchValue({...n,converter:{...e,...t?{reportStrategy:t}:{}}},{emitEvent:!1})}observeConverterTypeChange(){this.mappingFormGroup.get("converter").get("type").valueChanges.pipe(bn(this.destroyRef)).subscribe((e=>this.toggleConverterFieldsByType(e)))}toggleConverterFieldsByType(e){const t=e===gi.JSON;this.mappingFormGroup.get("converter").get("attributes")[t?"enable":"disable"]({emitEvent:!1}),this.mappingFormGroup.get("converter").get("timeseries")[t?"enable":"disable"]({emitEvent:!1}),this.mappingFormGroup.get("converter").get("extension")[t?"disable":"enable"]({emitEvent:!1}),this.mappingFormGroup.get("converter").get("extensionConfig")[t?"disable":"enable"]({emitEvent:!1})}static{this.ɵfac=function(e){return new(e||n0)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:n0,selectors:[["tb-rest-mapping-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:59,vars:45,consts:[["attributesButton",""],["telemetryButton",""],["keysButton",""],[1,"h-full","w-[77vw]","max-w-3xl",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row"],[1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","endpoint",3,"placeholder"],["matSuffix","",3,"tooltipText"],["formControlName","HTTPMethods","multiple",""],[3,"value"],["formControlName","security",3,"mode"],["formGroupName","converter"],[1,"tb-form-row","space-between","tb-flex"],[1,"fixed-title-width"],["formControlName","type","appearance","fill"],[1,"tb-form-panel","stroked"],[1,"tb-form-panel-title"],[1,"tb-form-hint","tb-primary-fill"],["formControlName","deviceInfo","required","true",1,"device-info",3,"sourceTypes","convertorType","deviceInfoType","connectorType"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"],[1,"tb-form-panel","no-border","no-padding","w-full"],["formControlName","response"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],[1,"tb-flex","max-w-70%"],[1,"tb-flex","gw-chip-list",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["translate","",1,"fixed-title-width"],["tbTruncateWithTooltip",""],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex"],["matInput","","name","value","formControlName","extension",3,"placeholder"],[1,"tb-form-row","space-between","same-padding","tb-flex","column"],[1,"tb-flex","ellipsis-chips-container"],[4,"ngFor","ngForOf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",3)(1,"mat-toolbar",4)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",5)(6,"div",6),t.ɵɵelementStart(7,"button",7),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",8),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",9)(11,"div",10)(12,"div",11)(13,"div",12),t.ɵɵpipe(14,"translate"),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-form-field",13),t.ɵɵelement(18,"input",14),t.ɵɵpipe(19,"translate"),t.ɵɵtemplate(20,H1,2,3,"tb-error-icon",15),t.ɵɵelementEnd()(),t.ɵɵelementStart(21,"div",11)(22,"div",12),t.ɵɵpipe(23,"translate"),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",13)(27,"mat-select",16),t.ɵɵrepeaterCreate(28,W1,2,2,"mat-option",17,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd(),t.ɵɵtemplate(30,$1,2,3,"tb-error-icon",15),t.ɵɵelementEnd()(),t.ɵɵelement(31,"tb-security-config",18),t.ɵɵelementContainerStart(32,19),t.ɵɵelementStart(33,"div",20)(34,"div",21),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"tb-toggle-select",22),t.ɵɵrepeaterCreate(38,K1,3,4,"tb-toggle-option",17,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()(),t.ɵɵelementStart(40,"div",23)(41,"div",24),t.ɵɵtext(42),t.ɵɵpipe(43,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"div",25),t.ɵɵtext(45),t.ɵɵpipe(46,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(47,"tb-device-info-table",26),t.ɵɵtemplate(48,Y1,1,2,"tb-report-strategy",27)(49,Q1,30,11,"div",28)(50,t0,31,24,"div",28),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelement(51,"tb-rest-response-config",29),t.ɵɵelementEnd()(),t.ɵɵelementStart(52,"div",30)(53,"button",31),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(54),t.ɵɵpipe(55,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(56,"button",32),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(57),t.ɵɵpipe(58,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,23,"gateway.data-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.helpLink),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(14,25,"gateway.hints.rest.endpoint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,27,"gateway.rest.endpoint")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(19,29,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(n.mappingFormGroup.get("endpoint").hasError("required")&&n.mappingFormGroup.get("endpoint").touched?20:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(23,31,"gateway.hints.rest.http-methods")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(25,33,"gateway.rest.http-methods")),t.ɵɵadvance(4),t.ɵɵrepeater(n.httpMethods),t.ɵɵadvance(2),t.ɵɵconditional(n.mappingFormGroup.get("HTTPMethods").hasError("required")&&n.mappingFormGroup.get("HTTPMethods").touched?30:-1),t.ɵɵadvance(),t.ɵɵproperty("mode",n.SecurityMode.basic),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(36,35,"gateway.payload-type")),t.ɵɵadvance(3),t.ɵɵrepeater(n.converterTypes),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(43,37,"gateway.data-conversion")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(46,39,n.RestDataConversionTranslationsMap.get(n.converterType))," "),t.ɵɵadvance(2),t.ɵɵproperty("sourceTypes",n.restSourceTypes)("convertorType",n.RestConverterType.JSON)("deviceInfoType",n.DeviceInfoType.FULL)("connectorType",n.ConnectorType.REST),t.ɵɵadvance(),t.ɵɵconditional(n.data.withReportStrategy?48:-1),t.ɵɵadvance(),t.ɵɵconditional(n.converterType===n.RestConverterType.JSON?49:50),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(55,41,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.mappingFormGroup.invalid||!n.mappingFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(58,43,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(n0,[j,_,Gn,I$,BY,j1,Qn,Xn]),styles:['@charset "UTF-8";[_nghost-%COMP%] .device-info .fixed-title-width{min-width:0}'],changeDetection:d.OnPush})}}const i0=()=>["endpoint","httpMethods","securityType","actions"];function a0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",6)(1,"div",29),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"span",8),t.ɵɵelementStart(5,"button",10),t.ɵɵpipe(6,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageMapping(n))})),t.ɵɵelementStart(7,"mat-icon"),t.ɵɵtext(8,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",10),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"gateway.data-mapping")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,7,"action.search")))}function r0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30)(1,"div",31),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rest.endpoint")))}function o0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",32)(1,"div",31),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.endpoint)}}function s0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",33),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.rest.http-methods")," "))}function l0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",38),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e)}}function p0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",34)(1,"mat-chip-listbox",35),t.ɵɵrepeaterCreate(2,l0,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementStart(4,"mat-chip",36),t.ɵɵelement(5,"label",37),t.ɵɵelementEnd()()()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵproperty("tbEllipsisChipList",e.HTTPMethods),t.ɵɵadvance(),t.ɵɵrepeater(e.HTTPMethods)}}function c0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.security-type")," "))}function d0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",39)(1,"div",31),t.ɵɵtext(2),t.ɵɵpipe(3,"titlecase"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,null==e.security?null:e.security.type))}}function u0(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",40)}function m0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",10),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageMapping(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",10),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteMapping(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function h0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,m0,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",41),t.ɵɵelementContainer(4,42),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",43)(6,"button",44),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",45),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",46,2),t.ɵɵelementContainer(11,42),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(4),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function g0(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",47)}function f0(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class y0 extends qa{getDatasource(){return new v0}manageMapping(e,t){e&&e.stopPropagation();const n=ke(t),i=n?this.entityFormArray.at(t).value:{};this.getMappingDialog(i,n?"action.apply":"action.add").afterClosed().pipe(bn(this.destroyRef)).subscribe((e=>{e&&(n?this.entityFormArray.at(t).patchValue(e):this.entityFormArray.push(this.fb.control(e)),this.entityFormArray.markAsDirty())}))}deleteMapping(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-mapping-title",{name:this.entityFormArray.at(t).value.endpoint}),this.translate.instant("gateway.delete-mapping-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(bn(this.destroyRef)).subscribe((e=>{e&&(this.entityFormArray.removeAt(t),this.entityFormArray.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>Object.values(e).some((e=>e.toString().toLowerCase().includes(t.toLowerCase())||e.type?.toLowerCase().includes(t.toLowerCase())))))),this.dataSource.loadData(e)}getMappingDialog(e,t){return this.dialog.open(n0,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,withReportStrategy:this.withReportStrategy}})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(y0)))(n||y0)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:y0,selectors:[["tb-rest-mapping-table"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>y0)),multi:!0},{provide:K,useExisting:c((()=>y0)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:47,vars:37,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-absolute-fill","size-full"],[1,"flex","size-full","flex-col"],[1,"gw-table-toolbar","mat-mdc-table-toolbar"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"gw-connector-table"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","!w-1/5",4,"matHeaderCellDef"],["class","w-1/5",4,"matCellDef"],["class","w-1/3 !pl-0",4,"matHeaderCellDef"],["class","w-1/3",4,"matCellDef"],["class","w-1/6",4,"matHeaderCellDef"],["class","w-1/6",4,"matCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"!w-1/5"],["tbTruncateWithTooltip",""],[1,"w-1/5"],[1,"w-1/3","!pl-0"],[1,"w-1/3"],[1,"tb-flex",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text","http-method-label","font-medium"],["tbTruncateWithTooltip","",1,"http-method-label","font-medium"],[1,"w-1/6"],[1,"w-12"],[1,"lt-lg:!hidden","flex","min-w-24","flex-1","flex-row","items-stretch","justify-end","text-center"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,a0,13,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"button",7),t.ɵɵpipe(7,"translate"),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",8)(11,"mat-label"),t.ɵɵtext(12," "),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",9,0),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵpipe(17,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(18,"mat-icon"),t.ɵɵtext(19,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",11)(21,"table",12),t.ɵɵelementContainerStart(22,13),t.ɵɵtemplate(23,r0,4,3,"mat-header-cell",14)(24,o0,3,1,"mat-cell",15),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(25,13),t.ɵɵtemplate(26,s0,3,3,"mat-header-cell",16)(27,p0,6,1,"mat-cell",17),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(28,13),t.ɵɵtemplate(29,c0,3,3,"mat-header-cell",18)(30,d0,4,3,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(31,20),t.ɵɵtemplate(32,u0,1,0,"mat-header-cell",21)(33,h0,12,3,"mat-cell",22),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(34,g0,1,0,"mat-header-row",23)(35,f0,1,0,"mat-row",24),t.ɵɵelementEnd(),t.ɵɵelementStart(36,"section",25),t.ɵɵpipe(37,"async"),t.ɵɵelementStart(38,"button",26),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageMapping(i))})),t.ɵɵelementStart(39,"mat-icon",27),t.ɵɵtext(40,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"span"),t.ɵɵtext(42),t.ɵɵpipe(43,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(44,"span",28),t.ɵɵpipe(45,"async"),t.ɵɵtext(46," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵconditional(!1===t.ɵɵpipeBind1(4,21,n.dataSource.isEmpty())?3:-1),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,23,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,25,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,27,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","endpoint"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","httpMethods"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","securityType"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(35,i0))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(36,i0)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(37,29,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(43,31,"gateway.add-mapping")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(45,33,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(y0,[j,_,qn,Gn]),styles:[".http-method-label[_ngcontent-%COMP%]{font-size:12px}"],changeDetection:d.OnPush})}}let v0=class extends G{constructor(){super()}};function x0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" : ",e.value.value," ")}}function b0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.key-required"))}function w0(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",21),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e&&t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,"gateway.value-required"))}function S0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",16)(1,"div",17),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",18)(5,"mat-form-field",19),t.ɵɵelement(6,"input",20),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,b0,3,3,"mat-icon",21),t.ɵɵelementEnd()()(),t.ɵɵelementStart(9,"div",16)(10,"div",22),t.ɵɵtext(11,"gateway.value"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",19),t.ɵɵelement(13,"input",23),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,w0,3,3,"mat-icon",21),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,5,"gateway.key")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.get("key").hasError("required")&&e.get("key").touched?8:-1),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.get("value").hasError("required")&&e.get("value").touched?15:-1)}}function C0(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵelementContainerStart(2,11),t.ɵɵelementStart(3,"mat-expansion-panel",12)(4,"mat-expansion-panel-header")(5,"mat-panel-title")(6,"div",13),t.ɵɵtext(7),t.ɵɵelementEnd(),t.ɵɵtemplate(8,x0,2,1,"div",13),t.ɵɵelementEnd()(),t.ɵɵtemplate(9,S0,16,11,"ng-template",14),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"button",15),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.deleteKey(n,i))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.$index,a=n.$count;t.ɵɵadvance(2),t.ɵɵproperty("formGroup",e),t.ɵɵadvance(),t.ɵɵproperty("expanded",i===a-1),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",null==e.value?null:e.value.key," "),t.ɵɵadvance(),t.ɵɵconditional(null!=e.value&&e.value.value?8:-1),t.ɵɵadvance(2),t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(11,5,"gateway.rest.delete-http-header"))}}function _0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",3),t.ɵɵrepeaterCreate(1,C0,14,7,"div",9,t.ɵɵrepeaterTrackByIndex),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵrepeater(e.keysListFormArray.controls)}}function T0(e,n){1&e&&(t.ɵɵelementStart(0,"div",4)(1,"span",24),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.rest.no-http-headers")))}class I0 extends q{constructor(e,t){super(t),this.fb=e,this.store=t,this.withReportStrategy=!0,this.keysDataApplied=p(),this.ReportStrategyDefaultValue=en}ngOnInit(){this.keysListFormArray=this.prepareKeysFormArray(this.keys)}addKey(){this.keysListFormArray.push(this.fb.group({key:["",[$.required,$.pattern(an)]],value:["",[$.required,$.pattern(an)]]}))}deleteKey(e,t){e&&e.stopPropagation(),this.keysListFormArray.removeAt(t),this.keysListFormArray.markAsDirty()}cancel(){this.popover?.hide()}applyKeysData(){this.keysDataApplied.emit(this.keysListFormArray.value.reduce(((e,{key:t,value:n})=>({...e,[t]:n})),{}))}prepareKeysFormArray(e){const t=[];return Object.keys(e)?.forEach((n=>{t.push(this.fb.group({key:[n,[$.required,$.pattern(an)]],value:[e[n],[$.required,$.pattern(an)]]}))})),this.fb.array(t)}static{this.ɵfac=function(e){return new(e||I0)(t.ɵɵdirectiveInject(H.UntypedFormBuilder),t.ɵɵdirectiveInject(Ye.Store))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:I0,selectors:[["tb-rest-http-headers-panel"]],inputs:{keys:"keys",popover:"popover",withReportStrategy:[2,"withReportStrategy","withReportStrategy",m]},outputs:{keysDataApplied:"keysDataApplied"},standalone:!0,features:[t.ɵɵInputTransformsFeature,t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:18,vars:15,consts:[[1,"w-[77vw]","max-w-2xl"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-panel-title"],[1,"tb-form-panel","no-border","no-padding","h-[500px]"],[1,"tb-flex","no-flex","center","align-center","h-[500px]"],["type","button","mat-stroked-button","","color","primary",3,"click"],[1,"tb-flex","flex-end"],["mat-button","","color","primary","type","button",3,"click"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[1,"tb-form-panel","no-border","no-padding","tb-flex","no-flex","row","center","fill-width"],[1,"tb-form-panel","stroked","tb-flex"],[3,"formGroup"],[1,"tb-settings",3,"expanded"],["tbTruncateWithTooltip","",1,"max-w-[11vw]"],["matExpansionPanelContent",""],["type","button","mat-icon-button","","matTooltipPosition","above",1,"gw-delete-icon-button",3,"click","matTooltip"],[1,"tb-form-row"],[1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","name","value","formControlName","key",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","type","text","name","value","formControlName","value",3,"placeholder"],[1,"tb-prompt"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(5,_0,3,0,"div",3)(6,T0,4,3,"div",4),t.ɵɵelementStart(7,"div")(8,"button",5),t.ɵɵlistener("click",(function(){return n.addKey()})),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",6)(12,"button",7),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"button",8),t.ɵɵlistener("click",(function(){return n.applyKeysData()})),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate2("",t.ɵɵpipeBind1(4,7,"gateway.rest.http-headers"),""," ("+n.keysListFormArray.controls.length+")"," "),t.ɵɵadvance(2),t.ɵɵconditional(n.keysListFormArray.controls.length?5:6),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(10,9,"gateway.rest.add-http-header")," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,11,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.keysListFormArray.invalid||!n.keysListFormArray.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,13,"action.apply")," "))},dependencies:t.ɵɵgetComponentDepsFactory(I0,[j,_]),encapsulation:2,changeDetection:d.OnPush})}}const E0=()=>({min:1}),M0=()=>({maxWidth:"970px"});function k0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,i.RestRequestTypesTranslationsMap.get(e)))}}function P0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.endpoint-required"))}function D0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.endpoint-pattern"))}function O0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function A0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.http-methods-required"))}function F0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"titlecase"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,2,e))}}function R0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.rest.endpoint"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",21),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,P0,2,3,"tb-error-icon",22)(8,D0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",9)(10,"div",19),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"gateway.rest.http-methods"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",20)(14,"mat-select",23),t.ɵɵrepeaterCreate(15,O0,2,2,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd(),t.ɵɵtemplate(17,A0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",9)(19,"div",24),t.ɵɵpipe(20,"translate"),t.ɵɵtext(21,"gateway.type"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",20)(23,"mat-select",25),t.ɵɵrepeaterCreate(24,F0,3,4,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,6,"gateway.hints.rest.endpoint")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,8,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("endpoint").hasError("required")&&e.mappingFormGroup.get("endpoint").touched?7:e.mappingFormGroup.get("endpoint").hasError("pattern")&&e.mappingFormGroup.get("endpoint").touched?8:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,10,"gateway.hints.rest.http-methods")),t.ɵɵadvance(5),t.ɵɵrepeater(e.httpMethods),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("HTTPMethods").hasError("required")&&e.mappingFormGroup.get("HTTPMethods").touched?17:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(20,12,"gateway.hints.rest.scope-type")),t.ɵɵadvance(5),t.ɵɵrepeater(e.requestsScopeType)}}function B0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-required"))}function N0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-pattern"))}function L0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.device-name-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",26),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,B0,2,3,"tb-error-icon",22)(8,N0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.device-name-filter-hint")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("deviceNameFilter").hasError("required")&&e.mappingFormGroup.get("deviceNameFilter").touched?7:e.mappingFormGroup.get("deviceNameFilter").hasError("pattern")&&e.mappingFormGroup.get("deviceNameFilter").touched?8:-1)}}function V0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.method-filter-required"))}function q0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.method-filter-pattern"))}function G0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.method-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",27),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,V0,2,3,"tb-error-icon",22)(8,q0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.rest.method-filter")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("methodFilter").hasError("required")&&e.mappingFormGroup.get("methodFilter").touched?7:e.mappingFormGroup.get("methodFilter").hasError("pattern")&&e.mappingFormGroup.get("methodFilter").touched?8:-1)}}function z0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-filter-required"))}function U0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-filter-pattern"))}function j0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.attribute-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",28),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,z0,2,3,"tb-error-icon",22)(8,U0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,3,"gateway.hints.rest.attribute-filter")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,5,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("attributeFilter").hasError("required")&&e.mappingFormGroup.get("attributeFilter").touched?7:e.mappingFormGroup.get("attributeFilter").hasError("pattern")&&e.mappingFormGroup.get("attributeFilter").touched?8:-1)}}function H0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.request-url-expression-required"))}function W0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.request-url-expression-pattern"))}function $0(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function K0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.value-expression-required"))}function Y0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.value-expression-pattern"))}function X0(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",29)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.rest.request-url-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",30),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,H0,2,3,"tb-error-icon",22)(8,W0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",9)(10,"div",19),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"gateway.rest.http-method"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",20)(14,"mat-select",31),t.ɵɵrepeaterCreate(15,$0,2,2,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div",9)(18,"div",19),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"gateway.value-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",20),t.ɵɵelement(22,"input",32),t.ɵɵpipe(23,"translate"),t.ɵɵtemplate(24,K0,2,3,"tb-error-icon",22)(25,Y0,2,3,"tb-error-icon",22),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,7,"gateway.hints.rest.attribute-url-expression")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("requestUrlExpression").hasError("required")&&e.mappingFormGroup.get("requestUrlExpression").touched?7:e.mappingFormGroup.get("requestUrlExpression").hasError("pattern")&&e.mappingFormGroup.get("requestUrlExpression").touched?8:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,11,"gateway.hints.rest.http-method")),t.ɵɵadvance(5),t.ɵɵrepeater(e.httpMethods),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(19,13,"gateway.hints.rest.value-expression")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(23,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("valueExpression").hasError("required")&&e.mappingFormGroup.get("valueExpression").touched?24:e.mappingFormGroup.get("valueExpression").hasError("pattern")&&e.mappingFormGroup.get("valueExpression").touched?25:-1)}}function Z0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,E0)))}function Q0(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function J0(e,n){1&e&&(t.ɵɵelementStart(0,"span",34),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function e2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵtext(2,"gateway.response-timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",20),t.ɵɵelement(4,"input",33),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,Z0,2,5,"tb-error-icon",22)(7,Q0,2,3,"tb-error-icon",22)(8,J0,2,0,"span",34),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,2,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("responseTimeout").hasError("min")&&e.mappingFormGroup.get("responseTimeout").touched?6:e.mappingFormGroup.get("responseTimeout").hasError("pattern")&&e.mappingFormGroup.get("responseTimeout").touched?7:8)}}function t2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-required"))}function n2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.device-name-filter-pattern"))}function i2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-name-expression-required"))}function a2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.attribute-name-expression-pattern"))}function r2(e,n){1&e&&t.ɵɵelement(0,"div",38),2&e&&t.ɵɵproperty("tb-help-popup","widget/lib/gateway/rest-json_fn")("tb-help-popup-style",t.ɵɵpureFunction0(2,M0))}function o2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",19),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"gateway.device-name-filter"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",20),t.ɵɵelement(5,"input",35),t.ɵɵpipe(6,"translate"),t.ɵɵtemplate(7,t2,2,3,"tb-error-icon",22)(8,n2,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",9)(10,"div",36),t.ɵɵtext(11,"gateway.attribute-name-expression"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",20),t.ɵɵelement(13,"input",37),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,i2,2,3,"tb-error-icon",22)(16,a2,2,3,"tb-error-icon",22)(17,r2,1,3,"div",38),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,5,"gateway.device-name-filter-hint")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,7,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("deviceNameExpression").hasError("required")&&e.mappingFormGroup.get("deviceNameExpression").touched?7:e.mappingFormGroup.get("deviceNameExpression").hasError("pattern")&&e.mappingFormGroup.get("deviceNameExpression").touched?8:-1),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("attributeNameExpression").hasError("required")&&e.mappingFormGroup.get("attributeNameExpression").touched?15:e.mappingFormGroup.get("attributeNameExpression").hasError("pattern")&&e.mappingFormGroup.get("attributeNameExpression").touched?16:17)}}function s2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,E0)))}function l2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function p2(e,n){1&e&&(t.ɵɵelementStart(0,"span",34),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function c2(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",10),t.ɵɵtext(2,"gateway.timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",20),t.ɵɵelement(4,"input",39),t.ɵɵpipe(5,"translate"),t.ɵɵtemplate(6,s2,2,5,"tb-error-icon",22)(7,l2,2,3,"tb-error-icon",22)(8,p2,2,0,"span",34),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(5,2,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("timeout").hasError("min")&&e.mappingFormGroup.get("timeout").touched?6:e.mappingFormGroup.get("timeout").hasError("pattern")&&e.mappingFormGroup.get("timeout").touched?7:8)}}function d2(e,n){1&e&&(t.ɵɵelementStart(0,"div",14)(1,"mat-slide-toggle",40)(2,"mat-label",41),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.hints.rest.update-ssl")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.rest.ssl-verify")," "))}function u2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip")(1,"div",53),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵadvance(2),t.ɵɵtextInterpolate(e.value)}}function m2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind2(1,1,"gateway.response-timeout-limits-error",t.ɵɵpureFunction0(4,E0)))}function h2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.timeout-pattern"))}function g2(e,n){1&e&&(t.ɵɵelementStart(0,"span",34),t.ɵɵtext(1,"gateway.suffix.s"),t.ɵɵelementEnd())}function f2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.tries-min"))}function y2(e,n){1&e&&(t.ɵɵelement(0,"tb-error-icon",22),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("tooltipText",t.ɵɵpipeBind1(1,1,"gateway.hints.rest.tries-pattern"))}function v2(e,n){1&e&&(t.ɵɵelementStart(0,"div",14)(1,"mat-slide-toggle",54)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,1,"gateway.rest.allow-redirects")," "))}function x2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",8)(1,"mat-expansion-panel",42)(2,"mat-expansion-panel-header")(3,"mat-panel-title",43)(4,"div",44),t.ɵɵtext(5,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(6,"div",45)(7,"div",10),t.ɵɵtext(8,"gateway.rest.http-headers"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"div",46)(10,"mat-chip-listbox",47),t.ɵɵpipe(11,"keyvalue"),t.ɵɵrepeaterCreate(12,u2,3,1,"mat-chip",null,t.ɵɵrepeaterTrackByIdentity),t.ɵɵpipe(14,"keyvalue"),t.ɵɵelementStart(15,"mat-chip",48),t.ɵɵelement(16,"label",49),t.ɵɵelementEnd()(),t.ɵɵelementStart(17,"button",50,0),t.ɵɵpipe(19,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵreference(18),a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageKeys(n,i))})),t.ɵɵelementStart(20,"tb-icon",51),t.ɵɵtext(21,"edit"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(22,"div",9)(23,"div",10),t.ɵɵtext(24,"gateway.timeout"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",20),t.ɵɵelement(26,"input",39),t.ɵɵpipe(27,"translate"),t.ɵɵtemplate(28,m2,2,5,"tb-error-icon",22)(29,h2,2,3,"tb-error-icon",22)(30,g2,2,0,"span",34),t.ɵɵelementEnd()(),t.ɵɵelementStart(31,"div",9)(32,"div",10),t.ɵɵtext(33,"gateway.rest.tries"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"mat-form-field",20),t.ɵɵelement(35,"input",52),t.ɵɵpipe(36,"translate"),t.ɵɵtemplate(37,f2,2,3,"tb-error-icon",22)(38,y2,2,3,"tb-error-icon",22),t.ɵɵelementEnd()(),t.ɵɵtemplate(39,v2,5,3,"div",14),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(10),t.ɵɵproperty("tbEllipsisChipList",t.ɵɵpipeBind1(11,7,e.mappingFormGroup.get("httpHeaders").value)),t.ɵɵadvance(2),t.ɵɵrepeater(t.ɵɵpipeBind1(14,9,e.mappingFormGroup.get("httpHeaders").value)),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,11,"action.edit")),t.ɵɵadvance(9),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(27,13,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("timeout").hasError("min")&&e.mappingFormGroup.get("timeout").touched?28:e.mappingFormGroup.get("timeout").hasError("pattern")&&e.mappingFormGroup.get("timeout").touched?29:30),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(36,15,"gateway.set")),t.ɵɵadvance(2),t.ɵɵconditional(e.mappingFormGroup.get("tries").hasError("min")&&e.mappingFormGroup.get("tries").touched?37:e.mappingFormGroup.get("tries").hasError("pattern")&&e.mappingFormGroup.get("tries").touched?38:-1),t.ɵɵadvance(2),t.ɵɵconditional(e.requestType===e.RestRequestType.ATTRIBUTE_UPDATE?39:-1)}}class b2 extends A{constructor(e,t,n,i,a,r,o,s,l,p){super(e,t,i),this.store=e,this.router=t,this.data=n,this.dialogRef=i,this.fb=a,this.popoverService=r,this.renderer=o,this.viewContainerRef=s,this.cdr=l,this.destroyRef=p,this.requestTypes=Object.values(bi),this.requestsScopeType=Object.values(Si),this.httpMethods=Object.values(ht),this.helpLink=`${O}/docs/iot-gateway/config/rest/#attribute-request-section`,this.RestRequestTypesTranslationsMap=wi,this.SecurityMode=$i,this.RestRequestType=bi,this.mappingFormGroup=this.fb.group({requestType:[bi.ATTRIBUTE_REQUEST],endpoint:["",[$.required,$.pattern(an)]],HTTPMethods:[[ht.POST],[$.required]],HTTPMethod:[ht.POST,[$.required]],type:[Si.Shared],security:[{type:ki.ANONYMOUS}],timeout:[null,[$.min(.001),$.pattern(nn)]],deviceNameExpression:["",[$.required,$.pattern(an)]],attributeNameExpression:["",[$.required,$.pattern(an)]],SSLVerify:[!1],deviceNameFilter:["",[$.required,$.pattern(an)]],methodFilter:["",[$.required,$.pattern(an)]],attributeFilter:["",[$.required,$.pattern(an)]],requestUrlExpression:[this.data.defaultRequestUrl,[$.required,$.pattern(an)]],valueExpression:["",[$.required,$.pattern(an)]],responseTimeout:[null,[$.min(.001),$.pattern(nn)]],tries:[null,[$.min(1),$.pattern(nn)]],allowRedirects:[!1],httpHeaders:[{}]}),this.keysPopupClosed=!0,this.mappingFormGroup.patchValue(this.data.value,{emitEvent:!1}),this.observeRequestTypeChange(),this.toggleFieldsByRequestType(this.mappingFormGroup.get("requestType").value)}get requestType(){return this.mappingFormGroup.get("requestType").value}cancel(){this.keysPopupClosed&&this.dialogRef.close(null)}add(){if(this.mappingFormGroup.valid){const{requestType:e,...t}=this.mappingFormGroup.value;Te(t),this.dialogRef.close({requestType:e,requestValue:t})}}manageKeys(e,t){e.stopPropagation();const n=t._elementRef.nativeElement;if(this.popoverService.hasPopover(n))return void this.popoverService.hidePopover(n);this.popoverService.hasPopover(n)&&this.popoverService.hidePopover(n),this.keysPopupClosed=!1;const i=this.popoverService.displayPopover(n,this.renderer,this.viewContainerRef,I0,"leftBottom",!1,null,{keys:this.mappingFormGroup.get("httpHeaders").value,withReportStrategy:this.data.withReportStrategy},{},{},{},!0);i.tbComponentRef.instance.popover=i,i.tbComponentRef.instance.keysDataApplied.subscribe((e=>{i.hide(),this.mappingFormGroup.get("httpHeaders").patchValue(e),this.mappingFormGroup.get("httpHeaders").markAsDirty(),this.cdr.markForCheck()})),i.tbHideStart.pipe(bn(this.destroyRef)).subscribe((()=>{this.keysPopupClosed=!0}))}observeRequestTypeChange(){this.mappingFormGroup.get("requestType").valueChanges.pipe(ce(),bn(this.destroyRef)).subscribe((e=>this.toggleFieldsByRequestType(e)))}toggleFieldsByRequestType(e){this.mappingFormGroup.disable({emitEvent:!1}),Ci.get("all").forEach((e=>this.mappingFormGroup.get(e).enable({emitEvent:!1}))),Ci.get(e).forEach((e=>this.mappingFormGroup.get(e).enable({emitEvent:!1})))}static{this.ɵfac=function(e){return new(e||b2)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(Xe.Router),t.ɵɵdirectiveInject(Ve),t.ɵɵdirectiveInject(qe.MatDialogRef),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(at.TbPopoverService),t.ɵɵdirectiveInject(t.Renderer2),t.ɵɵdirectiveInject(t.ViewContainerRef),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:b2,selectors:[["tb-rest-requests-mapping-dialog"]],standalone:!0,features:[t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:37,vars:23,consts:[["button",""],[1,"h-full","w-[77vw]","max-w-3xl",3,"formGroup"],["color","primary"],[1,"flex-1"],[3,"tb-help"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["mat-dialog-content",""],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-row"],["translate","",1,"fixed-title-width"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex"],["formControlName","requestType"],[3,"value"],[1,"tb-form-row","tb-flex","fill-width"],["formControlName","security",3,"mode"],["mat-dialog-actions","",1,"justify-end"],["mat-button","","color","primary","type","button","cdkFocusInitial","",3,"click"],["mat-raised-button","","color","primary",3,"click","disabled"],["translate","",1,"fixed-title-width","tb-required",3,"tb-hint-tooltip-icon"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","type","text","name","value","formControlName","endpoint",3,"placeholder"],["matSuffix","",3,"tooltipText"],["formControlName","HTTPMethods","multiple",""],["translate","",1,"fixed-title-width",3,"tb-hint-tooltip-icon"],["formControlName","type"],["matInput","","type","text","name","value","formControlName","deviceNameFilter",3,"placeholder"],["matInput","","type","text","name","value","formControlName","methodFilter",3,"placeholder"],["matInput","","type","text","name","value","formControlName","attributeFilter",3,"placeholder"],[1,"tb-form-row","request-url-row"],["matInput","","type","text","name","value","formControlName","requestUrlExpression",3,"placeholder"],["formControlName","HTTPMethod"],["matInput","","type","text","name","value","formControlName","valueExpression",3,"placeholder"],["matInput","","type","number","min","0","name","value","formControlName","responseTimeout",3,"placeholder"],["translate","","matSuffix","",1,"block","pr-2"],["matInput","","type","text","name","value","formControlName","deviceNameExpression",3,"placeholder"],["translate","",1,"fixed-title-width","tb-required"],["matInput","","type","text","name","value","formControlName","attributeNameExpression",3,"placeholder"],["matSuffix","","tb-help-popup-placement","left",1,"see-example","p-1",3,"tb-help-popup","tb-help-popup-style"],["matInput","","type","number","min","0","name","value","formControlName","timeout",3,"placeholder"],["formControlName","SSLVerify",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],[1,"tb-settings"],[1,"justify-end"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","space-between","tb-flex"],[1,"tb-flex","gw-chip-list"],[1,"tb-flex",3,"tbEllipsisChipList"],[1,"mat-mdc-chip","ellipsis-chip"],[1,"ellipsis-text"],["type","button","mat-icon-button","","color","primary","matTooltipPosition","above",3,"click","matTooltip"],["matButtonIcon",""],["matInput","","type","number","min","0","name","value","formControlName","tries",3,"placeholder"],["tbTruncateWithTooltip",""],["formControlName","allowRedirects",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",1)(1,"mat-toolbar",2)(2,"h2"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"span",3)(6,"div",4),t.ɵɵelementStart(7,"button",5),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵelementStart(8,"mat-icon",6),t.ɵɵtext(9,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(10,"div",7)(11,"div",8)(12,"div",9)(13,"div",10),t.ɵɵtext(14,"gateway.request-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-form-field",11)(16,"mat-select",12),t.ɵɵrepeaterCreate(17,k0,3,4,"mat-option",13,t.ɵɵrepeaterTrackByIdentity),t.ɵɵelementEnd()()(),t.ɵɵtemplate(19,R0,26,14)(20,L0,9,7,"div",9)(21,G0,9,7,"div",9)(22,j0,9,7,"div",9)(23,X0,26,17)(24,e2,9,4,"div",9)(25,o2,18,11)(26,c2,9,4,"div",9)(27,d2,6,6,"div",14),t.ɵɵelement(28,"tb-security-config",15),t.ɵɵtemplate(29,x2,40,17,"div",8),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"div",16)(31,"button",17),t.ɵɵlistener("click",(function(){return n.cancel()})),t.ɵɵtext(32),t.ɵɵpipe(33,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"button",18),t.ɵɵlistener("click",(function(){return n.add()})),t.ɵɵtext(35),t.ɵɵpipe(36,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.mappingFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,17,"gateway.requests-mapping")),t.ɵɵadvance(3),t.ɵɵproperty("tb-help",n.helpLink),t.ɵɵadvance(11),t.ɵɵrepeater(n.requestTypes),t.ɵɵadvance(2),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_REQUEST?19:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType!==n.RestRequestType.ATTRIBUTE_REQUEST?20:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.SERVER_SIDE_RPC?21:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_UPDATE?22:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType!==n.RestRequestType.ATTRIBUTE_REQUEST?23:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.SERVER_SIDE_RPC?24:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_REQUEST?25:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_REQUEST?26:-1),t.ɵɵadvance(),t.ɵɵconditional(n.requestType===n.RestRequestType.ATTRIBUTE_UPDATE?27:-1),t.ɵɵadvance(),t.ɵɵproperty("mode",n.SecurityMode.basic),t.ɵɵadvance(),t.ɵɵconditional(n.requestType!==n.RestRequestType.ATTRIBUTE_REQUEST?29:-1),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(33,19,"action.cancel")," "),t.ɵɵadvance(2),t.ɵɵproperty("disabled",n.mappingFormGroup.invalid||!n.mappingFormGroup.dirty||!n.keysPopupClosed),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(36,21,n.data.buttonTitle)," "))},dependencies:t.ɵɵgetComponentDepsFactory(b2,[j,_,Gn,BY,Qn]),styles:["[_nghost-%COMP%] .tb-form-row.request-url-row[_ngcontent-%COMP%]{gap:6px}"],changeDetection:d.OnPush})}}const w2=()=>["type","details","actions"],S2=()=>({});function C2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",6)(1,"div",26),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"span",8),t.ɵɵelementStart(5,"button",10),t.ɵɵpipe(6,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.manageMapping(n))})),t.ɵɵelementStart(7,"mat-icon"),t.ɵɵtext(8,"add"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",10),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.enterFilterMode())})),t.ɵɵelementStart(11,"mat-icon"),t.ɵɵtext(12,"search"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"gateway.requests-mapping")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,5,"action.add")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,7,"action.search")))}function _2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",27)(1,"div",28),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,"gateway.type")))}function T2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",29)(1,"div",28),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,i.RestRequestTypesTranslationsMap.get(e.requestType)))}}function I2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",30),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.details")," "))}function E2(e,n){if(1&e&&t.ɵɵtext(0),2&e){let e;const n=t.ɵɵnextContext().$implicit,i=t.ɵɵnextContext();t.ɵɵtextInterpolate1(" ",i.Object.values(null!==(e=n.requestValue.httpHeaders)&&void 0!==e?e:t.ɵɵpureFunction0(1,S2)).join(", ")," ")}}function M2(e,n){if(1&e&&t.ɵɵtext(0),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵtextInterpolate1(" ",e.requestValue.methodFilter," ")}}function k2(e,n){if(1&e&&t.ɵɵtext(0),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵtextInterpolate1(" ",e.requestValue.endpoint," ")}}function P2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",29)(1,"div",28),t.ɵɵtemplate(2,E2,1,2)(3,M2,1,1)(4,k2,1,1),t.ɵɵelementEnd()()),2&e){let e;const i=n.$implicit;t.ɵɵadvance(2),t.ɵɵconditional("attributeUpdates"===(e=i.requestType)?2:"serverSideRpc"===e?3:4)}}function D2(e,n){1&e&&t.ɵɵelement(0,"mat-header-cell",31)}function O2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",10),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.manageMapping(n,i))})),t.ɵɵelementStart(2,"tb-icon"),t.ɵɵtext(3,"edit"),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"button",10),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext().index,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteMapping(n,i))})),t.ɵɵelementStart(6,"tb-icon"),t.ɵɵtext(7,"delete"),t.ɵɵelementEnd()()}2&e&&(t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.edit")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,4,"action.delete")))}function A2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtemplate(1,O2,8,6,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(3,"div",32),t.ɵɵelementContainer(4,33),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",34)(6,"button",35),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(7,"mat-icon",36),t.ɵɵtext(8,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-menu",37,2),t.ɵɵelementContainer(11,33),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(2),n=t.ɵɵreference(10);t.ɵɵadvance(4),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(2),t.ɵɵproperty("matMenuTriggerFor",n),t.ɵɵadvance(5),t.ɵɵproperty("ngTemplateOutlet",e)}}function F2(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",38)}function R2(e,n){1&e&&t.ɵɵelement(0,"mat-row")}class B2 extends qa{constructor(){super(...arguments),this.Object=Object,this.RestRequestTypesTranslationsMap=wi}getDatasource(){return new N2}manageMapping(e,t){e&&e.stopPropagation();const n=ke(t),{requestType:i=bi.ATTRIBUTE_REQUEST,requestValue:a={}}=n?this.entityFormArray.at(t).value:{};this.getMappingDialog({requestType:i,...a},n?"action.apply":"action.add").afterClosed().pipe(bn(this.destroyRef)).subscribe((e=>{e&&(n?this.entityFormArray.at(t).patchValue(e):this.entityFormArray.push(this.fb.control(e)),this.entityFormArray.markAsDirty())}))}deleteMapping(e,t){e&&e.stopPropagation(),this.dialogService.confirm(this.translate.instant("gateway.delete-mapping-title",{name:this.getRequestDetails(this.entityFormArray.at(t).value)}),this.translate.instant("gateway.delete-mapping-description"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0).pipe(bn(this.destroyRef)).subscribe((e=>{e&&(this.entityFormArray.removeAt(t),this.entityFormArray.markAsDirty())}))}updateTableData(e,t){t&&(e=e.filter((e=>{const n=this.getRequestDetails(e);return Object.values(e).some((e=>Me(e)&&this.translate.instant(wi.get(e)).toLowerCase().includes(t.toLowerCase())||n?.toLowerCase().includes(t.toLowerCase())))}))),this.dataSource.loadData(e)}getRequestDetails(e){let t;switch(e.requestType){case"attributeUpdates":t=Object.values(e.requestValue.httpHeaders).join(", ");break;case"serverSideRpc":t=e.requestValue.methodFilter;break;default:t=e.requestValue.endpoint}return t}validate(){return null}getMappingDialog(e,t){return this.dialog.open(b2,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{value:e,buttonTitle:t,defaultRequestUrl:this.defaultRequestUrl,withReportStrategy:this.withReportStrategy}})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(B2)))(n||B2)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:B2,selectors:[["tb-rest-request-mapping-table"]],inputs:{defaultRequestUrl:"defaultRequestUrl"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>B2)),multi:!0},{provide:K,useExisting:c((()=>B2)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:44,vars:36,consts:[["searchInput",""],["rowActions",""],["cellActionsMenu","matMenu"],[1,"tb-absolute-fill","size-full"],[1,"flex","size-full","flex-col"],[1,"gw-table-toolbar","mat-mdc-table-toolbar"],[1,"mat-toolbar-tools"],["mat-icon-button","","matTooltipPosition","above",3,"matTooltip"],[1,"flex-1"],["matInput","",3,"formControl","placeholder"],["mat-icon-button","","matTooltipPosition","above",3,"click","matTooltip"],[1,"gw-connector-table"],["mat-table","",3,"dataSource"],[3,"matColumnDef"],["class","!w-1/3",4,"matHeaderCellDef"],["class","w-1/3",4,"matCellDef"],["class","w-1/3 !pl-0",4,"matHeaderCellDef"],["matColumnDef","actions","stickyEnd",""],["class","w-12",4,"matHeaderCellDef"],[4,"matCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],[4,"matRowDef","matRowDefColumns"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],["translate","",1,"no-data-found","items-center","justify-center"],[1,"title-container"],[1,"!w-1/3"],["tbTruncateWithTooltip",""],[1,"w-1/3"],[1,"w-1/3","!pl-0"],[1,"w-12"],[1,"lt-lg:!hidden","flex","min-w-24","flex-1","flex-row","items-stretch","justify-end","text-center"],[3,"ngTemplateOutlet"],[1,"gt-md:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",3)(1,"div",4)(2,"mat-toolbar",5),t.ɵɵtemplate(3,C2,13,9,"div",6),t.ɵɵpipe(4,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-toolbar",5)(6,"button",7),t.ɵɵpipe(7,"translate"),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"search"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",8)(11,"mat-label"),t.ɵɵtext(12," "),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",9,0),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"button",10),t.ɵɵpipe(17,"translate"),t.ɵɵlistener("click",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.exitFilterMode())})),t.ɵɵelementStart(18,"mat-icon"),t.ɵɵtext(19,"close"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",11)(21,"table",12),t.ɵɵelementContainerStart(22,13),t.ɵɵtemplate(23,_2,4,3,"mat-header-cell",14)(24,T2,4,3,"mat-cell",15),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(25,13),t.ɵɵtemplate(26,I2,3,3,"mat-header-cell",16)(27,P2,5,1,"mat-cell",15),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(28,17),t.ɵɵtemplate(29,D2,1,0,"mat-header-cell",18)(30,A2,12,3,"mat-cell",19),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(31,F2,1,0,"mat-header-row",20)(32,R2,1,0,"mat-row",21),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"section",22),t.ɵɵpipe(34,"async"),t.ɵɵelementStart(35,"button",23),t.ɵɵlistener("click",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.manageMapping(i))})),t.ɵɵelementStart(36,"mat-icon",24),t.ɵɵtext(37,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"span"),t.ɵɵtext(39),t.ɵɵpipe(40,"translate"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(41,"span",25),t.ɵɵpipe(42,"async"),t.ɵɵtext(43," widget.no-data-found "),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.textSearchMode),t.ɵɵadvance(),t.ɵɵconditional(!1===t.ɵɵpipeBind1(4,20,n.dataSource.isEmpty())?3:-1),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,22,"action.search")),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(15,24,"common.enter-search")),t.ɵɵproperty("formControl",n.textSearch),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,26,"action.close")),t.ɵɵadvance(5),t.ɵɵproperty("dataSource",n.dataSource),t.ɵɵadvance(),t.ɵɵproperty("matColumnDef","type"),t.ɵɵadvance(3),t.ɵɵproperty("matColumnDef","details"),t.ɵɵadvance(6),t.ɵɵproperty("matHeaderRowDef",t.ɵɵpureFunction0(34,w2))("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",t.ɵɵpureFunction0(35,w2)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.textSearchMode||!1===t.ɵɵpipeBind1(34,28,n.dataSource.isEmpty())),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(40,30,"gateway.add-mapping")),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",!n.textSearchMode||!1===t.ɵɵpipeBind1(42,32,n.dataSource.isEmpty())))},dependencies:t.ɵɵgetComponentDepsFactory(B2,[j,_,qn]),encapsulation:2,changeDetection:d.OnPush})}}class N2 extends G{constructor(){super()}}class L2 extends Ba{initBasicFormGroup(){return this.fb.group({server:[],mapping:[],requestsMapping:[]})}getRequestDataArray(e){const t=[];return Fe(e)&&Object.keys(e).forEach((n=>{for(const i of e[n])t.push({requestType:n,requestValue:i})})),t}getRequestDataObject(e){return e.reduce(((e,{requestType:t,requestValue:n})=>(e[t]?.push(n),e)),{attributeRequests:[],attributeUpdates:[],serverSideRpc:[]})}updateDefaultUrl({host:e,port:t,SSL:n}){this.defaultRequestUrl=e&&t?`${n?"https":"http"}//${e}:${t}/`:document.location.origin}writeValue(e){this.basicFormGroup.setValue(this.mapConfigToFormValue(e),{emitEvent:!1})}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(L2)))(n||L2)}})()}static{this.ɵdir=t.ɵɵdefineDirective({type:L2,features:[t.ɵɵInheritDefinitionFeature]})}}class V2 extends L2{mapConfigToFormValue(e){return{server:e.server??{},mapping:e.mapping??[],requestsMapping:this.getRequestDataArray(e.requestsMapping)}}getMappedValue(e){return this.updateDefaultUrl(e?.server??{}),{server:e.server??{},mapping:e.mapping??[],requestsMapping:this.getRequestDataObject(e.requestsMapping??[])}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(V2)))(n||V2)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:V2,selectors:[["tb-rest-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>V2)),multi:!0},{provide:K,useExisting:c((()=>V2)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:13,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server"],["formControlName","mapping"],["formControlName","requestsMapping",3,"defaultRequestUrl"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-rest-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-rest-mapping-table",4),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-tab",1),t.ɵɵpipe(11,"translate"),t.ɵɵelement(12,"tb-rest-request-mapping-table",5),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.server"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(11,15,"gateway.requests-mapping")),t.ɵɵadvance(2),t.ɵɵproperty("defaultRequestUrl",n.defaultRequestUrl))},dependencies:t.ɵɵgetComponentDepsFactory(V2,[j,_,D1,y0,B2]),encapsulation:2,changeDetection:d.OnPush})}}class q2 extends L2{mapConfigToFormValue({attributeRequests:e=[],attributeUpdates:t=[],serverSideRpc:n=[],mapping:i=[],...a}){return{server:{...a??{}},mapping:$a.mapMappingToUpgradedVersion(i),requestsMapping:this.getRequestDataArray({attributeRequests:e,attributeUpdates:t,serverSideRpc:n})}}getMappedValue({requestsMapping:e,mapping:t=[],server:n={}}){this.updateDefaultUrl(n);const{attributeRequests:i=[],attributeUpdates:a=[],serverSideRpc:r=[]}=this.getRequestDataObject(e);return{...n,mapping:$a.mapMappingToDowngradedVersion(t),attributeRequests:i,attributeUpdates:a,serverSideRpc:r}}static{this.ɵfac=(()=>{let e;return function(n){return(e||(e=t.ɵɵgetInheritedFactory(q2)))(n||q2)}})()}static{this.ɵcmp=t.ɵɵdefineComponent({type:q2,selectors:[["tb-rest-legacy-basic-config"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>q2)),multi:!0},{provide:K,useExisting:c((()=>q2)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:13,vars:17,consts:[[3,"formGroup"],[3,"label"],[3,"ngTemplateOutlet"],["formControlName","server"],["formControlName","mapping"],["formControlName","requestsMapping",3,"defaultRequestUrl"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"mat-tab-group",0)(1,"mat-tab",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,2),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",1),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-rest-server-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-tab",1),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-rest-mapping-table",4),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-tab",1),t.ɵɵpipe(11,"translate"),t.ɵɵelement(12,"tb-rest-request-mapping-table",5),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,9,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",n.generalTabContent),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,11,"gateway.server"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(8,13,"gateway.data-mapping"),"*"),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(11,15,"gateway.requests-mapping")),t.ɵɵadvance(2),t.ɵɵproperty("defaultRequestUrl",n.defaultRequestUrl))},dependencies:t.ɵɵgetComponentDepsFactory(q2,[j,_,D1,y0,B2]),encapsulation:2,changeDetection:d.OnPush})}}const G2=(e,t)=>({hasErrors:e,noErrors:t}),z2=()=>({minWidth:"144px",maxWidth:"144px",textAlign:"center"}),U2=()=>({minWidth:"144px",maxWidth:"144px",width:"144px",textAlign:"center"}),j2=e=>({"tb-current-entity":e});function H2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",32),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"async"),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onAddConnector(n))})),t.ɵɵelementStart(3,"mat-icon"),t.ɵɵtext(4,"add"),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"action.add")),t.ɵɵproperty("disabled",t.ɵɵpipeBind1(2,4,e.isLoading$))}}function W2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",33)(1,"button",34),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onAddConnector(n))})),t.ɵɵelementStart(2,"mat-icon",35),t.ɵɵtext(3,"add"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"span"),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()}2&e&&(t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,1,"gateway.add-connector")))}function $2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",36),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-enabled")," "))}function K2(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell")(1,"mat-slide-toggle",37),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return n.stopPropagation(),t.ɵɵresetView(a.onEnableConnector(i))})),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("checked",i.activeConnectors.includes(e.key))}}function Y2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",38),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-name"),""))}function X2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.key," ")}}function Z2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-type")," "))}function Q2(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",40),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.returnType(e)," ")}}function J2(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.configuration")," "))}function e3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-cell",40)(1,"div",41),t.ɵɵtext(2),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassMap(i.isConnectorSynced(e)?"status-sync":"status-unsync"),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",i.isConnectorSynced(e)?"sync":"out of sync"," ")}}function t3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell",39),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.connectors-table-status")," "))}function n3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell",40)(1,"span",42),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorLogs(i,n))})),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassMap(t.ɵɵpureFunction2(3,G2,+i.getErrorsCount(e)>0,0==+i.getErrorsCount(e)||""===i.getErrorsCount(e))),t.ɵɵpropertyInterpolate("matTooltip","Errors: "+i.getErrorsCount(e))}}function i3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-header-cell"),t.ɵɵelement(1,"div",43),t.ɵɵelementStart(2,"div",44),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,z2)),t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"gateway.connectors-table-actions")))}function a3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-cell")(1,"div",45)(2,"button",46),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorRpc(i,n))})),t.ɵɵelementStart(3,"mat-icon"),t.ɵɵtext(4,"private_connectivity"),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"button",47),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorLogs(i,n))})),t.ɵɵelementStart(6,"mat-icon"),t.ɵɵtext(7,"list"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"button",48),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteConnector(i,n))})),t.ɵɵelementStart(9,"mat-icon"),t.ɵɵtext(10,"delete"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(11,"div",49)(12,"button",50),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵelementStart(13,"mat-icon",51),t.ɵɵtext(14,"more_vert"),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"mat-menu",52,1)(17,"button",46),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorRpc(i,n))})),t.ɵɵelementStart(18,"mat-icon"),t.ɵɵtext(19,"private_connectivity"),t.ɵɵelementEnd()(),t.ɵɵelementStart(20,"button",47),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.connectorLogs(i,n))})),t.ɵɵelementStart(21,"mat-icon"),t.ɵɵtext(22,"list"),t.ɵɵelementEnd()(),t.ɵɵelementStart(23,"button",48),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.deleteConnector(i,n))})),t.ɵɵelementStart(24,"mat-icon"),t.ɵɵtext(25,"delete"),t.ɵɵelementEnd()()()()()}if(2&e){const e=n.$implicit,i=t.ɵɵreference(16);t.ɵɵadvance(),t.ɵɵstyleMap(t.ɵɵpureFunction0(5,U2)),t.ɵɵadvance(),t.ɵɵproperty("disabled",!e.value.configurationJson.id),t.ɵɵadvance(10),t.ɵɵproperty("matMenuTriggerFor",i),t.ɵɵadvance(5),t.ɵɵproperty("disabled",!e.value.configurationJson.id)}}function r3(e,n){1&e&&t.ɵɵelement(0,"mat-header-row",53)}function o3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-row",54),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).$implicit,a=t.ɵɵnextContext();return t.ɵɵresetView(a.selectConnector(n,i))})),t.ɵɵelementEnd()}if(2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵclassMap(t.ɵɵpureFunction1(2,j2,i.isSameConnector(e)))}}function s3(e,n){if(1&e&&(t.ɵɵelementStart(0,"span",55),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1("v",e.connectorForm.get("configVersion").value,"")}}function l3(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-select",56)(1,"tb-toggle-option",57),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"tb-toggle-option",57),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("value",e.ConnectorConfigurationModes.BASIC),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,4,"gateway.basic")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",e.ConnectorConfigurationModes.ADVANCED),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,6,"gateway.advanced")," ")}}function p3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-mqtt-basic-config",65),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function c3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-mqtt-legacy-basic-config",65),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function d3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,p3,2,4,"tb-mqtt-basic-config",64),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,c3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.MQTT))("ngIfElse",e)}}function u3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-opc-ua-basic-config",65),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function m3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-opc-ua-legacy-basic-config",65),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function h3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,u3,2,4,"tb-opc-ua-basic-config",64),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,m3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.OPCUA))("ngIfElse",e)}}function g3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-modbus-basic-config",67),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function f3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-modbus-legacy-basic-config",67),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function y3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,g3,1,1,"tb-modbus-basic-config",66),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,f3,1,1,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.MODBUS))("ngIfElse",e)}}function v3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-socket-basic-config",67),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function x3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-socket-legacy-basic-config",67),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){t.ɵɵnextContext(4);const e=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",e)}}function b3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,v3,1,1,"tb-socket-basic-config",66),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,x3,1,1,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.SOCKET))("ngIfElse",e)}}function w3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-bacnet-basic-config",65),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function S3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-bacnet-legacy-basic-config",65),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function C3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,w3,2,4,"tb-bacnet-basic-config",64),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,S3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.BACNET))("ngIfElse",e)}}function _3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-rest-basic-config",69),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function T3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-rest-legacy-basic-config",69),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(4);return t.ɵɵresetView(n.basicConfigInitSubject.next())})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(4),n=t.ɵɵreference(41);t.ɵɵproperty("generalTabContent",n)("withReportStrategy",t.ɵɵpipeBind1(1,2,e.connectorForm.get("configVersion").value))}}function I3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtemplate(1,_3,2,4,"tb-rest-basic-config",68),t.ɵɵpipe(2,"isLatestVersionConfig"),t.ɵɵtemplate(3,T3,2,4,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext(3);t.ɵɵadvance(),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(2,2,n.connectorForm.get("configVersion").value,n.ConnectorType.REST))("ngIfElse",e)}}function E3(e,n){if(1&e&&(t.ɵɵelementContainerStart(0)(1,62),t.ɵɵtemplate(2,d3,5,5,"ng-container",63)(3,h3,5,5,"ng-container",63)(4,y3,5,5,"ng-container",63)(5,b3,5,5,"ng-container",63)(6,C3,5,5,"ng-container",63)(7,I3,5,5,"ng-container",63),t.ɵɵelementContainerEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("ngSwitch",e.initialConnector.type),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MQTT),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.OPCUA),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.MODBUS),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.SOCKET),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.BACNET),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",e.ConnectorType.REST)}}function M3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-tab-group")(1,"mat-tab",70),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainer(3,71),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-tab",70),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"tb-json-object-edit",72),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()),2&e){t.ɵɵnextContext(2);const e=t.ɵɵreference(41);t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,6,"gateway.general")),t.ɵɵadvance(2),t.ɵɵproperty("ngTemplateOutlet",e),t.ɵɵadvance(),t.ɵɵpropertyInterpolate1("label","",t.ɵɵpipeBind1(5,8,"gateway.configuration"),"*"),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,10,"gateway.configuration")),t.ɵɵproperty("fillHeight",!0)}}function k3(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",58),t.ɵɵtemplate(1,E3,8,7,"ng-container",59)(2,M3,8,12,"ng-template",null,2,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(4,"div",60)(5,"button",61),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.onSaveConnector())})),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()}if(2&e){let e;const n=t.ɵɵreference(3),i=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngIf",(null==(e=i.connectorForm.get("mode"))?null:e.value)===i.ConnectorConfigurationModes.BASIC)("ngIfElse",n),t.ɵɵadvance(4),t.ɵɵproperty("disabled",!i.connectorForm.dirty||i.connectorForm.invalid),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,4,"action.save")," ")}}function P3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-icon",89),t.ɵɵpipe(1,"translate"),t.ɵɵtext(2," warning "),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("matTooltip",t.ɵɵpipeBind1(1,1,e.connectorForm.get("name").hasError("duplicateName")?"gateway.connector-duplicate-name":"gateway.name-required"))}}function D3(e,n){1&e&&(t.ɵɵelementStart(0,"div",74)(1,"div",85),t.ɵɵtext(2,"gateway.connectors-table-class"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",76)(4,"mat-form-field",77),t.ɵɵelement(5,"input",90),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,1,"gateway.set")))}function O3(e,n){1&e&&(t.ɵɵelementStart(0,"div",74)(1,"div",85),t.ɵɵtext(2,"gateway.connectors-table-key"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",76)(4,"mat-form-field",77),t.ɵɵelement(5,"input",91),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(6,1,"gateway.set")))}function A3(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",57),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function F3(e,n){1&e&&(t.ɵɵelementStart(0,"div",74)(1,"mat-slide-toggle",92)(2,"mat-label",93),t.ɵɵpipe(3,"translate"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,2,"gateway.send-change-data-hint")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,4,"gateway.send-change-data")," "))}function R3(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",94),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("isExpansionMode",!0)("defaultValue",e.ReportStrategyDefaultValue.Connector)}}function B3(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",73)(1,"div",74)(2,"div",75),t.ɵɵtext(3,"gateway.name"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",76)(5,"mat-form-field",77),t.ɵɵelement(6,"input",78),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,P3,3,3,"mat-icon",79),t.ɵɵelementEnd()()(),t.ɵɵtemplate(9,D3,7,3,"div",80)(10,O3,7,3,"div",80),t.ɵɵelementStart(11,"div",81)(12,"div",82),t.ɵɵtext(13,"gateway.logs-configuration"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",83)(15,"mat-slide-toggle",84)(16,"mat-label"),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(19,"div",74)(20,"div",85),t.ɵɵtext(21,"gateway.remote-logging-level"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"div",76)(23,"mat-form-field",77)(24,"mat-select",86),t.ɵɵtemplate(25,A3,2,2,"mat-option",87),t.ɵɵelementEnd()()()()(),t.ɵɵtemplate(26,F3,6,6,"div",80),t.ɵɵpipe(27,"withReportStrategy"),t.ɵɵtemplate(28,R3,1,2,"tb-report-strategy",88),t.ɵɵpipe(29,"withReportStrategy"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("formGroup",e.connectorForm),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,9,"gateway.set")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.connectorForm.get("name").hasError("required")&&e.connectorForm.get("name").touched||e.connectorForm.get("name").hasError("duplicateName")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.connectorForm.get("type").value===e.ConnectorType.CUSTOM),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.connectorForm.get("type").value===e.ConnectorType.GRPC),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,11,"gateway.enable-remote-logging")," "),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",e.gatewayLogLevel),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.connectorForm.get("type").value===e.ConnectorType.MQTT&&!t.ɵɵpipeBind2(27,13,e.connectorForm.get("configVersion").value,e.ConnectorType.MQTT)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",t.ɵɵpipeBind2(29,16,e.connectorForm.get("configVersion").value,e.connectorForm.get("type").value))}}class N3{isErrorState(e){return e&&e.invalid}}e("ForceErrorStateMatcher",N3);class L3 extends q{constructor(e,t,n,i,a,r,o,s,l,p,c){super(e),this.store=e,this.fb=t,this.translate=n,this.attributeService=i,this.dialogService=a,this.dialog=r,this.telemetryWsService=o,this.zone=s,this.utils=l,this.withReportStrategy=p,this.cd=c,this.ConnectorType=ct,this.allowBasicConfig=new Set([ct.MQTT,ct.OPCUA,ct.MODBUS,ct.SOCKET,ct.BACNET,ct.REST]),this.gatewayLogLevel=Object.values(lt),this.displayedColumns=["enabled","key","type","syncStatus","errors","actions"],this.GatewayConnectorTypesTranslatesMap=dt,this.ConnectorConfigurationModes=Qt,this.ReportStrategyDefaultValue=en,this.basicConfigInitSubject=new te,this.activeData=[],this.inactiveData=[],this.sharedAttributeData=[],this.subscriptionOptions={callbacks:{onDataUpdated:()=>this.ctx.ngZone.run((()=>{this.onErrorsUpdated()})),onDataUpdateError:(e,t)=>this.ctx.ngZone.run((()=>{this.onDataUpdateError(t)}))}},this.destroy$=new te,this.attributeUpdateSubject=new te,this.initDataSources(),this.initConnectorForm(),this.observeAttributeChange()}ngAfterViewInit(){this.dataSource.sort=this.sort,this.dataSource.sortingDataAccessor=this.getSortingDataAccessor(),this.ctx.$scope.gatewayConnectors=this,this.loadConnectors(),this.loadGatewayState(),this.observeModeChange()}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete(),super.ngOnDestroy()}onSaveConnector(){this.saveConnector(this.getUpdatedConnectorData(this.connectorForm.value),!1)}saveConnector(e,t=!0){const n=t||this.activeConnectors.includes(this.initialConnector.name)?D.SHARED_SCOPE:D.SERVER_SCOPE;oe(this.getEntityAttributeTasks(e,n)).pipe(ye(1)).subscribe((n=>{this.showToast(t?this.translate.instant("gateway.connector-created"):this.translate.instant("gateway.connector-updated")),this.initialConnector=e,this.updateData(!0),this.connectorForm.markAsPristine()}))}getEntityAttributeTasks(e,t){const n=[],i=[{key:e.name,value:e}],a=[],r=!this.activeConnectors.includes(e.name)&&t===D.SHARED_SCOPE||!this.inactiveConnectors.includes(e.name)&&t===D.SERVER_SCOPE,o=this.initialConnector&&this.initialConnector.name!==e.name;return o&&(a.push({key:this.initialConnector.name}),this.removeConnectorFromList(this.initialConnector.name,!0),this.removeConnectorFromList(this.initialConnector.name,!1)),r&&(t===D.SHARED_SCOPE?this.activeConnectors.push(e.name):this.inactiveConnectors.push(e.name)),(o||r)&&n.push(this.getSaveEntityAttributesTask(t)),n.push(this.attributeService.saveEntityAttributes(this.device,t,i)),a.length&&n.push(this.attributeService.deleteEntityAttributes(this.device,t,a)),n}getSaveEntityAttributesTask(e){const t=e===D.SHARED_SCOPE?"active_connectors":"inactive_connectors",n=e===D.SHARED_SCOPE?this.activeConnectors:this.inactiveConnectors;return this.attributeService.saveEntityAttributes(this.device,e,[{key:t,value:n}])}removeConnectorFromList(e,t){const n=t?this.activeConnectors:this.inactiveConnectors,i=n.indexOf(e);-1!==i&&n.splice(i,1)}getUpdatedConnectorData(e){const t={...e};return t.configuration=`${Be(t.name)}.json`,delete t.basicConfig,t.type!==ct.GRPC&&delete t.key,t.type!==ct.CUSTOM&&delete t.class,this.allowBasicConfig.has(t.type)||delete t.mode,this.withReportStrategy.transform(t.configVersion,t.type)&&(t.configurationJson.reportStrategy=t.reportStrategy,Ae(t.reportStrategy)&&delete t.reportStrategy,Ae(t.configurationJson.reportStrategy)&&delete t.configurationJson.reportStrategy),this.gatewayVersion&&!t.configVersion&&(t.configVersion=this.gatewayVersion),t.ts=Date.now(),t}updateData(e=!1){this.pageLink.sortOrder.property=this.sort.active,this.pageLink.sortOrder.direction=w[this.sort.direction.toUpperCase()],this.attributeDataSource.loadAttributes(this.device,D.CLIENT_SCOPE,this.pageLink,e).subscribe((e=>{this.activeData=e.data.filter((e=>this.activeConnectors.includes(e.key))),this.combineData(),this.generateSubscription(),this.setClientData(e)})),this.inactiveConnectorsDataSource.loadAttributes(this.device,D.SHARED_SCOPE,this.pageLink,e).subscribe((e=>{this.sharedAttributeData=e.data.filter((e=>this.activeConnectors.includes(e.key))),this.combineData()})),this.serverDataSource.loadAttributes(this.device,D.SERVER_SCOPE,this.pageLink,e).subscribe((e=>{this.inactiveData=e.data.filter((e=>this.inactiveConnectors.includes(e.key))),this.combineData()}))}isConnectorSynced(e){const t=e.value;if(!t.ts||e.skipSync||!this.isGatewayActive)return!1;if(-1===this.activeData.findIndex((e=>("string"==typeof e.value?JSON.parse(e.value):e.value).name===t.name)))return!1;return-1!==this.sharedAttributeData.findIndex((e=>{const n=e.value,i=n.name===t.name,a=Se(n.configurationJson,{})&&i,r=this.hasSameConfig(n.configurationJson,t.configurationJson),o=n.ts&&n.ts<=t.ts;return i&&o&&(r||a)}))}hasSameConfig(e,t){const{name:n,id:i,enableRemoteLogging:a,logLevel:r,reportStrategy:o,configVersion:s,...l}=e,{name:p,id:c,enableRemoteLogging:d,logLevel:u,reportStrategy:m,configVersion:h,...g}=t;return Se(l,g)}combineData(){const e=[...this.activeData,...this.inactiveData,...this.sharedAttributeData].reduce(((e,t)=>{const n=e.findIndex((e=>e.key===t.key));return-1===n?e.push(t):t.lastUpdateTs>e[n].lastUpdateTs&&!this.isConnectorSynced(e[n])&&(e[n]={...t,skipSync:!0}),e}),[]);this.dataSource.data=e.map((e=>({...e,value:"string"==typeof e.value?JSON.parse(e.value):e.value})))}clearOutConnectorForm(){this.initialConnector=null,this.connectorForm.setValue({mode:Qt.BASIC,name:"",type:ct.MQTT,sendDataOnlyOnChange:!1,enableRemoteLogging:!1,logLevel:lt.INFO,key:"auto",class:"",configuration:"",configurationJson:{},basicConfig:{},configVersion:"",reportStrategy:[{value:{},disabled:!0}]},{emitEvent:!1}),this.connectorForm.markAsPristine()}selectConnector(e,t){e&&e.stopPropagation();const n=t.value;n?.name!==this.initialConnector?.name&&this.confirmConnectorChange().subscribe((e=>{e&&this.setFormValue(n)}))}isSameConnector(e){if(!this.initialConnector)return!1;const t=e.value;return this.initialConnector.name===t.name}showToast(e){this.store.dispatch({type:"[Notification] Show",notification:{message:e,type:"success",duration:1e3,verticalPosition:"top",horizontalPosition:"left",target:"dashboardRoot",forceDismiss:!0}})}returnType(e){const t=e.value;return this.GatewayConnectorTypesTranslatesMap.get(t.type)}deleteConnector(e,t){t?.stopPropagation();const n=`Delete connector "${e.key}"?`;this.dialogService.confirm(n,"All connector data will be deleted.","Cancel","Delete").pipe(ye(1),xe((t=>{if(!t)return;const n=[],i=this.activeConnectors.includes(e.value?.name)?D.SHARED_SCOPE:D.SERVER_SCOPE;return n.push(this.attributeService.deleteEntityAttributes(this.device,i,[e])),this.removeConnectorFromList(e.key,!0),this.removeConnectorFromList(e.key,!1),n.push(this.getSaveEntityAttributesTask(i)),oe(n)}))).subscribe((()=>{this.initialConnector&&this.initialConnector.name!==e.key||(this.clearOutConnectorForm(),this.cd.detectChanges()),this.updateData(!0)}))}connectorLogs(e,t){t&&t.stopPropagation();const n=Oe(this.ctx.stateController.getStateParams());n.connector_logs=e,n.targetEntityParamName="connector_logs",this.ctx.stateController.openState("connector_logs",n)}connectorRpc(e,t){t&&t.stopPropagation();const n=Oe(this.ctx.stateController.getStateParams());n.connector_rpc=e,n.targetEntityParamName="connector_rpc",this.ctx.stateController.openState("connector_rpc",n)}onEnableConnector(e){e.value.ts=(new Date).getTime(),this.updateActiveConnectorKeys(e.key),this.attributeUpdateSubject.next(e)}getErrorsCount(e){const t=e.key,n=this.subscription&&this.subscription.data.find((e=>e&&e.dataKey.name===`${t}_ERRORS_COUNT`));return n&&this.activeConnectors.includes(t)?n.data[0][1]||0:"Inactive"}onAddConnector(e){e?.stopPropagation(),this.confirmConnectorChange().pipe(ye(1),ue(Boolean),xe((()=>this.openAddConnectorDialog())),ue(Boolean)).subscribe((e=>this.addConnector(e)))}addConnector(e){e.configurationJson||(e.configurationJson={}),this.gatewayVersion&&!e.configVersion&&(e.configVersion=this.gatewayVersion),e.basicConfig=e.configurationJson,this.initialConnector=e;const t=this.connectorForm.get("type").value;this.setInitialConnectorValues(e),this.saveConnector(this.getUpdatedConnectorData(e)),t!==e.type&&this.allowBasicConfig.has(e.type)?this.basicConfigInitSubject.pipe(ye(1)).subscribe((()=>{this.patchConnectorBasicConfig(e.basicConfig)})):this.patchConnectorBasicConfig(e.basicConfig)}setInitialConnectorValues(e){const{basicConfig:t,mode:n,enableRemoteLogging:i,...a}=e;this.toggleReportStrategy(e),this.connectorForm.get("mode").setValue(this.allowBasicConfig.has(e.type)?e.mode??Qt.BASIC:null,{emitEvent:!1}),this.connectorForm.get("enableRemoteLogging").setValue(i,{emitEvent:!1}),this.connectorForm.patchValue(a,{emitEvent:!1})}openAddConnectorDialog(){return this.ctx.ngZone.run((()=>this.dialog.open(h$,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{dataSourceData:this.dataSource.data,gatewayVersion:this.gatewayVersion}}).afterClosed()))}uniqNameRequired(){return e=>{const t=e.value?.trim().toLowerCase(),n=this.dataSource.data.some((e=>e.value.name.toLowerCase()===t)),i=this.initialConnector?.name.toLowerCase()===t;return n&&!i?{duplicateName:{valid:!1}}:null}}initDataSources(){const e={property:"key",direction:w.ASC};this.pageLink=new S(1e3,0,null,e),this.attributeDataSource=new zn(this.attributeService,this.telemetryWsService,this.zone,this.translate),this.inactiveConnectorsDataSource=new zn(this.attributeService,this.telemetryWsService,this.zone,this.translate),this.serverDataSource=new zn(this.attributeService,this.telemetryWsService,this.zone,this.translate),this.dataSource=new x([])}initConnectorForm(){this.connectorForm=this.fb.group({mode:[Qt.BASIC],name:["",[$.required,this.uniqNameRequired(),$.pattern(an)]],type:["",[$.required]],enableRemoteLogging:[!1],logLevel:["",[$.required]],sendDataOnlyOnChange:[!1],key:["auto"],class:[""],configuration:[""],configurationJson:[{},[$.required]],basicConfig:[{}],configVersion:[""],reportStrategy:[{value:{},disabled:!0}]})}getSortingDataAccessor(){return(e,t)=>{switch(t){case"syncStatus":return this.isConnectorSynced(e)?1:0;case"enabled":return this.activeConnectors.includes(e.key)?1:0;case"errors":const n=this.getErrorsCount(e);return"string"==typeof n?this.sort.direction.toUpperCase()===w.DESC?-1:1/0:n;default:return e[t]||e.value[t]}}}loadConnectors(){this.device&&this.device.id!==B&&oe([this.attributeService.getEntityAttributes(this.device,D.SHARED_SCOPE,["active_connectors"]),this.attributeService.getEntityAttributes(this.device,D.SERVER_SCOPE,["inactive_connectors"]),this.attributeService.getEntityAttributes(this.device,D.CLIENT_SCOPE,["Version"])]).pipe(le(this.destroy$)).subscribe((e=>{this.activeConnectors=this.parseConnectors(e[0]),this.inactiveConnectors=this.parseConnectors(e[1]),this.gatewayVersion=e[2][0]?.value,this.updateData(!0)}))}loadGatewayState(){this.attributeService.getEntityAttributes(this.device,D.SERVER_SCOPE).pipe(le(this.destroy$)).subscribe((e=>{const t=e.find((e=>"active"===e.key)).value,n=e.find((e=>"lastDisconnectTime"===e.key))?.value,i=e.find((e=>"lastConnectTime"===e.key))?.value;this.isGatewayActive=this.getGatewayStatus(t,i,n)}))}parseConnectors(e){const t=e?.[0]?.value||[];return Me(t)?JSON.parse(t):t}observeModeChange(){this.connectorForm.get("mode").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{e===Qt.BASIC&&this.patchConnectorBasicConfig(this.connectorForm.get("configurationJson").value)}))}observeAttributeChange(){this.attributeUpdateSubject.pipe(de(300),me((e=>this.executeAttributeUpdates(e))),le(this.destroy$)).subscribe()}updateActiveConnectorKeys(e){if(this.activeConnectors.includes(e)){const t=this.activeConnectors.indexOf(e);-1!==t&&this.activeConnectors.splice(t,1),this.inactiveConnectors.push(e)}else{const t=this.inactiveConnectors.indexOf(e);-1!==t&&this.inactiveConnectors.splice(t,1),this.activeConnectors.push(e)}}executeAttributeUpdates(e){oe(this.getAttributeExecutionTasks(e)).pipe(ye(1),me((()=>this.updateData(!0))),le(this.destroy$)).subscribe()}getAttributeExecutionTasks(e){const t=this.activeConnectors.includes(e.key),n=t?D.SERVER_SCOPE:D.SHARED_SCOPE,i=t?D.SHARED_SCOPE:D.SERVER_SCOPE;return[this.attributeService.saveEntityAttributes(this.device,D.SHARED_SCOPE,[{key:"active_connectors",value:this.activeConnectors}]),this.attributeService.saveEntityAttributes(this.device,D.SERVER_SCOPE,[{key:"inactive_connectors",value:this.inactiveConnectors}]),this.attributeService.deleteEntityAttributes(this.device,n,[e]),this.attributeService.saveEntityAttributes(this.device,i,[e])]}onDataUpdateError(e){const t=this.utils.parseException(e);let n=t.name;t.message&&(n+=": "+t.message),console.error(n)}onErrorsUpdated(){this.cd.detectChanges()}onDataUpdated(){const e=this.ctx.defaultSubscription.data,t=e.find((e=>"active"===e.dataKey.name)).data[0][1],n=e.find((e=>"lastDisconnectTime"===e.dataKey.name)).data[0][1],i=e.find((e=>"lastConnectTime"===e.dataKey.name)).data[0][1];this.isGatewayActive=this.getGatewayStatus(t,i,n),this.cd.detectChanges()}getGatewayStatus(e,t,n){return!!e&&(!n||t>n)}generateSubscription(){if(this.subscription&&this.subscription.unsubscribe(),this.device){const e=[{type:N.entity,entityType:L.DEVICE,entityId:this.device.id,entityName:"Gateway",timeseries:[]}];this.dataSource.data.forEach((t=>{e[0].timeseries.push({name:`${t.key}_ERRORS_COUNT`,label:`${t.key}_ERRORS_COUNT`})})),this.ctx.subscriptionApi.createSubscriptionFromInfo(R.latest,e,this.subscriptionOptions,!1,!0).subscribe((e=>{this.subscription=e}))}}createBasicConfigWatcher(){this.basicConfigSub&&this.basicConfigSub.unsubscribe(),this.basicConfigSub=this.connectorForm.get("basicConfig").valueChanges.pipe(ue((()=>!!this.initialConnector)),le(this.destroy$)).subscribe((e=>{const t=this.connectorForm.get("configurationJson"),n=this.connectorForm.get("type").value,i=this.connectorForm.get("mode").value;if(!Se(e,t?.value)&&this.allowBasicConfig.has(n)&&i===Qt.BASIC){const n={...t.value,...e};this.connectorForm.get("configurationJson").patchValue(n,{emitEvent:!1})}}))}createJsonConfigWatcher(){this.jsonConfigSub&&this.jsonConfigSub.unsubscribe(),this.jsonConfigSub=this.connectorForm.get("configurationJson").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{const t=this.connectorForm.get("basicConfig"),n=this.connectorForm.get("type").value,i=this.connectorForm.get("mode").value;!Se(e,t?.value)&&this.allowBasicConfig.has(n)&&i===Qt.ADVANCED&&this.connectorForm.get("basicConfig").patchValue(e,{emitEvent:!1})}))}confirmConnectorChange(){return this.initialConnector&&this.connectorForm.dirty?this.dialogService.confirm(this.translate.instant("gateway.change-connector-title"),this.translate.instant("gateway.change-connector-text"),this.translate.instant("action.no"),this.translate.instant("action.yes"),!0):ae(!0)}setFormValue(e){this.connectorForm.disabled&&this.connectorForm.enable();const t=za.getConfig({configuration:"",key:"auto",configurationJson:{},...e},this.gatewayVersion);this.gatewayVersion&&!t.configVersion&&(t.configVersion=this.gatewayVersion),t.basicConfig=t.configurationJson,this.initialConnector=t,this.updateConnector(t)}updateConnector(e){this.jsonConfigSub?.unsubscribe(),this.allowBasicConfig.has(e.type)?this.updateBasicConfigConnector(e):(this.setInitialConnectorValues(e),this.connectorForm.markAsPristine(),this.createJsonConfigWatcher())}updateBasicConfigConnector(e){this.basicConfigSub?.unsubscribe();const t=this.connectorForm.get("type").value;this.setInitialConnectorValues(e),t!==e.type&&this.allowBasicConfig.has(e.type)&&e.mode!==Qt.ADVANCED?this.basicConfigInitSubject.asObservable().pipe(ye(1)).subscribe((()=>{this.patchConnectorBasicConfig(e.basicConfig)})):this.patchConnectorBasicConfig(e.basicConfig)}patchConnectorBasicConfig(e){this.connectorForm.get("basicConfig").patchValue(e,{emitEvent:!1}),this.connectorForm.markAsPristine(),this.createBasicConfigWatcher(),this.createJsonConfigWatcher()}toggleReportStrategy(e){const t=this.connectorForm.get("reportStrategy"),n=this.connectorForm.get("sendDataOnlyOnChange");this.connectorForm.get("reportStrategy").reset(e.reportStrategy,{emitEvent:!1}),this.withReportStrategy.transform(e.configVersion,e.type)?(t.enable({emitEvent:!1}),n.disable({emitEvent:!1})):(t.disable({emitEvent:!1}),e.type===ct.MQTT&&n.enable({emitEvent:!1}))}setClientData(e){if(this.initialConnector){const t=e.data.find((e=>e.key===this.initialConnector.name));t&&(t.value="string"==typeof t.value?JSON.parse(t.value):t.value,this.isConnectorSynced(t)&&t.value.configurationJson&&this.setFormValue({...t.value,mode:this.connectorForm.get("mode").value??t.value.mode}))}}static{this.ɵfac=function(e){return new(e||L3)(t.ɵɵdirectiveInject(Ye.Store),t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(He.TranslateService),t.ɵɵdirectiveInject(_e.AttributeService),t.ɵɵdirectiveInject(_e.DialogService),t.ɵɵdirectiveInject(qe.MatDialog),t.ɵɵdirectiveInject(_e.TelemetryWebsocketService),t.ɵɵdirectiveInject(t.NgZone),t.ɵɵdirectiveInject(_e.UtilsService),t.ɵɵdirectiveInject(Ka),t.ɵɵdirectiveInject(t.ChangeDetectorRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:L3,selectors:[["tb-gateway-connector"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(v,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.sort=e.first)}},inputs:{ctx:"ctx",device:"device"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:ot,useClass:N3},Ka]),t.ɵɵInheritDefinitionFeature,t.ɵɵStandaloneFeature],decls:42,vars:21,consts:[["generalTabContent",""],["cellActionsMenu","matMenu"],["defaultConfig",""],["legacy",""],[1,"connector-container","tb-form-panel","no-border"],[1,"table-section","tb-form-panel","no-padding","section-container","flex"],[1,"mat-mdc-table-toolbar","justify-between"],["mat-icon-button","","matTooltipPosition","above",3,"disabled","matTooltip","click",4,"ngIf"],[1,"table-container"],["class","mat-headline-5 tb-absolute-fill tb-add-new items-center justify-center",4,"ngIf"],["mat-table","","matSort","","matSortDisableClear","",3,"dataSource","matSortActive","matSortDirection"],["matColumnDef","enabled","sticky",""],["style","width: 60px;min-width: 60px;",4,"matHeaderCellDef"],[4,"matCellDef"],["matColumnDef","key"],["mat-sort-header","","style","width: 40%",4,"matHeaderCellDef"],["matColumnDef","type"],["mat-sort-header","","style","width: 30%",4,"matHeaderCellDef"],["style","text-transform: uppercase",4,"matCellDef"],["matColumnDef","syncStatus"],["matColumnDef","errors"],["matColumnDef","actions","stickyEnd",""],[4,"matHeaderCellDef"],["class","mat-row-select",4,"matHeaderRowDef","matHeaderRowDefSticky"],["class","mat-row-select",3,"class","click",4,"matRowDef","matRowDefColumns"],[1,"tb-form-panel","section-container","flex",3,"formGroup"],[1,"tb-form-panel-title","tb-flex","no-flex","space-between","align-center"],[1,"tb-form-panel-title"],["class","version-placeholder",4,"ngIf"],["formControlName","mode","appearance","fill",4,"ngIf"],["translate","",1,"no-data-found","items-center","justify-center"],["class","tb-form-panel section-container no-border no-padding tb-flex space-between",4,"ngIf"],["mat-icon-button","","matTooltipPosition","above",3,"click","disabled","matTooltip"],[1,"mat-headline-5","tb-absolute-fill","tb-add-new","items-center","justify-center"],["mat-button","",1,"connector",3,"click"],[1,"tb-mat-96"],[2,"width","60px","min-width","60px"],[3,"click","checked"],["mat-sort-header","",2,"width","40%"],["mat-sort-header","",2,"width","30%"],[2,"text-transform","uppercase"],[1,"status"],["matTooltipPosition","above",1,"dot",3,"click","matTooltip"],[1,"gt-md:!hidden",2,"width","48px","min-width","48px","max-width","48px"],[1,"lt-lg:!hidden"],[1,"lt-md:!hidden","flex-row","justify-end"],["mat-icon-button","","matTooltip","RPC","matTooltipPosition","above",3,"click","disabled"],["mat-icon-button","","matTooltip","Logs","matTooltipPosition","above",3,"click"],["mat-icon-button","","matTooltip","Delete connector","matTooltipPosition","above",3,"click"],[1,"gt-sm:!hidden"],["mat-icon-button","",3,"click","matMenuTriggerFor"],[1,"material-icons"],["xPosition","before"],[1,"mat-row-select"],[1,"mat-row-select",3,"click"],[1,"version-placeholder"],["formControlName","mode","appearance","fill"],[3,"value"],[1,"tb-form-panel","section-container","no-border","no-padding","tb-flex","space-between"],[4,"ngIf","ngIfElse"],[1,"flex","justify-end"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],[3,"ngSwitch"],[4,"ngSwitchCase"],["formControlName","basicConfig",3,"generalTabContent","withReportStrategy","initialized",4,"ngIf","ngIfElse"],["formControlName","basicConfig",3,"initialized","generalTabContent","withReportStrategy"],["formControlName","basicConfig",3,"generalTabContent","initialized",4,"ngIf","ngIfElse"],["formControlName","basicConfig",3,"initialized","generalTabContent"],["class","h-full","formControlName","basicConfig",3,"generalTabContent","withReportStrategy","initialized",4,"ngIf","ngIfElse"],["formControlName","basicConfig",1,"h-full",3,"initialized","generalTabContent","withReportStrategy"],[3,"label"],[3,"ngTemplateOutlet"],["jsonRequired","","formControlName","configurationJson",1,"configuration-json",3,"fillHeight","label"],[1,"tb-form-panel","no-border","no-padding","padding-top","section-container","flex",3,"formGroup"],[1,"tb-form-row","column-xs"],["translate","",1,"fixed-title-width","tb-required"],[1,"tb-flex","no-gap"],["appearance","outline","subscriptSizing","dynamic",1,"tb-flex","no-gap"],["matInput","","autocomplete","off","name","value","formControlName","name",3,"placeholder"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip","class","tb-error",3,"matTooltip",4,"ngIf"],["class","tb-form-row column-xs",4,"ngIf"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row"],["formControlName","enableRemoteLogging",1,"mat-slide"],["translate","",1,"fixed-title-width"],["formControlName","logLevel"],[3,"value",4,"ngFor","ngForOf"],["class","stroked tb-form-panel","formControlName","reportStrategy",3,"isExpansionMode","defaultValue",4,"ngIf"],["matSuffix","","matTooltipPosition","above","matTooltipClass","tb-error-tooltip",1,"tb-error",3,"matTooltip"],["matInput","","name","value","formControlName","class",3,"placeholder"],["matInput","","name","value","formControlName","key",3,"placeholder"],["formControlName","sendDataOnlyOnChange",1,"mat-slide"],[3,"tb-hint-tooltip-icon"],["formControlName","reportStrategy",1,"stroked","tb-form-panel",3,"isExpansionMode","defaultValue"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",4)(1,"section",5)(2,"mat-toolbar",6)(3,"h2"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(6,H2,5,6,"button",7),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",8),t.ɵɵtemplate(8,W2,7,3,"section",9),t.ɵɵelementStart(9,"table",10),t.ɵɵelementContainerStart(10,11),t.ɵɵtemplate(11,$2,3,3,"mat-header-cell",12)(12,K2,2,1,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(13,14),t.ɵɵtemplate(14,Y2,3,3,"mat-header-cell",15)(15,X2,2,1,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(16,16),t.ɵɵtemplate(17,Z2,3,3,"mat-header-cell",17)(18,Q2,2,1,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(19,19),t.ɵɵtemplate(20,J2,3,3,"mat-header-cell",17)(21,e3,3,3,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(22,20),t.ɵɵtemplate(23,t3,3,3,"mat-header-cell",17)(24,n3,2,6,"mat-cell",18),t.ɵɵelementContainerEnd(),t.ɵɵelementContainerStart(25,21),t.ɵɵtemplate(26,i3,5,6,"mat-header-cell",22)(27,a3,26,6,"mat-cell",13),t.ɵɵelementContainerEnd(),t.ɵɵtemplate(28,r3,1,0,"mat-header-row",23)(29,o3,1,4,"mat-row",24),t.ɵɵelementEnd()()(),t.ɵɵelementStart(30,"section",25)(31,"div",26)(32,"div",27),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵtemplate(35,s3,2,1,"span",28),t.ɵɵelementEnd(),t.ɵɵtemplate(36,l3,7,8,"tb-toggle-select",29),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"span",30),t.ɵɵtext(38," gateway.select-connector "),t.ɵɵelementEnd(),t.ɵɵtemplate(39,k3,8,6,"section",31),t.ɵɵelementEnd()(),t.ɵɵtemplate(40,B3,30,19,"ng-template",null,0,t.ɵɵtemplateRefExtractor)),2&e&&(t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,17,"gateway.connectors")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",null==n.dataSource||null==n.dataSource.data?null:n.dataSource.data.length),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!(null!=n.dataSource&&(null!=n.dataSource.data&&n.dataSource.data.length))),t.ɵɵadvance(),t.ɵɵproperty("dataSource",n.dataSource)("matSortActive",n.pageLink.sortOrder.property)("matSortDirection",n.pageLink.sortDirection()),t.ɵɵadvance(19),t.ɵɵproperty("matHeaderRowDef",n.displayedColumns)("matHeaderRowDefSticky",!0),t.ɵɵadvance(),t.ɵɵproperty("matRowDefColumns",n.displayedColumns),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.connectorForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate2(" ",null!=n.initialConnector&&n.initialConnector.type?n.GatewayConnectorTypesTranslatesMap.get(n.initialConnector.type):""," ",t.ɵɵpipeBind1(34,19,"gateway.configuration")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.connectorForm.get("configVersion").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.initialConnector&&n.allowBasicConfig.has(n.initialConnector.type)),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",n.initialConnector),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.initialConnector))},dependencies:t.ɵɵgetComponentDepsFactory(L3,[j,_,Ka,a$,oX,sX,XY,YY,xQ,bQ,kJ,PJ,Xn,E1,M1,q2,V2]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:block;overflow-x:auto;padding:0}[_nghost-%COMP%] .version-placeholder[_ngcontent-%COMP%]{color:gray;font-size:12px}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%]{height:100%;width:100%;flex-direction:row}@media screen and (max-width: 1279px){[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%]{flex-direction:column}}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] > section[_ngcontent-%COMP%]:not(.table-section){max-width:unset}@media screen and (min-width: 1280px){[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] > section[_ngcontent-%COMP%]:not(.table-section){max-width:50%}}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .table-section[_ngcontent-%COMP%]{min-height:35vh;overflow:hidden}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .table-section[_ngcontent-%COMP%] .table-container[_ngcontent-%COMP%]{overflow:auto}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .flex[_ngcontent-%COMP%]{flex:1}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .input-container[_ngcontent-%COMP%]{height:auto}[_nghost-%COMP%] .connector-container[_ngcontent-%COMP%] .section-container[_ngcontent-%COMP%]{background-color:#fff}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{background:transparent;color:#000000de!important}[_nghost-%COMP%] .mat-mdc-slide-toggle[_ngcontent-%COMP%]{margin:0 8px}[_nghost-%COMP%] .status[_ngcontent-%COMP%]{text-align:center;border-radius:16px;font-weight:500;width:fit-content;padding:5px 15px}[_nghost-%COMP%] .status-sync[_ngcontent-%COMP%]{background:#1980380f;color:#198038}[_nghost-%COMP%] .status-unsync[_ngcontent-%COMP%]{background:#cb25300f;color:#cb2530}[_nghost-%COMP%] mat-row[_ngcontent-%COMP%]{cursor:pointer}[_nghost-%COMP%] .dot[_ngcontent-%COMP%]{height:12px;width:12px;background-color:#bbb;border-radius:50%;display:inline-block}[_nghost-%COMP%] .hasErrors[_ngcontent-%COMP%]{background-color:#cb2530}[_nghost-%COMP%] .noErrors[_ngcontent-%COMP%]{background-color:#198038}[_nghost-%COMP%] .connector-container .mat-mdc-tab-group, [_nghost-%COMP%] .connector-container .mat-mdc-tab-body-wrapper{height:100%}[_nghost-%COMP%] .connector-container .mat-mdc-tab-body.mat-mdc-tab-body-active{position:absolute}[_nghost-%COMP%] .connector-container .tb-form-row .fixed-title-width{min-width:120px;width:30%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[_nghost-%COMP%] .connector-container .tb-add-new{display:flex;z-index:999;pointer-events:none;background-color:#fff}[_nghost-%COMP%] .connector-container .tb-add-new button.connector{height:auto;padding-right:12px;font-size:20px;border-style:dashed;border-width:2px;border-radius:8px;display:flex;flex-wrap:wrap;justify-content:center;align-items:center;color:#00000061}@media screen and (min-width: 960px){[_nghost-%COMP%] .configuration-json .ace_tooltip{transform:translate(-250px,-120px)}}']})}}e("GatewayConnectorComponent",L3);class V3{constructor(e){this.deviceService=e}download(e){e&&e.stopPropagation(),this.deviceId&&this.deviceService.downloadGatewayDockerComposeFile(this.deviceId).subscribe((()=>{}))}static{this.ɵfac=function(e){return new(e||V3)(t.ɵɵdirectiveInject(_e.DeviceService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:V3,selectors:[["tb-gateway-command"]],inputs:{deviceId:"deviceId"},standalone:!0,features:[t.ɵɵStandaloneFeature],decls:32,vars:9,consts:[["mat-dialog-content","",1,"tb-form-panel","no-border",2,"padding","16px 16px 8px"],[1,"tb-no-data-text"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","no-border","no-padding","space-between"],["translate","",1,"tb-no-data-text","tb-commands-hint"],["mat-stroked-button","","color","primary","href","https://docs.docker.com/compose/install/","target","_blank"],["mat-stroked-button","","color","primary",3,"click"],["usePlainMarkdown","","containerClass","start-code","data","\n ```bash\n docker compose up\n {:copy-code}\n ```\n "]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",2)(5,"div",3),t.ɵɵtext(6,"device.connectivity.install-necessary-client-tools"),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",4)(8,"div",5),t.ɵɵtext(9,"gateway.install-docker-compose"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"a",6)(11,"mat-icon"),t.ɵɵtext(12,"description"),t.ɵɵelementEnd(),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(15,"div",2)(16,"div",3),t.ɵɵtext(17,"gateway.download-configuration-file"),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"div",4)(19,"div",5),t.ɵɵtext(20,"gateway.download-docker-compose"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"button",7),t.ɵɵlistener("click",(function(e){return n.download(e)})),t.ɵɵelementStart(22,"mat-icon"),t.ɵɵtext(23,"download"),t.ɵɵelementEnd(),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(26,"div",2)(27,"div",3),t.ɵɵtext(28,"gateway.launch-gateway"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"div",5),t.ɵɵtext(30,"gateway.launch-docker-compose"),t.ɵɵelementEnd(),t.ɵɵelement(31,"tb-markdown",8),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"gateway.docker-label")),t.ɵɵadvance(11),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,5,"common.documentation")," "),t.ɵɵadvance(11),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,7,"action.download")," "))},dependencies:t.ɵɵgetComponentDepsFactory(V3,[j,_]),styles:['@charset "UTF-8";[_nghost-%COMP%] .tb-commands-hint[_ngcontent-%COMP%]{color:inherit;font-weight:400;flex:1}[_nghost-%COMP%] .tb-markdown-view .start-code .code-wrapper{padding:0}[_nghost-%COMP%] .tb-markdown-view .start-code .code-wrapper pre[class*=language-]{margin:0;background:#f3f6fa;border-color:#305680;padding-right:38px;overflow:scroll;padding-bottom:4px;min-height:42px;scrollbar-width:thin}[_nghost-%COMP%] .tb-markdown-view .start-code .code-wrapper pre[class*=language-]::-webkit-scrollbar{width:4px;height:4px}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn{right:-2px}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn p{color:#305680}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn p, [_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn div{background-color:#f3f6fa}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn div img{display:none}[_nghost-%COMP%] .tb-markdown-view .start-code button.clipboard-btn div:after{content:"";position:initial;display:block;width:18px;height:18px;background:#305680;mask-image:url(/assets/copy-code-icon.svg);-webkit-mask-image:url(/assets/copy-code-icon.svg);mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat}']})}}var q3,G3,z3;e("DeviceGatewayCommandComponent",V3),e("GatewayBasicConfigTab",q3),function(e){e[e.general=0]="general",e[e.logs=1]="logs",e[e.storage=2]="storage",e[e.grpc=3]="grpc",e[e.statistics=4]="statistics",e[e.other=5]="other"}(q3||e("GatewayBasicConfigTab",q3={})),e("StorageTypes",G3),function(e){e.MEMORY="memory",e.FILE="file",e.SQLITE="sqlite"}(G3||e("StorageTypes",G3={})),e("LocalLogsConfigs",z3),function(e){e.service="service",e.connector="connector",e.converter="converter",e.tb_connection="tb_connection",e.storage="storage",e.extension="extension"}(z3||e("LocalLogsConfigs",z3={}));const U3=e("LocalLogsConfigTranslateMap",new Map([[z3.service,"Service"],[z3.connector,"Connector"],[z3.converter,"Converter"],[z3.tb_connection,"TB Connection"],[z3.storage,"Storage"],[z3.extension,"Extension"]])),j3=e("StorageTypesTranslationMap",new Map([[G3.MEMORY,"gateway.storage-types.memory-storage"],[G3.FILE,"gateway.storage-types.file-storage"],[G3.SQLITE,"gateway.storage-types.sqlite"]]));var H3;e("LogSavingPeriod",H3),function(e){e.days="D",e.hours="H",e.minutes="M",e.seconds="S"}(H3||e("LogSavingPeriod",H3={}));const W3=e("LogSavingPeriodTranslations",new Map([[H3.days,"gateway.logs.days"],[H3.hours,"gateway.logs.hours"],[H3.minutes,"gateway.logs.minutes"],[H3.seconds,"gateway.logs.seconds"]]));var $3;e("SecurityTypes",$3),function(e){e.ACCESS_TOKEN="accessToken",e.USERNAME_PASSWORD="usernamePassword",e.TLS_ACCESS_TOKEN="tlsAccessToken",e.TLS_PRIVATE_KEY="tlsPrivateKey"}($3||e("SecurityTypes",$3={}));const K3=e("SecurityTypesTranslationsMap",new Map([[$3.ACCESS_TOKEN,"gateway.security-types.access-token"],[$3.USERNAME_PASSWORD,"gateway.security-types.username-password"],[$3.TLS_ACCESS_TOKEN,"gateway.security-types.tls-access-token"]])),Y3=e("logsHandlerClass","thingsboard_gateway.tb_utility.tb_rotating_file_handler.TimedRotatingFileHandler"),X3=e("logsLegacyHandlerClass","thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler");function Z3(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",10),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,i.storageTypesTranslationMap.get(e))," ")}}function Q3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-read-record-count-required")," "))}function J3(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-read-record-count-min")," "))}function e4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-read-record-count-pattern")," "))}function t4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-required")," "))}function n4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-min")," "))}function i4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-pattern")," "))}function a4(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",11)(1,"mat-form-field",12)(2,"mat-label",13),t.ɵɵtext(3,"gateway.storage-read-record-count"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",14),t.ɵɵtemplate(5,Q3,3,3,"mat-error",15)(6,J3,3,3,"mat-error",15)(7,e4,3,3,"mat-error",15),t.ɵɵelementStart(8,"mat-icon",16),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",12)(12,"mat-label",13),t.ɵɵtext(13,"gateway.storage-max-records"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",17),t.ɵɵtemplate(15,t4,3,3,"mat-error",15)(16,n4,3,3,"mat-error",15)(17,i4,3,3,"mat-error",15),t.ɵɵelementStart(18,"mat-icon",16),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"info_outlined "),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.storageFormGroup.get("read_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("read_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("read_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,8,"gateway.hints.read-record-count")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,10,"gateway.hints.max-records-count"))}}function r4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-data-folder-path-required")," "))}function o4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-files-required")," "))}function s4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-files-min")," "))}function l4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-files-pattern")," "))}function p4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-required")," "))}function c4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-min")," "))}function d4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-read-record-count-pattern")," "))}function u4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-required")," "))}function m4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-min")," "))}function h4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-pattern")," "))}function g4(e,n){if(1&e&&(t.ɵɵelementStart(0,"section")(1,"div",11)(2,"mat-form-field",12)(3,"mat-label",13),t.ɵɵtext(4,"gateway.storage-data-folder-path"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",18),t.ɵɵtemplate(6,r4,3,3,"mat-error",15),t.ɵɵelementStart(7,"mat-icon",19),t.ɵɵpipe(8,"translate"),t.ɵɵtext(9,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",12)(11,"mat-label",13),t.ɵɵtext(12,"gateway.storage-max-files"),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",20),t.ɵɵtemplate(14,o4,3,3,"mat-error",15)(15,s4,3,3,"mat-error",15)(16,l4,3,3,"mat-error",15),t.ɵɵelementStart(17,"mat-icon",16),t.ɵɵpipe(18,"translate"),t.ɵɵtext(19,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",11)(21,"mat-form-field",12)(22,"mat-label",13),t.ɵɵtext(23,"gateway.storage-max-read-record-count"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",21),t.ɵɵtemplate(25,p4,3,3,"mat-error",15)(26,c4,3,3,"mat-error",15)(27,d4,3,3,"mat-error",15),t.ɵɵelementStart(28,"mat-icon",16),t.ɵɵpipe(29,"translate"),t.ɵɵtext(30,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(31,"mat-form-field",12)(32,"mat-label",13),t.ɵɵtext(33,"gateway.storage-max-records"),t.ɵɵelementEnd(),t.ɵɵelement(34,"input",22),t.ɵɵtemplate(35,u4,3,3,"mat-error",15)(36,m4,3,3,"mat-error",15)(37,h4,3,3,"mat-error",15),t.ɵɵelementStart(38,"mat-icon",16),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40,"info_outlined "),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.storageFormGroup.get("data_folder_path").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,14,"gateway.hints.data-folder")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_file_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_file_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_file_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,16,"gateway.hints.max-file-count")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_read_records_count").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(29,18,"gateway.hints.max-read-count")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(39,20,"gateway.hints.max-records"))}}function f4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-path-required")," "))}function y4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-check-in-hours-required")," "))}function v4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-check-in-hours-min")," "))}function x4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-check-in-hours-pattern")," "))}function b4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-in-days-required")," "))}function w4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-in-days-min")," "))}function S4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.messages-ttl-in-days-pattern")," "))}function C4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-required")," "))}function _4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-min")," "))}function T4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.storage-max-records-pattern")," "))}function I4(e,n){if(1&e&&(t.ɵɵelementStart(0,"section")(1,"div",11)(2,"mat-form-field",12)(3,"mat-label",13),t.ɵɵtext(4,"gateway.storage-path"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",23),t.ɵɵtemplate(6,f4,3,3,"mat-error",15),t.ɵɵelementStart(7,"mat-icon",16),t.ɵɵpipe(8,"translate"),t.ɵɵtext(9,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",12)(11,"mat-label",13),t.ɵɵtext(12,"gateway.messages-ttl-check-in-hours"),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",24),t.ɵɵtemplate(14,y4,3,3,"mat-error",15)(15,v4,3,3,"mat-error",15)(16,x4,3,3,"mat-error",15),t.ɵɵelementStart(17,"mat-icon",16),t.ɵɵpipe(18,"translate"),t.ɵɵtext(19,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"div",11)(21,"mat-form-field",12)(22,"mat-label",13),t.ɵɵtext(23,"gateway.messages-ttl-in-days"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",25),t.ɵɵtemplate(25,b4,3,3,"mat-error",15)(26,w4,3,3,"mat-error",15)(27,S4,3,3,"mat-error",15),t.ɵɵelementStart(28,"mat-icon",26),t.ɵɵpipe(29,"translate"),t.ɵɵtext(30,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(31,"mat-form-field",12)(32,"mat-label",13),t.ɵɵtext(33,"gateway.storage-max-records"),t.ɵɵelementEnd(),t.ɵɵelement(34,"input",22),t.ɵɵtemplate(35,C4,3,3,"mat-error",15)(36,_4,3,3,"mat-error",15)(37,T4,3,3,"mat-error",15),t.ɵɵelementStart(38,"mat-icon",26),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40,"info_outlined "),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.storageFormGroup.get("data_file_path").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,14,"gateway.hints.data-folder")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_check_in_hours").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_check_in_hours").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_check_in_hours").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,16,"gateway.hints.ttl-check-hour")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_in_days").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_in_days").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("messages_ttl_in_days").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(29,18,"gateway.hints.ttl-messages-day")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.storageFormGroup.get("max_records_per_file").hasError("pattern")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(39,20,"gateway.hints.max-records"))}}class E4{constructor(e){this.fb=e,this.initialized=new u,this.StorageTypes=G3,this.storageTypes=Object.values(G3),this.storageTypesTranslationMap=j3,this.onChange=()=>{},this.storageFormGroup=this.initStorageFormGroup(),this.observeStorageTypeChanges(),this.storageFormGroup.valueChanges.pipe(bn()).subscribe((e=>{this.onChange(e)}))}ngAfterViewInit(){this.initialized.emit({storage:this.storageFormGroup.value})}writeValue(e){this.storageFormGroup.patchValue(e,{emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.storageFormGroup.valid?null:{storageFormGroup:{valid:!1}}}removeAllStorageValidators(){for(const e in this.storageFormGroup.controls)"type"!==e&&(this.storageFormGroup.controls[e].clearValidators(),this.storageFormGroup.controls[e].setErrors(null),this.storageFormGroup.controls[e].updateValueAndValidity())}initStorageFormGroup(){return this.fb.group({type:[G3.MEMORY,[$.required]],read_records_count:[100,[$.required,$.min(1),$.pattern(nn)]],max_records_count:[1e5,[$.required,$.min(1),$.pattern(nn)]],data_folder_path:["./data/",[$.required]],max_file_count:[10,[$.min(1),$.pattern(nn)]],max_read_records_count:[10,[$.min(1),$.pattern(nn)]],max_records_per_file:[1e4,[$.min(1),$.pattern(nn)]],data_file_path:["./data/data.db",[$.required]],messages_ttl_check_in_hours:[1,[$.min(1),$.pattern(nn)]],messages_ttl_in_days:[7,[$.min(1),$.pattern(nn)]]})}observeStorageTypeChanges(){this.storageFormGroup.get("type").valueChanges.pipe(bn()).subscribe((e=>{switch(this.removeAllStorageValidators(),e){case G3.MEMORY:this.addMemoryStorageValidators(this.storageFormGroup);break;case G3.FILE:this.addFileStorageValidators(this.storageFormGroup);break;case G3.SQLITE:this.addSqliteStorageValidators(this.storageFormGroup)}}))}addMemoryStorageValidators(e){e.get("read_records_count").addValidators([$.required,$.min(1),$.pattern(nn)]),e.get("max_records_count").addValidators([$.required,$.min(1),$.pattern(nn)]),e.get("read_records_count").updateValueAndValidity({emitEvent:!1}),e.get("max_records_count").updateValueAndValidity({emitEvent:!1})}addFileStorageValidators(e){["max_file_count","max_read_records_count","max_records_per_file"].forEach((t=>{e.get(t).addValidators([$.required,$.min(1),$.pattern(nn)]),e.get(t).updateValueAndValidity({emitEvent:!1})}))}addSqliteStorageValidators(e){["messages_ttl_check_in_hours","messages_ttl_in_days","max_records_per_file"].forEach((t=>{e.get(t).addValidators([$.required,$.min(1),$.pattern(nn)]),e.get(t).updateValueAndValidity({emitEvent:!1})}))}static{this.ɵfac=function(e){return new(e||E4)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:E4,selectors:[["tb-gateway-storage-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>E4)),multi:!0},{provide:K,useExisting:c((()=>E4)),multi:!0}]),t.ɵɵStandaloneFeature],decls:15,vars:9,consts:[[1,"mat-content","mat-padding","flex","w-full","flex-col","gap-2",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom","w-full"],["translate","",1,"tb-form-panel-title"],["translate","",1,"tb-form-panel-hint"],["formControlName","type",1,"flex"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-panel-hint"],[3,"ngSwitch"],["class","tb-form-row no-border no-padding tb-standard-fields column-xs",4,"ngSwitchCase"],[4,"ngSwitchCase"],[3,"value"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["translate",""],["type","number","matInput","","formControlName","read_records_count"],[4,"ngIf"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["type","number","matInput","","formControlName","max_records_count"],["matInput","","formControlName","data_folder_path"],["aria-hidden","false","aria-label","help-icon","matSuffix","",1,"mat-form-field-infix","pointer-event","suffix-icon",2,"cursor","pointer",3,"matTooltip"],["matInput","","type","number","formControlName","max_file_count"],["matInput","","type","number","formControlName","max_read_records_count"],["matInput","","type","number","formControlName","max_records_per_file"],["matInput","","formControlName","data_file_path"],["matInput","","type","number","formControlName","messages_ttl_check_in_hours"],["matInput","","type","number","formControlName","messages_ttl_in_days"],["matIconSuffix","",1,"cursor-pointer",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"gateway.storage"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5,"gateway.hints.storage"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-toggle-select",4),t.ɵɵtemplate(7,Z3,3,4,"tb-toggle-option",5),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"div",6),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(11,7),t.ɵɵtemplate(12,a4,21,12,"section",8)(13,g4,41,22,"section",9)(14,I4,41,22,"section",9),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.storageFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.storageTypes),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,7,"gateway.hints."+n.storageFormGroup.get("type").value)),t.ɵɵadvance(2),t.ɵɵproperty("ngSwitch",n.storageFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.StorageTypes.MEMORY),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.StorageTypes.FILE),t.ɵɵadvance(),t.ɵɵproperty("ngSwitchCase",n.StorageTypes.SQLITE))},dependencies:t.ɵɵgetComponentDepsFactory(E4,[j,_]),encapsulation:2})}}function M4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-required")," "))}function k4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-min")," "))}function P4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-max")," "))}function D4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-pattern")," "))}function O4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-timeout-required")," "))}function A4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-timeout-min")," "))}function F4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-timeout-pattern")," "))}function R4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-required")," "))}function B4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-min")," "))}function N4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-keep-alive-pattern")," "))}function L4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-time-between-pings-required")," "))}function V4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-time-between-pings-min")," "))}function q4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-time-between-pings-pattern")," "))}function G4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-max-pings-without-data-required")," "))}function z4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-max-pings-without-data-min")," "))}function U4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-max-pings-without-data-pattern")," "))}function j4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-ping-interval-without-data-required")," "))}function H4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-ping-interval-without-data-min")," "))}function W4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.grpc-min-ping-interval-without-data-pattern")," "))}class $4{constructor(e){this.fb=e,this.initialized=new u,this.onChange=()=>{},this.grpcFormGroup=this.initGrpcFormGroup(),this.grpcFormGroup.valueChanges.pipe(bn()).subscribe((e=>{this.onChange(e)})),this.grpcFormGroup.get("enabled").valueChanges.pipe(bn()).subscribe((e=>{this.toggleRpcFields(e)}))}ngAfterViewInit(){this.initialized.emit({grpc:this.grpcFormGroup.value})}writeValue(e){e&&this.toggleRpcFields(e.enabled),this.grpcFormGroup.patchValue(e,{emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.grpcFormGroup.valid?null:{grpcFormGroup:{valid:!1}}}toggleRpcFields(e){const t=this.grpcFormGroup;e?(t.get("serverPort").enable({emitEvent:!1}),t.get("keepAliveTimeMs").enable({emitEvent:!1}),t.get("keepAliveTimeoutMs").enable({emitEvent:!1}),t.get("keepalivePermitWithoutCalls").enable({emitEvent:!1}),t.get("maxPingsWithoutData").enable({emitEvent:!1}),t.get("minTimeBetweenPingsMs").enable({emitEvent:!1}),t.get("minPingIntervalWithoutDataMs").enable({emitEvent:!1})):(t.get("serverPort").disable({emitEvent:!1}),t.get("keepAliveTimeMs").disable({emitEvent:!1}),t.get("keepAliveTimeoutMs").disable({emitEvent:!1}),t.get("keepalivePermitWithoutCalls").disable({emitEvent:!1}),t.get("maxPingsWithoutData").disable({emitEvent:!1}),t.get("minTimeBetweenPingsMs").disable({emitEvent:!1}),t.get("minPingIntervalWithoutDataMs").disable({emitEvent:!1}))}initGrpcFormGroup(){return this.fb.group({enabled:[!1],serverPort:[9595,[$.required,$.min(1),$.max(65535),$.pattern(nn)]],keepAliveTimeMs:[1e4,[$.required,$.min(1),$.pattern(nn)]],keepAliveTimeoutMs:[5e3,[$.required,$.min(1),$.pattern(nn)]],keepalivePermitWithoutCalls:[!0],maxPingsWithoutData:[0,[$.required,$.min(0),$.pattern(nn)]],minTimeBetweenPingsMs:[1e4,[$.required,$.min(1),$.pattern(nn)]],minPingIntervalWithoutDataMs:[5e3,[$.required,$.min(1),$.pattern(nn)]]})}static{this.ɵfac=function(e){return new(e||$4)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:$4,selectors:[["tb-gateway-grpc-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>$4)),multi:!0},{provide:K,useExisting:c((()=>$4)),multi:!0}]),t.ɵɵStandaloneFeature],decls:75,vars:47,consts:[[1,"mat-content","mat-padding","flex","flex-col","gap-2",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom","w-full"],["color","primary","formControlName","enabled",1,"mat-slide"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["color","primary","formControlName","keepalivePermitWithoutCalls",1,"mat-slide"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["translate",""],["matInput","","formControlName","serverPort","type","number","min","0"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],[4,"ngIf"],["matInput","","formControlName","keepAliveTimeoutMs","type","number","min","0"],["matInput","","formControlName","keepAliveTimeMs","type","number","min","0"],["matInput","","formControlName","minTimeBetweenPingsMs","type","number","min","0"],["matInput","","formControlName","maxPingsWithoutData","type","number","min","0"],["matInput","","formControlName","minPingIntervalWithoutDataMs","type","number","min","0"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"mat-slide-toggle",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",3),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"mat-slide-toggle",4),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"section")(11,"section",5)(12,"mat-form-field",6)(13,"mat-label",7),t.ɵɵtext(14,"gateway.server-port"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",8),t.ɵɵelementStart(16,"mat-icon",9),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(19,M4,3,3,"mat-error",10)(20,k4,3,3,"mat-error",10)(21,P4,3,3,"mat-error",10)(22,D4,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-form-field",6)(24,"mat-label",7),t.ɵɵtext(25,"gateway.grpc-keep-alive-timeout"),t.ɵɵelementEnd(),t.ɵɵelement(26,"input",11),t.ɵɵelementStart(27,"mat-icon",9),t.ɵɵpipe(28,"translate"),t.ɵɵtext(29,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(30,O4,3,3,"mat-error",10)(31,A4,3,3,"mat-error",10)(32,F4,3,3,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"section",5)(34,"mat-form-field",6)(35,"mat-label",7),t.ɵɵtext(36,"gateway.grpc-keep-alive"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",12),t.ɵɵelementStart(38,"mat-icon",9),t.ɵɵpipe(39,"translate"),t.ɵɵtext(40,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(41,R4,3,3,"mat-error",10)(42,B4,3,3,"mat-error",10)(43,N4,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-form-field",6)(45,"mat-label",7),t.ɵɵtext(46,"gateway.grpc-min-time-between-pings"),t.ɵɵelementEnd(),t.ɵɵelement(47,"input",13),t.ɵɵelementStart(48,"mat-icon",9),t.ɵɵpipe(49,"translate"),t.ɵɵtext(50,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(51,L4,3,3,"mat-error",10)(52,V4,3,3,"mat-error",10)(53,q4,3,3,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(54,"section",5)(55,"mat-form-field",6)(56,"mat-label",7),t.ɵɵtext(57,"gateway.grpc-max-pings-without-data"),t.ɵɵelementEnd(),t.ɵɵelement(58,"input",14),t.ɵɵelementStart(59,"mat-icon",9),t.ɵɵpipe(60,"translate"),t.ɵɵtext(61,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(62,G4,3,3,"mat-error",10)(63,z4,3,3,"mat-error",10)(64,U4,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(65,"mat-form-field",6)(66,"mat-label",7),t.ɵɵtext(67,"gateway.grpc-min-ping-interval-without-data"),t.ɵɵelementEnd(),t.ɵɵelement(68,"input",15),t.ɵɵelementStart(69,"mat-icon",9),t.ɵɵpipe(70,"translate"),t.ɵɵtext(71,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(72,j4,3,3,"mat-error",10)(73,H4,3,3,"mat-error",10)(74,W4,3,3,"mat-error",10),t.ɵɵelementEnd()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.grpcFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,29,"gateway.grpc")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(6,31,"gateway.hints.permit-without-calls")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,33,"gateway.permit-without-calls")," "),t.ɵɵadvance(8),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,35,"gateway.hints.server-port")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("max")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("serverPort").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(28,37,"gateway.hints.grpc-keep-alive-timeout")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeoutMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeoutMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeoutMs").hasError("pattern")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(39,39,"gateway.hints.grpc-keep-alive")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("keepAliveTimeMs").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(49,41,"gateway.hints.grpc-min-time-between-pings")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minTimeBetweenPingsMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minTimeBetweenPingsMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minTimeBetweenPingsMs").hasError("pattern")),t.ɵɵadvance(6),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(60,43,"gateway.hints.grpc-max-pings-without-data")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("maxPingsWithoutData").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("maxPingsWithoutData").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("maxPingsWithoutData").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(70,45,"gateway.hints.grpc-min-ping-interval-without-data")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minPingIntervalWithoutDataMs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minPingIntervalWithoutDataMs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.grpcFormGroup.get("minPingIntervalWithoutDataMs").hasError("pattern")))},dependencies:t.ɵɵgetComponentDepsFactory($4,[j,_]),encapsulation:2})}}function K4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.date-format-required")," "))}function Y4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.log-format-required")," "))}function X4(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function Z4(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",30),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,i=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(i.localLogsConfigTranslateMap.get(e))}}function Q4(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate(e)}}function J4(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.file-path-required")," "))}function e5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.saving-period-required")," "))}function t5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.saving-period-min")," "))}function n5(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,e.value)," ")}}function i5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.backup-count-required")," "))}function a5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.logs.backup-count-min")," "))}class r5{constructor(e){this.fb=e,this.initialized=new u,this.logSavingPeriods=W3,this.localLogsConfigs=Object.keys(z3),this.localLogsConfigTranslateMap=U3,this.gatewayLogLevel=Object.values(lt),this.remoteLogLevel=Object.values(lt).filter((e=>e!==lt.NONE)),this.onChange=()=>{},this.logsFormGroup=this.initLogsFormGroup(),this.showRemoteLogsControl=this.fb.control(!1),this.logsFormGroup.valueChanges.pipe(bn()).subscribe((e=>{this.onChange(e)})),this.logSelector=this.fb.control(z3.service);for(const e of Object.keys(z3))this.addLocalLogConfig(e,{});this.showRemoteLogsControl.valueChanges.pipe(bn()).subscribe((e=>this.logsFormGroup.get("logLevel")[e?"enable":"disable"]()))}ngAfterViewInit(){this.initialized.emit({logs:this.logsFormGroup.value})}writeValue(e){this.logsFormGroup.patchValue(e,{emitEvent:!1}),this.updateRemoteLogs(e?.logLevel??lt.NONE)}registerOnChange(e){this.onChange=e}registerOnTouched(e){}getLogFormGroup(e){return this.logsFormGroup.get(`local.${e}`)}validate(){return this.logsFormGroup.valid?null:{logsFormGroup:{valid:!1}}}initLogsFormGroup(){return this.fb.group({dateFormat:["%Y-%m-%d %H:%M:%S",[$.required,$.pattern(/^[^\s].*[^\s]$/)]],logFormat:["%(asctime)s.%(msecs)03d - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s",[$.required,$.pattern(/^[^\s].*[^\s]$/)]],type:["remote",[$.required]],logLevel:[{value:lt.INFO,disabled:!0}],local:this.fb.group({})})}addLocalLogConfig(e,t){const n=this.logsFormGroup.get("local"),i=this.fb.group({logLevel:[t.logLevel||lt.INFO,[$.required]],filePath:[t.filePath||"./logs",[$.required]],backupCount:[t.backupCount||7,[$.required,$.min(0)]],savingTime:[t.savingTime||3,[$.required,$.min(0)]],savingPeriod:[t.savingPeriod||H3.days,[$.required]]});n.addControl(e,i,{emitEvent:!1})}updateRemoteLogs(e){const t=e&&e!==lt.NONE;this.showRemoteLogsControl.patchValue(t,{emitEvent:!1}),this.logsFormGroup.get("logLevel")[t?"enable":"disable"]({emitEvent:!1}),this.logsFormGroup.get("logLevel").patchValue(e===lt.NONE?lt.INFO:e,{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||r5)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:r5,selectors:[["tb-gateway-logs-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>r5)),multi:!0},{provide:K,useExisting:c((()=>r5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:72,vars:33,consts:[[1,"mat-content","mat-padding","flex","flex-col","gap-2",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom"],[1,"flex","flex-col"],["appearance","outline"],["translate",""],["matInput","","formControlName","dateFormat"],[4,"ngIf"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["matInput","","formControlName","logFormat","rows","2"],[1,"tb-form-panel"],[1,"tb-settings",3,"expanded"],[1,"flex-wrap"],[1,"mat-slide",3,"click","formControl"],[3,"tb-hint-tooltip-icon"],["formControlName","logLevel"],[3,"value",4,"ngFor","ngForOf"],["formGroupName","local",1,"tb-form-panel","no-padding-bottom"],["translate","",1,"tb-form-panel-title"],[1,"toggle-group",3,"formControl"],["class","first-capital",3,"value",4,"ngFor","ngForOf"],[3,"formGroup"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["matInput","","formControlName","filePath"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","saving-period"],["matInput","","formControlName","savingTime","type","number","min","0"],["appearance","outline","hideRequiredMarker","",2,"min-width","110px","width","30%"],["formControlName","savingPeriod"],["matInput","","formControlName","backupCount","type","number","min","0"],[3,"value"],[1,"first-capital",3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1)(2,"div",2)(3,"mat-form-field",3)(4,"mat-label",4),t.ɵɵtext(5,"gateway.logs.date-format"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",5),t.ɵɵtemplate(7,K4,3,3,"mat-error",6),t.ɵɵelementStart(8,"mat-icon",7),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",3)(12,"mat-label",4),t.ɵɵtext(13,"gateway.logs.log-format"),t.ɵɵelementEnd(),t.ɵɵelement(14,"textarea",8),t.ɵɵtemplate(15,Y4,3,3,"mat-error",6),t.ɵɵelementStart(16,"mat-icon",7),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"info_outlined "),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(19,"div",9)(20,"mat-expansion-panel",10)(21,"mat-expansion-panel-header",11)(22,"mat-panel-title")(23,"mat-slide-toggle",12),t.ɵɵlistener("click",(function(e){return e.stopPropagation()})),t.ɵɵelementStart(24,"mat-label")(25,"div",13),t.ɵɵpipe(26,"translate"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(29,"mat-form-field",3)(30,"mat-label",4),t.ɵɵtext(31,"gateway.logs.level"),t.ɵɵelementEnd(),t.ɵɵelementStart(32,"mat-select",14),t.ɵɵtemplate(33,X4,2,2,"mat-option",15),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(34,"div",16)(35,"div",17),t.ɵɵtext(36,"gateway.logs.local"),t.ɵɵelementEnd(),t.ɵɵelementStart(37,"tb-toggle-select",18),t.ɵɵtemplate(38,Z4,2,2,"tb-toggle-option",19),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(39,20),t.ɵɵelementStart(40,"div",21)(41,"mat-form-field",22)(42,"mat-label",4),t.ɵɵtext(43,"gateway.logs.level"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-select",14),t.ɵɵtemplate(45,Q4,2,2,"mat-option",15),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"mat-form-field",22)(47,"mat-label",4),t.ɵɵtext(48,"gateway.logs.file-path"),t.ɵɵelementEnd(),t.ɵɵelement(49,"input",23),t.ɵɵtemplate(50,J4,3,3,"mat-error",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(51,"div",21)(52,"div",24)(53,"mat-form-field",22)(54,"mat-label",4),t.ɵɵtext(55,"gateway.logs.saving-period"),t.ɵɵelementEnd(),t.ɵɵelement(56,"input",25),t.ɵɵtemplate(57,e5,3,3,"mat-error",6)(58,t5,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(59,"mat-form-field",26)(60,"mat-select",27),t.ɵɵtemplate(61,n5,3,4,"mat-option",15),t.ɵɵpipe(62,"keyvalue"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(63,"mat-form-field",22)(64,"mat-label",4),t.ɵɵtext(65,"gateway.logs.backup-count"),t.ɵɵelementEnd(),t.ɵɵelement(66,"input",28),t.ɵɵtemplate(67,i5,3,3,"mat-error",6)(68,a5,3,3,"mat-error",6),t.ɵɵelementStart(69,"mat-icon",7),t.ɵɵpipe(70,"translate"),t.ɵɵtext(71,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.logsFormGroup),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.logsFormGroup.get("dateFormat").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,21,"gateway.hints.date-form")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.logsFormGroup.get("logFormat").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(17,23,"gateway.hints.log-format")),t.ɵɵadvance(4),t.ɵɵproperty("expanded",n.showRemoteLogsControl.value),t.ɵɵadvance(3),t.ɵɵproperty("formControl",n.showRemoteLogsControl),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(26,25,"gateway.hints.remote-log")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,27,"gateway.logs.remote")),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.remoteLogLevel),t.ɵɵadvance(4),t.ɵɵproperty("formControl",n.logSelector),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.localLogsConfigs),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.getLogFormGroup(n.logSelector.value)),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.gatewayLogLevel),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".filePath").hasError("required")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".savingTime").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".savingTime").hasError("min")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(62,29,n.logSavingPeriods)),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".backupCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.logsFormGroup.get("local."+n.logSelector.value+".backupCount").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(70,31,"gateway.hints.backup-count")))},dependencies:t.ɵɵgetComponentDepsFactory(r5,[j,_]),encapsulation:2})}}function o5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"security.clientId-required")," "))}function s5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",6),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"gateway.copy-client-id")),t.ɵɵproperty("copyText",e.usernameFormGroup.get("clientId").value)}}function l5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("clientId"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-client-id"))}function p5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"security.username-required")," "))}function c5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",6),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"gateway.copy-username")),t.ɵɵproperty("copyText",e.usernameFormGroup.get("username").value)}}function d5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("username"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-user-name"))}function u5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",6),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"gateway.copy-password")),t.ɵɵproperty("copyText",e.usernameFormGroup.get("password").value)}}function m5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",13),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.generate("password"))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-password"))}function h5(e,n){1&e&&(t.ɵɵelement(0,"tb-error",14),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("error",t.ɵɵpipeBind1(1,1,"device.client-id-or-user-name-necessary"))}function g5(e,n){1&e&&(t.ɵɵelement(0,"tb-error",14),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("error",t.ɵɵpipeBind1(1,1,"gateway.hints.username-required-with-password"))}class f5{constructor(e){this.fb=e,this.onChange=()=>{},this.initForm(),this.usernameFormGroup.valueChanges.pipe(bn()).subscribe((e=>this.onChange(e)))}writeValue(e){this.usernameFormGroup.patchValue(e,{emitEvent:!1})}registerOnChange(e){this.onChange=e}registerOnTouched(e){}setDisabledState(e){e?this.usernameFormGroup.disable({emitEvent:!1}):this.usernameFormGroup.enable({emitEvent:!1})}validate(){return this.usernameFormGroup.valid?null:{usernameFormGroup:{valid:!1}}}initForm(){this.usernameFormGroup=this.createSecurityFormGroup()}createSecurityFormGroup(){return this.fb.group({clientId:[null,[$.pattern(/^[^.\s]+$/)]],username:[null,[$.pattern(/^[^.\s]+$/)]],password:[null,[$.pattern(/^[^.\s]+$/)]]},{validators:[this.atLeastOneRequired,this.usernameRequired]})}atLeastOneRequired(e){const t=e.get("clientId").value,n=e.get("username").value;return t||n?null:{atLeastOneRequired:!0}}usernameRequired(e){const t=e.get("username").value,n=e.get("password").value;return!t&&n?{usernameRequired:!0}:null}generate(e){this.usernameFormGroup.get(e).patchValue(Re(20))}static{this.ɵfac=function(e){return new(e||f5)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:f5,selectors:[["tb-gateway-username-configuration"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>f5)),multi:!0},{provide:K,useExisting:c((()=>f5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:33,vars:17,consts:[[3,"formGroup"],[1,"xs:flex-col","no-border","no-padding","tb-standard-fields","flex","gap-2"],["appearance","outline",1,"flex","flex-1"],["translate",""],["matInput","","formControlName","clientId"],[4,"ngIf"],["matSuffix","","miniButton","false","tooltipPosition","above","icon","content_copy",3,"copyText","tooltipText"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"matTooltip"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["matInput","","formControlName","username"],["appearance","outline","subscriptSizing","dynamic",2,"width","100%"],["matInput","","formControlName","password"],["class","block",3,"error",4,"ngIf"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"click","matTooltip"],[1,"block",3,"error"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"mat-form-field",2)(3,"mat-label",3),t.ɵɵtext(4,"security.clientId"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",4),t.ɵɵtemplate(6,o5,3,3,"mat-error",5)(7,s5,2,4,"tb-copy-button",6)(8,l5,4,3,"button",7),t.ɵɵelementStart(9,"mat-icon",8),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"mat-form-field",2)(13,"mat-label",3),t.ɵɵtext(14,"security.username"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",9),t.ɵɵtemplate(16,p5,3,3,"mat-error",5)(17,c5,2,4,"tb-copy-button",6)(18,d5,4,3,"button",7),t.ɵɵelementStart(19,"mat-icon",8),t.ɵɵpipe(20,"translate"),t.ɵɵtext(21,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(22,"mat-form-field",10)(23,"mat-label",3),t.ɵɵtext(24,"gateway.password"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",11),t.ɵɵtemplate(26,u5,2,4,"tb-copy-button",6)(27,m5,4,3,"button",7),t.ɵɵelementStart(28,"mat-icon",8),t.ɵɵpipe(29,"translate"),t.ɵɵtext(30,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵtemplate(31,h5,2,3,"tb-error",12)(32,g5,2,3,"tb-error",12)),2&e&&(t.ɵɵproperty("formGroup",n.usernameFormGroup),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.usernameFormGroup.get("clientId").hasError("required")),t.ɵɵadvance(),t.ɵɵconditional(n.usernameFormGroup.get("clientId").value?7:8),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,11,"gateway.hints.client-id")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.usernameFormGroup.get("username").hasError("required")),t.ɵɵadvance(),t.ɵɵconditional(n.usernameFormGroup.get("username").value?17:18),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(20,13,"gateway.hints.username")),t.ɵɵadvance(7),t.ɵɵconditional(n.usernameFormGroup.get("password").value?26:27),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(29,15,"gateway.hints.password")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.usernameFormGroup.hasError("atLeastOneRequired")&&n.usernameFormGroup.touched),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.usernameFormGroup.hasError("usernameRequired")&&n.usernameFormGroup.touched))},dependencies:t.ɵɵgetComponentDepsFactory(f5,[j,_]),encapsulation:2})}}class y5{constructor(e,t){this.deviceService=e,this.destroyRef=t,this.initialCredentialsSubject=new ie(null)}get initialCredentials(){return this.initialCredentialsSubject.value}get initialCredentials$(){return this.initialCredentialsSubject.asObservable()}updateCredentials(e){let t={};switch(e.type){case $3.USERNAME_PASSWORD:this.shouldUpdateCredentials(e)&&(t=this.generateMqttCredentials(e));break;case $3.ACCESS_TOKEN:case $3.TLS_ACCESS_TOKEN:this.shouldUpdateAccessToken(e)&&(t={credentialsType:U.ACCESS_TOKEN,credentialsId:e.accessToken,credentialsValue:null})}return this.initialCredentialsSubject.next({...this.initialCredentials,...t}),Object.keys(t).length?this.deviceService.saveDeviceCredentials(this.initialCredentials):ae(null)}setInitialCredentials(e){this.deviceService.getDeviceCredentials(e.id).pipe(bn(this.destroyRef)).subscribe((e=>{this.initialCredentialsSubject.next({...e,version:null})}))}shouldUpdateSecurityConfig(e){switch(e.type){case $3.USERNAME_PASSWORD:return this.shouldUpdateCredentials(e);case $3.ACCESS_TOKEN:case $3.TLS_ACCESS_TOKEN:return this.shouldUpdateAccessToken(e)}}credentialsToSecurityConfig(e){const t=e.credentialsType===U.MQTT_BASIC?$3.USERNAME_PASSWORD:$3.ACCESS_TOKEN;if(e.credentialsType!==U.MQTT_BASIC)return{type:t,accessToken:e.credentialsId};if(e.credentialsValue){const{clientId:n,userName:i,password:a}=JSON.parse(e.credentialsValue);return{type:t,clientId:n,username:i,password:a}}}shouldUpdateCredentials(e){if(this.initialCredentials.credentialsType!==U.MQTT_BASIC)return!0;const t=JSON.parse(this.initialCredentials.credentialsValue);return!(t.clientId===e.clientId&&t.userName===e.username&&t.password===e.password)}shouldUpdateAccessToken(e){return this.initialCredentials.credentialsType!==U.ACCESS_TOKEN||this.initialCredentials.credentialsId!==e.accessToken}generateMqttCredentials(e){const{clientId:t,username:n,password:i}=e,a={...t&&{clientId:t},...n&&{userName:n},...i&&{password:i}};return{credentialsType:U.MQTT_BASIC,credentialsValue:JSON.stringify(a)}}static{this.ɵfac=function(e){return new(e||y5)(t.ɵɵinject(_e.DeviceService),t.ɵɵinject(t.DestroyRef))}}static{this.ɵprov=t.ɵɵdefineInjectable({token:y5,factory:y5.ɵfac})}}function v5(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",8),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.key),t.ɵɵadvance(),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(2,2,e.value)," ")}}function x5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"security.access-token-required")," "))}function b5(e,n){if(1&e&&(t.ɵɵelement(0,"tb-copy-button",13),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext(2);t.ɵɵpropertyInterpolate("tooltipText",t.ɵɵpipeBind1(1,2,"device.copy-access-token")),t.ɵɵproperty("copyText",e.securityFormGroup.get("accessToken").value)}}function w5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",16),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.generateAccessToken())})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"autorenew"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"device.generate-access-token"))}function S5(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",9)(1,"mat-label",10),t.ɵɵtext(2,"security.access-token"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",11),t.ɵɵtemplate(4,x5,3,3,"mat-error",12)(5,b5,2,4,"tb-copy-button",13)(6,w5,4,3,"button",14),t.ɵɵelementStart(7,"mat-icon",15),t.ɵɵpipe(8,"translate"),t.ɵɵtext(9,"info_outlined "),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.securityFormGroup.get("accessToken").hasError("required")),t.ɵɵadvance(),t.ɵɵconditional(e.securityFormGroup.get("accessToken").value?5:6),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,3,"gateway.hints.token"))}}function C5(e,n){1&e&&(t.ɵɵelement(0,"tb-file-input",17),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"translate")),2&e&&(t.ɵɵpropertyInterpolate("hint",t.ɵɵpipeBind1(1,5,"gateway.hints.ca-cert")),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(2,7,"security.ca-cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(3,9,"gateway.drop-file")),t.ɵɵproperty("allowedExtensions","pem,cert,key")("accept",".pem, application/pem,.cert, application/cert, .key,application/key"))}class _5{constructor(e,t,n){this.fb=e,this.cd=t,this.gatewayCredentialsService=n,this.initialized=new u,this.securityTypes=K3,this.onChange=()=>{},this.securityFormGroup=this.createSecurityFormGroup(),this.setupFormListeners()}ngAfterViewInit(){const{usernamePassword:e,...t}=this.securityFormGroup.value;this.initialized.emit({thingsboard:{security:e?{...t,...e}:t}})}writeValue(e){e?this.updateFormBySecurityConfig(e):this.updateFormBySecurityConfig(this.gatewayCredentialsService.credentialsToSecurityConfig(this.gatewayCredentialsService.initialCredentials))}registerOnChange(e){this.onChange=e}registerOnTouched(e){}validate(){return this.securityFormGroup.valid?null:{securityFormGroup:{valid:!1}}}updateFormBySecurityConfig(e){const{clientId:t,username:n,password:i,...a}=e??{};a?.type===$3.USERNAME_PASSWORD?this.securityFormGroup.patchValue({...a,usernamePassword:{clientId:t,username:n,password:i}},{emitEvent:!1}):this.securityFormGroup.patchValue(a,{emitEvent:!1}),this.toggleBySecurityType(this.securityFormGroup.get("type").value)}createSecurityFormGroup(){return this.fb.group({type:[$3.ACCESS_TOKEN,[$.required]],accessToken:[null,[$.required,$.pattern(/^[^.\s]+$/)]],caCert:[null,[$.required]],usernamePassword:[]})}setupFormListeners(){this.securityFormGroup.valueChanges.pipe(bn()).subscribe((({usernamePassword:e,...t})=>{this.onChange(e?{...t,...e}:t)})),this.securityFormGroup.get("type").valueChanges.pipe(bn()).subscribe((e=>{this.toggleBySecurityType(e)})),this.securityFormGroup.get("caCert").valueChanges.pipe(bn()).subscribe((()=>this.cd.detectChanges()))}toggleBySecurityType(e){switch(this.securityFormGroup.disable({emitEvent:!1}),this.securityFormGroup.get("type").enable({emitEvent:!1}),e){case $3.ACCESS_TOKEN:this.securityFormGroup.get("accessToken").enable({emitEvent:!1});break;case $3.TLS_PRIVATE_KEY:this.securityFormGroup.get("caCert").enable({emitEvent:!1});break;case $3.TLS_ACCESS_TOKEN:this.securityFormGroup.get("accessToken").enable({emitEvent:!1}),this.securityFormGroup.get("caCert").enable({emitEvent:!1});break;case $3.USERNAME_PASSWORD:this.securityFormGroup.get("usernamePassword").enable({emitEvent:!1})}}generateAccessToken(){this.securityFormGroup.get("accessToken").patchValue(Re(20))}static{this.ɵfac=function(e){return new(e||_5)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(y5))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:_5,selectors:[["tb-gateway-security-configuration"]],outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>_5)),multi:!0},{provide:K,useExisting:c((()=>_5)),multi:!0}]),t.ɵɵStandaloneFeature],decls:10,vars:8,consts:[[1,"tb-form-panel"],["translate","",1,"tb-form-panel-title"],[3,"formGroup"],["formControlName","type",1,"toggle-group","flex"],[3,"value",4,"ngFor","ngForOf"],["appearance","outline",4,"ngIf"],["formControlName","usernamePassword"],["formControlName","caCert",3,"hint","label","allowedExtensions","accept","dropLabel",4,"ngIf"],[3,"value"],["appearance","outline"],["translate",""],["matInput","","formControlName","accessToken"],[4,"ngIf"],["matSuffix","","miniButton","false","tooltipPosition","above","icon","content_copy",3,"copyText","tooltipText"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"matTooltip"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["type","button","matSuffix","","mat-icon-button","","aria-label","Generate","matTooltipPosition","above",3,"click","matTooltip"],["formControlName","caCert",3,"hint","label","allowedExtensions","accept","dropLabel"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2,"security.security"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(3,2),t.ɵɵelementStart(4,"tb-toggle-select",3),t.ɵɵtemplate(5,v5,3,4,"tb-toggle-option",4),t.ɵɵpipe(6,"keyvalue"),t.ɵɵelementEnd(),t.ɵɵtemplate(7,S5,10,5,"mat-form-field",5),t.ɵɵelement(8,"tb-gateway-username-configuration",6),t.ɵɵtemplate(9,C5,4,11,"tb-file-input",7),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(3),t.ɵɵproperty("formGroup",n.securityFormGroup),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(6,6,n.securityTypes)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.securityFormGroup.get("type").value.toLowerCase().includes("accesstoken")),t.ɵɵadvance(),t.ɵɵclassProp("hidden","usernamePassword"!==n.securityFormGroup.get("type").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.securityFormGroup.get("type").value.toLowerCase().includes("tls")))},dependencies:t.ɵɵgetComponentDepsFactory(_5,[j,_,f5]),encapsulation:2})}}const T5=["configGroup"];function I5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-host-required")))}function E5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-required")))}function M5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-min")))}function k5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-max")))}function P5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.thingsboard-port-pattern")))}function D5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-security-configuration",20),t.ɵɵlistener("initialized",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext(2);return t.ɵɵresetView(i.onInitialized(n))})),t.ɵɵelementEnd()}}function O5(e,n){if(1&e&&t.ɵɵelement(0,"tb-report-strategy",21),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("defaultValue",e.ReportStrategyDefaultValue.Gateway)}}function A5(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",7)(1,"div",8)(2,"div",9),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"mat-slide-toggle",10),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",9),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-slide-toggle",11),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"div",12)(13,"mat-form-field",13)(14,"mat-label",14),t.ɵɵtext(15,"gateway.thingsboard-host"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",15),t.ɵɵelementStart(17,"mat-icon",16),t.ɵɵpipe(18,"translate"),t.ɵɵtext(19,"info_outlined "),t.ɵɵelementEnd(),t.ɵɵtemplate(20,I5,3,3,"mat-error"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",13)(22,"mat-label",14),t.ɵɵtext(23,"gateway.thingsboard-port"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",17),t.ɵɵtemplate(25,E5,3,3,"mat-error")(26,M5,3,3,"mat-error")(27,k5,3,3,"mat-error")(28,P5,3,3,"mat-error"),t.ɵɵelementStart(29,"mat-icon",16),t.ɵɵpipe(30,"translate"),t.ɵɵtext(31,"info_outlined "),t.ɵɵelementEnd()()()(),t.ɵɵtemplate(32,D5,1,0,"tb-gateway-security-configuration",18),t.ɵɵpipe(33,"async"),t.ɵɵtemplate(34,O5,1,1,"tb-report-strategy",19),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,10,"gateway.hints.remote-configuration")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,12,"gateway.remote-configuration")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(8,14,"gateway.hints.remote-shell")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,16,"gateway.remote-shell")," "),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(18,18,"gateway.hints.host")),t.ɵɵadvance(3),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.host").hasError("required")?20:-1),t.ɵɵadvance(5),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.port").hasError("required")?25:e.basicFormGroup.get("thingsboard.port").hasError("min")?26:e.basicFormGroup.get("thingsboard.port").hasError("max")?27:e.basicFormGroup.get("thingsboard.port").hasError("pattern")?28:-1),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(30,20,"gateway.hints.port")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",t.ɵɵpipeBind1(33,22,e.initialCredentials$)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.withReportStrategy)}}function F5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-slide-toggle",23)(1,"mat-label",33),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(5,"mat-slide-toggle",34)(6,"mat-label",33),t.ɵɵpipe(7,"translate"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(2,4,"gateway.hints.enable-general-statistics")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,6,"gateway.statistics.general-statistics")," "),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(7,8,"gateway.hints.enable-custom-statistics")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,10,"gateway.statistics.custom-statistics")," "))}function R5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-slide-toggle",23),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.statistics")," "))}function B5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.send-period-required")))}function N5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.send-period-min")))}function L5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.send-period-pattern")))}function V5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.custom-send-period-required")))}function q5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.custom-send-period-min")))}function G5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.custom-send-period-pattern")))}function z5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-required")))}function U5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-pattern")," "))}function j5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.time-series-name-already-exists")))}function H5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-required")))}function W5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-min")))}function $5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.timeout-pattern")))}function K5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.command-required")))}function Y5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.command-pattern")))}function X5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",35)(1,"section",36)(2,"section",37)(3,"mat-form-field",38)(4,"mat-label",14),t.ɵɵtext(5,"gateway.statistics.name"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",39),t.ɵɵtemplate(7,z5,3,3,"mat-error")(8,U5,3,3,"mat-error")(9,j5,3,3,"mat-error"),t.ɵɵelementStart(10,"mat-icon",16),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(13,"mat-form-field",38)(14,"mat-label",14),t.ɵɵtext(15,"gateway.statistics.timeout"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",40),t.ɵɵtemplate(17,H5,3,3,"mat-error")(18,W5,3,3,"mat-error")(19,$5,3,3,"mat-error"),t.ɵɵelementStart(20,"mat-icon",16),t.ɵɵpipe(21,"translate"),t.ɵɵtext(22,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(23,"section")(24,"mat-form-field",41)(25,"mat-label",14),t.ɵɵtext(26,"gateway.statistics.command"),t.ɵɵelementEnd(),t.ɵɵelement(27,"input",42),t.ɵɵtemplate(28,K5,3,3,"mat-error")(29,Y5,3,3,"mat-error"),t.ɵɵelementStart(30,"mat-icon",16),t.ɵɵpipe(31,"translate"),t.ɵɵtext(32,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(33,"section")(34,"mat-expansion-panel",43)(35,"mat-expansion-panel-header")(36,"mat-panel-title")(37,"div",28),t.ɵɵtext(38,"gateway.advanced-settings"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(39,"mat-form-field",38)(40,"mat-label",14),t.ɵɵtext(41,"gateway.statistics.install-cmd"),t.ɵɵelementEnd(),t.ɵɵelement(42,"input",44),t.ɵɵelementStart(43,"mat-icon",45),t.ɵɵpipe(44,"translate"),t.ɵɵtext(45,"info_outlined "),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(46,"button",46),t.ɵɵpipe(47,"translate"),t.ɵɵlistener("click",(function(n){const i=t.ɵɵrestoreView(e).index,a=t.ɵɵnextContext(2);return t.ɵɵresetView(a.removeCommandControl(i,n))})),t.ɵɵelementStart(48,"mat-icon"),t.ɵɵtext(49,"delete"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,i=n.index,a=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("formGroupName",i),t.ɵɵadvance(6),t.ɵɵconditional(e.get("attributeOnGateway").hasError("required")?7:e.get("attributeOnGateway").hasError("pattern")?8:e.get("attributeOnGateway").hasError("duplicateName")?9:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(11,10,"gateway.hints.attribute")),t.ɵɵadvance(7),t.ɵɵconditional(e.get("timeout").hasError("required")?17:e.get("timeout").hasError("min")?18:e.get("timeout").hasError("pattern")?19:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(21,12,"gateway.hints.timeout")),t.ɵɵadvance(8),t.ɵɵconditional(e.get("command").hasError("required")?28:e.get("command").hasError("pattern")?29:-1),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(31,14,"gateway.hints.command")),t.ɵɵadvance(13),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(44,16,"gateway.hints.install-cmd")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(47,18,"gateway.statistics.remove")),t.ɵɵproperty("disabled",!a.basicFormGroup.get("thingsboard.remoteConfiguration").value)}}function Z5(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",7)(1,"div",22),t.ɵɵtemplate(2,F5,10,12)(3,R5,3,3,"mat-slide-toggle",23),t.ɵɵelementStart(4,"mat-form-field",24)(5,"mat-label",14),t.ɵɵtext(6,"gateway.statistics.send-period"),t.ɵɵelementEnd(),t.ɵɵelement(7,"input",25),t.ɵɵtemplate(8,B5,3,3,"mat-error")(9,N5,3,3,"mat-error")(10,L5,3,3,"mat-error"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",24)(12,"mat-label",14),t.ɵɵtext(13,"gateway.statistics.custom-send-period"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",26),t.ɵɵtemplate(15,V5,3,3,"mat-error")(16,q5,3,3,"mat-error")(17,G5,3,3,"mat-error"),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",27)(19,"div",28),t.ɵɵtext(20,"gateway.statistics.commands"),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",29),t.ɵɵtext(22,"gateway.hints.commands"),t.ɵɵelementEnd(),t.ɵɵelementContainerStart(23,30),t.ɵɵtemplate(24,X5,50,20,"div",31),t.ɵɵelementStart(25,"button",32),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.addCommand())})),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵelementContainerEnd(),t.ɵɵelementEnd()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵconditional(e.hasUpdatedStatistics?2:3),t.ɵɵadvance(6),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.statistics.statsSendPeriodInSeconds").hasError("required")?8:e.basicFormGroup.get("thingsboard.statistics.statsSendPeriodInSeconds").hasError("min")?9:e.basicFormGroup.get("thingsboard.statistics.statsSendPeriodInSeconds").hasError("pattern")?10:-1),t.ɵɵadvance(7),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.statistics.customStatsSendPeriodInSeconds").hasError("required")?15:e.basicFormGroup.get("thingsboard.statistics.customStatsSendPeriodInSeconds").hasError("min")?16:e.basicFormGroup.get("thingsboard.statistics.customStatsSendPeriodInSeconds").hasError("pattern")?17:-1),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",e.commandFormArray().controls),t.ɵɵadvance(),t.ɵɵproperty("disabled",!e.basicFormGroup.get("thingsboard.remoteConfiguration").value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(27,6,"gateway.statistics.add")," ")}}function Q5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-timeout-seconds-required")))}function J5(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-timeout-seconds-min")))}function e6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-timeout-seconds-pattern")))}function t6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-check-period-seconds-required")))}function n6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-check-period-seconds-min")))}function i6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.inactivity-check-period-seconds-pattern")))}function a6(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",37)(1,"mat-form-field",38)(2,"mat-label",14),t.ɵɵtext(3,"gateway.inactivity-timeout-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",56),t.ɵɵtemplate(5,Q5,3,3,"mat-error")(6,J5,3,3,"mat-error")(7,e6,3,3,"mat-error"),t.ɵɵelementStart(8,"mat-icon",16),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-form-field",38)(12,"mat-label",14),t.ɵɵtext(13,"gateway.inactivity-check-period-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",57),t.ɵɵtemplate(15,t6,3,3,"mat-error")(16,n6,3,3,"mat-error")(17,i6,3,3,"mat-error"),t.ɵɵelementStart(18,"mat-icon",16),t.ɵɵpipe(19,"translate"),t.ɵɵtext(20,"info_outlined "),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(5),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityTimeoutSeconds").hasError("required")?5:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityTimeoutSeconds").hasError("min")?6:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityTimeoutSeconds").hasError("pattern")?7:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,4,"gateway.hints.inactivity-timeout")),t.ɵɵadvance(7),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds").hasError("required")?15:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds").hasError("min")?16:e.basicFormGroup.get("thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds").hasError("pattern")?17:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(19,6,"gateway.hints.inactivity-period"))}}function r6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.min-pack-send-delay-required")))}function o6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.min-pack-send-delay-min")))}function s6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.min-pack-send-delay-pattern")))}function l6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.mqtt-qos-required")))}function p6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.mqtt-qos-range")))}function c6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.mqtt-qos-range")))}function d6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.check-connectors-configuration-required")))}function u6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.check-connectors-configuration-min")))}function m6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.check-connectors-configuration-pattern")))}function h6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.max-payload-size-bytes-required")))}function g6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.max-payload-size-bytes-min")))}function f6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.max-payload-size-bytes-pattern")))}function y6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.min-pack-size-to-send-required")))}function v6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.min-pack-size-to-send-min")))}function x6(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"gateway.statistics.min-pack-size-to-send-pattern")))}function b6(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",7)(1,"div",47)(2,"div",9),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"mat-slide-toggle",48),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(7,a6,21,8,"section",49),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"div",8)(9,"div",28),t.ɵɵtext(10,"gateway.advanced"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"section",37)(12,"mat-form-field",38)(13,"mat-label",14),t.ɵɵtext(14,"gateway.min-pack-send-delay"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",50),t.ɵɵtemplate(16,r6,3,3,"mat-error")(17,o6,3,3,"mat-error")(18,s6,3,3,"mat-error"),t.ɵɵelementStart(19,"mat-icon",16),t.ɵɵpipe(20,"translate"),t.ɵɵtext(21,"info_outlined "),t.ɵɵelementEnd()(),t.ɵɵelementStart(22,"mat-form-field",38)(23,"mat-label",14),t.ɵɵtext(24,"gateway.mqtt-qos"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-select",51)(26,"mat-option",52),t.ɵɵtext(27,"0"),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-option",52),t.ɵɵtext(29,"1"),t.ɵɵelementEnd()(),t.ɵɵtemplate(30,l6,3,3,"mat-error")(31,p6,3,3,"mat-error")(32,c6,3,3,"mat-error"),t.ɵɵelementStart(33,"mat-icon",16),t.ɵɵpipe(34,"translate"),t.ɵɵtext(35,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(36,"section",37)(37,"mat-form-field",38)(38,"mat-label",14),t.ɵɵtext(39,"gateway.statistics.check-connectors-configuration"),t.ɵɵelementEnd(),t.ɵɵelement(40,"input",53),t.ɵɵtemplate(41,d6,3,3,"mat-error")(42,u6,3,3,"mat-error")(43,m6,3,3,"mat-error"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-form-field",38)(45,"mat-label",14),t.ɵɵtext(46,"gateway.statistics.max-payload-size-bytes"),t.ɵɵelementEnd(),t.ɵɵelement(47,"input",54),t.ɵɵtemplate(48,h6,3,3,"mat-error")(49,g6,3,3,"mat-error")(50,f6,3,3,"mat-error"),t.ɵɵelementStart(51,"mat-icon",16),t.ɵɵpipe(52,"translate"),t.ɵɵtext(53,"info_outlined "),t.ɵɵelementEnd()()(),t.ɵɵelementStart(54,"section",37)(55,"mat-form-field",38)(56,"mat-label",14),t.ɵɵtext(57,"gateway.statistics.min-pack-size-to-send"),t.ɵɵelementEnd(),t.ɵɵelement(58,"input",55),t.ɵɵtemplate(59,y6,3,3,"mat-error")(60,v6,3,3,"mat-error")(61,x6,3,3,"mat-error"),t.ɵɵelementStart(62,"mat-icon",16),t.ɵɵpipe(63,"translate"),t.ɵɵtext(64,"info_outlined "),t.ɵɵelementEnd()()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵclassProp("no-padding-bottom",e.basicFormGroup.get("thingsboard.checkingDeviceActivity.checkDeviceInactivity").value),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,16,"gateway.hints.check-device-activity")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,18,"gateway.checking-device-activity")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.basicFormGroup.get("thingsboard.checkingDeviceActivity.checkDeviceInactivity").value),t.ɵɵadvance(9),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.minPackSendDelayMS").hasError("required")?16:e.basicFormGroup.get("thingsboard.minPackSendDelayMS").hasError("min")?17:e.basicFormGroup.get("thingsboard.minPackSendDelayMS").hasError("pattern")?18:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(20,20,"gateway.hints.minimal-pack-delay")),t.ɵɵadvance(7),t.ɵɵproperty("value",0),t.ɵɵadvance(2),t.ɵɵproperty("value",1),t.ɵɵadvance(2),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.qos").hasError("required")?30:e.basicFormGroup.get("thingsboard.qos").hasError("min")?31:e.basicFormGroup.get("thingsboard.qos").hasError("max")?32:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(34,22,"gateway.hints.qos")),t.ɵɵadvance(8),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.checkConnectorsConfigurationInSeconds").hasError("required")?41:e.basicFormGroup.get("thingsboard.checkConnectorsConfigurationInSeconds").hasError("min")?42:e.basicFormGroup.get("thingsboard.checkConnectorsConfigurationInSeconds").hasError("pattern")?43:-1),t.ɵɵadvance(7),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.maxPayloadSizeBytes").hasError("required")?48:e.basicFormGroup.get("thingsboard.maxPayloadSizeBytes").hasError("min")?49:e.basicFormGroup.get("thingsboard.maxPayloadSizeBytes").hasError("pattern")?50:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(52,24,"gateway.hints.max-payload-size-bytes")),t.ɵɵadvance(8),t.ɵɵconditional(e.basicFormGroup.get("thingsboard.minPackSizeToSend").hasError("required")?59:e.basicFormGroup.get("thingsboard.minPackSizeToSend").hasError("min")?60:e.basicFormGroup.get("thingsboard.minPackSizeToSend").hasError("pattern")?61:-1),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(63,26,"gateway.hints.min-pack-size-to-send"))}}class w6{constructor(e,t,n,i,a){this.fb=e,this.deviceService=t,this.gatewayCredentialsService=n,this.destroyRef=i,this.dialog=a,this.gatewayVersion=pt.Legacy,this.dialogMode=!1,this.withReportStrategy=!1,this.initialized=new u,this.ReportStrategyDefaultValue=en,this.initialCredentials$=this.gatewayCredentialsService.initialCredentials$,this.onChange=()=>{},this.destroy$=new te,this.initBasicFormGroup(),this.observeFormChanges(),this.basicFormGroup.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e)}))}ngOnChanges(e){e.withReportStrategy&&!e.withReportStrategy.firstChange&&this.withReportStrategy&&this.basicFormGroup.get("thingsboard.reportStrategy").enable({emitEvent:!1}),e.gatewayVersion?.previousValue!==e.gatewayVersion.currentValue&&this.onVersionChange()}ngAfterViewInit(){this.defaultTab&&(this.configGroup.selectedIndex=q3[this.defaultTab])}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){}writeValue(e){this.basicFormGroup.patchValue(e,{emitEvent:!1});const t=e?.thingsboard?.statistics?.commands??[];this.commandFormArray().clear({emitEvent:!1}),t.forEach((e=>this.addCommand(e,!1)))}validate(){return this.basicFormGroup.valid?null:{basicFormGroup:{valid:!1}}}commandFormArray(){return this.basicFormGroup.get("thingsboard.statistics.commands")}removeCommandControl(e,t){""!==t.pointerType&&(this.commandFormArray().removeAt(e),this.basicFormGroup.markAsDirty())}openConfigurationConfirmDialog(){this.deviceService.getDevice(this.device.id).pipe(le(this.destroy$)).subscribe((e=>{this.dialog.open(pW,{disableClose:!0,panelClass:["tb-dialog","tb-fullscreen-dialog"],data:{gatewayName:e.name}}).afterClosed().pipe(ye(1)).subscribe((e=>{e||this.basicFormGroup.get("thingsboard.remoteConfiguration").setValue(!0,{emitEvent:!1})}))}))}addCommand(e,t=!0){const{attributeOnGateway:n=null,command:i=null,timeout:a=10,installCmd:r=""}=e||{},o=this.fb.group({attributeOnGateway:[n,[$.required,$.pattern(an),this.uniqNameRequired()]],command:[i,[$.required,$.pattern(/^(?=\S).*\S$/)]],timeout:[a,[$.required,$.min(1),$.pattern(nn),$.pattern(/^[^.\s]+$/)]],installCmd:[r,$.pattern(an)]});this.commandFormArray().push(o,{emitEvent:t})}uniqNameRequired(){return e=>{const t=e.value?.trim().toLowerCase(),n=e.dirty&&t&&this.commandFormArray().value.some((e=>e.attributeOnGateway?.toLowerCase()===t));return n?{duplicateName:{valid:!1}}:null}}onInitialized(e){this.basicFormGroup.patchValue(e,{emitEvent:!1}),this.initialized.emit(this.basicFormGroup.value)}initBasicFormGroup(){this.basicFormGroup=this.fb.group({thingsboard:this.initThingsboardFormGroup(),storage:[],grpc:[],connectors:this.fb.array([]),logs:[]})}initThingsboardFormGroup(){return this.fb.group({host:[window.location.hostname,[$.required,$.pattern(/^[^\s]+$/)]],port:[1883,[$.required,$.min(1),$.max(65535),$.pattern(nn)]],remoteShell:[!1],remoteConfiguration:[!0],checkConnectorsConfigurationInSeconds:[60,[$.required,$.min(1),$.pattern(nn)]],statistics:this.fb.group({enable:[!0],enableCustom:[{value:!1,disabled:!0}],statsSendPeriodInSeconds:[3600,[$.required,$.min(60),$.pattern(nn)]],customStatsSendPeriodInSeconds:[3600,[$.required,$.min(60),$.pattern(nn)]],commands:this.fb.array([])}),maxPayloadSizeBytes:[8196,[$.required,$.min(100),$.pattern(nn)]],minPackSendDelayMS:[50,[$.required,$.min(10),$.pattern(nn)]],minPackSizeToSend:[500,[$.required,$.min(100),$.pattern(nn)]],handleDeviceRenaming:[!0],checkingDeviceActivity:this.initCheckingDeviceActivityFormGroup(),security:[],qos:[1],reportStrategy:[{value:{type:Jt.OnReceived},disabled:!0}]})}initCheckingDeviceActivityFormGroup(){return this.fb.group({checkDeviceInactivity:[!1],inactivityTimeoutSeconds:[300,[$.min(1),$.pattern(nn)]],inactivityCheckPeriodSeconds:[10,[$.min(1),$.pattern(nn)]]})}onVersionChange(){if(this.hasUpdatedStatistics=za.parseVersion(this.gatewayVersion)>=za.parseVersion(pt.v3_7_3),this.hasUpdatedStatistics){const e=this.basicFormGroup.get("thingsboard.statistics.enableCustom");e.enable({emitEvent:!1}),this.basicFormGroup.get("thingsboard.statistics.enable").valueChanges.pipe(bn(this.destroyRef)).subscribe((t=>{t||e.patchValue(!1,{emitEvent:!1}),e[t?"enable":"disable"]({emitEvent:!1})}))}}observeFormChanges(){this.observeRemoteConfigurationChanges(),this.observeDeviceActivityChanges()}observeRemoteConfigurationChanges(){this.basicFormGroup.get("thingsboard.remoteConfiguration").valueChanges.pipe(le(this.destroy$)).subscribe((e=>{e||this.openConfigurationConfirmDialog()}))}observeDeviceActivityChanges(){const e=this.basicFormGroup.get("thingsboard.checkingDeviceActivity");e.get("checkDeviceInactivity").valueChanges.pipe(le(this.destroy$)).subscribe((t=>{e.updateValueAndValidity();const n=[$.min(1),$.required,$.pattern(nn)];t?(e.get("inactivityTimeoutSeconds").setValidators(n),e.get("inactivityCheckPeriodSeconds").setValidators(n)):(e.get("inactivityTimeoutSeconds").clearValidators(),e.get("inactivityCheckPeriodSeconds").clearValidators()),e.get("inactivityTimeoutSeconds").updateValueAndValidity({emitEvent:!1}),e.get("inactivityCheckPeriodSeconds").updateValueAndValidity({emitEvent:!1})}))}static{this.ɵfac=function(e){return new(e||w6)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(_e.DeviceService),t.ɵɵdirectiveInject(y5),t.ɵɵdirectiveInject(t.DestroyRef),t.ɵɵdirectiveInject(qe.MatDialog))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:w6,selectors:[["tb-gateway-basic-configuration"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(T5,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.configGroup=e.first)}},inputs:{device:"device",defaultTab:"defaultTab",gatewayVersion:"gatewayVersion",dialogMode:"dialogMode",withReportStrategy:"withReportStrategy"},outputs:{initialized:"initialized"},standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>w6)),multi:!0},{provide:K,useExisting:c((()=>w6)),multi:!0}]),t.ɵɵNgOnChangesFeature,t.ɵɵStandaloneFeature],decls:20,vars:21,consts:[["configGroup",""],[1,"tab-group-block",3,"formGroup"],[3,"label"],["matTabContent",""],["formControlName","logs",1,"configuration-block",3,"initialized"],["formControlName","storage",3,"initialized"],["formControlName","grpc",3,"initialized"],["formGroupName","thingsboard",1,"mat-content","mat-padding","configuration-block"],[1,"tb-form-panel","no-padding-bottom"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["color","primary","formControlName","remoteConfiguration",1,"mat-slide"],["color","primary","formControlName","remoteShell",1,"mat-slide"],[1,"no-border","no-padding","tb-standard-fields","xs:flex-col","flex","gap-2"],["appearance","outline",1,"flex","flex-1"],["translate",""],["matInput","","formControlName","host"],["matIconSuffix","",2,"cursor","pointer",3,"matTooltip"],["matInput","","formControlName","port","type","number","min","0"],["formControlName","security",3,"initialized",4,"ngIf"],["class","tb-form-panel","formControlName","reportStrategy",3,"defaultValue",4,"ngIf"],["formControlName","security",3,"initialized"],["formControlName","reportStrategy",1,"tb-form-panel",3,"defaultValue"],["formGroupName","statistics",1,"tb-form-panel","no-padding-bottom"],["color","primary","formControlName","enable",1,"mat-slide"],["appearance","outline"],["matInput","","formControlName","statsSendPeriodInSeconds","type","number","min","60"],["matInput","","formControlName","customStatsSendPeriodInSeconds","type","number","min","60"],[1,"tb-form-panel"],["translate","",1,"tb-form-panel-title"],["translate","",1,"tb-form-panel-hint"],["formGroupName","statistics"],["formArrayName","commands","class","statistics-container flex flex-row",4,"ngFor","ngForOf"],["mat-stroked-button","","color","primary","type","button",2,"width","fit-content",3,"click","disabled"],[3,"tb-hint-tooltip-icon"],["color","primary","formControlName","enableCustom",1,"mat-slide"],["formArrayName","commands",1,"statistics-container","flex","flex-row"],[1,"tb-form-panel","stroked","no-padding-bottom","no-gap","command-container",3,"formGroupName"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],["appearance","outline",1,"flex"],["matInput","","formControlName","attributeOnGateway"],["matInput","","formControlName","timeout","type","number","min","0"],["appearance","outline",1,"mat-block"],["matInput","","formControlName","command"],[1,"tb-settings","pb-4"],["matInput","","formControlName","installCmd"],["matIconSuffix","",1,"cursor-pointer",3,"matTooltip"],["mat-icon-button","","matTooltipPosition","above",1,"tb-box-button",3,"click","disabled","matTooltip"],["formGroupName","checkingDeviceActivity",1,"tb-form-panel"],["color","primary","formControlName","checkDeviceInactivity",1,"mat-slide"],["class","tb-form-row no-border no-padding tb-standard-fields column-xs",4,"ngIf"],["matInput","","formControlName","minPackSendDelayMS","type","number","min","0"],["formControlName","qos"],[3,"value"],["matInput","","formControlName","checkConnectorsConfigurationInSeconds","type","number","min","0"],["matInput","","formControlName","maxPayloadSizeBytes","type","number","min","0"],["matInput","","formControlName","minPackSizeToSend","type","number","min","0"],["matInput","","formControlName","inactivityTimeoutSeconds","type","number","min","0"],["matInput","","type","number","min","0","formControlName","inactivityCheckPeriodSeconds"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-tab-group",1,0)(2,"mat-tab",2),t.ɵɵpipe(3,"translate"),t.ɵɵtemplate(4,A5,35,24,"ng-template",3),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-tab",2),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"tb-gateway-logs-configuration",4),t.ɵɵlistener("initialized",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onInitialized(i))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-tab",2),t.ɵɵpipe(9,"translate"),t.ɵɵelementStart(10,"tb-gateway-storage-configuration",5),t.ɵɵlistener("initialized",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onInitialized(i))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"mat-tab",2),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"tb-gateway-grpc-configuration",6),t.ɵɵlistener("initialized",(function(i){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onInitialized(i))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-tab",2),t.ɵɵpipe(15,"translate"),t.ɵɵtemplate(16,Z5,28,8,"ng-template",3),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-tab",2),t.ɵɵpipe(18,"translate"),t.ɵɵtemplate(19,b6,65,28,"ng-template",3),t.ɵɵelementEnd()()}2&e&&(t.ɵɵclassProp("dialog-mode",n.dialogMode),t.ɵɵproperty("formGroup",n.basicFormGroup),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(3,9,"gateway.general")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(6,11,"gateway.logs.logs")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(9,13,"gateway.storage")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,15,"gateway.grpc")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(15,17,"gateway.statistics.statistics")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(18,19,"gateway.other")))},dependencies:t.ɵɵgetComponentDepsFactory(w6,[j,_,Xn,E4,$4,r5,_5]),styles:['@charset "UTF-8";[_nghost-%COMP%]{width:100%;height:100%;display:grid;grid-template-rows:min-content minmax(auto,1fr) min-content}[_nghost-%COMP%] .configuration-block[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:16px}[_nghost-%COMP%] .mat-toolbar[_ngcontent-%COMP%]{grid-row:1;background:transparent;color:#000000de!important}[_nghost-%COMP%] .tab-group-block[_ngcontent-%COMP%]{min-width:0;height:100%;min-height:0;grid-row:2}[_nghost-%COMP%] .toggle-group[_ngcontent-%COMP%]{margin-right:auto}[_nghost-%COMP%] .first-capital[_ngcontent-%COMP%]{text-transform:capitalize}[_nghost-%COMP%] textarea[_ngcontent-%COMP%]{resize:none}[_nghost-%COMP%] .saving-period[_ngcontent-%COMP%]{flex:1}[_nghost-%COMP%] .statistics-container[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .statistics-container[_ngcontent-%COMP%] .command-container[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] mat-form-field[_ngcontent-%COMP%] mat-error[_ngcontent-%COMP%]{display:none!important}[_nghost-%COMP%] mat-form-field[_ngcontent-%COMP%] mat-error[_ngcontent-%COMP%]:first-child{display:block!important}[_nghost-%COMP%] .pointer-event{pointer-events:all}[_nghost-%COMP%] .toggle-group span{padding:0 25px}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{color:#e0e0e0}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix:hover{color:#9e9e9e}[_nghost-%COMP%] .mat-mdc-form-field-icon-suffix{display:flex;align-items:center}']})}}e("GatewayBasicConfigurationComponent",w6),Ge([I()],w6.prototype,"dialogMode",void 0),Ge([I()],w6.prototype,"withReportStrategy",void 0);class S6{constructor(e){this.fb=e,this.destroy$=new te,this.advancedFormControl=this.fb.control(""),this.advancedFormControl.valueChanges.pipe(le(this.destroy$)).subscribe((e=>{this.onChange(e),this.onTouched()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}writeValue(e){this.advancedFormControl.reset(e,{emitEvent:!1})}validate(){return this.advancedFormControl.valid?null:{advancedFormControl:{valid:!1}}}static{this.ɵfac=function(e){return new(e||S6)(t.ɵɵdirectiveInject(H.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:S6,selectors:[["tb-gateway-advanced-configuration"]],standalone:!0,features:[t.ɵɵProvidersFeature([{provide:W,useExisting:c((()=>S6)),multi:!0},{provide:K,useExisting:c((()=>S6)),multi:!0}]),t.ɵɵStandaloneFeature],decls:2,vars:4,consts:[["fillHeight","true","jsonRequired","",1,"flex","h-full","flex-col","p-2",3,"label","formControl"]],template:function(e,n){1&e&&(t.ɵɵelement(0,"tb-json-object-edit",0),t.ɵɵpipe(1,"translate")),2&e&&(t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(1,2,"gateway.configuration")),t.ɵɵproperty("formControl",n.advancedFormControl))},dependencies:t.ɵɵgetComponentDepsFactory(S6,[j,_]),encapsulation:2})}}function C6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",14),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.cancel())})),t.ɵɵelementStart(1,"mat-icon",15),t.ɵɵtext(2,"close"),t.ɵɵelementEnd()()}}function _6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-gateway-basic-configuration",16),t.ɵɵpipe(1,"withReportStrategy"),t.ɵɵlistener("initialized",(function(n){t.ɵɵrestoreView(e);const i=t.ɵɵnextContext();return t.ɵɵresetView(i.onInitialized(n))})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("device",e.device)("defaultTab",e.defaultTab)("gatewayVersion",e.gatewayVersion)("dialogMode",!!e.dialogRef)("withReportStrategy",t.ɵɵpipeBind1(1,5,e.gatewayVersion))}}function T6(e,n){1&e&&t.ɵɵelement(0,"tb-gateway-advanced-configuration",10)}function I6(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",17),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.cancel())})),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()}2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"action.cancel")," "))}e("GatewayAdvancedConfigurationComponent",S6);class E6{constructor(e,t,n,i,a){this.fb=e,this.attributeService=t,this.cd=n,this.gatewayCredentialsService=i,this.destroyRef=a,this.ConfigurationModes=Qt,this.gatewayConfigAttributeKeys=["general_configuration","grpc_configuration","logs_configuration","storage_configuration","RemoteLoggingLevel","mode"],this.useUpdatedLogs=!1,this.gatewayConfigGroup=this.fb.group({basicConfig:[],advancedConfig:[],mode:[Qt.BASIC]}),this.observeAlignConfigs()}ngAfterViewInit(){this.fetchConfigAttribute(this.device)}saveConfig(){const{mode:e,advancedConfig:t}=Ne(this.removeEmpty(this.gatewayConfigGroup.value)),n={mode:e,...t};n.thingsboard.statistics.commands=Object.values(n.thingsboard.statistics.commands??[]);const i=this.generateAttributes(n);this.attributeService.saveEntityAttributes(this.device,D.SHARED_SCOPE,i).pipe(xe((e=>this.gatewayCredentialsService.updateCredentials(n.thingsboard.security))),bn(this.destroyRef)).subscribe((()=>{this.dialogRef?this.dialogRef.close():(this.gatewayConfigGroup.markAsPristine(),this.cd.detectChanges())}))}onInitialized(e){this.gatewayConfigGroup.get("basicConfig").patchValue(e,{emitEvent:!1}),this.gatewayConfigGroup.get("advancedConfig").patchValue(e,{emitEvent:!1})}observeAlignConfigs(){this.gatewayConfigGroup.get("basicConfig").valueChanges.pipe(bn(this.destroyRef)).subscribe((e=>{const t=this.gatewayConfigGroup.get("advancedConfig");Se(t.value,e)||this.gatewayConfigGroup.get("mode").value!==Qt.BASIC||t.patchValue(e,{emitEvent:!1})})),this.gatewayConfigGroup.get("advancedConfig").valueChanges.pipe(bn(this.destroyRef)).subscribe((e=>{const t=this.gatewayConfigGroup.get("basicConfig");Se(t.value,e)||this.gatewayConfigGroup.get("mode").value!==Qt.ADVANCED||t.patchValue(e,{emitEvent:!1})}))}generateAttributes(e){const t=[],n=(e,n)=>{t.push({key:e,value:n})},i=(e,t)=>{t={...t,ts:(new Date).getTime()},n(e,t)};return n("RemoteLoggingLevel",e.logs?.logLevel??lt.NONE),delete e.connectors,n("logs_configuration",this.generateLogsFile(e.logs)),i("grpc_configuration",e.grpc),i("storage_configuration",e.storage),i("general_configuration",e.thingsboard),n("mode",e.mode),t}cancel(){this.dialogRef&&this.dialogRef.close()}removeEmpty(e){return Object.fromEntries(Object.entries(e).filter((([e,t])=>null!=t)).map((([e,t])=>[e,t===Object(t)?this.removeEmpty(t):t])))}generateLogsFile(e){const t={version:1,disable_existing_loggers:!1,formatters:{LogFormatter:{class:"logging.Formatter",format:e.logFormat,datefmt:e.dateFormat}},handlers:{consoleHandler:{class:"logging.StreamHandler",formatter:"LogFormatter",level:0,stream:"ext://sys.stdout"},...this.useUpdatedLogs?{}:{databaseHandler:{class:this.useUpdatedLogs?Y3:X3,formatter:"LogFormatter",filename:"./logs/database.log",backupCount:1,encoding:"utf-8"}}},loggers:{...this.useUpdatedLogs?{}:{database:{handlers:["databaseHandler","consoleHandler"],level:"DEBUG",propagate:!1}}},root:{level:"ERROR",handlers:["consoleHandler"]},ts:(new Date).getTime()};return this.addLocalLoggers(t,e?.local),t}addLocalLoggers(e,t){if(t)for(const n of Object.keys(t))e.handlers[n+"Handler"]=this.createHandlerObj(t[n],n),e.loggers[n]=this.createLoggerObj(t[n],n)}createHandlerObj(e,t){return{class:this.useUpdatedLogs?Y3:X3,formatter:"LogFormatter",filename:`${e.filePath}/${t}.log`,backupCount:e.backupCount,interval:e.savingTime,when:e.savingPeriod,encoding:"utf-8"}}createLoggerObj(e,t){return{handlers:[`${t}Handler`,"consoleHandler"],level:e.logLevel,propagate:!1}}fetchConfigAttribute(e){e.id!==B&&this.attributeService.getEntityAttributes(e,D.CLIENT_SCOPE).pipe(we((t=>t.length?ae(t):this.attributeService.getEntityAttributes(e,D.SHARED_SCOPE,this.gatewayConfigAttributeKeys))),bn(this.destroyRef)).subscribe((e=>{this.gatewayVersion=e.find((e=>"Version"===e.key))?.value,this.useUpdatedLogs=za.parseVersion(this.gatewayVersion??pt.Legacy)>=za.parseVersion(pt.v3_6_3),this.updateConfigs(e),this.cd.detectChanges()}))}updateConfigs(e){let t={},n=lt.NONE;this.gatewayCredentialsService.setInitialCredentials(this.device),e.forEach((e=>{switch(e.key){case"general_configuration":e.value&&(t={...t,thingsboard:e.value});break;case"grpc_configuration":e.value&&(t={...t,grpc:e.value});break;case"logs_configuration":e.value&&(t={...t,logs:this.logsToObj(e.value)});break;case"storage_configuration":e.value&&(t={...t,storage:e.value});break;case"mode":t={...t,mode:e.value??Qt.BASIC};break;case"RemoteLoggingLevel":e.value&&(n=e.value)}})),t.logs&&(t={...t,logs:{...t.logs,logLevel:n}}),t.thingsboard?.security?this.gatewayCredentialsService.initialCredentials$.pipe(ue(Boolean),ye(1),bn(this.destroyRef)).subscribe((e=>{this.gatewayCredentialsService.shouldUpdateSecurityConfig(t.thingsboard.security)&&(t.thingsboard.security=this.gatewayCredentialsService.credentialsToSecurityConfig(e)),this.gatewayConfigGroup.get("basicConfig").patchValue(t,{emitEvent:!1}),this.gatewayConfigGroup.get("advancedConfig").patchValue(t,{emitEvent:!1})})):(this.gatewayConfigGroup.get("basicConfig").patchValue(t,{emitEvent:!1}),this.gatewayConfigGroup.get("advancedConfig").patchValue(t,{emitEvent:!1}))}logsToObj(e){const{format:t,datefmt:n}=e.formatters.LogFormatter;return{local:Object.keys(z3).reduce(((t,n)=>{const i=e.handlers[`${n}Handler`]||{},a=e.loggers[n]||{};return t[n]={logLevel:a.level||lt.INFO,filePath:i.filename?.split(`/${n}`)[0]||"./logs",backupCount:i.backupCount||7,savingTime:i.interval||3,savingPeriod:i.when||H3.days},t}),{}),logFormat:t,dateFormat:n}}static{this.ɵfac=function(e){return new(e||E6)(t.ɵɵdirectiveInject(H.FormBuilder),t.ɵɵdirectiveInject(_e.AttributeService),t.ɵɵdirectiveInject(t.ChangeDetectorRef),t.ɵɵdirectiveInject(y5),t.ɵɵdirectiveInject(t.DestroyRef))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:E6,selectors:[["tb-gateway-configuration"]],inputs:{device:"device",defaultTab:"defaultTab",dialogRef:"dialogRef"},standalone:!0,features:[t.ɵɵProvidersFeature([y5]),t.ɵɵStandaloneFeature],decls:24,vars:24,consts:[[1,"flex","size-full","max-w-full","flex-col",3,"formGroup"],["color","primary"],[1,"tb-flex","space-between","align-center","h-16"],["tbTruncateWithTooltip",""],[1,"flex","items-center"],["formControlName","mode",3,"appearance"],[3,"value"],["mat-icon-button","","type","button",3,"click",4,"ngIf"],[1,"content-wrapper","flex-1"],["formControlName","basicConfig",3,"device","defaultTab","gatewayVersion","dialogMode","withReportStrategy"],["formControlName","advancedConfig"],[1,"flex","h-full","items-center","justify-end","gap-2","pr-4"],["mat-button","","color","primary","type","button"],["mat-raised-button","","color","primary","type","button",3,"click","disabled"],["mat-icon-button","","type","button",3,"click"],[1,"material-icons"],["formControlName","basicConfig",3,"initialized","device","defaultTab","gatewayVersion","dialogMode","withReportStrategy"],["mat-button","","color","primary","type","button",3,"click"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"mat-toolbar",1)(2,"div",2)(3,"h2")(4,"div",3),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"div",4)(8,"tb-toggle-select",5)(9,"tb-toggle-option",6),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"tb-toggle-option",6),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(15,C6,3,0,"button",7),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",8),t.ɵɵtemplate(17,_6,2,7,"tb-gateway-basic-configuration",9)(18,T6,1,0,"tb-gateway-advanced-configuration",10),t.ɵɵelementEnd(),t.ɵɵelementStart(19,"div",11),t.ɵɵtemplate(20,I6,3,3,"button",12),t.ɵɵelementStart(21,"button",13),t.ɵɵlistener("click",(function(){return n.saveConfig()})),t.ɵɵtext(22),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.gatewayConfigGroup),t.ɵɵadvance(),t.ɵɵclassProp("page-header",!n.dialogRef),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(6,16,"gateway.gateway-configuration")),t.ɵɵadvance(3),t.ɵɵclassProp("dialog-toggle",!!n.dialogRef),t.ɵɵpropertyInterpolate("appearance",n.dialogRef?"stroked":"fill"),t.ɵɵadvance(),t.ɵɵproperty("value",n.ConfigurationModes.BASIC),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,18,"gateway.basic")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",n.ConfigurationModes.ADVANCED),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,20,"gateway.advanced")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.dialogRef),t.ɵɵadvance(2),t.ɵɵconditional(n.gatewayConfigGroup.get("mode").value===n.ConfigurationModes.BASIC?17:18),t.ɵɵadvance(3),t.ɵɵconditional(n.dialogRef?20:-1),t.ɵɵadvance(),t.ɵɵproperty("disabled",n.gatewayConfigGroup.invalid||!n.gatewayConfigGroup.dirty),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(23,22,"action.save")," "))},dependencies:t.ɵɵgetComponentDepsFactory(E6,[j,_,Ka,w6,S6]),styles:['@charset "UTF-8";[_nghost-%COMP%]{overflow:hidden;padding:0!important;max-height:75vh;height:75vh;width:800px;max-width:80vw}@media screen and (max-width: 599px){[_nghost-%COMP%]{max-height:100%;height:100%;width:100%;max-width:100%}}[_nghost-%COMP%] .page-header.mat-toolbar[_ngcontent-%COMP%]{background:transparent;color:#000000de!important}[_nghost-%COMP%] .content-wrapper[_ngcontent-%COMP%]{min-height:calc(100% - 116px)}.dialog-toggle[_ngcontent-%COMP%] .mat-button-toggle-button{color:#ffffffbf}']})}}e("GatewayConfigurationComponent",E6);class M6{constructor(){}static{this.ɵfac=function(e){return new(e||M6)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:M6,selectors:[["tb-lib-styles-entry"]],standalone:!0,features:[t.ɵɵStandaloneFeature],decls:0,vars:0,template:function(e,t){},styles:['@charset "UTF-8";.gw-delete-icon-button{color:#0000008a}.gw-connector-table .mat-mdc-table{table-layout:fixed}.gw-chip-list .mdc-evolution-chip-set__chips{justify-content:flex-end;align-items:center}.gw-table-toolbar.mat-mdc-table-toolbar.mat-mdc-table-toolbar{padding:0 8px}.tb-default .h-16{height:4rem}.tb-default .h-\\[500px\\]{height:500px}.tb-default .max-h-full{max-height:100%}.tb-default .min-h-10{min-height:2.5rem}.tb-default .\\!w-1\\/5{width:20%!important}.tb-default .w-1\\/2{width:50%}.tb-default .w-12{width:3rem}.tb-default .w-\\[77vw\\]{width:77vw}.tb-default .min-w-16{min-width:4rem}.tb-default .min-w-24{min-width:6rem}.tb-default .max-w-2xl{max-width:42rem}.tb-default .max-w-3xl{max-width:48rem}.tb-default .max-w-4\\/12{max-width:33.333333%}.tb-default .max-w-\\[11vw\\]{max-width:11vw}.tb-default .max-w-full{max-width:100%}.tb-default .flex-\\[1_1_30px\\]{flex:1 1 30px}.tb-default .flex-\\[1_1_33\\%\\]{flex:1 1 33%}.tb-default .flex-grow{flex-grow:1}.tb-default .gap-1\\.25{gap:.3125rem}.tb-default .p-1{padding:.25rem}.tb-default .\\!pl-0{padding-left:0!important}.tb-default .pr-4{padding-right:1rem}.tb-default .pt-4{padding-top:1rem}.tb-default .text-center{text-align:center}@media (max-width: 599px){.tb-default .lt-sm\\:flex-col{flex-direction:column}}\n'],encapsulation:2})}}const k6=(e,t)=>{const n=e[y];if(n.styles?.length){const e=n.styles[0];let i=document.getElementById(t);if(!i){i=document.createElement("style"),i.id=t;(document.head||document.getElementsByTagName("head")[0]).appendChild(i)}i.innerHTML=e}};class P6{constructor(e){this.translate=e,function(e){e.setTranslation("en_US",gt,!0),e.setTranslation("ar_AE",ft,!0),e.setTranslation("ca_ES",yt,!0),e.setTranslation("cs_CZ",vt,!0),e.setTranslation("da_DK",xt,!0),e.setTranslation("es_ES",bt,!0),e.setTranslation("ko_KR",wt,!0),e.setTranslation("lt_LT",St,!0),e.setTranslation("nl_BE",Ct,!0),e.setTranslation("pl_PL",_t,!0),e.setTranslation("pt_BR",Tt,!0),e.setTranslation("sl_SI",It,!0),e.setTranslation("tr_TR",Et,!0),e.setTranslation("zh_CN",Mt,!0),e.setTranslation("zh_TW",kt,!0)}(e),(e=>{k6(M6,e)})("tb-gateway-css")}static{this.ɵfac=function(e){return new(e||P6)(t.ɵɵinject(He.TranslateService))}}static{this.ɵmod=t.ɵɵdefineNgModule({type:P6})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[j,_,$e,hj,sW,i$,E6,xn,V3,L3]})}}e("GatewayExtensionModule",P6),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(P6,{imports:[j,_,$e,hj,sW,i$,E6,xn,V3,L3]})}}}));//# sourceMappingURL=gateway-management-extension.js.map 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 d344a9d92d..20782cf071 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.actors; +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; @@ -55,7 +56,6 @@ import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.msg.TbMsgType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbMsg; @@ -95,6 +95,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; @@ -118,7 +119,7 @@ import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; import org.thingsboard.server.service.cf.CalculatedFieldQueueService; import org.thingsboard.server.service.cf.CalculatedFieldStateService; -import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.OwnerService; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; @@ -143,14 +144,12 @@ import org.thingsboard.server.utils.DebugModeRateLimitsConfig; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; @Slf4j @Component @@ -572,6 +571,14 @@ public class ActorSystemContext { @Getter private JobManager jobManager; + @Autowired + @Getter + private ApiKeyService apiKeyService; + + @Autowired + @Getter + private OwnerService ownerService; + @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter private int maxConcurrentSessionsPerDevice; @@ -837,7 +844,7 @@ public class ActorSystemContext { persistCalculatedFieldDebugEvent(cfe.getCtx().getTenantId(), cfe.getCtx().getCfId(), cfe.getEventEntity(), cfe.getArguments(), cfe.getMsgId(), cfe.getMsgType(), null, message); } - public void persistCalculatedFieldDebugEvent(TenantId tenantId, CalculatedFieldId calculatedFieldId, EntityId entityId, Map arguments, UUID tbMsgId, TbMsgType tbMsgType, String result, String errorMessage) { + public void persistCalculatedFieldDebugEvent(TenantId tenantId, CalculatedFieldId calculatedFieldId, EntityId entityId, JsonNode arguments, UUID tbMsgId, String tbMsgType, String result, String errorMessage) { if (checkLimits(tenantId)) { try { CalculatedFieldDebugEvent.CalculatedFieldDebugEventBuilder eventBuilder = CalculatedFieldDebugEvent.builder() @@ -850,13 +857,10 @@ public class ActorSystemContext { eventBuilder.msgId(tbMsgId); } if (tbMsgType != null) { - eventBuilder.msgType(tbMsgType.name()); + eventBuilder.msgType(tbMsgType); } - if (arguments != null) { - eventBuilder.arguments(JacksonUtil.toString( - arguments.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toTbelCfArg())) - )); + if (arguments != null && !arguments.isEmpty()) { + eventBuilder.arguments(JacksonUtil.toString(arguments)); } if (result != null) { eventBuilder.result(result); @@ -864,8 +868,9 @@ public class ActorSystemContext { if (errorMessage != null) { eventBuilder.error(errorMessage); } - - ListenableFuture future = eventService.saveAsync(eventBuilder.build()); + CalculatedFieldDebugEvent event = eventBuilder.build(); + log.debug("Persisting calculated field debug event: {}", event); + ListenableFuture future = eventService.saveAsync(event); Futures.addCallback(future, CALCULATED_FIELD_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor()); } catch (IllegalArgumentException ex) { log.warn("Failed to persist calculated field debug message", ex); @@ -875,7 +880,7 @@ public class ActorSystemContext { private boolean checkLimits(TenantId tenantId) { if (debugModeRateLimitsConfig.isCalculatedFieldDebugPerTenantLimitsEnabled() && - !rateLimitService.checkRateLimit(LimitedApi.CALCULATED_FIELD_DEBUG_EVENTS, (Object) tenantId, debugModeRateLimitsConfig.getCalculatedFieldDebugPerTenantLimitsConfiguration())) { + !rateLimitService.checkRateLimit(LimitedApi.CALCULATED_FIELD_DEBUG_EVENTS, (Object) tenantId, debugModeRateLimitsConfig.getCalculatedFieldDebugPerTenantLimitsConfiguration())) { log.trace("[{}] Calculated field debug event limits exceeded!", tenantId); return false; } @@ -899,12 +904,13 @@ public class ActorSystemContext { return getScheduler().scheduleWithFixedDelay(() -> ctx.tell(msg), delayInMs, periodInMs, TimeUnit.MILLISECONDS); } - public void scheduleMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs) { + public ScheduledFuture scheduleMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs) { log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); if (delayInMs > 0) { - getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS); + return getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS); } else { ctx.tell(msg); + return null; } } 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 6b780a2945..7e6cfc1b15 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 @@ -32,6 +32,7 @@ import org.thingsboard.server.actors.tenant.TenantActor; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.MsgType; @@ -43,7 +44,6 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import java.util.HashSet; import java.util.Optional; @@ -89,16 +89,20 @@ public class AppActor extends ContextAwareActor { break; case PARTITION_CHANGE_MSG: case CF_PARTITIONS_CHANGE_MSG: + case CF_STATE_PARTITION_RESTORE_MSG: ctx.broadcastToChildren(msg, true); break; case COMPONENT_LIFE_CYCLE_MSG: onComponentLifecycleMsg((ComponentLifecycleMsg) msg); break; + case CF_ENTITY_ACTION_EVENT_MSG: + forwardToTenantActor((TenantAwareMsg) msg, true); + break; case QUEUE_TO_RULE_ENGINE_MSG: onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); break; case TRANSPORT_TO_DEVICE_ACTOR_MSG: - onToDeviceActorMsg((TenantAwareMsg) msg, false); + forwardToTenantActor((TenantAwareMsg) msg, false); break; case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: @@ -108,7 +112,7 @@ public class AppActor extends ContextAwareActor { case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: case REMOVE_RPC_TO_DEVICE_ACTOR_MSG: - onToDeviceActorMsg((TenantAwareMsg) msg, true); + forwardToTenantActor((TenantAwareMsg) msg, true); break; case SESSION_TIMEOUT_MSG: ctx.broadcastToChildrenByType(msg, EntityType.TENANT); @@ -117,11 +121,11 @@ public class AppActor extends ContextAwareActor { case CF_STATE_RESTORE_MSG: //TODO: use priority from the message body. For example, messages about CF lifecycle are important and Device lifecycle are not. // same for the Linked telemetry. - onToCalculatedFieldSystemActorMsg((ToCalculatedFieldSystemMsg) msg, true); + forwardToTenantActor((ToCalculatedFieldSystemMsg) msg, true); break; case CF_TELEMETRY_MSG: case CF_LINKED_TELEMETRY_MSG: - onToCalculatedFieldSystemActorMsg((ToCalculatedFieldSystemMsg) msg, false); + forwardToTenantActor((ToCalculatedFieldSystemMsg) msg, false); break; default: return false; @@ -162,7 +166,19 @@ public class AppActor extends ContextAwareActor { private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { TbActorRef target = null; if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { - if (!EntityType.TENANT_PROFILE.equals(msg.getEntityId().getEntityType())) { + if (systemContext.isTenantComponentsInitEnabled()) { + if (msg.getEntityId() instanceof TenantProfileId tenantProfileId) { + tenantService.findTenantIdsByTenantProfileId(tenantProfileId).forEach(tenantId -> { + getOrCreateTenantActor(tenantId).ifPresentOrElse(tenantActor -> { + log.debug("[{}] Sending component lifecycle msg for tenant.", tenantId); + tenantActor.tellWithHighPriority(msg); + }, () -> { + log.debug("Ignoring component lifecycle msg for tenant {} because it is not managed by this service", tenantId); + }); + }); + } + } + if (!msg.getEntityId().getEntityType().isOneOf(EntityType.TENANT_PROFILE, EntityType.TB_RESOURCE, EntityType.USER)) { log.warn("Message has system tenant id: {}", msg); } } else { @@ -187,7 +203,7 @@ public class AppActor extends ContextAwareActor { } } - private void onToCalculatedFieldSystemActorMsg(ToCalculatedFieldSystemMsg msg, boolean priority) { + private void forwardToTenantActor(TenantAwareMsg msg, boolean priority) { getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(tenantActor -> { if (priority) { tenantActor.tellWithHighPriority(msg); @@ -199,21 +215,6 @@ public class AppActor extends ContextAwareActor { }); } - - private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) { - getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(tenantActor -> { - if (priority) { - tenantActor.tellWithHighPriority(msg); - } else { - tenantActor.tell(msg); - } - }, () -> { - if (msg instanceof TransportToDeviceActorMsgWrapper) { - ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess(); - } - }); - } - private Optional getOrCreateTenantActor(TenantId tenantId) { if (deletedTenants.contains(tenantId)) { return Optional.empty(); @@ -245,6 +246,7 @@ public class AppActor extends ContextAwareActor { public TbActor createActor() { return new AppActor(context); } + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldAlarmActionMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldAlarmActionMsg.java new file mode 100644 index 0000000000..26b00bdfe2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldAlarmActionMsg.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors.calculatedField; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; + +@Data +@Builder +public class CalculatedFieldAlarmActionMsg implements ToCalculatedFieldSystemMsg { + + private final TenantId tenantId; + private final Alarm alarm; + private final ActionType action; + private final TbCallback callback; + + @Override + public MsgType getMsgType() { + return MsgType.CF_ALARM_ACTION_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldArgumentResetMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldArgumentResetMsg.java new file mode 100644 index 0000000000..01a61a0158 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldArgumentResetMsg.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors.calculatedField; + +import lombok.Data; +import org.thingsboard.server.common.data.cf.CalculatedFieldEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; + +@Data +public class CalculatedFieldArgumentResetMsg implements ToCalculatedFieldSystemMsg { + + private final TenantId tenantId; + private final CalculatedFieldCtx ctx; + private final CalculatedFieldEventType eventType; + private final TbCallback callback; + + @Override + public MsgType getMsgType() { + return MsgType.CF_ARGUMENT_RESET_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActionEventMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActionEventMsg.java new file mode 100644 index 0000000000..582a150f9c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActionEventMsg.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors.calculatedField; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Data; +import org.thingsboard.common.util.JacksonUtil; +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.msg.MsgType; +import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.util.ProtoUtils; +import org.thingsboard.server.gen.transport.TransportProtos.EntityActionEventProto; + +@Data +@Builder +public class CalculatedFieldEntityActionEventMsg implements ToCalculatedFieldSystemMsg { + + private final TenantId tenantId; + private final EntityId entityId; + private final JsonNode entity; + private final ActionType action; + private final TbCallback callback; + + public static CalculatedFieldEntityActionEventMsg fromProto(EntityActionEventProto proto, + TbCallback callback) { + return CalculatedFieldEntityActionEventMsg.builder() + .tenantId((TenantId) ProtoUtils.fromProto(proto.getTenantId())) + .entityId(ProtoUtils.fromProto(proto.getEntityId())) + .entity(JacksonUtil.toJsonNode(proto.getEntity())) + .action(ActionType.valueOf(proto.getAction())) + .callback(callback) + .build(); + } + + @Override + public MsgType getMsgType() { + return MsgType.CF_ENTITY_ACTION_EVENT_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActor.java index c2f0208b56..dbc97bb240 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActor.java @@ -21,6 +21,7 @@ import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.CalculatedFieldStatePartitionRestoreMsg; import org.thingsboard.server.common.msg.TbActorStopReason; import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg; @@ -51,7 +52,7 @@ public class CalculatedFieldEntityActor extends AbstractCalculatedFieldActor { @Override public void destroy(TbActorStopReason stopReason, Throwable cause) throws TbActorException { log.debug("[{}] Stopping CF entity actor.", processor.tenantId); - processor.stop(); + processor.stop(false); } @Override @@ -63,18 +64,33 @@ public class CalculatedFieldEntityActor extends AbstractCalculatedFieldActor { case CF_STATE_RESTORE_MSG: processor.process((CalculatedFieldStateRestoreMsg) msg); break; + case CF_STATE_PARTITION_RESTORE_MSG: + processor.process((CalculatedFieldStatePartitionRestoreMsg) msg); + break; case CF_ENTITY_INIT_CF_MSG: processor.process((EntityInitCalculatedFieldMsg) msg); break; case CF_ENTITY_DELETE_MSG: processor.process((CalculatedFieldEntityDeleteMsg) msg); break; + case CF_RELATION_ACTION_MSG: + processor.process((CalculatedFieldRelationActionMsg) msg); + break; case CF_ENTITY_TELEMETRY_MSG: processor.process((EntityCalculatedFieldTelemetryMsg) msg); break; case CF_LINKED_TELEMETRY_MSG: processor.process((EntityCalculatedFieldLinkedTelemetryMsg) msg); break; + case CF_REEVALUATE_MSG: + processor.process((CalculatedFieldReevaluateMsg) msg); + break; + case CF_ALARM_ACTION_MSG: + processor.process((CalculatedFieldAlarmActionMsg) msg); + break; + case CF_ARGUMENT_RESET_MSG: + processor.process((CalculatedFieldArgumentResetMsg) msg); + break; default: return false; } @@ -85,4 +101,5 @@ public class CalculatedFieldEntityActor extends AbstractCalculatedFieldActor { void logProcessingException(Exception e) { log.warn("[{}][{}] Processing failure", tenantId, processor.entityId, e); } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java index 4f59628656..a2dc9b50f4 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java @@ -21,10 +21,14 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.calculatedField.EntityInitCalculatedFieldMsg.StateAction; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.cf.CalculatedFieldEventType; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; @@ -33,6 +37,8 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.CollectionsUtil; +import org.thingsboard.server.common.msg.CalculatedFieldStatePartitionRestoreMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; @@ -50,6 +56,12 @@ import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.TsRollingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.alarm.AlarmCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; import java.util.ArrayList; import java.util.Collection; @@ -64,7 +76,9 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; import static org.thingsboard.server.service.cf.ctx.state.TsRollingArgumentEntry.getValueForTsRecord; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createStateByType; /** * @author Andrew Shvayka @@ -79,7 +93,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM final CalculatedFieldProcessingService cfService; final CalculatedFieldStateService cfStateService; - TbActorCtx ctx; + TbActorCtx actorCtx; Map states = new HashMap<>(); CalculatedFieldEntityMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, EntityId entityId) { @@ -91,48 +105,79 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } void init(TbActorCtx ctx) { - this.ctx = ctx; + this.actorCtx = ctx; } - public void stop() { - log.info("[{}][{}] Stopping entity actor.", tenantId, entityId); + public void stop(boolean partitionChanged) { + log.info(partitionChanged ? + "[{}][{}] Stopping entity actor due to change partition event." : + "[{}][{}] Stopping entity actor.", + tenantId, entityId); + states.values().forEach(this::closeState); states.clear(); - ctx.stop(ctx.getSelf()); + actorCtx.stop(actorCtx.getSelf()); } public void process(CalculatedFieldPartitionChangeMsg msg) { if (!systemContext.getPartitionService().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId).isMyPartition()) { - log.info("[{}] Stopping entity actor due to change partition event.", entityId); - ctx.stop(ctx.getSelf()); + stop(true); } } public void process(CalculatedFieldStateRestoreMsg msg) { CalculatedFieldId cfId = msg.getId().cfId(); log.debug("[{}] [{}] Processing CF state restore msg.", msg.getId().entityId(), cfId); - if (msg.getState() != null) { - states.put(cfId, msg.getState()); + CalculatedFieldState state = msg.getState(); + if (state != null) { + state.setCtx(msg.getCtx(), actorCtx); + state.setPartition(msg.getPartition()); + states.put(cfId, state); } else { - states.remove(cfId); + removeState(cfId); } msg.getCallback().onSuccess(); } + public void process(CalculatedFieldStatePartitionRestoreMsg msg) { + log.debug("Processing CF state partition restore msg: {}", msg); + for (CalculatedFieldState state : states.values()) { + if (msg.getPartition().equals(state.getPartition())) { + state.init(true); + } + } + } + public void process(EntityInitCalculatedFieldMsg msg) throws CalculatedFieldException { - log.debug("[{}] Processing entity init CF msg.", msg.getCtx().getCfId()); + log.debug("[{}] Processing entity init CF msg: {}", msg.getCtx().getCfId(), msg); var ctx = msg.getCtx(); - if (msg.isForceReinit()) { - log.debug("Force reinitialization of CF: [{}].", ctx.getCfId()); - states.remove(ctx.getCfId()); + CalculatedFieldState state; + if (msg.getStateAction() == StateAction.RECREATE) { + removeState(ctx.getCfId()); + state = null; + } else { + state = states.get(ctx.getCfId()); } try { - var state = getOrInitState(ctx); - if (state.isSizeOk()) { - processStateIfReady(ctx, Collections.singletonList(ctx.getCfId()), state, null, null, msg.getCallback()); + if (state == null) { + state = createState(ctx); + } else if (msg.getStateAction() == StateAction.REINIT) { + log.debug("Force reinitialization of CF: [{}].", ctx.getCfId()); + state.reset(); + initState(state, ctx); } else { - throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(); + state.setCtx(ctx, actorCtx); + } + if (msg.getStateAction() != StateAction.REFRESH_CTX) { + if (state.isSizeOk()) { + processStateIfReady(state, Collections.emptyMap(), ctx, Collections.singletonList(ctx.getCfId()), null, msg.getEventType().name(), msg.getCallback()); + } else { + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(); + } + } else { + msg.getCallback().onSuccess(); } } catch (Exception e) { + log.debug("[{}][{}] Failed to initialize CF state", entityId, ctx.getCfId(), e); if (e instanceof CalculatedFieldException cfe) { throw cfe; } @@ -140,31 +185,133 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } } - public void process(CalculatedFieldEntityDeleteMsg msg) { + public void process(CalculatedFieldArgumentResetMsg msg) throws CalculatedFieldException { + log.debug("[{}] Processing CF argument reset msg.", entityId); + var ctx = msg.getCtx(); + try { + Map dynamicSourceArgs = ctx.getArguments().entrySet().stream() + .filter(entry -> entry.getValue().hasOwnerSource()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + Map fetchedArgs = cfService.fetchArgsFromDb(tenantId, entityId, dynamicSourceArgs); + fetchedArgs.values().forEach(arg -> arg.setForceResetPrevious(true)); + + processArgumentValuesUpdate(ctx, Collections.singletonList(ctx.getCfId()), msg.getCallback(), fetchedArgs, null, msg.getEventType().name()); + } catch (Exception e) { + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build(); + } + } + + public void process(CalculatedFieldEntityDeleteMsg msg) throws CalculatedFieldException { log.debug("[{}] Processing CF entity delete msg.", msg.getEntityId()); if (this.entityId.equals(msg.getEntityId())) { if (states.isEmpty()) { msg.getCallback().onSuccess(); } else { MultipleTbCallback multipleTbCallback = new MultipleTbCallback(states.size(), msg.getCallback()); - states.forEach((cfId, state) -> cfStateService.removeState(new CalculatedFieldEntityCtxId(tenantId, cfId, entityId), multipleTbCallback)); - ctx.stop(ctx.getSelf()); + states.forEach((cfId, state) -> cfStateService.deleteState(new CalculatedFieldEntityCtxId(tenantId, cfId, entityId), multipleTbCallback)); + actorCtx.stop(actorCtx.getSelf()); } } else { var cfId = new CalculatedFieldId(msg.getEntityId().getId()); - var state = states.remove(cfId); + var state = removeState(cfId); if (state != null) { - cfStateService.removeState(new CalculatedFieldEntityCtxId(tenantId, cfId, entityId), msg.getCallback()); + cfStateService.deleteState(new CalculatedFieldEntityCtxId(tenantId, cfId, entityId), msg.getCallback()); } else { msg.getCallback().onSuccess(); } } } + public void process(CalculatedFieldRelationActionMsg msg) throws CalculatedFieldException { + log.debug("[{}] Processing CF {} related entity msg.", msg.getRelatedEntityId(), msg.getAction()); + switch (msg.getAction()) { + case UPDATED -> handleRelationUpdate(msg); + case DELETED -> handleRelationDelete(msg); + default -> msg.getCallback().onSuccess(); + } + } + + private void handleRelationUpdate(CalculatedFieldRelationActionMsg msg) throws CalculatedFieldException { + CalculatedFieldCtx ctx = msg.getCalculatedField(); + var state = states.get(ctx.getCfId()); + try { + Map updatedArgs = null; + if (state == null) { + state = createState(ctx); + } else { + if (state instanceof RelatedEntitiesAggregationCalculatedFieldState relatedEntitiesAggState) { + Map fetchedArgs = cfService.fetchArgsFromDb(tenantId, msg.getRelatedEntityId(), ctx.getArguments()); + updatedArgs = relatedEntitiesAggState.updateEntityData(setEntityIdToSingleEntityArguments(msg.getRelatedEntityId(), fetchedArgs)); + } + if (state instanceof PropagationCalculatedFieldState propagationState) { + PropagationArgumentEntry entry = new PropagationArgumentEntry(); + entry.setAdded(List.of(msg.getRelatedEntityId())); + updatedArgs = propagationState.update(Map.of(PROPAGATION_CONFIG_ARGUMENT, entry), ctx); + } + if (CollectionsUtil.isEmpty(updatedArgs)) { + msg.getCallback().onSuccess(); + return; + } + state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize()); + } + if (state.isSizeOk()) { + processStateIfReady(state, updatedArgs, ctx, Collections.singletonList(ctx.getCfId()), null, TbMsgType.RELATION_ADD_OR_UPDATE.name(), msg.getCallback()); + } else { + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(); + } + } catch (Exception e) { + log.debug("[{}][{}] Failed to handle relation update", entityId, ctx.getCfId(), e); + if (e instanceof CalculatedFieldException cfe) { + throw cfe; + } + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build(); + } + } + + private void handleRelationDelete(CalculatedFieldRelationActionMsg msg) throws CalculatedFieldException { + CalculatedFieldCtx ctx = msg.getCalculatedField(); + CalculatedFieldId cfId = ctx.getCfId(); + CalculatedFieldState state = states.get(cfId); + try { + if (state == null) { + msg.getCallback().onSuccess(); + return; + } + if (state instanceof RelatedEntitiesAggregationCalculatedFieldState aggState) { + aggState.cleanupEntityData(msg.getRelatedEntityId()); + + state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize()); + + if (state.isSizeOk()) { + processStateIfReady(state, Collections.emptyMap(), ctx, Collections.singletonList(ctx.getCfId()), null, TbMsgType.RELATION_DELETED.name(), msg.getCallback()); + } else { + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(); + } + return; + } + if (state instanceof PropagationCalculatedFieldState propagationState) { + PropagationArgumentEntry entry = new PropagationArgumentEntry(); + entry.setRemoved(msg.getRelatedEntityId()); + propagationState.update(Map.of(PROPAGATION_CONFIG_ARGUMENT, entry), ctx); + if (DebugModeUtil.isDebugAllAvailable(ctx.getCalculatedField())) { + systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArgumentsJson(), null, TbMsgType.RELATION_DELETED.name(), null, null); + } + } + msg.getCallback().onSuccess(); + } catch (Exception e) { + log.debug("[{}][{}] Failed to handle relation delete", entityId, ctx.getCfId(), e); + if (e instanceof CalculatedFieldException cfe) { + throw cfe; + } + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build(); + } + } + public void process(EntityCalculatedFieldTelemetryMsg msg) throws CalculatedFieldException { - log.debug("[{}] Processing CF telemetry msg.", msg.getEntityId()); + log.trace("[{}] Processing CF telemetry msg: {}", msg.getEntityId(), msg); var proto = msg.getProto(); - var numberOfCallbacks = CALLBACKS_PER_CF * (msg.getEntityIdFields().size() + msg.getProfileIdFields().size()); + var numberOfCallbacks = msg.getEntityIdFields().size() + msg.getProfileIdFields().size(); MultipleTbCallback callback = new MultipleTbCallback(numberOfCallbacks, msg.getCallback()); List cfIdList = getCalculatedFieldIds(proto); Set cfIdSet = new HashSet<>(cfIdList); @@ -177,28 +324,29 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } public void process(EntityCalculatedFieldLinkedTelemetryMsg msg) throws CalculatedFieldException { - log.debug("[{}] Processing CF link telemetry msg.", msg.getEntityId()); + log.trace("[{}] Processing CF link telemetry msg: {}", msg.getEntityId(), msg); var proto = msg.getProto(); var ctx = msg.getCtx(); - var callback = new MultipleTbCallback(CALLBACKS_PER_CF, msg.getCallback()); + var callback = msg.getCallback(); try { List cfIds = getCalculatedFieldIds(proto); if (cfIds.contains(ctx.getCfId())) { - callback.onSuccess(CALLBACKS_PER_CF); + callback.onSuccess(); } else { if (proto.getTsDataCount() > 0) { - processArgumentValuesUpdate(ctx, cfIds, callback, mapToArguments(ctx, msg.getEntityId(), proto.getTsDataList()), toTbMsgId(proto), toTbMsgType(proto)); + processArgumentValuesUpdate(ctx, cfIds, callback, mapToArguments(ctx, msg.getEntityId(), proto.getTsDataList()), toTbMsgId(proto), toMsgType(proto)); } else if (proto.getAttrDataCount() > 0) { - processArgumentValuesUpdate(ctx, cfIds, callback, mapToArguments(ctx, msg.getEntityId(), proto.getScope(), proto.getAttrDataList()), toTbMsgId(proto), toTbMsgType(proto)); + processArgumentValuesUpdate(ctx, cfIds, callback, mapToArguments(ctx, msg.getEntityId(), proto.getScope(), proto.getAttrDataList()), toTbMsgId(proto), toMsgType(proto)); } else if (proto.getRemovedTsKeysCount() > 0) { - processArgumentValuesUpdate(ctx, cfIds, callback, mapToArgumentsWithFetchedValue(ctx, proto.getRemovedTsKeysList()), toTbMsgId(proto), toTbMsgType(proto)); + processArgumentValuesUpdate(ctx, cfIds, callback, mapToArgumentsWithFetchedValue(ctx, msg.getEntityId(), proto.getRemovedTsKeysList()), toTbMsgId(proto), toMsgType(proto)); } else if (proto.getRemovedAttrKeysCount() > 0) { - processArgumentValuesUpdate(ctx, cfIds, callback, mapToArgumentsWithDefaultValue(ctx, msg.getEntityId(), proto.getScope(), proto.getRemovedAttrKeysList()), toTbMsgId(proto), toTbMsgType(proto)); + processArgumentValuesUpdate(ctx, cfIds, callback, mapToArgumentsWithDefaultValue(ctx, msg.getEntityId(), proto.getScope(), proto.getRemovedAttrKeysList()), toTbMsgId(proto), toMsgType(proto)); } else { - callback.onSuccess(CALLBACKS_PER_CF); + callback.onSuccess(); } } } catch (Exception e) { + log.debug("[{}][{}] Failed to process linked CF telemetry msg: {}", entityId, ctx.getCfId(), msg, e); if (e instanceof CalculatedFieldException cfe) { throw cfe; } @@ -206,10 +354,10 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } } - private void process(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, Collection cfIds, List cfIdList, MultipleTbCallback callback) throws CalculatedFieldException { + private void process(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, Collection cfIds, List cfIdList, TbCallback callback) throws CalculatedFieldException { try { if (cfIds.contains(ctx.getCfId())) { - callback.onSuccess(CALLBACKS_PER_CF); + callback.onSuccess(); } else { if (proto.getTsDataCount() > 0) { processTelemetry(ctx, proto, cfIdList, callback); @@ -220,10 +368,11 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } else if (proto.getRemovedAttrKeysCount() > 0) { processRemovedAttributes(ctx, proto, cfIdList, callback); } else { - callback.onSuccess(CALLBACKS_PER_CF); + callback.onSuccess(); } } } catch (Exception e) { + log.debug("[{}][{}] Failed to process CF telemetry msg: {}", entityId, ctx.getCfId(), proto, e); if (e instanceof CalculatedFieldException cfe) { if (DebugModeUtil.isDebugFailuresAvailable(cfe.getCtx().getCalculatedField())) { systemContext.persistCalculatedFieldDebugError(cfe); @@ -235,88 +384,163 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } } - private void processTelemetry(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, MultipleTbCallback callback) throws CalculatedFieldException { - processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArguments(ctx, proto.getTsDataList()), toTbMsgId(proto), toTbMsgType(proto)); + public void process(CalculatedFieldReevaluateMsg msg) throws CalculatedFieldException { + CalculatedFieldCtx ctx = msg.getCtx(); + CalculatedFieldId cfId = ctx.getCfId(); + CalculatedFieldState state = states.get(cfId); + if (state == null) { + log.warn("[{}][{}] Failed to find CF state (probably wasn't restored properly) for entity to handle {}", entityId, cfId, msg); + state = createState(ctx); + } + if (state.isSizeOk()) { + log.debug("[{}][{}] Reevaluating CF state", entityId, cfId); + processStateIfReady(state, null, ctx, Collections.singletonList(cfId), null, CalculatedFieldEventType.REEVALUATION_MSG.name(), msg.getCallback()); + } else { + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(); + } + } + + public void process(CalculatedFieldAlarmActionMsg msg) { + log.debug("[{}] Processing alarm action event msg: {}", entityId, msg); + for (CalculatedFieldState state : states.values()) { + if (state instanceof AlarmCalculatedFieldState alarmCfState) { + Alarm stateAlarm = alarmCfState.getCurrentAlarm(); + if (stateAlarm != null && stateAlarm.getId().equals(msg.getAlarm().getId())) { + alarmCfState.processAlarmAction(msg.getAlarm(), msg.getAction()); + } + } + } + msg.getCallback().onSuccess(); + } + + private void processTelemetry(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, TbCallback callback) throws CalculatedFieldException { + processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArguments(ctx, proto.getTsDataList()), toTbMsgId(proto), toMsgType(proto)); } - private void processAttributes(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, MultipleTbCallback callback) throws CalculatedFieldException { - processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArguments(ctx, proto.getScope(), proto.getAttrDataList()), toTbMsgId(proto), toTbMsgType(proto)); + private void processAttributes(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, TbCallback callback) throws CalculatedFieldException { + processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArguments(ctx, proto.getScope(), proto.getAttrDataList()), toTbMsgId(proto), toMsgType(proto)); } - private void processRemovedTelemetry(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, MultipleTbCallback callback) throws CalculatedFieldException { - processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArgumentsWithFetchedValue(ctx, proto.getRemovedTsKeysList()), toTbMsgId(proto), toTbMsgType(proto)); + private void processRemovedTelemetry(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, TbCallback callback) throws CalculatedFieldException { + processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArgumentsWithFetchedValue(ctx, entityId, proto.getRemovedTsKeysList()), toTbMsgId(proto), toMsgType(proto)); } - private void processRemovedAttributes(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, MultipleTbCallback callback) throws CalculatedFieldException { - processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArgumentsWithDefaultValue(ctx, proto.getScope(), proto.getRemovedAttrKeysList()), toTbMsgId(proto), toTbMsgType(proto)); + private void processRemovedAttributes(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List cfIdList, TbCallback callback) throws CalculatedFieldException { + processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArgumentsWithDefaultValue(ctx, proto.getScope(), proto.getRemovedAttrKeysList()), toTbMsgId(proto), toMsgType(proto)); } - private void processArgumentValuesUpdate(CalculatedFieldCtx ctx, List cfIdList, MultipleTbCallback callback, - Map newArgValues, UUID tbMsgId, TbMsgType tbMsgType) throws CalculatedFieldException { + private void processArgumentValuesUpdate(CalculatedFieldCtx ctx, List cfIdList, TbCallback callback, + Map newArgValues, UUID tbMsgId, String msgType) throws CalculatedFieldException { if (newArgValues.isEmpty()) { log.debug("[{}] No new argument values to process for CF.", ctx.getCfId()); - callback.onSuccess(CALLBACKS_PER_CF); + callback.onSuccess(); } CalculatedFieldState state = states.get(ctx.getCfId()); boolean justRestored = false; if (state == null) { - state = getOrInitState(ctx); + state = createState(ctx); justRestored = true; + } else if (ctx.shouldFetchRelatedEntities(state)) { + log.debug("[{}][{}] Going to update related entities for CF.", entityId, ctx.getCfId()); + try { + if (state instanceof RelatedEntitiesAggregationCalculatedFieldState relatedEntitiesState) { + List relatedEntities = cfService.fetchRelatedEntities(ctx, entityId); + List missingEntities = relatedEntitiesState.checkRelatedEntities(relatedEntities); + if (!missingEntities.isEmpty()) { + missingEntities.forEach(missingEntityId -> { + Map fetchedArgs = cfService.fetchArgsFromDb(tenantId, missingEntityId, ctx.getArguments()); + relatedEntitiesState.updateEntityData(setEntityIdToSingleEntityArguments(missingEntityId, fetchedArgs)); + }); + justRestored = true; + } + } + if (state instanceof GeofencingCalculatedFieldState geofencingCalculatedFieldState) { + Map dynamicArgsFromDb = cfService.fetchDynamicArgsFromDb(ctx, entityId); + dynamicArgsFromDb.forEach(newArgValues::putIfAbsent); + geofencingCalculatedFieldState.updateScheduledRefreshTs(); + } + } catch (Exception e) { + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build(); + } } if (state.isSizeOk()) { - if (state.updateState(ctx, newArgValues) || justRestored) { + Map updatedArgs = state.update(newArgValues, ctx); + if (!updatedArgs.isEmpty() || justRestored) { cfIdList = new ArrayList<>(cfIdList); cfIdList.add(ctx.getCfId()); - processStateIfReady(ctx, cfIdList, state, tbMsgId, tbMsgType, callback); + processStateIfReady(state, updatedArgs, ctx, cfIdList, tbMsgId, msgType, callback); } else { - callback.onSuccess(CALLBACKS_PER_CF); + callback.onSuccess(); } } else { throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(); } } - @SneakyThrows - private CalculatedFieldState getOrInitState(CalculatedFieldCtx ctx) { - CalculatedFieldState state = states.get(ctx.getCfId()); - if (state != null) { - return state; - } else { - ListenableFuture stateFuture = systemContext.getCalculatedFieldProcessingService().fetchStateFromDb(ctx, entityId); - // Ugly but necessary. We do not expect to often fetch data from DB. Only once per pair lifetime. - // This call happens while processing the CF pack from the queue consumer. So the timeout should be relatively low. - // Alternatively, we can fetch the state outside the actor system and push separate command to create this actor, - // but this will significantly complicate the code. - state = stateFuture.get(1, TimeUnit.MINUTES); - state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize()); - states.put(ctx.getCfId(), state); - } + private CalculatedFieldState createState(CalculatedFieldCtx ctx) { + CalculatedFieldState state = createStateByType(ctx, entityId); + initState(state, ctx); return state; } - private void processStateIfReady(CalculatedFieldCtx ctx, List cfIdList, CalculatedFieldState state, UUID tbMsgId, TbMsgType tbMsgType, TbCallback callback) throws CalculatedFieldException { + private void initState(CalculatedFieldState state, CalculatedFieldCtx ctx) { + state.setCtx(ctx, actorCtx); + state.init(false); + + if (ctx.getCfType() == CalculatedFieldType.GEOFENCING && ctx.isCfHasRelationPathQuerySource()) { + GeofencingCalculatedFieldState geofencingState = (GeofencingCalculatedFieldState) state; + geofencingState.updateScheduledRefreshTs(); + } + + Map arguments = fetchArguments(ctx); + state.update(arguments, ctx); + + state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize()); + states.put(ctx.getCfId(), state); + } + + @SneakyThrows + private Map fetchArguments(CalculatedFieldCtx ctx) { + ListenableFuture> argumentsFuture = cfService.fetchArguments(ctx, entityId); + // Ugly but necessary. We do not expect to often fetch data from DB. Only once per pair lifetime. + // This call happens while processing the CF pack from the queue consumer. So the timeout should be relatively low. + // Alternatively, we can fetch the state outside the actor system and push separate command to create this actor, + // but this will significantly complicate the code. + return argumentsFuture.get(1, TimeUnit.MINUTES); + } + + private void processStateIfReady(CalculatedFieldState state, Map updatedArgs, CalculatedFieldCtx ctx, + List cfIdList, UUID tbMsgId, String tbMsgType, TbCallback callback) throws CalculatedFieldException { + callback = new MultipleTbCallback(CALLBACKS_PER_CF, callback); + log.trace("[{}][{}] Processing state if ready. Current args: {}, updated args: {}", entityId, ctx.getCfId(), state.getArguments(), updatedArgs); CalculatedFieldEntityCtxId ctxId = new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId); boolean stateSizeChecked = false; try { if (ctx.isInitialized() && state.isReady()) { - CalculatedFieldResult calculationResult = state.performCalculation(ctx).get(systemContext.getCfCalculationResultTimeout(), TimeUnit.SECONDS); + log.trace("[{}][{}] Performing calculation. Updated args: {}", entityId, ctx.getCfId(), updatedArgs); + CalculatedFieldResult calculationResult = state.performCalculation(updatedArgs, ctx).get(systemContext.getCfCalculationResultTimeout(), TimeUnit.SECONDS); state.checkStateSize(ctxId, ctx.getMaxStateSize()); stateSizeChecked = true; if (state.isSizeOk()) { if (!calculationResult.isEmpty()) { - cfService.pushMsgToRuleEngine(tenantId, entityId, calculationResult, cfIdList, callback); + cfService.processResult(tenantId, entityId, ctx.getCfName(), calculationResult, cfIdList, callback); } else { callback.onSuccess(); } if (DebugModeUtil.isDebugAllAvailable(ctx.getCalculatedField())) { - systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArguments(), tbMsgId, tbMsgType, calculationResult.getResult().toString(), null); + systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArgumentsJson(), tbMsgId, tbMsgType, calculationResult.stringValue(), null); } } } else { + if (DebugModeUtil.isDebugFailuresAvailable(ctx.getCalculatedField())) { + String errorMsg = ctx.isInitialized() ? state.getReadinessStatus().errorMsg() : "Calculated field state is not initialized!"; + systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArgumentsJson(), tbMsgId, tbMsgType, null, errorMsg); + } callback.onSuccess(); } } catch (Exception e) { - throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).msgId(tbMsgId).msgType(tbMsgType).arguments(state.getArguments()).cause(e).build(); + log.debug("[{}][{}] Failed to process CF state", entityId, ctx.getCfId(), e); + throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).msgId(tbMsgId).msgType(tbMsgType).arguments(state.getArgumentsJson()).cause(e).build(); } finally { if (!stateSizeChecked) { state.checkStateSize(ctxId, ctx.getMaxStateSize()); @@ -324,14 +548,30 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM if (state.isSizeOk()) { cfStateService.persistState(ctxId, state, callback); } else { - removeStateAndRaiseSizeException(ctxId, CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(), callback); + deleteStateAndRaiseSizeException(ctxId, CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(), callback); } } } - private void removeStateAndRaiseSizeException(CalculatedFieldEntityCtxId ctxId, CalculatedFieldException ex, TbCallback callback) throws CalculatedFieldException { + private CalculatedFieldState removeState(CalculatedFieldId cfId) { + CalculatedFieldState state = states.remove(cfId); + closeState(state); + return state; + } + + private void closeState(CalculatedFieldState state) { + if (state != null) { + try { + state.close(); + } catch (Exception e) { + log.warn("[{}][{}] Failed to close CF state", tenantId, state.getEntityId(), e); + } + } + } + + private void deleteStateAndRaiseSizeException(CalculatedFieldEntityCtxId ctxId, CalculatedFieldException ex, TbCallback callback) throws CalculatedFieldException { // We remove the state, but remember that it is over-sized in a local map. - cfStateService.removeState(ctxId, new TbCallback() { + cfStateService.deleteState(ctxId, new TbCallback() { @Override public void onSuccess() { callback.onFailure(ex); @@ -346,129 +586,196 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } private Map mapToArguments(CalculatedFieldCtx ctx, List data) { - return mapToArguments(ctx.getMainEntityArguments(), data); + return mapToArguments(entityId, ctx, ctx.getMainEntityArguments(), Collections.emptyMap(), data); } private Map mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, List data) { - var argNames = ctx.getLinkedEntityArguments().get(entityId); - if (argNames.isEmpty()) { - return Collections.emptyMap(); - } - return mapToArguments(argNames, data); + return mapToArguments(entityId, ctx, ctx.getLinkedAndDynamicArgs(entityId), ctx.getRelatedEntityArguments(), data); } - private Map mapToArguments(Map> args, List data) { - if (args.isEmpty()) { - return Collections.emptyMap(); - } + private Map mapToArguments(EntityId originator, CalculatedFieldCtx ctx, Map> args, Map> relatedEntityArgs, List data) { Map arguments = new HashMap<>(); - for (TsKvProto item : data) { - ReferencedEntityKey key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_LATEST, null); - Set argNames = args.get(key); - if (argNames != null) { + if (!relatedEntityArgs.isEmpty() || !args.isEmpty()) { + for (TsKvProto item : data) { + ReferencedEntityKey key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_LATEST, null); + + SingleValueArgumentEntry relatedArgIncoming = new SingleValueArgumentEntry(originator, item); + mapLatest(ctx, relatedArgIncoming, relatedEntityArgs.get(key), arguments); + SingleValueArgumentEntry incoming = new SingleValueArgumentEntry(item); - argNames.forEach(argName -> arguments.compute(argName, (name, existing) -> { - if (existing == null) { - return incoming; - } - existing.updateEntry(incoming); - return existing; - })); + mapLatest(ctx, incoming, args.get(key), arguments); + + key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_ROLLING, null); + mapRolling(item, args.get(key), arguments); } + } + return arguments; + } - key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_ROLLING, null); - argNames = args.get(key); - if (argNames != null) { - Double recordValue = getValueForTsRecord(ProtoUtils.fromProto(item.getKv())); - argNames.forEach(argName -> arguments.compute(argName, (name, existing) -> { - if (existing instanceof TsRollingArgumentEntry rolling) { - if (recordValue != null) { - rolling.getTsRecords().put(item.getTs(), recordValue); - } - return rolling; - } - TsRollingArgumentEntry rolling = new TsRollingArgumentEntry(); + private void mapLatest(CalculatedFieldCtx ctx, + SingleValueArgumentEntry incoming, + Set argNames, + Map arguments) { + if (argNames != null) { + argNames.forEach(argName -> arguments.compute(argName, (name, existing) -> { + if (existing == null) { + return incoming; + } + existing.updateEntry(incoming, ctx); + return existing; + })); + } + } + + private void mapRolling(TsKvProto item, + Set argNames, + Map arguments) { + if (argNames != null) { + Double recordValue = getValueForTsRecord(ProtoUtils.fromProto(item.getKv())); + argNames.forEach(argName -> arguments.compute(argName, (name, existing) -> { + if (existing instanceof TsRollingArgumentEntry rolling) { if (recordValue != null) { rolling.getTsRecords().put(item.getTs(), recordValue); } - if (existing instanceof SingleValueArgumentEntry single) { - Double existingValue = getValueForTsRecord(single.getKvEntryValue()); - if (existingValue != null) { - rolling.getTsRecords().put(single.getTs(), existingValue); - } - } return rolling; - })); - } + } + TsRollingArgumentEntry rolling = new TsRollingArgumentEntry(); + if (recordValue != null) { + rolling.getTsRecords().put(item.getTs(), recordValue); + } + if (existing instanceof SingleValueArgumentEntry single) { + Double existingValue = getValueForTsRecord(single.getKvEntryValue()); + if (existingValue != null) { + rolling.getTsRecords().put(single.getTs(), existingValue); + } + } + return rolling; + })); } - return arguments; } private Map mapToArguments(CalculatedFieldCtx ctx, AttributeScopeProto scope, List attrDataList) { - return mapToArguments(ctx.getMainEntityArguments(), scope, attrDataList); + return mapToArguments(entityId, ctx.getMainEntityArguments(), ctx.getMainEntityGeofencingArgumentNames(), Collections.emptyMap(), scope, attrDataList); } private Map mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, AttributeScopeProto scope, List attrDataList) { - var argNames = ctx.getLinkedEntityArguments().get(entityId); - if (argNames.isEmpty()) { - return Collections.emptyMap(); - } - return mapToArguments(argNames, scope, attrDataList); + var args = ctx.getLinkedAndDynamicArgs(entityId); + var relatedEntityArgs = ctx.getRelatedEntityArguments(); + List geofencingArgumentNames = ctx.getLinkedEntityAndCurrentOwnerGeofencingArgumentNames(); + return mapToArguments(entityId, args, geofencingArgumentNames, relatedEntityArgs, scope, attrDataList); } - private Map mapToArguments(Map> args, AttributeScopeProto scope, List attrDataList) { + private Map mapToArguments(EntityId entityId, Map> args, List geofencingArgNames, Map> relatedEntityArgs, AttributeScopeProto scope, List attrDataList) { + if (args.isEmpty() && relatedEntityArgs.isEmpty()) { + return Collections.emptyMap(); + } Map arguments = new HashMap<>(); for (AttributeValueProto item : attrDataList) { ReferencedEntityKey key = new ReferencedEntityKey(item.getKey(), ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name())); - Set argNames = args.get(key); + Set argNames = relatedEntityArgs.get(key); if (argNames != null) { - argNames.forEach(argName -> arguments.put(argName, new SingleValueArgumentEntry(item))); + argNames.forEach(argName -> { + arguments.put(argName, new SingleValueArgumentEntry(entityId, item)); + }); } + argNames = args.get(key); + if (argNames == null) { + continue; + } + argNames.forEach(argName -> { + if (geofencingArgNames.contains(argName)) { + arguments.put(argName, new GeofencingArgumentEntry(entityId, item)); + } else { + arguments.put(argName, new SingleValueArgumentEntry(item)); + } + }); } return arguments; } private Map mapToArgumentsWithDefaultValue(CalculatedFieldCtx ctx, EntityId entityId, AttributeScopeProto scope, List removedAttrKeys) { - var argNames = ctx.getLinkedEntityArguments().get(entityId); - if (argNames.isEmpty()) { - return Collections.emptyMap(); - } - return mapToArgumentsWithDefaultValue(argNames, ctx.getArguments(), scope, removedAttrKeys); + var args = ctx.getLinkedAndDynamicArgs(entityId); + var relatedEntityArgs = ctx.getRelatedEntityArguments(); + List geofencingArgumentNames = ctx.getLinkedEntityAndCurrentOwnerGeofencingArgumentNames(); + return mapToArgumentsWithDefaultValue(entityId, args, ctx.getArguments(), geofencingArgumentNames, relatedEntityArgs, scope, removedAttrKeys); } private Map mapToArgumentsWithDefaultValue(CalculatedFieldCtx ctx, AttributeScopeProto scope, List removedAttrKeys) { - return mapToArgumentsWithDefaultValue(ctx.getMainEntityArguments(), ctx.getArguments(), scope, removedAttrKeys); + return mapToArgumentsWithDefaultValue(null, ctx.getMainEntityArguments(), ctx.getArguments(), ctx.getMainEntityGeofencingArgumentNames(), Collections.emptyMap(), scope, removedAttrKeys); } - private Map mapToArgumentsWithDefaultValue(Map> args, Map configArguments, AttributeScopeProto scope, List removedAttrKeys) { + private Map mapToArgumentsWithDefaultValue(EntityId msgEntityId, + Map> args, + Map configArguments, + List geofencingArgNames, + Map> relatedEntityArgs, + AttributeScopeProto scope, + List removedAttrKeys) { + if (args.isEmpty() && relatedEntityArgs.isEmpty()) { + return Collections.emptyMap(); + } Map arguments = new HashMap<>(); for (String removedKey : removedAttrKeys) { ReferencedEntityKey key = new ReferencedEntityKey(removedKey, ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name())); - Set argNames = args.get(key); + Set argNames = relatedEntityArgs.get(key); if (argNames != null) { argNames.forEach(argName -> { - Argument argument = configArguments.get(argName); - String defaultValue = (argument != null) ? argument.getDefaultValue() : null; - arguments.put(argName, StringUtils.isNotEmpty(defaultValue) - ? new SingleValueArgumentEntry(System.currentTimeMillis(), new StringDataEntry(removedKey, defaultValue), null) - : new SingleValueArgumentEntry()); + String defaultValue = getDefaultValue(configArguments, argName); + SingleValueArgumentEntry argumentEntry = buildSingleValue(removedKey, defaultValue, System.currentTimeMillis()); + arguments.put(argName, new SingleValueArgumentEntry(msgEntityId, argumentEntry)); }); } + argNames = args.get(key); + if (argNames == null) { + continue; + } + argNames.forEach(argName -> { + if (geofencingArgNames.contains(argName)) { + arguments.put(argName, new GeofencingArgumentEntry()); + } else { + String defaultValue = getDefaultValue(configArguments, argName); + SingleValueArgumentEntry argumentEntry = buildSingleValue(removedKey, defaultValue, System.currentTimeMillis()); + arguments.put(argName, new SingleValueArgumentEntry(argumentEntry)); + } + }); } return arguments; } - private Map mapToArgumentsWithFetchedValue(CalculatedFieldCtx ctx, List removedTelemetryKeys) { + private String getDefaultValue(Map configArguments, String argNames) { + Argument argument = configArguments.get(argNames); + return argument != null ? argument.getDefaultValue() : null; + } + + private SingleValueArgumentEntry buildSingleValue(String attrKey, String defaultValue, long ts) { + return StringUtils.isNotEmpty(defaultValue) + ? new SingleValueArgumentEntry(ts, new StringDataEntry(attrKey, defaultValue), null) + : new SingleValueArgumentEntry(); + } + + private Map mapToArgumentsWithFetchedValue(CalculatedFieldCtx ctx, EntityId entityId, List removedTelemetryKeys) { Map deletedArguments = ctx.getArguments().entrySet().stream() .filter(entry -> removedTelemetryKeys.contains(entry.getValue().getRefEntityKey().getKey())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map fetchedArgs = cfService.fetchArgsFromDb(tenantId, entityId, deletedArguments); + if (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION.equals(ctx.getCfType())) { + fetchedArgs = setEntityIdToSingleEntityArguments(entityId, fetchedArgs); + } fetchedArgs.values().forEach(arg -> arg.setForceResetPrevious(true)); + return fetchedArgs; } + private Map setEntityIdToSingleEntityArguments(EntityId relatedEntityId, Map fetchedArgs) { + return fetchedArgs.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + argEntry -> new SingleValueArgumentEntry(relatedEntityId, argEntry.getValue()) + )); + } + private static List getCalculatedFieldIds(CalculatedFieldTelemetryMsgProto proto) { List cfIds = new LinkedList<>(); for (var cfId : proto.getPreviousCalculatedFieldsList()) { @@ -484,9 +791,9 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM return null; } - private TbMsgType toTbMsgType(CalculatedFieldTelemetryMsgProto proto) { + private String toMsgType(CalculatedFieldTelemetryMsgProto proto) { if (!proto.getTbMsgType().isEmpty()) { - return TbMsgType.valueOf(proto.getTbMsgType()); + return proto.getTbMsgType(); } return null; } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldException.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldException.java index de11061e02..97cb4c9640 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldException.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldException.java @@ -15,14 +15,12 @@ */ package org.thingsboard.server.actors.calculatedField; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Builder; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.msg.TbMsgType; -import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; -import java.util.Map; import java.util.UUID; @Getter @@ -32,8 +30,8 @@ public class CalculatedFieldException extends Exception { private final CalculatedFieldCtx ctx; private final EntityId eventEntity; private final UUID msgId; - private final TbMsgType msgType; - private Map arguments; + private final String msgType; + private JsonNode arguments; private String errorMessage; private Exception cause; diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldLinkedTelemetryMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldLinkedTelemetryMsg.java index 5870e8ddb5..1d52c230d3 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldLinkedTelemetryMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldLinkedTelemetryMsg.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldLinkedTelemetryMsgProto; -import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto; @Data public class CalculatedFieldLinkedTelemetryMsg implements ToCalculatedFieldSystemMsg { @@ -32,9 +31,9 @@ public class CalculatedFieldLinkedTelemetryMsg implements ToCalculatedFieldSyste private final CalculatedFieldLinkedTelemetryMsgProto proto; private final TbCallback callback; - @Override public MsgType getMsgType() { return MsgType.CF_LINKED_TELEMETRY_MSG; } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActor.java index 16d5db362a..503de0e652 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActor.java @@ -20,6 +20,7 @@ import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.CalculatedFieldStatePartitionRestoreMsg; import org.thingsboard.server.common.msg.TbActorStopReason; import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldCacheInitMsg; @@ -70,9 +71,15 @@ public class CalculatedFieldManagerActor extends AbstractCalculatedFieldActor { case CF_STATE_RESTORE_MSG: processor.onStateRestoreMsg((CalculatedFieldStateRestoreMsg) msg); break; + case CF_STATE_PARTITION_RESTORE_MSG: + processor.onStatePartitionRestoreMsg((CalculatedFieldStatePartitionRestoreMsg) msg); + break; case CF_ENTITY_LIFECYCLE_MSG: processor.onEntityLifecycleMsg((CalculatedFieldEntityLifecycleMsg) msg); break; + case CF_ENTITY_ACTION_EVENT_MSG: + processor.onEntityActionEventMsg((CalculatedFieldEntityActionEventMsg) msg); + break; case CF_TELEMETRY_MSG: processor.onTelemetryMsg((CalculatedFieldTelemetryMsg) msg); break; diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java index 65ec0b332b..a1d7477a36 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java @@ -16,23 +16,44 @@ package org.thingsboard.server.actors.calculatedField; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.function.TriConsumer; +import org.thingsboard.common.util.DebugModeUtil; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.actors.TbCalculatedFieldEntityActorId; +import org.thingsboard.server.actors.calculatedField.EntityInitCalculatedFieldMsg.StateAction; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; +import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ProfileEntityIdInfo; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldEventType; import org.thingsboard.server.common.data.cf.CalculatedFieldLink; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.HasRelationPathLevel; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.data.page.PageDataIterable; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationPathQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.msg.CalculatedFieldStatePartitionRestoreMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldCacheInitMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg; @@ -41,10 +62,13 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.cf.CalculatedFieldService; +import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.queue.settings.TbQueueCalculatedFieldSettings; import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; import org.thingsboard.server.service.cf.CalculatedFieldStateService; +import org.thingsboard.server.service.cf.OwnerService; import org.thingsboard.server.service.cf.cache.TenantEntityProfileCache; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; @@ -52,11 +76,21 @@ import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import java.util.ArrayList; +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.Optional; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; import static org.thingsboard.server.utils.CalculatedFieldUtils.fromProto; @@ -69,18 +103,25 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware private final Map calculatedFields = new HashMap<>(); private final Map> entityIdCalculatedFields = new HashMap<>(); private final Map> entityIdCalculatedFieldLinks = new HashMap<>(); + private final Map> ownerEntities = new HashMap<>(); + private ScheduledFuture cfsReevaluationTask; private final CalculatedFieldProcessingService cfExecService; private final CalculatedFieldStateService cfStateService; private final CalculatedFieldService cfDaoService; private final DeviceService deviceService; private final AssetService assetService; + private final CustomerService customerService; + private final RelationService relationService; private final TbAssetProfileCache assetProfileCache; private final TbDeviceProfileCache deviceProfileCache; private final TenantEntityProfileCache entityProfileCache; + private final OwnerService ownerService; private final TbQueueCalculatedFieldSettings cfSettings; protected final TenantId tenantId; + private long cfCheckInterval; + protected TbActorCtx ctx; CalculatedFieldManagerMessageProcessor(ActorSystemContext systemContext, TenantId tenantId) { @@ -90,9 +131,12 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware this.cfDaoService = systemContext.getCalculatedFieldService(); this.deviceService = systemContext.getDeviceService(); this.assetService = systemContext.getAssetService(); + this.customerService = systemContext.getCustomerService(); + this.relationService = systemContext.getRelationService(); this.assetProfileCache = systemContext.getAssetProfileCache(); this.deviceProfileCache = systemContext.getDeviceProfileCache(); this.entityProfileCache = new TenantEntityProfileCache(); + this.ownerService = systemContext.getOwnerService(); this.cfSettings = systemContext.getCalculatedFieldSettings(); this.tenantId = tenantId; } @@ -103,93 +147,115 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware public void stop() { log.info("[{}] Stopping CF manager actor.", tenantId); - calculatedFields.values().forEach(CalculatedFieldCtx::stop); + calculatedFields.values().forEach(CalculatedFieldCtx::close); calculatedFields.clear(); entityIdCalculatedFields.clear(); entityIdCalculatedFieldLinks.clear(); + cancelReevaluationTask(); ctx.stop(ctx.getSelf()); } public void onCacheInitMsg(CalculatedFieldCacheInitMsg msg) { log.debug("[{}] Processing CF actor init message.", msg.getTenantId().getId()); - initEntityProfileCache(); + initEntitiesCache(); initCalculatedFields(); + cfCheckInterval = systemContext.getApiLimitService().getLimit(tenantId, DefaultTenantProfileConfiguration::getCfReevaluationCheckInterval); + scheduleCfsReevaluation(); msg.getCallback().onSuccess(); } public void onStateRestoreMsg(CalculatedFieldStateRestoreMsg msg) { var cfId = msg.getId().cfId(); - var calculatedField = calculatedFields.get(cfId); + var ctx = calculatedFields.get(cfId); - if (calculatedField != null) { - if (msg.getState() != null) { - msg.getState().setRequiredArguments(calculatedField.getArgNames()); - } + if (ctx != null) { + msg.setCtx(ctx); log.debug("[{}] Pushing CF state restore msg to specific actor [{}]", tenantId, msg.getId().entityId()); - getOrCreateActor(msg.getId().entityId()).tell(msg); + getOrCreateActor(msg.getId().entityId()).tellWithHighPriority(msg); } else if (msg.getState() != null) { log.debug("[{}] Received CF state restore msg for non-existing CF [{}]. Removing state", tenantId, cfId); - cfStateService.removeState(msg.getId(), msg.getCallback()); + cfStateService.deleteState(msg.getId(), msg.getCallback()); } else { msg.getCallback().onSuccess(); } } + public void onStatePartitionRestoreMsg(CalculatedFieldStatePartitionRestoreMsg msg) { + ctx.broadcastToChildren(msg, true); + } + + private void scheduleCfsReevaluation() { + cfsReevaluationTask = systemContext.getScheduler().scheduleWithFixedDelay(() -> { + try { + calculatedFields.values().forEach(cf -> { + if (cf.requiresScheduledReevaluation()) { + applyToTargetCfEntityActors(cf, TbCallback.EMPTY, (entityId, callback) -> { + log.debug("[{}][{}] Pushing scheduled CF reevaluate msg", entityId, cf.getCfId()); + getOrCreateActor(entityId).tell(new CalculatedFieldReevaluateMsg(tenantId, cf)); + }); + } + }); + } catch (Exception e) { + log.warn("[{}] Failed to trigger CFs reevaluation", tenantId, e); + } + }, cfCheckInterval, cfCheckInterval, TimeUnit.SECONDS); + } + public void onEntityLifecycleMsg(CalculatedFieldEntityLifecycleMsg msg) throws CalculatedFieldException { - log.debug("Processing entity lifecycle event: [{}] for entity: [{}]", msg.getData().getEvent(), msg.getData().getEntityId()); - var entityType = msg.getData().getEntityId().getEntityType(); var event = msg.getData().getEvent(); + if (ComponentLifecycleEvent.RELATION_UPDATED.equals(event) || ComponentLifecycleEvent.RELATION_DELETED.equals(event)) { + log.debug("Processing relation [{}] event from entity: [{}]", event, msg.getData().getEntityId()); + onRelationChangedEvent(msg.getData(), msg.getCallback()); + return; + } + log.debug("Processing entity lifecycle event: [{}] for entity: [{}]", event, msg.getData().getEntityId()); + var entityType = msg.getData().getEntityId().getEntityType(); switch (entityType) { - case CALCULATED_FIELD: { + case CALCULATED_FIELD -> { switch (event) { - case CREATED: - onCfCreated(msg.getData(), msg.getCallback()); - break; - case UPDATED: - onCfUpdated(msg.getData(), msg.getCallback()); - break; - case DELETED: - onCfDeleted(msg.getData(), msg.getCallback()); - break; - default: - msg.getCallback().onSuccess(); - break; + case CREATED -> onCfCreated(msg.getData(), msg.getCallback()); + case UPDATED -> onCfUpdated(msg.getData(), msg.getCallback()); + case DELETED -> onCfDeleted(msg.getData(), msg.getCallback()); + default -> msg.getCallback().onSuccess(); } - break; } - case DEVICE: - case ASSET: { + case DEVICE, ASSET, CUSTOMER -> { switch (event) { - case CREATED: - onEntityCreated(msg.getData(), msg.getCallback()); - break; - case UPDATED: - onEntityUpdated(msg.getData(), msg.getCallback()); - break; - case DELETED: - onEntityDeleted(msg.getData(), msg.getCallback()); - break; - default: - msg.getCallback().onSuccess(); - break; + case CREATED -> onEntityCreated(msg.getData(), msg.getCallback()); + case UPDATED -> onEntityUpdated(msg.getData(), msg.getCallback()); + case DELETED -> onEntityDeleted(msg.getData(), msg.getCallback()); + default -> msg.getCallback().onSuccess(); } - break; } - case DEVICE_PROFILE: - case ASSET_PROFILE: { + case DEVICE_PROFILE, ASSET_PROFILE -> { switch (event) { - case DELETED: - onProfileDeleted(msg.getData(), msg.getCallback()); - break; - default: - msg.getCallback().onSuccess(); - break; + case DELETED -> onProfileDeleted(msg.getData(), msg.getCallback()); + default -> msg.getCallback().onSuccess(); } - break; } - default: { - msg.getCallback().onSuccess(); + case TENANT_PROFILE -> { + switch (event) { + case UPDATED -> onTenantProfileUpdated(msg.getData(), msg.getCallback()); + default -> msg.getCallback().onSuccess(); + } + } + default -> msg.getCallback().onSuccess(); + } + } + + public void onEntityActionEventMsg(CalculatedFieldEntityActionEventMsg msg) { + switch (msg.getAction()) { + case ALARM_ACK, ALARM_CLEAR, ALARM_DELETE -> { + Alarm alarm = JacksonUtil.treeToValue(msg.getEntity(), Alarm.class); + CalculatedFieldAlarmActionMsg alarmActionMsg = CalculatedFieldAlarmActionMsg.builder() + .tenantId(tenantId) + .alarm(alarm) + .action(msg.getAction()) + .callback(msg.getCallback()) + .build(); + getOrCreateActor(alarm.getOriginator()).tellWithHighPriority(alarmActionMsg); } + default -> msg.getCallback().onSuccess(); } } @@ -198,12 +264,46 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware callback.onSuccess(); } + private void onTenantProfileUpdated(ComponentLifecycleMsg msg, TbCallback callback) { + checkCfIntervalForUpdate(); + + long maxRelatedEntitiesPerCfArgument = systemContext.getApiLimitService().getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxRelatedEntitiesToReturnPerCfArgument); + List cfsToReinit = new ArrayList<>(); + Stream.concat( + calculatedFields.values().stream(), + entityIdCalculatedFields.values().stream().flatMap(Collection::stream) + ).forEach(ctx -> { + if (ctx.hasRelatedEntities() && ctx.getMaxRelatedEntitiesPerCfArgument() != maxRelatedEntitiesPerCfArgument) { + cfsToReinit.add(ctx); + } + ctx.setTenantProfileProperties(); + }); + + if (!cfsToReinit.isEmpty()) { + MultipleTbCallback cfsReinitCallback = new MultipleTbCallback(cfsToReinit.size(), callback); + cfsToReinit.forEach(ctx -> applyToTargetCfEntityActors(ctx, cfsReinitCallback, (id, cb) -> initCfForEntity(id, ctx, StateAction.REINIT, CalculatedFieldEventType.TENANT_PROFILE_UPDATED, cb))); + } else { + callback.onSuccess(); + } + } + + private void checkCfIntervalForUpdate() { + long updatedCfCheckInterval = systemContext.getApiLimitService().getLimit(tenantId, DefaultTenantProfileConfiguration::getCfReevaluationCheckInterval); + if (cfCheckInterval != updatedCfCheckInterval) { + cfCheckInterval = updatedCfCheckInterval; + cancelReevaluationTask(); + scheduleCfsReevaluation(); + } + } + private void onEntityCreated(ComponentLifecycleMsg msg, TbCallback callback) { EntityId entityId = msg.getEntityId(); EntityId profileId = getProfileId(tenantId, entityId); if (profileId != null) { entityProfileCache.add(profileId, entityId); } + updateEntityOwner(entityId); + if (!isMyPartition(entityId, callback)) { return; } @@ -212,8 +312,8 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware var fieldsCount = entityIdFields.size() + profileIdFields.size(); if (fieldsCount > 0) { MultipleTbCallback multiCallback = new MultipleTbCallback(fieldsCount, callback); - entityIdFields.forEach(ctx -> initCfForEntity(entityId, ctx, true, multiCallback)); - profileIdFields.forEach(ctx -> initCfForEntity(entityId, ctx, true, multiCallback)); + entityIdFields.forEach(ctx -> initCfForEntity(entityId, ctx, StateAction.INIT, CalculatedFieldEventType.INITIALIZED, multiCallback)); + profileIdFields.forEach(ctx -> initCfForEntity(entityId, ctx, StateAction.INIT, CalculatedFieldEventType.INITIALIZED, multiCallback)); } else { callback.onSuccess(); } @@ -232,21 +332,84 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware MultipleTbCallback multiCallback = new MultipleTbCallback(fieldsCount, callback); var entityId = msg.getEntityId(); oldProfileCfs.forEach(ctx -> deleteCfForEntity(entityId, ctx.getCfId(), multiCallback)); - newProfileCfs.forEach(ctx -> initCfForEntity(entityId, ctx, true, multiCallback)); + newProfileCfs.forEach(ctx -> initCfForEntity(entityId, ctx, StateAction.INIT, CalculatedFieldEventType.INITIALIZED, multiCallback)); } else { callback.onSuccess(); } + } else if (msg.isOwnerChanged()) { + onEntityOwnerChanged(msg, callback); + } else { + callback.onSuccess(); } } private void onEntityDeleted(ComponentLifecycleMsg msg, TbCallback callback) { - entityProfileCache.removeEntityId(msg.getEntityId()); + switch (msg.getEntityId().getEntityType()) { + case DEVICE, ASSET -> entityProfileCache.removeEntityId(msg.getEntityId()); + case CUSTOMER -> ownerEntities.remove(msg.getEntityId()); + } + ownerEntities.values().forEach(entities -> entities.remove(msg.getEntityId())); if (isMyPartition(msg.getEntityId(), callback)) { log.debug("Pushing entity lifecycle msg to specific actor [{}]", msg.getEntityId()); getOrCreateActor(msg.getEntityId()).tell(new CalculatedFieldEntityDeleteMsg(tenantId, msg.getEntityId(), callback)); } } + private void onRelationChangedEvent(ComponentLifecycleMsg msg, TbCallback callback) { + Function> relationAction = switch (msg.getEvent()) { + case RELATION_UPDATED -> relatedId -> (entityId, ctx, cb) -> initRelatedEntity(entityId, relatedId, ctx, cb); + case RELATION_DELETED -> relatedId -> (entityId, ctx, cb) -> deleteRelatedEntity(entityId, relatedId, ctx, cb); + default -> null; + }; + + if (relationAction == null) { + callback.onSuccess(); + return; + } + + EntityRelation entityRelation = JacksonUtil.treeToValue(msg.getInfo(), EntityRelation.class); + EntityId toId = entityRelation.getTo(); + EntityId fromId = entityRelation.getFrom(); + String relationType = entityRelation.getType(); + + if (!(CalculatedField.isSupportedRefEntity(toId) || CalculatedField.isSupportedRefEntity(fromId))) { + callback.onSuccess(); + return; + } + + MultipleTbCallback callbackForToAndFrom = new MultipleTbCallback(2, callback); + processRelationByDirection(EntitySearchDirection.TO, relationType, toId, callbackForToAndFrom, relationAction.apply(fromId)); + processRelationByDirection(EntitySearchDirection.FROM, relationType, fromId, callbackForToAndFrom, relationAction.apply(toId)); + } + + private void processRelationByDirection(EntitySearchDirection direction, + String relationType, + EntityId mainId, + MultipleTbCallback parentCallback, + TriConsumer relationAction) { + if (!isMyPartition(mainId, parentCallback)) { + return; + } + List cfsByEntityIdAndProfile = getCalculatedFieldsByEntityIdAndProfile(mainId); + if (cfsByEntityIdAndProfile.isEmpty()) { + parentCallback.onSuccess(); + return; + } + + List matchingCfs = cfsByEntityIdAndProfile.stream() + .filter(cf -> { + if (cf.getCalculatedField().getConfiguration() instanceof HasRelationPathLevel config) { + RelationPathLevel relation = config.getRelation(); + return direction.equals(relation.direction()) && relationType.equals(relation.relationType()); + } + return false; + }) + .toList(); + + MultipleTbCallback directionCallback = new MultipleTbCallback(matchingCfs.size(), parentCallback); + matchingCfs.forEach(ctx -> relationAction.accept(mainId, ctx, directionCallback)); + } + private void onCfCreated(ComponentLifecycleMsg msg, TbCallback callback) throws CalculatedFieldException { var cfId = new CalculatedFieldId(msg.getEntityId().getId()); if (calculatedFields.containsKey(cfId)) { @@ -258,7 +421,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware log.debug("[{}] Failed to lookup CF by id [{}]", tenantId, cfId); callback.onSuccess(); } else { - var cfCtx = new CalculatedFieldCtx(cf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService()); + var cfCtx = getCfCtx(cf); try { cfCtx.init(); } catch (Exception e) { @@ -269,11 +432,15 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware // Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead) entityIdCalculatedFields.computeIfAbsent(cf.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(cfCtx); addLinks(cf); - initCf(cfCtx, callback, false); + applyToTargetCfEntityActors(cfCtx, callback, (id, cb) -> initCfForEntity(id, cfCtx, StateAction.INIT, CalculatedFieldEventType.INITIALIZED, cb)); } } } + private CalculatedFieldCtx getCfCtx(CalculatedField cf) { + return new CalculatedFieldCtx(cf, systemContext); + } + private void onCfUpdated(ComponentLifecycleMsg msg, TbCallback callback) throws CalculatedFieldException { var cfId = new CalculatedFieldId(msg.getEntityId().getId()); var oldCfCtx = calculatedFields.get(cfId); @@ -285,7 +452,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware log.debug("[{}] Failed to lookup CF by id [{}]", tenantId, cfId); callback.onSuccess(); } else { - var newCfCtx = new CalculatedFieldCtx(newCf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService()); + var newCfCtx = getCfCtx(newCf); try { newCfCtx.init(); } catch (Exception e) { @@ -313,12 +480,33 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware addLinks(newCf); } - var stateChanges = newCfCtx.hasStateChanges(oldCfCtx); - if (stateChanges || newCfCtx.hasOtherSignificantChanges(oldCfCtx)) { - initCf(newCfCtx, callback, stateChanges); + StateAction stateAction; + if (newCfCtx.getCfType() != oldCfCtx.getCfType()) { + stateAction = StateAction.RECREATE; // completely recreate state, then calculate + } else if (newCfCtx.hasStateChanges(oldCfCtx)) { + stateAction = StateAction.REINIT; // refetch arguments, call state.init, then calculate + } else if (newCfCtx.hasContextOnlyChanges(oldCfCtx)) { + stateAction = StateAction.REPROCESS; // call state.setCtx, then calculate + } else if (newCfCtx.hasRefreshContextOnlyChanges(oldCfCtx)) { + stateAction = StateAction.REFRESH_CTX; } else { callback.onSuccess(); + return; } + + applyToTargetCfEntityActors(newCfCtx, new TbCallback() { + @Override + public void onSuccess() { + oldCfCtx.close(); + callback.onSuccess(); + } + + @Override + public void onFailure(Throwable t) { + oldCfCtx.close(); + callback.onFailure(t); + } + }, (id, cb) -> initCfForEntity(id, newCfCtx, stateAction, CalculatedFieldEventType.UPDATED, cb)); } } } @@ -329,38 +517,30 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware if (cfCtx == null) { log.debug("[{}] CF was already deleted [{}]", tenantId, cfId); callback.onSuccess(); - } else { - entityIdCalculatedFields.get(cfCtx.getEntityId()).remove(cfCtx); - deleteLinks(cfCtx); - - EntityId entityId = cfCtx.getEntityId(); - EntityType entityType = cfCtx.getEntityId().getEntityType(); - if (isProfileEntity(entityType)) { - var entityIds = entityProfileCache.getEntityIdsByProfileId(entityId); - if (!entityIds.isEmpty()) { - //TODO: no need to do this if we cache all created actors and know which one belong to us; - var multiCallback = new MultipleTbCallback(entityIds.size(), callback); - entityIds.forEach(id -> { - if (isMyPartition(id, multiCallback)) { - deleteCfForEntity(id, cfId, multiCallback); - } - }); - } else { - callback.onSuccess(); - } - } else { - if (isMyPartition(entityId, callback)) { - deleteCfForEntity(entityId, cfId, callback); - } - } + return; } + entityIdCalculatedFields.get(cfCtx.getEntityId()).remove(cfCtx); + deleteLinks(cfCtx); + applyToTargetCfEntityActors(cfCtx, new TbCallback() { + @Override + public void onSuccess() { + cfCtx.close(); + callback.onSuccess(); + } + + @Override + public void onFailure(Throwable t) { + cfCtx.close(); + callback.onFailure(t); + } + }, (id, cb) -> deleteCfForEntity(id, cfId, cb)); } public void onTelemetryMsg(CalculatedFieldTelemetryMsg msg) { EntityId entityId = msg.getEntityId(); log.debug("Received telemetry msg from entity [{}]", entityId); - // 2 = 1 for CF processing + 1 for links processing - MultipleTbCallback callback = new MultipleTbCallback(2, msg.getCallback()); + // 4 = 1 for CF processing + 1 for links processing + 1 for owner entity processing + 1 for aggregation processing + MultipleTbCallback callback = new MultipleTbCallback(4, msg.getCallback()); // process all cfs related to entity, or it's profile; var entityIdFields = getCalculatedFieldsByEntityId(entityId); var profileIdFields = getCalculatedFieldsByEntityId(getProfileId(tenantId, entityId)); @@ -378,6 +558,59 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware } else { callback.onSuccess(); } + // process all cfs related to owner entity + if (entityId.getEntityType().isOneOf(EntityType.TENANT, EntityType.CUSTOMER)) { + List ownedEntitiesCFs = filterOwnedEntitiesCFs(msg); + if (!ownedEntitiesCFs.isEmpty()) { + cfExecService.pushMsgToLinks(msg, ownedEntitiesCFs, callback); + } else { + callback.onSuccess(); + } + } else { + callback.onSuccess(); + } + // process all aggregation cfs (if any); + List aggregationCalculatedFields = filterAggregationCfs(msg); + if (!aggregationCalculatedFields.isEmpty()) { + cfExecService.pushMsgToLinks(msg, aggregationCalculatedFields, callback); + } else { + callback.onSuccess(); + } + } + + private List filterAggregationCfs(CalculatedFieldTelemetryMsg msg) { + EntityId entityId = msg.getEntityId(); + return calculatedFields.values().stream() + .filter(cf -> CalculatedFieldType.RELATED_ENTITIES_AGGREGATION.equals(cf.getCfType())) + .filter(cf -> cf.relatedEntityMatches(msg.getProto())) + .flatMap(cf -> findRelationsForCf(entityId, cf).stream()) + .toList(); + } + + private List findRelationsForCf(EntityId entityId, CalculatedFieldCtx cf) { + List result = new ArrayList<>(); + if (cf.getCalculatedField().getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration configuration) { + RelationPathLevel relation = configuration.getRelation(); + EntitySearchDirection inverseDirection = switch (relation.direction()) { + case FROM -> EntitySearchDirection.TO; + case TO -> EntitySearchDirection.FROM; + }; + RelationPathLevel inverseRelation = new RelationPathLevel(inverseDirection, relation.relationType()); + List byRelationPathQuery = relationService.findByRelationPathQuery(tenantId, new EntityRelationPathQuery(entityId, List.of(inverseRelation))); + EntityId cfEntityId = cf.getEntityId(); + Predicate matchesCfEntity = relatedEntity -> cfEntityId.equals(relatedEntity) || cfEntityId.equals(getProfileId(tenantId, relatedEntity)); + if (byRelationPathQuery != null && !byRelationPathQuery.isEmpty()) { + switch (relation.direction()) { + case FROM -> byRelationPathQuery.stream() + .filter(entityRelation -> matchesCfEntity.test(entityRelation.getFrom())) + .forEach(entityRelation -> result.add(new CalculatedFieldEntityCtxId(tenantId, cf.getCfId(), entityRelation.getFrom()))); + case TO -> byRelationPathQuery.stream() + .filter(entityRelation -> matchesCfEntity.test(entityRelation.getTo())) + .forEach(entityRelation -> result.add(new CalculatedFieldEntityCtxId(tenantId, cf.getCfId(), entityRelation.getTo()))); + } + } + } + return result; } public void onLinkedTelemetryMsg(CalculatedFieldLinkedTelemetryMsg msg) { @@ -392,32 +625,35 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware } for (var linkProto : linksList) { var link = fromProto(linkProto); - var targetEntityId = link.entityId(); - var targetEntityType = targetEntityId.getEntityType(); var cf = calculatedFields.get(link.cfId()); - if (EntityType.DEVICE_PROFILE.equals(targetEntityType) || EntityType.ASSET_PROFILE.equals(targetEntityType)) { - // iterate over all entities that belong to profile and push the message for corresponding CF - var entityIds = entityProfileCache.getEntityIdsByProfileId(targetEntityId); - if (!entityIds.isEmpty()) { - MultipleTbCallback multipleCallback = new MultipleTbCallback(entityIds.size(), callback); - var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, multipleCallback); - entityIds.forEach(entityId -> { - if (isMyPartition(entityId, multipleCallback)) { - log.debug("Pushing linked telemetry msg to specific actor [{}]", entityId); - getOrCreateActor(entityId).tell(newMsg); - } - }); + withTargetEntities(link.entityId(), callback, (ids, cb) -> { + var linkedTelemetryMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, cb); + ids.forEach(id -> linkedTelemetryMsgForEntity(id, linkedTelemetryMsg)); + }); + } + } + + private void onEntityOwnerChanged(ComponentLifecycleMsg msg, TbCallback msgCallback) { + EntityId entityId = msg.getEntityId(); + log.debug("Received changed owner msg from entity [{}]", entityId); + updateEntityOwner(entityId); + List cfs = getCalculatedFieldsByEntityIdAndProfile(entityId); + if (cfs.isEmpty()) { + msgCallback.onSuccess(); + return; + } + MultipleTbCallback callback = new MultipleTbCallback(cfs.size(), msgCallback); + cfs.forEach(cf -> { + if (isMyPartition(entityId, callback)) { + if (cf.hasCurrentOwnerSourceArguments()) { + CalculatedFieldArgumentResetMsg argResetMsg = new CalculatedFieldArgumentResetMsg(tenantId, cf, CalculatedFieldEventType.OWNER_CHANGED, callback); + log.debug("Pushing CF argument reset msg to specific actor [{}]", entityId); + getOrCreateActor(entityId).tell(argResetMsg); } else { callback.onSuccess(); } - } else { - if (isMyPartition(targetEntityId, callback)) { - log.debug("Pushing linked telemetry msg to specific actor [{}]", targetEntityId); - var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, callback); - getOrCreateActor(targetEntityId).tell(newMsg); - } } - } + }); } private List filterCalculatedFieldLinks(CalculatedFieldTelemetryMsg msg) { @@ -425,7 +661,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware var proto = msg.getProto(); List result = new ArrayList<>(); for (var link : getCalculatedFieldLinksByEntityId(entityId)) { - CalculatedFieldCtx ctx = calculatedFields.get(link.getCalculatedFieldId()); + CalculatedFieldCtx ctx = calculatedFields.get(link.calculatedFieldId()); if (ctx.linkMatches(entityId, proto)) { result.add(ctx.toCalculatedFieldEntityCtxId()); } @@ -433,6 +669,27 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware return result; } + private List filterOwnedEntitiesCFs(CalculatedFieldTelemetryMsg msg) { + Set entities = getOwnedEntities(msg.getEntityId()); + var proto = msg.getProto(); + List result = new ArrayList<>(); + for (var entityId : entities) { + var ownerEntityCFs = getCalculatedFieldsByEntityId(entityId); + for (var ctx : ownerEntityCFs) { + if (ctx.dynamicSourceMatches(proto)) { + result.add(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId)); + } + } + var ownerEntityProfileCFs = getCalculatedFieldsByEntityId(getProfileId(tenantId, entityId)); + for (var ctx : ownerEntityProfileCFs) { + if (ctx.dynamicSourceMatches(proto)) { + result.add(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId)); + } + } + } + return result; + } + private List getCalculatedFieldsByEntityId(EntityId entityId) { if (entityId == null) { return Collections.emptyList(); @@ -444,6 +701,16 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware return result; } + private List getCalculatedFieldsByEntityIdAndProfile(EntityId entityId) { + List cfsByEntityIdAndProfile = new ArrayList<>(); + cfsByEntityIdAndProfile.addAll(getCalculatedFieldsByEntityId(entityId)); + EntityId profileId = getProfileId(tenantId, entityId); + if (profileId != null) { + cfsByEntityIdAndProfile.addAll(getCalculatedFieldsByEntityId(profileId)); + } + return cfsByEntityIdAndProfile; + } + private List getCalculatedFieldLinksByEntityId(EntityId entityId) { if (entityId == null) { return Collections.emptyList(); @@ -455,26 +722,30 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware return result; } - private void initCf(CalculatedFieldCtx cfCtx, TbCallback callback, boolean forceStateReinit) { - EntityId entityId = cfCtx.getEntityId(); - EntityType entityType = cfCtx.getEntityId().getEntityType(); - if (isProfileEntity(entityType)) { - var entityIds = entityProfileCache.getEntityIdsByProfileId(entityId); - if (!entityIds.isEmpty()) { - var multiCallback = new MultipleTbCallback(entityIds.size(), callback); - entityIds.forEach(id -> { - if (isMyPartition(id, multiCallback)) { - initCfForEntity(id, cfCtx, forceStateReinit, multiCallback); - } - }); - } else { - callback.onSuccess(); - } - } else { - if (isMyPartition(entityId, callback)) { - initCfForEntity(entityId, cfCtx, forceStateReinit, callback); - } + private Set getOwnedEntities(EntityId entityId) { + if (entityId == null) { + return Collections.emptySet(); + } + var result = ownerEntities.get(entityId); + if (result == null) { + result = Collections.emptySet(); } + return result; + } + + private void linkedTelemetryMsgForEntity(EntityId entityId, EntityCalculatedFieldLinkedTelemetryMsg msg) { + log.debug("Pushing linked telemetry msg to specific actor [{}]", entityId); + getOrCreateActor(entityId).tell(msg); + } + + private void deleteRelatedEntity(EntityId entityId, EntityId relatedEntityId, CalculatedFieldCtx cf, TbCallback callback) { + log.debug("Pushing delete related entity msg to specific actor [{}]", relatedEntityId); + getOrCreateActor(entityId).tell(new CalculatedFieldRelationActionMsg(tenantId, relatedEntityId, ActionType.DELETED, cf, callback)); + } + + private void initRelatedEntity(EntityId entityId, EntityId relatedEntityId, CalculatedFieldCtx cf, TbCallback callback) { + log.debug("Pushing init related entity msg to specific actor [{}]", relatedEntityId); + getOrCreateActor(entityId).tell(new CalculatedFieldRelationActionMsg(tenantId, relatedEntityId, ActionType.UPDATED, cf, callback)); } private void deleteCfForEntity(EntityId entityId, CalculatedFieldId cfId, TbCallback callback) { @@ -482,9 +753,9 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware getOrCreateActor(entityId).tell(new CalculatedFieldEntityDeleteMsg(tenantId, cfId, callback)); } - private void initCfForEntity(EntityId entityId, CalculatedFieldCtx cfCtx, boolean forceStateReinit, TbCallback callback) { + private void initCfForEntity(EntityId entityId, CalculatedFieldCtx cfCtx, StateAction stateAction, CalculatedFieldEventType eventType, TbCallback callback) { log.debug("Pushing entity init CF msg to specific actor [{}]", entityId); - getOrCreateActor(entityId).tell(new EntityInitCalculatedFieldMsg(tenantId, cfCtx, callback, forceStateReinit)); + getOrCreateActor(entityId).tell(new EntityInitCalculatedFieldMsg(tenantId, cfCtx, stateAction, eventType, callback)); } private boolean isMyPartition(EntityId entityId, TbCallback callback) { @@ -502,8 +773,8 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware private EntityId getProfileId(TenantId tenantId, EntityId entityId) { return switch (entityId.getEntityType()) { - case ASSET -> assetProfileCache.get(tenantId, (AssetId) entityId).getId(); - case DEVICE -> deviceProfileCache.get(tenantId, (DeviceId) entityId).getId(); + case ASSET -> Optional.ofNullable(assetProfileCache.get(tenantId, (AssetId) entityId)).map(AssetProfile::getId).orElse(null); + case DEVICE -> Optional.ofNullable(deviceProfileCache.get(tenantId, (DeviceId) entityId)).map(DeviceProfile::getId).orElse(null); default -> null; }; } @@ -517,13 +788,13 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware private void addLinks(CalculatedField newCf) { var newLinks = newCf.getConfiguration().buildCalculatedFieldLinks(tenantId, newCf.getEntityId(), newCf.getId()); - newLinks.forEach(link -> entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(link)); + newLinks.forEach(link -> entityIdCalculatedFieldLinks.computeIfAbsent(link.entityId(), id -> new CopyOnWriteArrayList<>()).add(link)); } private void deleteLinks(CalculatedFieldCtx cfCtx) { var oldCf = cfCtx.getCalculatedField(); var oldLinks = oldCf.getConfiguration().buildCalculatedFieldLinks(tenantId, oldCf.getEntityId(), oldCf.getId()); - oldLinks.forEach(link -> entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).remove(link)); + oldLinks.forEach(link -> entityIdCalculatedFieldLinks.computeIfAbsent(link.entityId(), id -> new CopyOnWriteArrayList<>()).remove(link)); } public void onPartitionChange(CalculatedFieldPartitionChangeMsg msg) { @@ -536,19 +807,15 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware log.trace("Processing calculated field record: {}", cf); try { initCalculatedField(cf); + initCalculatedFieldLinks(cf); } catch (CalculatedFieldException e) { log.error("Failed to process calculated field record: {}", cf, e); } }); - PageDataIterable cfls = new PageDataIterable<>(pageLink -> cfDaoService.findAllCalculatedFieldLinksByTenantId(tenantId, pageLink), cfSettings.getInitTenantFetchPackSize()); - cfls.forEach(link -> { - log.trace("Processing calculated field link record: {}", link); - initCalculatedFieldLink(link); - }); } private void initCalculatedField(CalculatedField cf) throws CalculatedFieldException { - var cfCtx = new CalculatedFieldCtx(cf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService()); + var cfCtx = new CalculatedFieldCtx(cf, systemContext); try { cfCtx.init(); } catch (Exception e) { @@ -561,31 +828,86 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware } } - private void initCalculatedFieldLink(CalculatedFieldLink link) { - // We use copy on write lists to safely pass the reference to another actor for the iteration. - // Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead) - entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(link); + private void initCalculatedFieldLinks(CalculatedField cf) { + List links = cf.getConfiguration().buildCalculatedFieldLinks(cf.getTenantId(), cf.getEntityId(), cf.getId()); + for (CalculatedFieldLink link : links) { + // We use copy on write lists to safely pass the reference to another actor for the iteration. + // Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead) + entityIdCalculatedFieldLinks.computeIfAbsent(link.entityId(), id -> new CopyOnWriteArrayList<>()).add(link); + } } - private void initEntityProfileCache() { + private void initEntitiesCache() { PageDataIterable deviceIdInfos = new PageDataIterable<>(pageLink -> deviceService.findProfileEntityIdInfosByTenantId(tenantId, pageLink), cfSettings.getInitTenantFetchPackSize()); for (ProfileEntityIdInfo idInfo : deviceIdInfos) { log.trace("Processing device record: {}", idInfo); try { entityProfileCache.add(idInfo.getProfileId(), idInfo.getEntityId()); + ownerEntities.computeIfAbsent(idInfo.getOwnerId(), __ -> new HashSet<>()).add(idInfo.getEntityId()); } catch (Exception e) { log.error("Failed to process device record: {}", idInfo, e); } } + PageDataIterable assetIdInfos = new PageDataIterable<>(pageLink -> assetService.findProfileEntityIdInfosByTenantId(tenantId, pageLink), cfSettings.getInitTenantFetchPackSize()); for (ProfileEntityIdInfo idInfo : assetIdInfos) { log.trace("Processing asset record: {}", idInfo); try { entityProfileCache.add(idInfo.getProfileId(), idInfo.getEntityId()); + ownerEntities.computeIfAbsent(idInfo.getOwnerId(), __ -> new HashSet<>()).add(idInfo.getEntityId()); } catch (Exception e) { log.error("Failed to process asset record: {}", idInfo, e); } } + + PageDataIterable customers = new PageDataIterable<>(pageLink -> customerService.findCustomersByTenantId(tenantId, pageLink), cfSettings.getInitTenantFetchPackSize()); + for (Customer customer : customers) { + log.trace("Processing customer record: {}", customer); + try { + ownerEntities.computeIfAbsent(customer.getTenantId(), __ -> new HashSet<>()).add(customer.getId()); + } catch (Exception e) { + log.error("Failed to process customer record: {}", customer, e); + } + } + } + + private void updateEntityOwner(EntityId entityId) { + ownerEntities.values().forEach(entities -> entities.remove(entityId)); + EntityId owner = ownerService.getOwner(tenantId, entityId); + ownerEntities.computeIfAbsent(owner, ownerId -> new HashSet<>()).add(entityId); + } + + private void applyToTargetCfEntityActors(CalculatedFieldCtx ctx, + TbCallback callback, + BiConsumer action) { + withTargetEntities(ctx.getEntityId(), callback, (ids, cb) -> ids.forEach(id -> action.accept(id, cb))); + } + + private void withTargetEntities(EntityId entityId, TbCallback parentCallback, BiConsumer, TbCallback> consumer) { + if (isProfileEntity(entityId.getEntityType())) { + var ids = entityProfileCache.getEntityIdsByProfileId(entityId); + if (ids.isEmpty()) { + parentCallback.onSuccess(); + return; + } + var multiCallback = new MultipleTbCallback(ids.size(), parentCallback); + var profileEntityIds = ids.stream().filter(id -> isMyPartition(id, multiCallback)).toList(); + if (profileEntityIds.isEmpty()) { + return; + } + consumer.accept(profileEntityIds, multiCallback); + return; + } + if (isMyPartition(entityId, parentCallback)) { + consumer.accept(List.of(entityId), parentCallback); + } + } + + private void cancelReevaluationTask() { + if (cfsReevaluationTask != null) { + cfsReevaluationTask.cancel(true); + cfsReevaluationTask = null; + } } } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldReevaluateMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldReevaluateMsg.java new file mode 100644 index 0000000000..74308fc35d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldReevaluateMsg.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors.calculatedField; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; + +@Data +public class CalculatedFieldReevaluateMsg implements ToCalculatedFieldSystemMsg { + + private final TenantId tenantId; + private final CalculatedFieldCtx ctx; + + @Override + public MsgType getMsgType() { + return MsgType.CF_REEVALUATE_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldRelationActionMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldRelationActionMsg.java new file mode 100644 index 0000000000..70e1136afe --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldRelationActionMsg.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors.calculatedField; + +import lombok.Data; +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.msg.MsgType; +import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; + +@Data +public class CalculatedFieldRelationActionMsg implements ToCalculatedFieldSystemMsg { + + private final TenantId tenantId; + private final EntityId relatedEntityId; + private final ActionType action; + private final CalculatedFieldCtx calculatedField; + private final TbCallback callback; + + public CalculatedFieldRelationActionMsg(TenantId tenantId, + EntityId relatedEntityId, ActionType action, + CalculatedFieldCtx calculatedField, + TbCallback callback) { + this.tenantId = tenantId; + this.relatedEntityId = relatedEntityId; + this.action = action; + this.calculatedField = calculatedField; + this.callback = callback; + } + + @Override + public MsgType getMsgType() { + return MsgType.CF_RELATION_ACTION_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldStateRestoreMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldStateRestoreMsg.java index 9fc5af4f26..781325066a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldStateRestoreMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldStateRestoreMsg.java @@ -20,7 +20,9 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; @Data @@ -28,7 +30,9 @@ public class CalculatedFieldStateRestoreMsg implements ToCalculatedFieldSystemMs private final CalculatedFieldEntityCtxId id; private final CalculatedFieldState state; + private final TopicPartitionInfo partition; private final TbCallback callback; + private CalculatedFieldCtx ctx; @Override public MsgType getMsgType() { @@ -39,4 +43,5 @@ public class CalculatedFieldStateRestoreMsg implements ToCalculatedFieldSystemMs public TenantId getTenantId() { return id.tenantId(); } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldTelemetryMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldTelemetryMsg.java index f01c41f4b6..f1b6e62a41 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldTelemetryMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldTelemetryMsg.java @@ -31,9 +31,9 @@ public class CalculatedFieldTelemetryMsg implements ToCalculatedFieldSystemMsg { private final CalculatedFieldTelemetryMsgProto proto; private final TbCallback callback; - @Override public MsgType getMsgType() { return MsgType.CF_TELEMETRY_MSG; } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityInitCalculatedFieldMsg.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityInitCalculatedFieldMsg.java index 6ac10e4fa1..8432748276 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityInitCalculatedFieldMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityInitCalculatedFieldMsg.java @@ -16,26 +16,32 @@ package org.thingsboard.server.actors.calculatedField; import lombok.Data; -import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.cf.CalculatedFieldEventType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; -import java.util.List; - @Data public class EntityInitCalculatedFieldMsg implements ToCalculatedFieldSystemMsg { private final TenantId tenantId; private final CalculatedFieldCtx ctx; + private final StateAction stateAction; + private final CalculatedFieldEventType eventType; private final TbCallback callback; - private final boolean forceReinit; @Override public MsgType getMsgType() { return MsgType.CF_ENTITY_INIT_CF_MSG; } + + public enum StateAction { + INIT, + REINIT, + RECREATE, + REPROCESS, + REFRESH_CTX + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/MultipleTbCallback.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/MultipleTbCallback.java index 289fe52d04..0be2eb4690 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/MultipleTbCallback.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/MultipleTbCallback.java @@ -50,7 +50,7 @@ public class MultipleTbCallback implements TbCallback { @Override public void onFailure(Throwable t) { - log.warn("[{}][{}] onFailure.", id, callback.getId()); + log.warn("[{}][{}] onFailure.", id, callback.getId(), t); callback.onFailure(t); } } 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 511479aa73..afe52bf942 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 @@ -109,6 +109,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; @@ -911,6 +912,11 @@ public class DefaultTbContext implements TbContext { return mainCtx.getJobManager(); } + @Override + public ApiKeyService getApiKeyService() { + return mainCtx.getApiKeyService(); + } + @Override public boolean isExternalNodeForceAck() { return mainCtx.isExternalNodeForceAck(); diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index 59663563e3..c679dc8724 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -49,6 +49,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; +import org.thingsboard.server.common.msg.aware.TenantAwareMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldCacheInitMsg; import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; @@ -163,6 +164,9 @@ public class TenantActor extends RuleChainManagerActor { case COMPONENT_LIFE_CYCLE_MSG: onComponentLifecycleMsg((ComponentLifecycleMsg) msg); break; + case CF_ENTITY_ACTION_EVENT_MSG: + forwardToCfActor((TenantAwareMsg) msg, true); + break; case QUEUE_TO_RULE_ENGINE_MSG: onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); break; @@ -190,11 +194,12 @@ public class TenantActor extends RuleChainManagerActor { case CF_CACHE_INIT_MSG: case CF_STATE_RESTORE_MSG: case CF_PARTITIONS_CHANGE_MSG: - onToCalculatedFieldSystemActorMsg((ToCalculatedFieldSystemMsg) msg, true); + case CF_STATE_PARTITION_RESTORE_MSG: + forwardToCfActor((ToCalculatedFieldSystemMsg) msg, true); break; case CF_TELEMETRY_MSG: case CF_LINKED_TELEMETRY_MSG: - onToCalculatedFieldSystemActorMsg((ToCalculatedFieldSystemMsg) msg, false); + forwardToCfActor((ToCalculatedFieldSystemMsg) msg, false); break; default: return false; @@ -202,7 +207,7 @@ public class TenantActor extends RuleChainManagerActor { return true; } - private void onToCalculatedFieldSystemActorMsg(ToCalculatedFieldSystemMsg msg, boolean priority) { + private void forwardToCfActor(TenantAwareMsg msg, boolean priority) { if (cfActor == null) { if (msg instanceof CalculatedFieldStateRestoreMsg) { log.warn("[{}] CF Actor is not initialized. ToCalculatedFieldSystemMsg: [{}]", tenantId, msg); @@ -353,7 +358,7 @@ public class TenantActor extends RuleChainManagerActor { } } if (cfActor != null) { - if (msg.getEntityId().getEntityType().isOneOf(EntityType.CALCULATED_FIELD, EntityType.DEVICE, EntityType.ASSET)) { + if (msg.getEntityId().getEntityType().isOneOf(EntityType.CALCULATED_FIELD, EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.TENANT_PROFILE)) { cfActor.tellWithHighPriority(new CalculatedFieldEntityLifecycleMsg(tenantId, msg)); } } 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 70259a15ef..1a1a409c1a 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -86,6 +86,9 @@ public class SwaggerConfiguration { public static final String LOGIN_ENDPOINT = "/api/auth/login"; public static final String REFRESH_TOKEN_ENDPOINT = "/api/auth/token"; + private static final String LOGIN_PASSWORD_SCHEME = "HTTP login form"; + private static final String API_KEY_SCHEME = "API key form"; + private static final ApiResponses loginResponses = loginResponses(); private static final ApiResponses defaultErrorResponses = defaultErrorResponses(false); private static final ApiResponses defaultPostErrorResponses = defaultErrorResponses(true); @@ -116,6 +119,8 @@ public class SwaggerConfiguration { private String appVersion; @Value("${swagger.group_name:thingsboard}") private String groupName; + @Value("${swagger.doc_expansion:list}") + private String docExpansion; @Bean public OpenAPI thingsboardApi() { @@ -140,14 +145,28 @@ public class SwaggerConfiguration { .license(license) .version(apiVersion); - SecurityScheme securityScheme = new SecurityScheme() + SecurityScheme loginPasswordScheme = new SecurityScheme() .type(SecurityScheme.Type.HTTP) .description("Enter Username / Password") .scheme("loginPassword") .bearerFormat("/api/auth/login|X-Authorization"); + SecurityScheme apiKeyScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .name("X-Authorization") + .in(SecurityScheme.In.HEADER) + .description(""" + Enter the API key value with 'ApiKey' prefix in format: **ApiKey ** + + Example: **ApiKey tb_5te51SkLRYpjGrujUGwqkjFvooWBlQpVe2An2Dr3w13wjfxDW** + +
**NOTE**: Use only ONE authentication method at a time. If both are authorized, JWT auth takes the priority.
+ """); + var openApi = new OpenAPI() - .components(new Components().addSecuritySchemes("HTTP login form", securityScheme)) + .components(new Components() + .addSecuritySchemes(LOGIN_PASSWORD_SCHEME, loginPasswordScheme) + .addSecuritySchemes(API_KEY_SCHEME, apiKeyScheme)) .info(info); addDefaultSchemas(openApi); addLoginOperation(openApi); @@ -172,7 +191,7 @@ public class SwaggerConfiguration { uiProperties.setDefaultModelExpandDepth(1); uiProperties.setDefaultModelRendering("example"); uiProperties.setDisplayRequestDuration(false); - uiProperties.setDocExpansion("list"); + uiProperties.setDocExpansion(docExpansion); uiProperties.setFilter("false"); uiProperties.setMaxDisplayedTags(null); uiProperties.setOperationsSorter("alpha"); @@ -196,13 +215,14 @@ public class SwaggerConfiguration { operation.summary("Login method to get user JWT token data"); operation.description(""" Login method used to authenticate user and get JWT token data. - + Value of the response **token** field can be used as **X-Authorization** header value: - + `X-Authorization: Bearer $JWT_TOKEN_VALUE`."""); + var requestBody = new RequestBody().description("Login request") .content(new Content().addMediaType(APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest")))); + new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest")))); operation.requestBody(requestBody); operation.responses(loginResponses); @@ -216,11 +236,11 @@ public class SwaggerConfiguration { var operation = new Operation(); operation.summary("Refresh user JWT token data"); operation.description(""" - Method to refresh JWT token. Provide a valid refresh token to get a new JWT token. - - The response contains a new token that can be used for authorization. - - `X-Authorization: Bearer $JWT_TOKEN_VALUE`"""); + Method to refresh JWT token. Provide a valid refresh token to get a new JWT token. + + The response contains a new token that can be used for authorization. + + `X-Authorization: Bearer $JWT_TOKEN_VALUE`"""); var requestBody = new RequestBody().description("Refresh token request") .content(new Content().addMediaType(APPLICATION_JSON_VALUE, @@ -289,8 +309,9 @@ public class SwaggerConfiguration { return (routerOperation, handlerMethod) -> { String[] pNames = localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod()); String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new); - if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull)) + if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull)) { pNames = reflectionParametersNames; + } MethodParameter[] parameters = handlerMethod.getMethodParameters(); List requestParams = new ArrayList<>(); for (var i = 0; i < parameters.length; i++) { @@ -322,26 +343,25 @@ public class SwaggerConfiguration { } private OpenApiCustomizer customOpenApiCustomizer() { - var loginForm = new SecurityRequirement().addList("HTTP login form", Arrays.asList( - Authority.SYS_ADMIN.name(), - Authority.TENANT_ADMIN.name(), - Authority.CUSTOMER_USER.name() - )); + var loginRequirement = createSecurityRequirement(LOGIN_PASSWORD_SCHEME); + var apiKeyRequirement = createSecurityRequirement(API_KEY_SCHEME); + return openAPI -> { var paths = openAPI.getPaths(); - paths.entrySet().stream().peek(entry -> { - securityCustomization(loginForm, entry); - if (!entry.getKey().equals(LOGIN_ENDPOINT)) { - defaultErrorResponsesCustomization(entry.getValue()); - } - }).map(this::tagsCustomization).filter(Objects::nonNull).distinct().sorted(Comparator.comparing(Tag::getName)).forEach(openAPI::addTagsItem); + paths.entrySet().stream() + .peek(entry -> { + securityCustomization(entry, loginRequirement, apiKeyRequirement); + if (!entry.getKey().equals(LOGIN_ENDPOINT)) { + defaultErrorResponsesCustomization(entry.getValue()); + } + }) + .map(this::extractTagFromPath).filter(Objects::nonNull).distinct().sorted(Comparator.comparing(Tag::getName)).forEach(openAPI::addTagsItem); var pathItemsByTags = new TreeMap>(); paths.forEach((k, v) -> { var tagItem = tagItemFromPathItem(v); if (tagItem != null) { - var pathItemMap = pathItemsByTags.computeIfAbsent(tagItem, k1 -> new TreeMap<>()); - pathItemMap.put(k, v); + pathItemsByTags.computeIfAbsent(tagItem, k1 -> new TreeMap<>()).put(k, v); } }); var sortedPaths = new Paths(); @@ -355,13 +375,17 @@ public class SwaggerConfiguration { }; } + private SecurityRequirement createSecurityRequirement(String schemeName) { + return new SecurityRequirement().addList(schemeName, Arrays.asList( + Authority.SYS_ADMIN.name(), + Authority.TENANT_ADMIN.name(), + Authority.CUSTOMER_USER.name() + )); + } - private Tag tagsCustomization(Map.Entry entry) { - var tagItem = tagItemFromPathItem(entry.getValue()); - if (tagItem != null) { - return tagFromTagItem(tagItem); - } - return null; + private Tag extractTagFromPath(Map.Entry entry) { + var tagName = tagItemFromPathItem(entry.getValue()); + return tagName != null ? tagFromTagItem(tagName) : null; } private String tagItemFromPathItem(PathItem item) { @@ -381,17 +405,20 @@ public class SwaggerConfiguration { StringBuilder sb = new StringBuilder(); for (String word : words) { - sb.append(word.substring(0, 1).toUpperCase()); - sb.append(word.substring(1).toLowerCase()); - sb.append(" "); + if (!word.isEmpty()) { + sb.append(word.substring(0, 1).toUpperCase()); + sb.append(word.substring(1).toLowerCase()); + sb.append(" "); + } } return new Tag().name(tagItem).description(sb.toString().trim()); } private void defaultErrorResponsesCustomization(PathItem pathItem) { - pathItem.readOperationsMap().forEach(((httpMethod, operation) -> { + pathItem.readOperationsMap().forEach((httpMethod, operation) -> { var errorResponses = httpMethod.equals(PathItem.HttpMethod.POST) ? defaultPostErrorResponses : defaultErrorResponses; + var responses = operation.getResponses(); if (responses == null) { responses = errorResponses; @@ -404,16 +431,19 @@ public class SwaggerConfiguration { }); } operation.setResponses(responses); - })); + }); } - private void securityCustomization(SecurityRequirement loginForm, Map.Entry entry) { + private void securityCustomization(Map.Entry entry, SecurityRequirement jwtBearerRequirement, SecurityRequirement apiKeyRequirement) { var path = entry.getKey(); - if (path.matches(securityPathRegex) && !path.matches(nonSecurityPathRegex) && !path.equals(LOGIN_ENDPOINT)) { + if (path.matches(securityPathRegex) && !path.matches(nonSecurityPathRegex) && !path.equals(LOGIN_ENDPOINT) && !path.equals(REFRESH_TOKEN_ENDPOINT)) { entry.getValue() .readOperationsMap() .values() - .forEach(operation -> operation.addSecurityItem(loginForm)); + .forEach(operation -> { + operation.addSecurityItem(jwtBearerRequirement); + operation.addSecurityItem(apiKeyRequirement); + }); } } @@ -428,6 +458,7 @@ public class SwaggerConfiguration { private static ApiResponses defaultErrorResponses(boolean isPost) { ApiResponses apiResponses = new ApiResponses(); + apiResponses.addApiResponse("400", errorResponse("400", "Bad Request", ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST))); @@ -463,8 +494,7 @@ public class SwaggerConfiguration { ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)) ) )); - var credentialsExpiredSchema = new Schema(); - credentialsExpiredSchema.$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse"); + var credentialsExpiredSchema = new Schema().$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse"); apiResponses.addApiResponse("401 ", errorResponse("Unauthorized (**Expired credentials**)", Map.of( "credentials-expired", errorExample("Expired credentials", @@ -480,15 +510,13 @@ public class SwaggerConfiguration { } private static ApiResponse errorResponse(String description, Map examples) { - var schema = new Schema(); - schema.$ref("#/components/schemas/ThingsboardErrorResponse"); + var schema = new Schema().$ref("#/components/schemas/ThingsboardErrorResponse"); return errorResponse(description, examples, schema); } private static ApiResponse errorResponse(String description, Map examples, Schema errorResponseSchema) { - MediaType mediaType = new MediaType().schema(errorResponseSchema); - mediaType.setExamples(examples); - Content content = new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE, mediaType); + MediaType mediaType = new MediaType().schema(errorResponseSchema).examples(examples); + Content content = new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE, mediaType); return new ApiResponse().description(description).content(content); } 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 cac4f4165e..cd5cecbef7 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -38,6 +38,7 @@ 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.header.writers.CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy; import org.springframework.security.web.header.writers.StaticHeadersWriter; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @@ -46,21 +47,23 @@ import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.AuthExceptionHandler; +import org.thingsboard.server.service.security.auth.extractor.TokenExtractor; import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenProcessingFilter; import org.thingsboard.server.service.security.auth.jwt.SkipPathRequestMatcher; -import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; import org.thingsboard.server.service.security.auth.oauth2.HttpCookieOAuth2AuthorizationRequestRepository; +import org.thingsboard.server.service.security.auth.pat.ApiKeyAuthenticationProvider; +import org.thingsboard.server.service.security.auth.pat.ApiKeyTokenAuthenticationProcessingFilter; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider; import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter; import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter; import org.thingsboard.server.transport.http.config.PayloadSizeFilter; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; @Configuration @EnableWebSecurity @@ -69,10 +72,13 @@ import java.util.List; @TbCoreComponent public class ThingsboardSecurityConfiguration { - public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; - public static final String JWT_TOKEN_HEADER_PARAM_V2 = "Authorization"; + public static final String AUTHORIZATION_HEADER = "X-Authorization"; + public static final String AUTHORIZATION_HEADER_V2 = "Authorization"; public static final String JWT_TOKEN_QUERY_PARAM = "token"; + public static final String API_KEY_HEADER_PREFIX = "ApiKey "; + public static final String BEARER_HEADER_PREFIX = "Bearer "; + public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**"; public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; @@ -114,6 +120,8 @@ public class ThingsboardSecurityConfiguration { private JwtAuthenticationProvider jwtAuthenticationProvider; @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider; + @Autowired + private ApiKeyAuthenticationProvider apiKeyAuthenticationProvider; @Autowired(required = false) OAuth2Configuration oauth2Configuration; @@ -122,6 +130,10 @@ public class ThingsboardSecurityConfiguration { @Qualifier("jwtHeaderTokenExtractor") private TokenExtractor jwtHeaderTokenExtractor; + @Autowired + @Qualifier("apiKeyHeaderTokenExtractor") + private TokenExtractor apiKeyHeaderTokenExtractor; + @Autowired private AuthenticationManager authenticationManager; @@ -137,7 +149,7 @@ public class ThingsboardSecurityConfiguration { } @Bean - protected FilterRegistrationBean buildEtagFilter() throws Exception { + protected FilterRegistrationBean buildEtagFilter() { ShallowEtagHeaderFilter etagFilter = new ShallowEtagHeaderFilter(); etagFilter.setWriteWeakETag(true); FilterRegistrationBean filterRegistrationBean @@ -148,25 +160,22 @@ public class ThingsboardSecurityConfiguration { } @Bean - protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception { + protected RestLoginProcessingFilter buildRestLoginProcessingFilter() { RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler); filter.setAuthenticationManager(this.authenticationManager); return filter; } @Bean - protected RestPublicLoginProcessingFilter buildRestPublicLoginProcessingFilter() throws Exception { + protected RestPublicLoginProcessingFilter buildRestPublicLoginProcessingFilter() { RestPublicLoginProcessingFilter filter = new RestPublicLoginProcessingFilter(PUBLIC_LOGIN_ENTRY_POINT, successHandler, failureHandler); filter.setAuthenticationManager(this.authenticationManager); return filter; } - protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { - List pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); - pathsToSkip.addAll(Arrays.asList(WS_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, - PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, MAIL_OAUTH2_PROCESSING_ENTRY_POINT, - DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT)); - SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); + @Bean + protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() { + SkipPathRequestMatcher matcher = buildSkipPathRequestMatcher(); JwtTokenAuthenticationProcessingFilter filter = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher); filter.setAuthenticationManager(this.authenticationManager); @@ -174,7 +183,30 @@ public class ThingsboardSecurityConfiguration { } @Bean - protected RefreshTokenProcessingFilter buildRefreshTokenProcessingFilter() throws Exception { + protected ApiKeyTokenAuthenticationProcessingFilter buildApiKeyTokenAuthenticationProcessingFilter() { + SkipPathRequestMatcher matcher = buildSkipPathRequestMatcher(); + ApiKeyTokenAuthenticationProcessingFilter filter = + new ApiKeyTokenAuthenticationProcessingFilter(failureHandler, apiKeyHeaderTokenExtractor, matcher); + filter.setAuthenticationManager(this.authenticationManager); + return filter; + } + + private SkipPathRequestMatcher buildSkipPathRequestMatcher() { + List pathsToSkip = Stream.concat( + Arrays.stream(NON_TOKEN_BASED_AUTH_ENTRY_POINTS), + Stream.of( + WS_ENTRY_POINT, + TOKEN_REFRESH_ENTRY_POINT, + FORM_BASED_LOGIN_ENTRY_POINT, + PUBLIC_LOGIN_ENTRY_POINT, + DEVICE_API_ENTRY_POINT, + MAIL_OAUTH2_PROCESSING_ENTRY_POINT, + DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT)).toList(); + return new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); + } + + @Bean + protected RefreshTokenProcessingFilter buildRefreshTokenProcessingFilter() { RefreshTokenProcessingFilter filter = new RefreshTokenProcessingFilter(TOKEN_REFRESH_ENTRY_POINT, successHandler, failureHandler); filter.setAuthenticationManager(this.authenticationManager); return filter; @@ -185,6 +217,7 @@ public class ThingsboardSecurityConfiguration { return new ProviderManager(List.of( restAuthenticationProvider, jwtAuthenticationProvider, + apiKeyAuthenticationProvider, refreshTokenAuthenticationProvider )); } @@ -210,9 +243,8 @@ public class ThingsboardSecurityConfiguration { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.headers(headers -> headers - .cacheControl(config -> {}) - .frameOptions(config -> {}).disable()) + http.headers(headers -> headers.defaultsDisabled() + .crossOriginOpenerPolicy(coop -> coop.policy(CrossOriginOpenerPolicy.SAME_ORIGIN))) .cors(cors -> {}) .csrf(AbstractHttpConfigurer::disable) .exceptionHandling(config -> {}) @@ -232,6 +264,7 @@ public class ThingsboardSecurityConfiguration { .addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(buildApiKeyTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(payloadSizeFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class) diff --git a/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java b/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java new file mode 100644 index 0000000000..09972bd6c2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java @@ -0,0 +1,158 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.ApiKeyId; +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.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.dao.pat.ApiKeyService; +import org.thingsboard.server.queue.util.TbCoreComponent; +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.Optional; +import java.util.UUID; + +import static org.thingsboard.server.controller.ControllerConstants.API_KEY_ID_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.API_KEY_TEXT_SEARCH_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; +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_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARAM_DESCRIPTION; + +@RestController +@TbCoreComponent +@Slf4j +@RequestMapping("/api") +@RequiredArgsConstructor +public class ApiKeyController extends BaseController { + + private final ApiKeyService apiKeyService; + + @ApiOperation(value = "Save API key for user (saveApiKey)", + notes = "Creates an API key for the given user and returns the token ONCE as 'ApiKey '." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @PostMapping(value = "/apiKey") + public ApiKey saveApiKey( + @Parameter(description = "A JSON value representing the Api Key token.") + @RequestBody @Valid ApiKeyInfo apiKeyInfo) throws ThingsboardException { + User user = checkUserId(apiKeyInfo.getUserId(), Operation.WRITE); + apiKeyInfo.setTenantId(user.getTenantId()); + checkEntity(apiKeyInfo.getId(), apiKeyInfo, Resource.API_KEY); + ApiKey savedApiKey = checkNotNull(apiKeyService.saveApiKey(apiKeyInfo.getTenantId(), apiKeyInfo)); + if (apiKeyInfo.getId() != null) { + savedApiKey.setValue(null); + } + return savedApiKey; + } + + @ApiOperation(value = "Get User Api Keys (getUserApiKeys)", + notes = "Returns a page of api keys owned by user. " + + PAGE_DATA_PARAMETERS + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/apiKeys/{userId}") + public PageData getUserApiKeys( + @Parameter(description = USER_ID_PARAM_DESCRIPTION) + @PathVariable("userId") String userIdStr, + @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @Parameter(description = API_KEY_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "expirationTime", "description", "enabled"})) + @RequestParam(required = false) String sortProperty, + @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + SecurityUser securityUser = getCurrentUser(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + UserId userId = new UserId(toUUID(userIdStr)); + accessControlService.checkPermission(securityUser, Resource.API_KEY, Operation.READ); + User user = checkUserId(userId, Operation.READ); + return apiKeyService.findApiKeysByUserId(user.getTenantId(), userId, pageLink); + } + + @ApiOperation(value = "Update API key Description", + notes = "Updates the description of the existing API key by apiKeyId. " + + "Only the description can be updated. " + + "Referencing a non-existing ApiKey Id will cause a 'Not Found' error." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @PutMapping("/apiKey/{id}/description") + public ApiKeyInfo updateApiKeyDescription( + @Parameter(description = API_KEY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable UUID id, + @Parameter(description = "New description for the API key", example = "Description") + @RequestBody Optional description) throws Exception { + ApiKeyId apiKeyId = new ApiKeyId(id); + ApiKey apiKey = checkApiKeyId(apiKeyId, Operation.WRITE); + checkUserId(apiKey.getUserId(), Operation.WRITE); + apiKey.setDescription(description.orElse(null)); + return new ApiKeyInfo(apiKeyService.saveApiKey(apiKey.getTenantId(), apiKey)); + } + + @ApiOperation(value = "Enable or disable API key (enableApiKey)", + notes = "Updates api key with enabled = true/false. " + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @PutMapping(value = "/apiKey/{id}/enabled/{enabledValue}") + public ApiKeyInfo enableApiKey( + @Parameter(description = "Unique identifier of the API key to enable/disable", required = true) + @PathVariable UUID id, + @Parameter(description = "Enabled or disabled api key", required = true) + @PathVariable(value = "enabledValue") Boolean enabledValue) throws ThingsboardException { + ApiKeyId apiKeyId = new ApiKeyId(id); + ApiKey apiKey = checkApiKeyId(apiKeyId, Operation.WRITE); + checkUserId(apiKey.getUserId(), Operation.WRITE); + apiKey.setEnabled(enabledValue); + return new ApiKeyInfo(apiKeyService.saveApiKey(apiKey.getTenantId(), apiKey)); + } + + @ApiOperation(value = "Delete API key by ID (deleteApiKey)", + notes = "Deletes the API key. Referencing non-existing ApiKey Id will cause an error." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @DeleteMapping(value = "/apiKey/{id}") + public void deleteApiKey(@PathVariable UUID id) throws ThingsboardException { + ApiKeyId apiKeyId = new ApiKeyId(id); + ApiKey apiKey = checkApiKeyId(apiKeyId, Operation.DELETE); + checkUserId(apiKey.getUserId(), Operation.WRITE); + apiKeyService.deleteApiKey(apiKey.getTenantId(), apiKey, false); + } + +} 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 0030896582..e4158d014f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -34,6 +34,9 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; +import org.thingsboard.server.common.data.UniquifyStrategy; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetSearchQuery; @@ -76,6 +79,8 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ 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.NAME_CONFLICT_POLICY_DESC; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC; 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; @@ -83,6 +88,7 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.controller.EdgeController.EDGE_ID; @@ -137,10 +143,16 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset", method = RequestMethod.POST) @ResponseBody - public Asset saveAsset(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception { + public Asset saveAsset(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the asset.") @RequestBody Asset asset, + @Parameter(description = NAME_CONFLICT_POLICY_DESC) + @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy, + @Parameter(description = UNIQUIFY_SEPARATOR_DESC) + @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator, + @Parameter(description = UNIQUIFY_STRATEGY_DESC) + @RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception { asset.setTenantId(getTenantId()); checkEntity(asset.getId(), asset, Resource.ASSET); - return tbAssetService.save(asset, getCurrentUser()); + return tbAssetService.save(asset, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser()); } @ApiOperation(value = "Delete asset (deleteAsset)", 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 0233a488d6..65e324a715 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -16,11 +16,13 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -45,7 +47,10 @@ 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.ArrayList; import java.util.List; +import java.util.Set; +import java.util.UUID; import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID; import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID_PARAM_DESCRIPTION; @@ -222,4 +227,20 @@ public class AssetProfileController extends BaseController { return checkNotNull(assetProfileService.findAssetProfileNamesByTenantId(tenantId, activeOnly)); } + @ApiOperation(value = "Get Asset Profiles By Ids (getAssetProfilesByIds)", + notes = "Requested asset profiles must be owned by tenant which is performing the request. " + + NEW_LINE) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping(value = "/assetProfileInfos", params = {"assetProfileIds"}) + public List getAssetProfilesByIds( + @Parameter(description = "A list of asset profile ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("assetProfileIds") Set assetProfileUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List assetProfileIds = new ArrayList<>(); + for (UUID assetProfileUUID : assetProfileUUIDs) { + assetProfileIds.add(new AssetProfileId(assetProfileUUID)); + } + return assetProfileService.findAssetProfilesByIds(tenantId, assetProfileIds); + } + } 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 166fe312f5..8bda29e98b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -39,6 +39,7 @@ 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.Authority; 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; @@ -48,7 +49,9 @@ import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.dao.settings.SecuritySettingsService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; +import org.thingsboard.server.service.security.auth.rest.RestAwareAuthenticationSuccessHandler; import org.thingsboard.server.service.security.model.ActivateUserRequest; import org.thingsboard.server.service.security.model.ChangePasswordRequest; import org.thingsboard.server.service.security.model.ResetPasswordEmailRequest; @@ -74,7 +77,8 @@ public class AuthController extends BaseController { private final SecuritySettingsService securitySettingsService; private final RateLimitService rateLimitService; private final ApplicationEventPublisher eventPublisher; - + private final TwoFactorAuthService twoFactorAuthService; + private final RestAwareAuthenticationSuccessHandler authenticationSuccessHandler; @ApiOperation(value = "Get current User (getUser)", notes = "Get the information about the User which credentials are used to perform this REST API call.") @@ -221,7 +225,13 @@ public class AuthController extends BaseController { } } - var tokenPair = tokenFactory.createTokenPair(securityUser); + JwtPair tokenPair; + if (twoFactorAuthService.isEnforceTwoFaEnabled(securityUser.getTenantId(), user)) { + tokenPair = authenticationSuccessHandler.createMfaTokenPair(securityUser, Authority.MFA_CONFIGURATION_TOKEN); + } else { + tokenPair = tokenFactory.createTokenPair(securityUser); + } + systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(request), ActionType.LOGIN, null); return tokenPair; } 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 de69c239e9..bd2a160d5b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -80,6 +80,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AiModelId; import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CalculatedFieldId; @@ -118,6 +119,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Client; 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.common.data.pat.ApiKey; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.query.EntityDataSortOrder; @@ -148,7 +150,6 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.domain.DomainService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.mobile.MobileAppBundleService; @@ -158,6 +159,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; @@ -171,6 +173,8 @@ 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.exception.DataValidationException; +import org.thingsboard.server.exception.EntitiesLimitExceededException; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; @@ -221,8 +225,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @TbCoreComponent public abstract class BaseController { - protected static final String DASHBOARD_ID = "dashboardId"; - protected static final String HOME_DASHBOARD_ID = "homeDashboardId"; protected static final String HOME_DASHBOARD_HIDE_TOOLBAR = "homeDashboardHideToolbar"; @@ -389,6 +391,9 @@ public abstract class BaseController { @Autowired protected TbAiModelService tbAiModelService; + @Autowired + protected ApiKeyService apiKeyService; + @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -448,6 +453,8 @@ public abstract class BaseController { } if (exception instanceof ThingsboardException) { return (ThingsboardException) exception; + } else if (exception instanceof EntitiesLimitExceededException) { + return new ThingsboardException(exception, ThingsboardErrorCode.ENTITIES_LIMIT_EXCEEDED); } else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException || exception instanceof DataValidationException || cause instanceof IncorrectParameterException) { return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); @@ -509,8 +516,8 @@ public abstract class BaseController { } } - void checkParameter(String name, String param) throws ThingsboardException { - if (StringUtils.isEmpty(param)) { + static void checkParameter(String name, String param) throws ThingsboardException { + if (StringUtils.isBlank(param)) { throw new ThingsboardException("Parameter '" + name + "' can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } @@ -648,6 +655,7 @@ public abstract class BaseController { case MOBILE_APP_BUNDLE -> checkMobileAppBundleId(new MobileAppBundleId(entityId.getId()), operation); case CALCULATED_FIELD -> checkCalculatedFieldId(new CalculatedFieldId(entityId.getId()), operation); case AI_MODEL -> checkAiModelId(new AiModelId(entityId.getId()), operation); + case API_KEY -> checkApiKeyId(new ApiKeyId(entityId.getId()), operation); default -> (HasId) checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); }; } catch (Exception e) { @@ -657,7 +665,7 @@ public abstract class BaseController { protected & HasTenantId, I extends EntityId> E checkEntityId(I entityId, ThrowingBiFunction findingFunction, Operation operation) throws ThingsboardException { try { - validateId((UUIDBased) entityId, "Invalid entity id"); + validateId((UUIDBased) entityId, id -> "Invalid entity id"); SecurityUser user = getCurrentUser(); E entity = findingFunction.apply(user.getTenantId(), entityId); checkNotNull(entity, entityId.getEntityType().getNormalName() + " with id [" + entityId + "] is not found"); @@ -855,12 +863,16 @@ public abstract class BaseController { return checkEntityId(settingsId, (tenantId, id) -> aiModelService.findAiModelByTenantIdAndId(tenantId, id).orElse(null), operation); } + ApiKey checkApiKeyId(ApiKeyId apiKeyId, Operation operation) throws ThingsboardException { + return checkEntityId(apiKeyId, apiKeyService::findApiKeyById, operation); + } + protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } public static Exception toException(Throwable error) { - return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; + return error != null ? (error instanceof Exception ? (Exception) error : new Exception(error)) : null; } protected > void logEntityAction(SecurityUser user, EntityType entityType, E savedEntity, ActionType actionType) { @@ -939,7 +951,7 @@ public abstract class BaseController { } private CalculatedField checkCalculatedFieldId(CalculatedFieldId calculatedFieldId, Operation operation) throws ThingsboardException { - validateId(calculatedFieldId, "Invalid entity id"); + validateId(calculatedFieldId, id -> "Invalid entity id"); SecurityUser user = getCurrentUser(); CalculatedField cf = calculatedFieldService.findById(user.getTenantId(), calculatedFieldId); checkNotNull(cf, calculatedFieldId.getEntityType().getNormalName() + " with id [" + calculatedFieldId + "] is not found"); diff --git a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java index bf42a5c044..8ef957066b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java @@ -21,10 +21,12 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -44,6 +46,9 @@ import org.thingsboard.script.api.tbel.TbelInvokeService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldFilter; +import org.thingsboard.server.common.data.cf.CalculatedFieldInfo; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -58,14 +63,18 @@ import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldTbelScriptEngine; import org.thingsboard.server.service.entitiy.cf.TbCalculatedFieldService; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.EnumSet; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.thingsboard.server.controller.ControllerConstants.CF_TEXT_SEARCH_DESCRIPTION; @@ -98,30 +107,30 @@ public class CalculatedFieldController extends BaseController { private static final String TEST_SCRIPT_EXPRESSION = "Execute the Script expression and return the result. The format of request: \n\n" - + MARKDOWN_CODE_BLOCK_START - + "{\n" + - " \"expression\": \"var temp = 0; foreach(element: temperature.values) {temp += element.value;} var avgTemperature = temp / temperature.values.size(); var adjustedTemperature = avgTemperature + 0.1 * humidity.value; return {\\\"adjustedTemperature\\\": adjustedTemperature};\",\n" + - " \"arguments\": {\n" + - " \"temperature\": {\n" + - " \"type\": \"TS_ROLLING\",\n" + - " \"timeWindow\": {\n" + - " \"startTs\": 1739775630002,\n" + - " \"endTs\": 65432211,\n" + - " \"limit\": 5\n" + - " },\n" + - " \"values\": [\n" + - " { \"ts\": 1739775639851, \"value\": 23 },\n" + - " { \"ts\": 1739775664561, \"value\": 43 },\n" + - " { \"ts\": 1739775713079, \"value\": 15 },\n" + - " { \"ts\": 1739775999522, \"value\": 34 },\n" + - " { \"ts\": 1739776228452, \"value\": 22 }\n" + - " ]\n" + - " },\n" + - " \"humidity\": { \"type\": \"SINGLE_VALUE\", \"ts\": 1739776478057, \"value\": 23 }\n" + - " }\n" + - "}" - + MARKDOWN_CODE_BLOCK_END - + "\n\n Expected result JSON contains \"output\" and \"error\"."; + + MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"expression\": \"var temp = 0; foreach(element: temperature.values) {temp += element.value;} var avgTemperature = temp / temperature.values.size(); var adjustedTemperature = avgTemperature + 0.1 * humidity.value; return {\\\"adjustedTemperature\\\": adjustedTemperature};\",\n" + + " \"arguments\": {\n" + + " \"temperature\": {\n" + + " \"type\": \"TS_ROLLING\",\n" + + " \"timeWindow\": {\n" + + " \"startTs\": 1739775630002,\n" + + " \"endTs\": 65432211,\n" + + " \"limit\": 5\n" + + " },\n" + + " \"values\": [\n" + + " { \"ts\": 1739775639851, \"value\": 23 },\n" + + " { \"ts\": 1739775664561, \"value\": 43 },\n" + + " { \"ts\": 1739775713079, \"value\": 15 },\n" + + " { \"ts\": 1739775999522, \"value\": 34 },\n" + + " { \"ts\": 1739776228452, \"value\": 22 }\n" + + " ]\n" + + " },\n" + + " \"humidity\": { \"type\": \"SINGLE_VALUE\", \"ts\": 1739776478057, \"value\": 23 }\n" + + " }\n" + + "}" + + MARKDOWN_CODE_BLOCK_END + + "\n\n Expected result JSON contains \"output\" and \"error\"."; @ApiOperation(value = "Create Or Update Calculated Field (saveCalculatedField)", notes = "Creates or Updates the Calculated Field. When creating calculated field, platform generates Calculated Field Id as " + UUID_WIKI_LINK + @@ -159,19 +168,92 @@ public class CalculatedFieldController extends BaseController { ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/{entityType}/{entityId}/calculatedFields", params = {"pageSize", "page"}) - public PageData getCalculatedFieldsByEntityId( - @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, - @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, - @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) @RequestParam int page, - @Parameter(description = CF_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, - @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name"})) @RequestParam(required = false) String sortProperty, - @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) @RequestParam(required = false) String sortOrder) throws ThingsboardException { + public PageData getCalculatedFieldsByEntityId(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) + @PathVariable("entityType") String entityType, + @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("entityId") String entityIdStr, + @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @Parameter(description = "Calculated field type. If not specified, all types will be returned.") + @RequestParam(required = false) CalculatedFieldType type, + @Parameter(description = CF_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name"})) + @RequestParam(required = false) String sortProperty, + @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); checkParameter("entityId", entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityIdStr); checkEntityId(entityId, Operation.READ_CALCULATED_FIELD); - return checkNotNull(tbCalculatedFieldService.findAllByTenantIdAndEntityId(entityId, getCurrentUser(), pageLink)); + return checkNotNull(tbCalculatedFieldService.findByTenantIdAndEntityId(getTenantId(), entityId, type, pageLink)); + } + + @ApiOperation(value = "Get calculated fields (getCalculatedFields)", + notes = "Fetch tenant calculated fields based on the filter.") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @GetMapping(value = "/calculatedFields") + public PageData getCalculatedFields(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @Parameter(description = "Calculated field types filter.") + @RequestParam(required = false) Set types, + @Parameter(description = "Entity type filter. If not specified, calculated fields for all supported entity types will be returned.") + @RequestParam(required = false) EntityType entityType, + @Parameter(description = "Entities filter. If not specified, calculated fields for entity type filter will be returned.") + @RequestParam(required = false) Set entities, + @Parameter(description = "Name filter. To specify multiple names, duplicate 'name' parameter for each name, for example '?name=name1&name=name2") + @RequestParam(required = false) String name, // for Swagger only, retrieved from MultiValueMap params (due to issues when name contains comma) + @Parameter(description = CF_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name"})) + @RequestParam(required = false) String sortProperty, + @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) + @RequestParam(required = false) String sortOrder, + @RequestParam MultiValueMap params) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + SecurityUser user = getCurrentUser(); + + if (CollectionUtils.isEmpty(types)) { + types = EnumSet.allOf(CalculatedFieldType.class); + types.remove(CalculatedFieldType.ALARM); + } + + Set entityTypes; + if (entityType == null) { + entityTypes = CalculatedField.SUPPORTED_ENTITIES.keySet(); + } else { + entityTypes = EnumSet.of(entityType); + } + + CalculatedFieldFilter filter = CalculatedFieldFilter.builder() + .types(types) + .entityTypes(entityTypes) + .entityIds(entities) + .names(Optional.ofNullable(params.get("name")).map(HashSet::new).orElse(null)) + .build(); + return calculatedFieldService.findCalculatedFieldsByTenantIdAndFilter(user.getTenantId(), filter, pageLink); + } + + @ApiOperation(value = "Get calculated field names (getCalculatedFieldNames)", + notes = "Fetch the list of calculated field names for specified type.") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @GetMapping(value = "/calculatedFields/names") + public PageData getCalculatedFieldNames(@Parameter(description = "Calculated field type filter.") + @RequestParam CalculatedFieldType type, + @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @Parameter(description = CF_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, "name", sortOrder); + return calculatedFieldService.findCalculatedFieldNamesByTenantIdAndType(getTenantId(), type, pageLink); } @ApiOperation(value = "Delete Calculated Field (deleteCalculatedField)", @@ -278,7 +360,7 @@ public class CalculatedFieldController extends BaseController { } private void checkReferencedEntities(CalculatedFieldConfiguration calculatedFieldConfig) throws ThingsboardException { - List referencedEntityIds = calculatedFieldConfig.getReferencedEntities(); + Set referencedEntityIds = calculatedFieldConfig.getReferencedEntities(); for (EntityId referencedEntityId : referencedEntityIds) { EntityType entityType = referencedEntityId.getEntityType(); switch (entityType) { @@ -289,7 +371,6 @@ public class CalculatedFieldController extends BaseController { default -> throw new IllegalArgumentException("Calculated fields do not support '" + entityType + "' for referenced entities."); } } - } } 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 3d2cf2e50c..ab5c1c0ae4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java @@ -64,6 +64,7 @@ public class ControllerConstants { protected static final String WIDGET_TYPE_ID_PARAM_DESCRIPTION = "A string value representing the widget type id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String VC_REQUEST_ID_PARAM_DESCRIPTION = "A string value representing the version control request id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String RESOURCE_ID_PARAM_DESCRIPTION = "A string value representing the resource id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; + protected static final String API_KEY_ID_PARAM_DESCRIPTION = "A string value representing the api key id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String SYSTEM_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'SYS_ADMIN' authority."; protected static final String SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'SYS_ADMIN' or 'TENANT_ADMIN' authority."; protected static final String TENANT_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'TENANT_ADMIN' authority."; @@ -91,6 +92,7 @@ public class ControllerConstants { protected static final String RULE_CHAIN_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the rule chain name."; protected static final String DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the device profile name."; protected static final String AI_MODEL_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the AI model name, provider and model ID."; + protected static final String API_KEY_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the description."; protected static final String ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the asset profile name."; protected static final String CUSTOMER_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the customer title."; @@ -1630,11 +1632,13 @@ public class ControllerConstants { protected static final String ENTITY_VIEW_INFO_DESCRIPTION = "Entity Views Info extends the Entity View with customer title and 'is public' flag. " + ENTITY_VIEW_DESCRIPTION; protected static final String ATTRIBUTES_SCOPE_DESCRIPTION = "A string value representing the attributes scope. For example, 'SERVER_SCOPE'."; - protected static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'."; + protected static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'. " + + "If attribute keys contain comma, duplicate 'key' parameter for each key, for example '?key=my,key&key=my,second,key"; protected static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'. See API call description for more details."; protected static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys."; - protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest time series. For example, 'temperature,humidity'."; + protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest time series. For example, 'temperature,humidity'. " + + "If telemetry keys contain comma, duplicate 'key' parameter for each key, for example '?key=my,key&key=my,second,key"; protected static final String TELEMETRY_SCOPE_DESCRIPTION = "Value is deprecated, reserved for backward compatibility and not used in the API call implementation. Specify any scope for compatibility"; protected static final String TELEMETRY_JSON_REQUEST_DESCRIPTION = "A JSON with the telemetry values. See API call description for more details."; @@ -1744,4 +1748,18 @@ public class ControllerConstants { MARKDOWN_CODE_BLOCK_END ; protected static final String SECURITY_WRITE_CHECK = " Security check is performed to verify that the user has 'WRITE' permission for the entity (entities)."; + + public static final String NAME_CONFLICT_POLICY_DESC = "Optional value of name conflict policy. Possible values: FAIL or UNIQUIFY. " + + " If omitted, FAIL policy is applied. FAIL policy implies exception will be thrown if an entity with the same name already exists. " + + " UNIQUIFY policy appends a suffix to the entity name, if a name conflict occurs."; + + public static final String UNIQUIFY_SEPARATOR_DESC = "Optional value of name suffix separator used by UNIQUIFY policy. By default, underscore separator is used. " + + "For example, strategy is UNIQUIFY, separator is '-'; if a name conflict occurs for entity name 'test-name', " + + "created entity will have name like 'test-name-7fsh4f'."; + + public static final String UNIQUIFY_STRATEGY_DESC = "Optional value of uniquify strategy used by UNIQUIFY policy. Possible values: RANDOM or INCREMENTAL. " + + "By default, RANDOM strategy is used, which means random alphanumeric string will be added as a suffix to entity name. " + + "INCREMENTAL implies the first possible number starting from 1 will be added as a name suffix. " + + "For example, strategy is UNIQUIFY, uniquify strategy is INCREMENTAL; if a name conflict occurs for entity name 'test-name', " + + "created entity will have name like 'test-name-1."; } diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index a29d8ab4c8..5ae949d92e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -18,10 +18,12 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -32,6 +34,9 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.NameConflictStrategy; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.UniquifyStrategy; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -43,10 +48,17 @@ import org.thingsboard.server.service.entitiy.customer.TbCustomerService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD; +import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC; 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; @@ -54,6 +66,7 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; @RestController @@ -128,10 +141,16 @@ public class CustomerController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer", method = RequestMethod.POST) @ResponseBody - public Customer saveCustomer(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception { + public Customer saveCustomer(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the customer.") @RequestBody Customer customer, + @Parameter(description = NAME_CONFLICT_POLICY_DESC) + @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy, + @Parameter(description = UNIQUIFY_SEPARATOR_DESC) + @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator, + @Parameter(description = UNIQUIFY_STRATEGY_DESC) + @RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception { customer.setTenantId(getTenantId()); checkEntity(customer.getId(), customer, Resource.CUSTOMER); - return tbCustomerService.save(customer, getCurrentUser()); + return tbCustomerService.save(customer, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser()); } @ApiOperation(value = "Delete Customer (deleteCustomer)", @@ -183,4 +202,20 @@ public class CustomerController extends BaseController { return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found"); } + @ApiOperation(value = "Get customers by Customer Ids (getCustomersByIds)", + notes = "Returns a list of Customer objects based on the provided ids." + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @GetMapping(value = "/customers", params = {"customerIds"}) + public List getCustomersByIds( + @Parameter(description = "A list of customer ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("customerIds") Set customerUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List customerIds = new ArrayList<>(); + for (UUID customerUUID : customerUUIDs) { + customerIds.add(new CustomerId(customerUUID)); + } + return customerService.findCustomersByTenantIdAndIds(tenantId, customerIds); + } + } 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 16f9e8f299..b16161437d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -18,6 +18,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; @@ -35,9 +36,7 @@ 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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; @@ -64,6 +63,7 @@ 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.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -120,7 +120,7 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/dashboard/serverTime") @ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", examples = @ExampleObject(value = "1636023857137"))) - public long getServerTime() throws ThingsboardException { + public long getServerTime() { return System.currentTimeMillis(); } @@ -132,7 +132,7 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/dashboard/maxDatapointsLimit") @ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", examples = @ExampleObject(value = "5000"))) - public long getMaxDatapointsLimit() throws ThingsboardException { + public long getMaxDatapointsLimit() { return maxDatapointsLimit; } @@ -154,11 +154,11 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/dashboard/{dashboardId}") public void getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION) - @PathVariable(DASHBOARD_ID) String strDashboardId, - @Parameter(description = INCLUDE_RESOURCES_DESCRIPTION) - @RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources, - @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, - HttpServletResponse response) throws Exception { + @PathVariable(DASHBOARD_ID) String strDashboardId, + @Parameter(description = INCLUDE_RESOURCES_DESCRIPTION) + @RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, + HttpServletResponse response) throws Exception { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ); @@ -179,9 +179,9 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @PostMapping(value = "/dashboard") public void saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.") - @RequestBody Dashboard dashboard, - @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, - HttpServletResponse response) throws Exception { + @RequestBody Dashboard dashboard, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, + HttpServletResponse response) throws Exception { dashboard.setTenantId(getTenantId()); checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD); var savedDashboard = tbDashboardService.save(dashboard, getCurrentUser()); @@ -301,8 +301,7 @@ public class DashboardController extends BaseController { "[assign Device to Public Customer](#!/device-controller/assignDeviceToPublicCustomerUsingPOST) for this purpose. " + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/customer/public/dashboard/{dashboardId}") public Dashboard assignDashboardToPublicCustomer( @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { @@ -316,8 +315,7 @@ public class DashboardController extends BaseController { notes = "Unassigns the dashboard from a special, auto-generated 'Public' Customer. Once unassigned, unauthenticated users may no longer browse the dashboard. " + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/customer/public/dashboard/{dashboardId}") public Dashboard unassignDashboardFromPublicCustomer( @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { @@ -331,8 +329,7 @@ public class DashboardController extends BaseController { notes = "Returns a page of dashboard info objects owned by tenant. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}) public PageData getTenantDashboards( @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TENANT_ID) String strTenantId, @@ -356,8 +353,7 @@ public class DashboardController extends BaseController { notes = "Returns a page of dashboard info objects owned by the tenant of a current user. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}) public PageData getTenantDashboards( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -384,8 +380,7 @@ public class DashboardController extends BaseController { notes = "Returns a page of dashboard info objects owned by the specified customer. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}) public PageData getCustomerDashboards( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -454,8 +449,7 @@ public class DashboardController extends BaseController { "If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/dashboard/home/info") public HomeDashboardInfo getHomeDashboardInfo() throws ThingsboardException { SecurityUser securityUser = getCurrentUser(); if (securityUser.isSystemAdmin()) { @@ -470,8 +464,7 @@ public class DashboardController extends BaseController { notes = "Returns the home dashboard info object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the corresponding tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/dashboard/home/info") public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException { Tenant tenant = tenantService.findTenantById(getTenantId()); JsonNode additionalInfo = tenant.getAdditionalInfo(); @@ -491,7 +484,7 @@ public class DashboardController extends BaseController { notes = "Update the home dashboard assignment for the current tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST) + @PostMapping(value = "/tenant/dashboard/home/info") @ResponseStatus(value = HttpStatus.OK) public void setTenantHomeDashboardInfo( @Parameter(description = "A JSON object that represents home dashboard id and other parameters", required = true) @@ -540,8 +533,7 @@ public class DashboardController extends BaseController { "Third, once dashboard will be delivered to edge service, it's going to be available for usage on remote edge instance." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}") public Dashboard assignDashboardToEdge(@PathVariable("edgeId") String strEdgeId, @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); @@ -563,8 +555,7 @@ public class DashboardController extends BaseController { "Third, once 'unassign' command will be delivered to edge service, it's going to remove dashboard locally." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}") public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId, @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); @@ -583,8 +574,7 @@ public class DashboardController extends BaseController { notes = "Returns a page of dashboard info objects assigned to the specified edge. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}) public PageData getEdgeDashboards( @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId, @@ -604,14 +594,7 @@ public class DashboardController extends BaseController { checkEdgeId(edgeId, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageData nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - List filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); + List filteredDashboards = filterDashboardsByReadPermission(nonFilteredResult.getData()); PageData filteredResult = new PageData<>(filteredDashboards, nonFilteredResult.getTotalPages(), nonFilteredResult.getTotalElements(), @@ -619,6 +602,22 @@ public class DashboardController extends BaseController { return checkNotNull(filteredResult); } + @ApiOperation(value = "Get dashboards by Dashboard Ids (getDashboardsByIds)", + notes = "Returns a list of DashboardInfo objects based on the provided ids. " + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/dashboards", params = {"dashboardIds"}) + public List getDashboardsByIds(@Parameter(description = "A list of dashboard ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("dashboardIds") Set dashboardUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List dashboardIds = new ArrayList<>(); + for (UUID dashboardUUID : dashboardUUIDs) { + dashboardIds.add(new DashboardId(dashboardUUID)); + } + List dashboards = dashboardService.findDashboardInfoByIds(tenantId, dashboardIds); + return filterDashboardsByReadPermission(dashboards); + } + private Set customerIdFromStr(String[] strCustomerIds) { Set customerIds = new HashSet<>(); if (strCustomerIds != null) { @@ -629,4 +628,14 @@ public class DashboardController extends BaseController { return customerIds; } + private List filterDashboardsByReadPermission(List dashboards) { + return dashboards.stream().filter(dashboard -> { + try { + return accessControlService.hasPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboard.getId(), dashboard); + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + } + } 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 03fc5552c9..78b4cc9880 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -46,8 +46,11 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceInfoFilter; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.UniquifyStrategy; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -108,6 +111,8 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ 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.NAME_CONFLICT_POLICY_DESC; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC; 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; @@ -117,6 +122,7 @@ import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHO import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID; import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.controller.EdgeController.EDGE_ID; @@ -177,14 +183,21 @@ public class DeviceController extends BaseController { @ResponseBody public Device saveDevice(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the device.") @RequestBody Device device, @Parameter(description = "Optional value of the device credentials to be used during device creation. " + - "If omitted, access token will be auto-generated.") @RequestParam(name = "accessToken", required = false) String accessToken) throws Exception { + "If omitted, access token will be auto-generated.") + @RequestParam(name = "accessToken", required = false) String accessToken, + @Parameter(description = NAME_CONFLICT_POLICY_DESC) + @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy, + @Parameter(description = UNIQUIFY_SEPARATOR_DESC) + @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator, + @Parameter(description = UNIQUIFY_STRATEGY_DESC) + @RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception { device.setTenantId(getCurrentUser().getTenantId()); if (device.getId() != null) { checkDeviceId(device.getId(), Operation.WRITE); } else { checkEntity(null, device, Resource.DEVICE); } - return tbDeviceService.save(device, accessToken, getCurrentUser()); + return tbDeviceService.save(device, accessToken, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser()); } @ApiOperation(value = "Create Device (saveDevice) with credentials ", @@ -209,12 +222,18 @@ public class DeviceController extends BaseController { @RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST) @ResponseBody public Device saveDeviceWithCredentials(@Parameter(description = "The JSON object with device and credentials. See method description above for example.") - @Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials) throws ThingsboardException { + @Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials, + @Parameter(description = NAME_CONFLICT_POLICY_DESC) + @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy, + @Parameter(description = UNIQUIFY_SEPARATOR_DESC) + @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator, + @Parameter(description = UNIQUIFY_STRATEGY_DESC) + @RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) 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()); + return tbDeviceService.saveDeviceWithCredentials(device, credentials, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser()); } @ApiOperation(value = "Delete device (deleteDevice)", 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 7cfb9dd274..343703cb7d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -16,12 +16,14 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -48,7 +50,9 @@ 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.ArrayList; import java.util.List; +import java.util.Set; import java.util.UUID; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_DATA; @@ -278,4 +282,20 @@ public class DeviceProfileController extends BaseController { return checkNotNull(deviceProfileService.findDeviceProfileNamesByTenantId(tenantId, activeOnly)); } + @ApiOperation(value = "Get Device Profile Infos By Ids (getDeviceProfilesByIds)", + notes = "Requested device profiles must be owned by tenant which is performing the request. " + + NEW_LINE) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping(value = "/deviceProfileInfos", params = {"deviceProfileIds"}) + public List getDeviceProfileInfosByIds( + @Parameter(description = "A list of device profile ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("deviceProfileIds") Set deviceProfileUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List deviceProfileIds = new ArrayList<>(); + for (UUID deviceProfileUUID : deviceProfileUUIDs) { + deviceProfileIds.add(new DeviceProfileId(deviceProfileUUID)); + } + return deviceProfileService.findDeviceProfilesByIds(tenantId, deviceProfileIds); + } + } 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 784153b650..f0d9e39d6a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -53,9 +53,8 @@ import org.thingsboard.server.common.data.rule.RuleChain; 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.msg.edge.FromEdgeSyncResponse; -import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; import org.thingsboard.server.config.annotations.ApiOperation; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -71,7 +70,6 @@ import org.thingsboard.server.service.security.permission.Resource; 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; @@ -268,7 +266,7 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { TenantId tenantId = getCurrentUser().getTenantId(); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); } else { return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); @@ -295,7 +293,7 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { TenantId tenantId = getCurrentUser().getTenantId(); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink)); } else { return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink)); @@ -359,7 +357,7 @@ public class EdgeController extends BaseController { checkCustomerId(customerId, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageData result; - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); } else { result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); @@ -394,7 +392,7 @@ public class EdgeController extends BaseController { checkCustomerId(customerId, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageData result; - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); } else { result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink); @@ -470,7 +468,7 @@ public class EdgeController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @PostMapping(value = "/edge/sync/{edgeId}") public DeferredResult syncEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true) - @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { + @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); final DeferredResult response = new DeferredResult<>(); if (isEdgesEnabled() && edgeRpcServiceOpt.isPresent()) { diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index 1eee81ddd3..bb025a5fcf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -17,33 +17,30 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; +import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; 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.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.edqs.EdqsState; import org.thingsboard.server.common.data.edqs.ToCoreEdqsRequest; import org.thingsboard.server.common.data.exception.ThingsboardException; -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.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; +import org.thingsboard.server.common.data.query.AvailableEntityKeys; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; 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.msg.edqs.EdqsApiService; import org.thingsboard.server.common.msg.edqs.EdqsService; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -51,52 +48,46 @@ import org.thingsboard.server.service.query.EntityQueryService; import org.thingsboard.server.service.security.permission.Operation; import static org.thingsboard.server.controller.ControllerConstants.ALARM_DATA_QUERY_DESCRIPTION; -import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_COUNT_QUERY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_DATA_QUERY_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor public class EntityQueryController extends BaseController { - @Autowired - private EntityQueryService entityQueryService; - @Autowired - private EdqsService edqsService; - @Autowired - private EdqsApiService edqsApiService; + private final EntityQueryService entityQueryService; + private final EdqsService edqsService; private static final int MAX_PAGE_SIZE = 100; @ApiOperation(value = "Count Entities by Query", notes = ENTITY_COUNT_QUERY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entitiesQuery/count", method = RequestMethod.POST) - @ResponseBody + @PostMapping("/entitiesQuery/count") public long countEntitiesByQuery( @Parameter(description = "A JSON value representing the entity count query. See API call notes above for more details.") @RequestBody EntityCountQuery query) throws ThingsboardException { checkNotNull(query); resolveQuery(query); - return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); + return entityQueryService.countEntitiesByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Entity Data by Query", notes = ENTITY_DATA_QUERY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entitiesQuery/find", method = RequestMethod.POST) - @ResponseBody + @PostMapping("/entitiesQuery/find") public PageData findEntityDataByQuery( @Parameter(description = "A JSON value representing the entity data query. See API call notes above for more details.") @RequestBody EntityDataQuery query) throws ThingsboardException { checkNotNull(query); resolveQuery(query); - return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); + return entityQueryService.findEntityDataByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Alarms by Query", notes = ALARM_DATA_QUERY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarmsQuery/find", method = RequestMethod.POST) - @ResponseBody + @PostMapping("/alarmsQuery/find") public PageData findAlarmDataByQuery( @Parameter(description = "A JSON value representing the alarm data query. See API call notes above for more details.") @RequestBody AlarmDataQuery query) throws ThingsboardException { @@ -107,13 +98,12 @@ public class EntityQueryController extends BaseController { checkUserId(assigneeId, Operation.READ); } resolveQuery(query); - return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); + return entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); } @ApiOperation(value = "Count Alarms by Query (countAlarmsByQuery)", notes = "Returns the number of alarms that match the query definition.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarmsQuery/count", method = RequestMethod.POST) - @ResponseBody + @PostMapping("/alarmsQuery/count") public long countAlarmsByQuery(@Parameter(description = "A JSON value representing the alarm count query.") @RequestBody AlarmCountQuery query) throws ThingsboardException { checkNotNull(query); @@ -122,31 +112,47 @@ public class EntityQueryController extends BaseController { checkUserId(assigneeId, Operation.READ); } resolveQuery(query); - return this.entityQueryService.countAlarmsByQuery(getCurrentUser(), query); + return entityQueryService.countAlarmsByQuery(getCurrentUser(), query); } - @ApiOperation(value = "Find Entity Keys by Query", - notes = "Uses entity data query (see 'Find Entity Data by Query') to find first 100 entities. Then fetch and return all unique time-series and/or attribute keys. Used mostly for UI hints.") + @ApiOperation( + value = "Find Available Entity Keys by Query", + notes = """ + Returns unique time series and/or attribute key names from entities matching the query.\n + Executes the Entity Data Query to find up to 100 entities, then fetches and aggregates all distinct key names.\n + Primarily used for UI features like autocomplete suggestions.""" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entitiesQuery/find/keys", method = RequestMethod.POST) - @ResponseBody - public DeferredResult findEntityTimeseriesAndAttributesKeysByQuery( - @Parameter(description = "A JSON value representing the entity data query. See API call notes above for more details.") + @PostMapping("/entitiesQuery/find/keys") + public DeferredResult findAvailableEntityKeysByQuery( + @Parameter(description = "Entity data query to find entities. Page size is capped at 100.") @RequestBody EntityDataQuery query, - @Parameter(description = "Include all unique time-series keys to the result.") - @RequestParam("timeseries") boolean isTimeseries, - @Parameter(description = "Include all unique attribute keys to the result.") - @RequestParam("attributes") boolean isAttributes, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) - @RequestParam(value = "scope", required = false) String scope) throws ThingsboardException { - TenantId tenantId = getTenantId(); - checkNotNull(query); + + // fixme: combination of timeseries = false and attributes = false is allowed, but always results in empty response, therefore does not make any sense + // such combinations should NOT be allowed, but changing this will break clients + + @Parameter(description = """ + When true, includes unique time series key names in the response. + When false, the 'timeseries' list will be empty.""") + @RequestParam("timeseries") boolean includeTimeseries, + + @Parameter(description = """ + When true, includes unique attribute key names in the response. + When false, the 'attribute' list will be empty. Use 'scope' parameter to filter by attribute scope.""") + @RequestParam("attributes") boolean includeAttributes, + + @Parameter(description = """ + Filters attribute keys by scope. Only applies when 'attributes' is true. + If not specified, returns attribute keys from all scopes.""", + schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) + @RequestParam(value = "scope", required = false) AttributeScope scope + ) throws ThingsboardException { resolveQuery(query); EntityDataPageLink pageLink = query.getPageLink(); if (pageLink.getPageSize() > MAX_PAGE_SIZE) { pageLink.setPageSize(MAX_PAGE_SIZE); } - return entityQueryService.getKeysByQuery(getCurrentUser(), tenantId, query, isTimeseries, isAttributes, scope); + return wrapFuture(entityQueryService.getKeysByQuery(getCurrentUser(), getTenantId(), query, includeTimeseries, includeAttributes, scope)); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 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 a87db28e92..9314f584ab 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -17,15 +17,15 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; 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.PostMapping; import org.springframework.web.bind.annotation.RequestBody; 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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.entity.relation.TbEntityRelationService; import org.thingsboard.server.service.security.model.SecurityUser; @@ -41,7 +42,6 @@ import org.thingsboard.server.service.security.permission.Operation; import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_PARAM_DESCRIPTION; @@ -76,10 +76,9 @@ public class EntityRelationController extends BaseController { "Relations unique key is a combination of from/to entity id and relation type group and relation type. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relation", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) + @PostMapping("/relation") public void saveRelation(@Parameter(description = "A JSON value representing the relation.", required = true) - @RequestBody EntityRelation relation) throws ThingsboardException { + @RequestBody EntityRelation relation) throws ThingsboardException { doSave(relation); } @@ -88,29 +87,26 @@ public class EntityRelationController extends BaseController { "Relations unique key is a combination of from/to entity id and relation type group and relation type. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/relation", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) + @PostMapping("/v2/relation") public EntityRelation saveRelationV2(@Parameter(description = "A JSON value representing the relation.", required = true) @RequestBody EntityRelation relation) throws ThingsboardException { return doSave(relation); } private EntityRelation doSave(EntityRelation relation) throws ThingsboardException { - checkNotNull(relation); - checkCanCreateRelation(relation.getFrom()); - checkCanCreateRelation(relation.getTo()); if (relation.getTypeGroup() == null) { relation.setTypeGroup(RelationTypeGroup.COMMON); } - + ConstraintValidator.validateFields(relation); + checkCanCreateRelation(relation.getFrom()); + checkCanCreateRelation(relation.getTo()); return tbEntityRelationService.save(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser()); } @ApiOperation(value = "Delete Relation (deleteRelation)", notes = "Deletes a relation between two entities in the platform. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) - @ResponseStatus(value = HttpStatus.OK) + @DeleteMapping(value = "/relation", params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) public void deleteRelation(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, @@ -123,21 +119,19 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Delete Relation (deleteRelationV2)", notes = "Deletes a relation between two entities in the platform. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) - @ResponseStatus(value = HttpStatus.OK) + @DeleteMapping(value = "/v2/relation", params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) public EntityRelation deleteRelationV2(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, - @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, - @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, - @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, - @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, - @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { + @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, + @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, + @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, + @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, + @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { return doDelete(strFromId, strFromType, strRelationType, strRelationTypeGroup, strToId, strToType); } private EntityRelation doDelete(String strFromId, String strFromType, String strRelationType, String strRelationTypeGroup, String strToId, String strToType) throws ThingsboardException { checkParameter(FROM_ID, strFromId); checkParameter(FROM_TYPE, strFromType); - checkParameter(RELATION_TYPE, strRelationType); checkParameter(TO_ID, strToId); checkParameter(TO_TYPE, strToType); EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); @@ -154,8 +148,7 @@ public class EntityRelationController extends BaseController { 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"}) - @ResponseStatus(value = HttpStatus.OK) + @DeleteMapping(value = "/relations", params = {"entityId", "entityType"}) public void deleteRelations(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam("entityId") String strId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam("entityType") String strType) throws ThingsboardException { checkParameter("entityId", strId); @@ -168,8 +161,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get Relation (getRelation)", notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) - @ResponseBody + @GetMapping(value = "/relation", params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) public EntityRelation getRelation(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, @@ -178,7 +170,6 @@ public class EntityRelationController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { checkParameter(FROM_ID, strFromId); checkParameter(FROM_TYPE, strFromType); - checkParameter(RELATION_TYPE, strRelationType); checkParameter(TO_ID, strToId); checkParameter(TO_TYPE, strToType); EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); @@ -193,8 +184,7 @@ public class EntityRelationController extends BaseController { notes = "Returns list of relation objects for the specified entity by the 'from' direction. " + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) - @ResponseBody + @GetMapping(value = "/relations", params = {FROM_ID, FROM_TYPE}) public List findByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @@ -211,8 +201,7 @@ public class EntityRelationController extends BaseController { notes = "Returns list of relation info objects for the specified entity by the 'from' direction. " + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) - @ResponseBody + @GetMapping(value = "/relations/info", params = {FROM_ID, FROM_TYPE}) public List findInfoByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @@ -229,8 +218,7 @@ public class EntityRelationController extends BaseController { notes = "Returns list of relation objects for the specified entity by the 'from' direction and relation type. " + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE}) - @ResponseBody + @GetMapping(value = "/relations", params = {FROM_ID, FROM_TYPE, RELATION_TYPE}) public List findByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, @@ -249,8 +237,7 @@ public class EntityRelationController extends BaseController { notes = "Returns list of relation objects for the specified entity by the 'to' direction. " + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) - @ResponseBody + @GetMapping(value = "/relations", params = {TO_ID, TO_TYPE}) public List findByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType, @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @@ -267,8 +254,7 @@ public class EntityRelationController extends BaseController { notes = "Returns list of relation info objects for the specified entity by the 'to' direction. " + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) - @ResponseBody + @GetMapping(value = "/relations/info", params = {TO_ID, TO_TYPE}) public List findInfoByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType, @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @@ -285,8 +271,7 @@ public class EntityRelationController extends BaseController { notes = "Returns list of relation objects for the specified entity by the 'to' direction and relation type. " + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE}) - @ResponseBody + @GetMapping(value = "/relations", params = {TO_ID, TO_TYPE, RELATION_TYPE}) public List findByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType, @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, @@ -306,11 +291,9 @@ public class EntityRelationController extends BaseController { "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + "See 'Model' tab of the Parameters for more info.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations", method = RequestMethod.POST) - @ResponseBody + @PostMapping("/relations") public List findByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true) @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { - checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getFilters()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); @@ -322,11 +305,9 @@ public class EntityRelationController extends BaseController { "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations/info", method = RequestMethod.POST) - @ResponseBody + @PostMapping("/relations/info") public List findInfoByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true) @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { - checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getFilters()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); @@ -354,18 +335,18 @@ public class EntityRelationController extends BaseController { return false; } return true; - }).collect(Collectors.toList()); + }).toList(); } - private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { - RelationTypeGroup result = defaultValue; - if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length() > 0) { - try { - result = RelationTypeGroup.valueOf(strRelationTypeGroup); - } catch (IllegalArgumentException e) { - } + private static RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { + if (StringUtils.isBlank(strRelationTypeGroup)) { + return defaultValue; + } + try { + return RelationTypeGroup.valueOf(strRelationTypeGroup); + } catch (IllegalArgumentException e) { + return defaultValue; } - return result; } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 15bda4b87a..e061c691ac 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -17,23 +17,29 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.ListenableFuture; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +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.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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; +import org.thingsboard.server.common.data.UniquifyStrategy; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -53,7 +59,10 @@ 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.ArrayList; import java.util.List; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -69,6 +78,8 @@ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TYPE; import static org.thingsboard.server.controller.ControllerConstants.MODEL_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC; 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; @@ -76,11 +87,9 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC; import static org.thingsboard.server.controller.EdgeController.EDGE_ID; -/** - * Created by Victor Basanets on 8/28/2017. - */ @RestController @TbCoreComponent @RequiredArgsConstructor @@ -96,8 +105,7 @@ public class EntityViewController extends BaseController { notes = "Fetch the EntityView object based on the provided entity view id. " + ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/entityView/{entityViewId}") public EntityView getEntityViewById( @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { @@ -109,8 +117,7 @@ public class EntityViewController extends BaseController { notes = "Fetch the Entity View info object based on the provided Entity View Id. " + ENTITY_VIEW_INFO_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entityView/info/{entityViewId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/entityView/info/{entityViewId}") public EntityViewInfo getEntityViewInfoById( @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { @@ -124,11 +131,16 @@ public class EntityViewController extends BaseController { "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entityView", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/entityView") public EntityView saveEntityView( @Parameter(description = "A JSON object representing the entity view.") - @RequestBody EntityView entityView) throws Exception { + @RequestBody EntityView entityView, + @Parameter(description = NAME_CONFLICT_POLICY_DESC) + @RequestParam(name = "nameConflictPolicy", defaultValue = "FAIL") NameConflictPolicy nameConflictPolicy, + @Parameter(description = UNIQUIFY_SEPARATOR_DESC) + @RequestParam(name = "uniquifySeparator", defaultValue = "_") String uniquifySeparator, + @Parameter(description = UNIQUIFY_STRATEGY_DESC) + @RequestParam(name = "uniquifyStrategy", defaultValue = "RANDOM") UniquifyStrategy uniquifyStrategy) throws Exception { entityView.setTenantId(getCurrentUser().getTenantId()); EntityView existingEntityView = null; if (entityView.getId() == null) { @@ -137,14 +149,14 @@ public class EntityViewController extends BaseController { } else { existingEntityView = checkEntityViewId(entityView.getId(), Operation.WRITE); } - return tbEntityViewService.save(entityView, existingEntityView, getCurrentUser()); + return tbEntityViewService.save(entityView, existingEntityView, new NameConflictStrategy(nameConflictPolicy, uniquifySeparator, uniquifyStrategy), getCurrentUser()); } @ApiOperation(value = "Delete entity view (deleteEntityView)", notes = "Delete the EntityView object based on the provided entity view id. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/entityView/{entityViewId}") @ResponseStatus(value = HttpStatus.OK) public void deleteEntityView( @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @@ -158,8 +170,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Get Entity View by name (getTenantEntityView)", notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/entityViews", params = {"entityViewName"}) public EntityView getTenantEntityView( @Parameter(description = "Entity View name") @RequestParam String entityViewName) throws ThingsboardException { @@ -170,8 +181,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Assign Entity View to customer (assignEntityViewToCustomer)", notes = "Creates assignment of the Entity View to customer. Customer will be able to query Entity View afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/customer/{customerId}/entityView/{entityViewId}") public EntityView assignEntityViewToCustomer( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -192,8 +202,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Unassign Entity View from customer (unassignEntityViewFromCustomer)", notes = "Clears assignment of the Entity View to customer. Customer will not be able to query Entity View afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/entityView/{entityViewId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/customer/entityView/{entityViewId}") public EntityView unassignEntityViewFromCustomer( @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { @@ -213,8 +222,7 @@ public class EntityViewController extends BaseController { notes = "Returns a page of Entity View objects assigned to customer. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/customer/{customerId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/entityViews", params = {"pageSize", "page"}) public PageData getCustomerEntityViews( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -235,7 +243,7 @@ public class EntityViewController extends BaseController { CustomerId customerId = new CustomerId(toUUID(strCustomerId)); checkCustomerId(customerId, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); } else { return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); @@ -246,8 +254,7 @@ public class EntityViewController extends BaseController { notes = "Returns a page of Entity View info objects assigned to customer. " + ENTITY_VIEW_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/customer/{customerId}/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/entityViewInfos", params = {"pageSize", "page"}) public PageData getCustomerEntityViewInfos( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -268,7 +275,7 @@ public class EntityViewController extends BaseController { CustomerId customerId = new CustomerId(toUUID(strCustomerId)); checkCustomerId(customerId, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); } else { return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); @@ -279,8 +286,7 @@ public class EntityViewController extends BaseController { notes = "Returns a page of entity views owned by tenant. " + ENTITY_VIEW_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/tenant/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/entityViews", params = {"pageSize", "page"}) public PageData getTenantEntityViews( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -297,7 +303,7 @@ public class EntityViewController extends BaseController { TenantId tenantId = getCurrentUser().getTenantId(); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); } else { return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); @@ -308,8 +314,7 @@ public class EntityViewController extends BaseController { notes = "Returns a page of entity views info owned by tenant. " + ENTITY_VIEW_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/tenant/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/entityViewInfos", params = {"pageSize", "page"}) public PageData getTenantEntityViewInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -325,7 +330,7 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { TenantId tenantId = getCurrentUser().getTenantId(); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndType(tenantId, type, pageLink)); } else { return checkNotNull(entityViewService.findEntityViewInfosByTenantId(tenantId, pageLink)); @@ -337,8 +342,7 @@ public class EntityViewController extends BaseController { "The entity id, relation type, entity view types, depth of the search, and other query parameters defined using complex 'EntityViewSearchQuery' object. " + "See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entityViews", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/entityViews") public List findByQuery( @Parameter(description = "The entity view search query JSON") @RequestBody EntityViewSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { @@ -347,14 +351,7 @@ public class EntityViewController extends BaseController { checkNotNull(query.getEntityViewTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); - entityViews = entityViews.stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); + entityViews = filterEntityViewsByReadPermission(entityViews); return entityViews; } @@ -362,8 +359,7 @@ public class EntityViewController extends BaseController { notes = "Returns a set of unique entity view types based on entity views that are either owned by the tenant or assigned to the customer which user is performing the request." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/entityView/types", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/entityView/types") public List getEntityViewTypes() throws ThingsboardException, ExecutionException, InterruptedException { SecurityUser user = getCurrentUser(); TenantId tenantId = user.getTenantId(); @@ -376,8 +372,7 @@ public class EntityViewController extends BaseController { "This is useful to create dashboards that you plan to share/embed on a publicly available website. " + "However, users that are logged-in and belong to different tenant will not be able to access the entity view." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/public/entityView/{entityViewId}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/customer/public/entityView/{entityViewId}") public EntityView assignEntityViewToPublicCustomer( @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { @@ -394,8 +389,7 @@ public class EntityViewController extends BaseController { EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/edge/{edgeId}/entityView/{entityViewId}") public EntityView assignEntityViewToEdge(@PathVariable(EDGE_ID) String strEdgeId, @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); @@ -418,8 +412,7 @@ public class EntityViewController extends BaseController { EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/edge/{edgeId}/entityView/{entityViewId}") public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId, @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); @@ -436,8 +429,7 @@ public class EntityViewController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}) public PageData getEdgeEntityViews( @PathVariable(EDGE_ID) String strEdgeId, @RequestParam int pageSize, @@ -454,23 +446,42 @@ public class EntityViewController extends BaseController { checkEdgeId(edgeId, Operation.READ); TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { + if (type != null && !type.trim().isEmpty()) { nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); } else { nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } - List filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); + List filteredEntityViews = filterEntityViewsByReadPermission(nonFilteredResult.getData()); PageData filteredResult = new PageData<>(filteredEntityViews, nonFilteredResult.getTotalPages(), nonFilteredResult.getTotalElements(), nonFilteredResult.hasNext()); return checkNotNull(filteredResult); } + + @ApiOperation(value = "Get Entity Views By Ids (getEntityViewsByIds)", + notes = "Requested entity views must be owned by tenant or assigned to customer which user is performing the request. ") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/entityViews", params = {"entityViewIds"}) + public List getEntityViewsByIds(@Parameter(description = "A list of entity view ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("entityViewIds") Set entityViewUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List entityViewIds = new ArrayList<>(); + for (UUID entityViewUUID : entityViewUUIDs) { + entityViewIds.add(new EntityViewId(entityViewUUID)); + } + List entityViews = entityViewService.findEntityViewsByTenantIdAndIds(tenantId, entityViewIds); + return filterEntityViewsByReadPermission(entityViews); + } + + private List filterEntityViewsByReadPermission(List entityViews) { + return entityViews.stream().filter(entityView -> { + try { + return accessControlService.hasPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index ab3d7239d9..5e3f3be442 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -27,6 +27,8 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -37,6 +39,7 @@ import org.thingsboard.server.service.lwm2m.LwM2MService; import java.util.Map; +import static org.thingsboard.server.common.data.NameConflictStrategy.DEFAULT; import static org.thingsboard.server.controller.ControllerConstants.IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; @@ -73,6 +76,6 @@ public class Lwm2mController extends BaseController { public Device saveDeviceWithCredentials(@RequestBody Map, Object> deviceWithDeviceCredentials) throws ThingsboardException { Device device = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class)); DeviceCredentials credentials = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class)); - return deviceController.saveDeviceWithCredentials(new SaveDeviceWithCredentialsRequest(device, credentials)); + return deviceController.saveDeviceWithCredentials(new SaveDeviceWithCredentialsRequest(device, credentials), DEFAULT.policy(), DEFAULT.separator(), DEFAULT.uniquifyStrategy()); } } 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 d504cf24b2..126f050bec 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -35,6 +36,8 @@ 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.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; +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.NotificationTargetId; @@ -44,6 +47,9 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho 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.NotificationType; +import org.thingsboard.server.common.data.notification.info.EntitiesLimitIncreaseRequestNotificationInfo; +import org.thingsboard.server.common.data.notification.info.NotificationInfo; 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; @@ -51,6 +57,7 @@ import org.thingsboard.server.common.data.notification.targets.NotificationRecip import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; 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.notification.targets.slack.SlackConversation; import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; @@ -69,6 +76,7 @@ import org.thingsboard.server.service.notification.NotificationProcessingContext 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 org.thingsboard.server.service.security.system.SystemSecurityService; import java.util.Comparator; import java.util.HashMap; @@ -76,6 +84,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -90,6 +99,7 @@ import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DE 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.TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.service.security.permission.Resource.NOTIFICATION; @RestController @@ -105,6 +115,7 @@ public class NotificationController extends BaseController { private final NotificationTargetService notificationTargetService; private final NotificationCenter notificationCenter; private final NotificationSettingsService notificationSettingsService; + private final SystemSecurityService systemSecurityService; @ApiOperation(value = "Get notifications (getNotifications)", notes = "Returns the page of notifications for current user." + NEW_LINE + @@ -282,6 +293,34 @@ public class NotificationController extends BaseController { return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, (tenantId, request) -> notificationCenter.processNotificationRequest(tenantId, request, null)); } + @ApiOperation(value = "Send entity limit increase request notification to System administrators (sendEntitiesLimitIncreaseRequest)", + notes = "Send entity limit increase request notification by Tenant Administrator to System administrators." + + TENANT_AUTHORITY_PARAGRAPH) + @PostMapping("/notification/entitiesLimitIncreaseRequest/{entityType}") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + public void sendEntitiesLimitIncreaseRequest(@Parameter(description = "Entity type", required = true, schema = @Schema(allowableValues = {"DEVICE", "ASSET", "CUSTOMER", "USER", "DASHBOARD", "RULE_CHAIN", "EDGE"})) + @PathVariable("entityType") String strEntityType, + @AuthenticationPrincipal SecurityUser user, + HttpServletRequest request) throws Exception { + EntityType entityType = checkEnumParameter("entityType", strEntityType, EntityType::valueOf); + Optional sysAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(TenantId.SYS_TENANT_ID, UsersFilterType.SYSTEM_ADMINISTRATORS) + .stream().findFirst(); + if (sysAdmins.isPresent()) { + NotificationTargetId notificationTargetId = sysAdmins.get().getId(); + String baseUrl = systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + NotificationInfo info = EntitiesLimitIncreaseRequestNotificationInfo.builder() + .entityType(entityType) + .userEmail(user.getEmail()) + .increaseLimitActionLabel("Set new limit") + .increaseLimitLink("/tenants/"+user.getTenantId().toString()) + .baseUrl(baseUrl) + .build(); + notificationCenter.sendSystemNotification(TenantId.SYS_TENANT_ID, notificationTargetId, NotificationType.ENTITIES_LIMIT_INCREASE_REQUEST, info); + } else { + throw new IllegalArgumentException("Notification target for 'System administrators' not found"); + } + } + @ApiOperation(value = "Get notification request preview (getNotificationRequestPreview)", notes = "Returns preview for notification request." + NEW_LINE + "`processedTemplates` shows how the notifications for each delivery method will look like " + diff --git a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java index e74763b8ad..d266b684d8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -31,15 +31,12 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.app.MobileApp; -import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings; import org.thingsboard.server.common.data.mobile.app.StoreInfo; -import org.thingsboard.server.common.data.oauth2.PlatformType; +import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings; import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.config.annotations.ApiOperation; -import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.mobile.QrCodeSettingService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.mobile.secret.MobileAppSecretService; 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 12c26dedb3..accec11afe 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; @@ -79,6 +80,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; @@ -90,6 +92,7 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIG import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END; import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START; +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; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; @@ -579,4 +582,20 @@ public class RuleChainController extends BaseController { return checkNotNull(result); } + @ApiOperation(value = "Get Rule Chains By Ids (getRuleChainsByIds)", + notes = "Requested rule chains must be owned by tenant which is performing the request. " + + NEW_LINE) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping(value = "/ruleChains", params = {"ruleChainIds"}) + public List getRuleChainsByIds( + @Parameter(description = "A list of rule chain ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("ruleChainIds") Set ruleChainUUIDs) throws Exception { + TenantId tenantId = getCurrentUser().getTenantId(); + List ruleChainIds = new ArrayList<>(); + for (UUID ruleChainUUID : ruleChainUUIDs) { + ruleChainIds.add(new RuleChainId(ruleChainUUID)); + } + return ruleChainService.findRuleChainsByIds(tenantId, ruleChainIds); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java index 96095ddf9e..2b4489e387 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.Parameter; import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -60,7 +61,10 @@ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_ @RequestMapping(TbUrlConstants.RULE_ENGINE_URL_PREFIX) @Slf4j public class RuleEngineController extends BaseController { - public static final int DEFAULT_TIMEOUT = 10000; + + @Value("${server.rest.rule_engine.response_timeout:10000}") + public int defaultResponseTimeout; + private static final String MSG_DESCRIPTION_PREFIX = "Creates the Message with type 'REST_API_REQUEST' and payload taken from the request body. "; private static final String MSG_DESCRIPTION = "This method allows you to extend the regular platform API with the power of Rule Engine. You may use default and custom rule nodes to handle the message. " + "The generated message contains two important metadata fields:\n\n" + @@ -85,7 +89,7 @@ public class RuleEngineController extends BaseController { public DeferredResult handleRuleEngineRequest( @Parameter(description = "A JSON value representing the message.", required = true) @RequestBody String requestBody) throws ThingsboardException { - return handleRuleEngineRequest(null, null, null, DEFAULT_TIMEOUT, requestBody); + return handleRuleEngineRequest(null, null, null, defaultResponseTimeout, requestBody); } @ApiOperation(value = "Push entity message to the rule engine (handleRuleEngineRequest)", @@ -104,7 +108,7 @@ public class RuleEngineController extends BaseController { @PathVariable("entityId") String entityIdStr, @Parameter(description = "A JSON value representing the message.", required = true) @RequestBody String requestBody) throws ThingsboardException { - return handleRuleEngineRequest(entityType, entityIdStr, null, DEFAULT_TIMEOUT, requestBody); + return handleRuleEngineRequest(entityType, entityIdStr, null, defaultResponseTimeout, requestBody); } @ApiOperation(value = "Push entity message with timeout to the rule engine (handleRuleEngineRequest)", 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 61240d304e..ee2e871d6e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -162,6 +162,12 @@ public class SystemInfoController extends BaseController { } systemParams.setMaxArgumentsPerCF(tenantProfileConfiguration.getMaxArgumentsPerCF()); systemParams.setMaxDataPointsPerRollingArg(tenantProfileConfiguration.getMaxDataPointsPerRollingArg()); + systemParams.setMinAllowedScheduledUpdateIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedScheduledUpdateIntervalInSecForCF()); + systemParams.setMaxRelationLevelPerCfArgument(tenantProfileConfiguration.getMaxRelationLevelPerCfArgument()); + systemParams.setMaxRelatedEntitiesToReturnPerCfArgument(tenantProfileConfiguration.getMaxRelatedEntitiesToReturnPerCfArgument()); + systemParams.setMinAllowedDeduplicationIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedDeduplicationIntervalInSecForCF()); + systemParams.setMinAllowedAggregationIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedAggregationIntervalInSecForCF()); + systemParams.setIntermediateAggregationIntervalInSecForCF(tenantProfileConfiguration.getIntermediateAggregationIntervalInSecForCF()); systemParams.setTrendzSettings(trendzSettingsService.findTrendzSettings(currentUser.getTenantId())); } systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID)) 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 14c0a3dcb2..a7aaf37ffd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -32,12 +32,16 @@ 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.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; 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.common.util.JacksonUtil; import org.thingsboard.server.common.data.ResourceSubType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; @@ -215,6 +219,7 @@ public class TbResourceController extends BaseController { "\n\nResource combination of the title with the key is unique in the scope of tenant. " + "Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @Deprecated // resource should be save or update with an upload request @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PostMapping(value = "/resource") public TbResourceInfo saveResource(@Parameter(description = "A JSON value representing the Resource.") @@ -224,6 +229,71 @@ public class TbResourceController extends BaseController { return tbResourceService.save(resource, getCurrentUser()); } + @ApiOperation(value = "Upload Resource via Multipart File (uploadResource)", + notes = "Create the Resource using multipart file upload. " + + "\n\nResource combination of the title with the key is unique in the scope of tenant. " + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PostMapping(value = "/resource/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public TbResourceInfo uploadResource(@Parameter(description = "Resource title.", example = "Title") + @RequestPart(name = "title", required = false) String title, + @Parameter(description = "Resource type.") + @RequestPart(name = "resourceType") String resourceTypeStr, + @Parameter(description = "Resource descriptor (JSON).") + @RequestPart(name = "descriptor", required = false) String descriptor, + @Parameter(description = "Resource sub type.") + @RequestPart(name = "resourceSubType", required = false) String resourceSubTypeStr, + @Parameter(description = "Resource file.") + @RequestPart MultipartFile file) throws Exception { + TbResource resource = new TbResource(); + resource.setTenantId(getTenantId()); + resource.setTitle(StringUtils.isNotEmpty(title) ? title : file.getOriginalFilename()); + ResourceType resourceType = ResourceType.valueOf(resourceTypeStr); + resource.setResourceType(resourceType); + + if (StringUtils.isNotEmpty(descriptor)) { + resource.setDescriptor(JacksonUtil.toJsonNode(descriptor)); + } else { + String mediaType = resourceType.getMediaType() != null ? resourceType.getMediaType() : file.getContentType(); + resource.setDescriptor(JacksonUtil.newObjectNode().put("mediaType", mediaType)); + } + + if (StringUtils.isNotEmpty(resourceSubTypeStr)) { + resource.setResourceSubType(ResourceSubType.valueOf(resourceSubTypeStr)); + } + resource.setFileName(file.getOriginalFilename()); + resource.setData(file.getBytes()); + + checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); + return tbResourceService.save(resource, getCurrentUser()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping(value = "/resource/{id}/data", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public TbResourceInfo updateResourceData(@Parameter(description = "Unique identifier of the Resource to update", required = true) + @PathVariable UUID id, + @Parameter(description = "Resource file.") + @RequestPart MultipartFile file) throws Exception { + TbResourceId tbResourceId = new TbResourceId(id); + TbResource resource = checkResourceId(tbResourceId, Operation.WRITE); + resource.setFileName(file.getOriginalFilename()); + resource.setData(file.getBytes()); + return tbResourceService.save(resource, getCurrentUser()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping("/resource/{id}/info") + public TbResourceInfo updateResourceInfo(@Parameter(description = "Unique identifier of the Resource to update", required = true) + @PathVariable UUID id, + @Parameter(description = "A JSON value representing the Resource Info.") + @RequestBody TbResourceInfo resourceInfo) throws Exception { + TbResourceId tbResourceId = new TbResourceId(id); + checkResourceInfoId(tbResourceId, Operation.WRITE); + resourceInfo.setId(tbResourceId); + TbResource resource = new TbResource(resourceInfo); + return tbResourceService.save(resource, getCurrentUser()); + } + @ApiOperation(value = "Get Resource Infos (getResources)", notes = "Returns a page of Resource Info objects owned by tenant or sysadmin. " + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) diff --git a/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java b/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java index 46291b7b3f..d74a1c02b3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.controller; -/** - * Created by ashvayka on 17.05.18. - */ public class TbUrlConstants { public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry"; public static final String RPC_V1_URL_PREFIX = "/api/plugins/rpc"; 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 8ff71494df..b0f5f41c9a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -36,13 +36,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.MultiValueMap; +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.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.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.JacksonUtil; @@ -86,6 +87,7 @@ import org.thingsboard.server.service.telemetry.TsData; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -129,10 +131,6 @@ import static org.thingsboard.server.controller.ControllerConstants.TELEMETRY_SC import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TS_STRICT_DATA_EXAMPLE; - -/** - * Created by ashvayka on 22.03.18. - */ @RestController @TbCoreComponent @RequestMapping(TbUrlConstants.TELEMETRY_URL_PREFIX) @@ -170,8 +168,7 @@ public class TelemetryController extends BaseController { "\n * SHARED_SCOPE - supported for devices. " + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/keys/attributes") public DeferredResult getAttributeKeys( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @@ -185,8 +182,7 @@ public class TelemetryController extends BaseController { "\n * SHARED_SCOPE - supported for devices. " + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}") public DeferredResult getAttributeKeysByScope( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @@ -203,15 +199,16 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_END + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/values/attributes") public DeferredResult getAttributes( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); 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, keys)); } @@ -225,24 +222,24 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_END + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}") public DeferredResult getAttributesByScope( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); 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, keys)); } @ApiOperation(value = "Get time series keys (getTimeseriesKeys)", notes = "Returns a set of unique time series key names for the selected entity. " + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/keys/timeseries") public DeferredResult getTimeseriesKeys( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @@ -263,17 +260,18 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_END + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/values/timeseries") public DeferredResult getLatestTimeseries( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, @Parameter(description = STRICT_DATA_TYPES_DESCRIPTION) - @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { + @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keys, useStrictDataTypes)); } @ApiOperation(value = "Get time series data (getTimeseries)", @@ -286,12 +284,11 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_END + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/values/timeseries", params = {"keys", "startTs", "endTs"}) public DeferredResult getTimeseries( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = TELEMETRY_KEYS_BASE_DESCRIPTION, required = true) @RequestParam(name = "keys") String keys, + @Parameter(description = TELEMETRY_KEYS_BASE_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, @Parameter(description = "A long value representing the start timestamp of the time range in milliseconds, UTC.") @RequestParam(name = "startTs") Long startTs, @Parameter(description = "A long value representing the end timestamp of the time range in milliseconds, UTC.") @@ -312,9 +309,11 @@ public class TelemetryController extends BaseController { @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @Parameter(description = STRICT_DATA_TYPES_DESCRIPTION) - @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { + @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); DeferredResult response = new DeferredResult<>(); - Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), toKeysList(keys), startTs, endTs, + Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), keys, startTs, endTs, intervalType, interval, timeZone, limit, Aggregation.valueOf(aggStr), orderBy, useStrictDataTypes, getCurrentUser()), getTsKvListCallback(response, useStrictDataTypes), MoreExecutors.directExecutor()); return response; @@ -408,8 +407,7 @@ public class TelemetryController extends BaseController { @ApiResponse(responseCode = "500", description = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/{entityType}/{entityId}/timeseries/{scope}") public DeferredResult saveEntityTelemetry( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @@ -432,8 +430,7 @@ public class TelemetryController extends BaseController { @ApiResponse(responseCode = "500", description = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}/{ttl}", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/{entityType}/{entityId}/timeseries/{scope}/{ttl}") public DeferredResult saveEntityTelemetryWithTTL( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @@ -461,12 +458,11 @@ public class TelemetryController extends BaseController { "Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/{entityType}/{entityId}/timeseries/delete") public DeferredResult deleteEntityTimeseries( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = TELEMETRY_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr, + @Parameter(description = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, @Parameter(description = "A boolean value to specify if should be deleted all data for selected keys or only data that are in the selected time range.") @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys, @Parameter(description = "A long value representing the start timestamp of removal time range in milliseconds.") @@ -476,16 +472,17 @@ public class TelemetryController extends BaseController { @Parameter(description = "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, @Parameter(description = "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 { + @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest); + return deleteTimeseries(entityId, keys, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest); } - private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, + private DeferredResult deleteTimeseries(EntityId entityIdStr, List keys, boolean deleteAllDataForKeys, 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); + return getImmediateDeferredResult("Empty keys: " + keys, HttpStatus.BAD_REQUEST); } SecurityUser user = getCurrentUser(); @@ -542,14 +539,15 @@ public class TelemetryController extends BaseController { "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/{deviceId}/{scope}") public DeferredResult deleteDeviceAttributes( @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return deleteAttributes(entityId, scope, keysStr); + return deleteAttributes(entityId, scope, keys); } @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", @@ -564,21 +562,25 @@ public class TelemetryController extends BaseController { "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/{entityType}/{entityId}/{scope}") public DeferredResult deleteEntityAttributes( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, + @RequestParam MultiValueMap params) throws ThingsboardException { + List keys = getKeys(keysStr, params); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteAttributes(entityId, scope, keysStr); + return deleteAttributes(entityId, scope, keys); } - private DeferredResult deleteAttributes(EntityId entityIdSrc, AttributeScope scope, String keysStr) throws ThingsboardException { - List keys = toKeysList(keysStr); + private List getKeys(String keysStr, MultiValueMap params) { + return params.get("key") != null ? params.get("key") : toKeysList(keysStr); + } + + private DeferredResult deleteAttributes(EntityId entityIdSrc, AttributeScope scope, List keys) throws ThingsboardException { if (keys.isEmpty()) { - return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); + return getImmediateDeferredResult("Empty keys: " + keys, HttpStatus.BAD_REQUEST); } SecurityUser user = getCurrentUser(); @@ -709,30 +711,29 @@ public class TelemetryController extends BaseController { }); } - private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, String keys, Boolean useStrictDataTypes) { + private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, List keys, Boolean useStrictDataTypes) { ListenableFuture> future; - if (StringUtils.isEmpty(keys)) { + if (keys.isEmpty()) { future = tsService.findAllLatest(user.getTenantId(), entityId); } else { - future = tsService.findLatest(user.getTenantId(), entityId, toKeysList(keys)); + future = tsService.findLatest(user.getTenantId(), entityId, keys); } Futures.addCallback(future, getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); } - private void getAttributeValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, AttributeScope scope, String keys) { - List keyList = toKeysList(keys); - FutureCallback> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList); + private void getAttributeValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, AttributeScope scope, List keys) { + FutureCallback> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keys); if (scope != null) { - if (keyList != null && !keyList.isEmpty()) { - Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keyList), callback, MoreExecutors.directExecutor()); + if (keys != null && !keys.isEmpty()) { + Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keys), callback, MoreExecutors.directExecutor()); } else { Futures.addCallback(attributesService.findAll(user.getTenantId(), entityId, scope), callback, MoreExecutors.directExecutor()); } } else { List>> futures = new ArrayList<>(); for (AttributeScope tmpScope : AttributeScope.values()) { - if (keyList != null && !keyList.isEmpty()) { - futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keyList)); + if (keys != null && !keys.isEmpty()) { + futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keys)); } else { futures.add(attributesService.findAll(user.getTenantId(), entityId, tmpScope)); } @@ -872,11 +873,11 @@ public class TelemetryController extends BaseController { } private List toKeysList(String keys) { - List keyList = null; if (!StringUtils.isEmpty(keys)) { - keyList = Arrays.asList(keys.split(",")); + return Arrays.asList(keys.split(",")); + } else { + return Collections.emptyList(); } - return keyList; } private DeferredResult getImmediateDeferredResult(String message, HttpStatus status) { diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java index 55bd76fdcb..efa1d36cff 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -16,17 +16,20 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +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.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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Tenant; @@ -42,6 +45,11 @@ import org.thingsboard.server.service.entitiy.tenant.TbTenantService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; @@ -70,8 +78,7 @@ public class TenantController extends BaseController { @ApiOperation(value = "Get Tenant (getTenantById)", notes = "Fetch the Tenant object based on the provided Tenant Id. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/{tenantId}") public Tenant getTenantById( @Parameter(description = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { @@ -86,8 +93,7 @@ public class TenantController extends BaseController { notes = "Fetch the Tenant Info object based on the provided Tenant Id. " + TENANT_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/tenant/info/{tenantId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/info/{tenantId}") public TenantInfo getTenantInfoById( @Parameter(description = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { @@ -105,8 +111,7 @@ public class TenantController extends BaseController { "Remove 'id', 'tenantId' from the request body example (below) to create new Tenant entity." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenant", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/tenant") public Tenant saveTenant(@Parameter(description = "A JSON value representing the tenant.") @RequestBody Tenant tenant) throws Exception { checkEntity(tenant.getId(), tenant, Resource.TENANT); @@ -114,9 +119,9 @@ public class TenantController extends BaseController { } @ApiOperation(value = "Delete Tenant (deleteTenant)", - notes = "Deletes the tenant, it's customers, rule chains, devices and all other related entities. Referencing non-existing tenant Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH) - @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.DELETE) + notes = "Deletes the tenant, it's customers, rule chains, devices and all other related entities. Referencing non-existing tenant Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @DeleteMapping(value = "/tenant/{tenantId}") @ResponseStatus(value = HttpStatus.OK) public void deleteTenant(@Parameter(description = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws Exception { @@ -128,8 +133,7 @@ public class TenantController extends BaseController { @ApiOperation(value = "Get Tenants (getTenants)", notes = "Returns a page of tenants registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenants", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenants", params = {"pageSize", "page"}) public PageData getTenants( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -148,8 +152,7 @@ public class TenantController extends BaseController { @ApiOperation(value = "Get Tenants Info (getTenants)", notes = "Returns a page of tenant info objects registered in the platform. " + TENANT_INFO_DESCRIPTION + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantInfos", params = {"pageSize", "page"}) public PageData getTenantInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -166,4 +169,28 @@ public class TenantController extends BaseController { return checkNotNull(tenantService.findTenantInfos(pageLink)); } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping(value = "/tenants", params = {"tenantIds"}) + public List getTenantsByIds( + @Parameter(description = "A list of tenant ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) + @RequestParam("tenantIds") Set tenantUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List tenantIds = new ArrayList<>(); + for (UUID tenantIdUUID : tenantUUIDs) { + tenantIds.add(TenantId.fromUUID(tenantIdUUID)); + } + List tenants = tenantService.findTenantsByIds(tenantId, tenantIds); + return filterTenantsByReadPermission(tenants); + } + + private List filterTenantsByReadPermission(List tenants) { + return tenants.stream().filter(tenant -> { + try { + return accessControlService.hasPermission(getCurrentUser(), Resource.TENANT, Operation.READ, tenant.getId(), tenant); + } catch (ThingsboardException e) { + return false; + } + }).toList(); + } + } 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 7d2f84e827..c0f7b9eb40 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -23,13 +23,13 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; 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.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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityInfo; @@ -74,8 +74,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Get Tenant Profile (getTenantProfileById)", notes = "Fetch the Tenant Profile object based on the provided Tenant Profile Id. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantProfile/{tenantProfileId}") public TenantProfile getTenantProfileById( @Parameter(description = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { @@ -87,8 +86,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Get Tenant Profile Info (getTenantProfileInfoById)", notes = "Fetch the Tenant Profile Info object based on the provided Tenant Profile Id. " + TENANT_PROFILE_INFO_DESCRIPTION + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfileInfo/{tenantProfileId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantProfileInfo/{tenantProfileId}") public EntityInfo getTenantProfileInfoById( @Parameter(description = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { @@ -100,8 +98,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Get default Tenant Profile Info (getDefaultTenantProfileInfo)", notes = "Fetch the default Tenant Profile Info object based. " + TENANT_PROFILE_INFO_DESCRIPTION + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantProfileInfo/default") public EntityInfo getDefaultTenantProfileInfo() throws ThingsboardException { return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId())); } @@ -164,9 +161,17 @@ public class TenantProfileController extends BaseController { " \"warnThreshold\": 0,\n" + " \"maxCalculatedFieldsPerEntity\": 5,\n" + " \"maxArgumentsPerCF\": 10,\n" + + " \"minAllowedScheduledUpdateIntervalInSecForCF\": 10,\n" + + " \"maxRelationLevelPerCfArgument\": 2,\n" + + " \"maxRelatedEntitiesToReturnPerCfArgument\": 100,\n" + " \"maxDataPointsPerRollingArg\": 1000,\n" + " \"maxStateSizeInKBytes\": 32,\n" + - " \"maxSingleValueArgumentSizeInKBytes\": 2" + + " \"maxSingleValueArgumentSizeInKBytes\": 2," + + " \"minAllowedDeduplicationIntervalInSecForCF\": 10," + + " \"minAllowedAggregationIntervalInSecForCF\": 60," + + " \"intermediateAggregationIntervalInSecForCF\": 300," + + " \"cfReevaluationCheckInterval\": 60," + + " \"alarmsReevaluationInterval\": 60" + " }\n" + " },\n" + " \"default\": false\n" + @@ -175,8 +180,7 @@ public class TenantProfileController extends BaseController { "Remove 'id', from the request body example (below) to create new Tenant Profile entity." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfile", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/tenantProfile") public TenantProfile saveTenantProfile(@Parameter(description = "A JSON value representing the tenant profile.") @Valid @RequestBody TenantProfile tenantProfile) throws ThingsboardException { TenantProfile oldProfile; @@ -193,7 +197,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Delete Tenant Profile (deleteTenantProfile)", notes = "Deletes the tenant profile. Referencing non-existing tenant profile Id will cause an error. Referencing profile that is used by the tenants will cause an error. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/tenantProfile/{tenantProfileId}") @ResponseStatus(value = HttpStatus.OK) public void deleteTenantProfile(@Parameter(description = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { @@ -206,8 +210,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)", notes = "Makes specified tenant profile to be default. Referencing non-existing tenant profile Id will cause an error. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfile/{tenantProfileId}/default", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/tenantProfile/{tenantProfileId}/default") public TenantProfile setDefaultTenantProfile( @Parameter(description = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { @@ -220,8 +223,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Get Tenant Profiles (getTenantProfiles)", notes = "Returns a page of tenant profiles registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantProfiles", params = {"pageSize", "page"}) public PageData getTenantProfiles( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -240,8 +242,7 @@ public class TenantProfileController extends BaseController { @ApiOperation(value = "Get Tenant Profiles Info (getTenantProfileInfos)", notes = "Returns a page of tenant profile info objects registered in the platform. " + TENANT_PROFILE_INFO_DESCRIPTION + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}) public PageData getTenantProfileInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -264,5 +265,4 @@ public class TenantProfileController extends BaseController { return tenantProfileService.findTenantProfilesByIds(TenantId.SYS_TENANT_ID, ids); } - } diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java index 5ad64a9f7b..fddeabfeaa 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java @@ -56,7 +56,6 @@ public class TwoFactorAuthConfigController extends BaseController { private final TwoFaConfigManager twoFaConfigManager; private final TwoFactorAuthService twoFactorAuthService; - @ApiOperation(value = "Get account 2FA settings (getAccountTwoFaSettings)", notes = "Get user's account 2FA configuration. Configuration contains configs for different 2FA providers." + NEW_LINE + "Example:\n" + @@ -67,13 +66,12 @@ public class TwoFactorAuthConfigController extends BaseController { " }\n}\n```" + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) @GetMapping("/account/settings") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER', 'MFA_CONFIGURATION_TOKEN')") public AccountTwoFaSettings getAccountTwoFaSettings() throws ThingsboardException { SecurityUser user = getCurrentUser(); - return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user.getId()).orElse(null); + return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user).orElse(null); } - @ApiOperation(value = "Generate 2FA account config (generateTwoFaAccountConfig)", notes = "Generate new 2FA account config template for specified provider type. " + NEW_LINE + "For TOTP, this will return a corresponding account config template " + @@ -99,7 +97,7 @@ public class TwoFactorAuthConfigController extends BaseController { "Will throw an error (Bad Request) if the provider is not configured for usage. " + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PostMapping("/account/config/generate") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER', 'MFA_CONFIGURATION_TOKEN')") public TwoFaAccountConfig generateTwoFaAccountConfig(@Parameter(description = "2FA provider type to generate new account config for", schema = @Schema(defaultValue = "TOTP", requiredMode = Schema.RequiredMode.REQUIRED)) @RequestParam TwoFaProviderType providerType) throws Exception { SecurityUser user = getCurrentUser(); @@ -127,7 +125,7 @@ public class TwoFactorAuthConfigController extends BaseController { "or if the provider is not configured for usage. " + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PostMapping("/account/config/submit") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER', 'MFA_CONFIGURATION_TOKEN')") public void submitTwoFaAccountConfig(@Valid @RequestBody TwoFaAccountConfig accountConfig) throws Exception { SecurityUser user = getCurrentUser(); twoFactorAuthService.prepareVerificationCode(user, accountConfig, false); @@ -139,11 +137,11 @@ public class TwoFactorAuthConfigController extends BaseController { "Will throw an error (Bad Request) if the provider is not configured for usage. " + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PostMapping("/account/config") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER', 'MFA_CONFIGURATION_TOKEN')") public AccountTwoFaSettings verifyAndSaveTwoFaAccountConfig(@Valid @RequestBody TwoFaAccountConfig accountConfig, @RequestParam(required = false) String verificationCode) throws Exception { SecurityUser user = getCurrentUser(); - if (twoFaConfigManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), accountConfig.getProviderType()).isPresent()) { + if (twoFaConfigManager.getTwoFaAccountConfig(user.getTenantId(), user, accountConfig.getProviderType()).isPresent()) { throw new IllegalArgumentException("2FA provider is already configured"); } @@ -154,7 +152,7 @@ public class TwoFactorAuthConfigController extends BaseController { verificationSuccess = true; } if (verificationSuccess) { - return twoFaConfigManager.saveTwoFaAccountConfig(user.getTenantId(), user.getId(), accountConfig); + return twoFaConfigManager.saveTwoFaAccountConfig(user.getTenantId(), user, accountConfig); } else { throw new IllegalArgumentException("Verification code is incorrect"); } @@ -162,42 +160,41 @@ public class TwoFactorAuthConfigController extends BaseController { @ApiOperation(value = "Update 2FA account config (updateTwoFaAccountConfig)", notes = "Update config for a given provider type. \n" + - "Update request example:\n" + - "```\n{\n \"useByDefault\": true\n}\n```\n" + - "Returns whole account's 2FA settings object.\n" + - ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) + "Update request example:\n" + + "```\n{\n \"useByDefault\": true\n}\n```\n" + + "Returns whole account's 2FA settings object.\n" + + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PutMapping("/account/config") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") public AccountTwoFaSettings updateTwoFaAccountConfig(@RequestParam TwoFaProviderType providerType, @RequestBody TwoFaAccountConfigUpdateRequest updateRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); - TwoFaAccountConfig accountConfig = twoFaConfigManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType) + TwoFaAccountConfig accountConfig = twoFaConfigManager.getTwoFaAccountConfig(user.getTenantId(), user, providerType) .orElseThrow(() -> new IllegalArgumentException("Config for " + providerType + " 2FA provider not found")); accountConfig.setUseByDefault(updateRequest.isUseByDefault()); - return twoFaConfigManager.saveTwoFaAccountConfig(user.getTenantId(), user.getId(), accountConfig); + return twoFaConfigManager.saveTwoFaAccountConfig(user.getTenantId(), user, accountConfig); } @ApiOperation(value = "Delete 2FA account config (deleteTwoFaAccountConfig)", notes = "Delete 2FA config for a given 2FA provider type. \n" + - "Returns whole account's 2FA settings object.\n" + - ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) + "Returns whole account's 2FA settings object.\n" + + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER) @DeleteMapping("/account/config") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") public AccountTwoFaSettings deleteTwoFaAccountConfig(@RequestParam TwoFaProviderType providerType) throws ThingsboardException { SecurityUser user = getCurrentUser(); - return twoFaConfigManager.deleteTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType); + return twoFaConfigManager.deleteTwoFaAccountConfig(user.getTenantId(), user, providerType); } - @ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviders)", notes = "Get the list of provider types available for user to use (the ones configured by tenant or sysadmin).\n" + - "Example of response:\n" + - "```\n[\n \"TOTP\",\n \"EMAIL\",\n \"SMS\"\n]\n```" + - ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER + "Example of response:\n" + + "```\n[\n \"TOTP\",\n \"EMAIL\",\n \"SMS\"\n]\n```" + + ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER ) @GetMapping("/providers") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER', 'MFA_CONFIGURATION_TOKEN')") public List getAvailableTwoFaProviders() throws ThingsboardException { return twoFaConfigManager.getPlatformTwoFaSettings(getTenantId(), true) .map(PlatformTwoFaSettings::getProviders).orElse(Collections.emptyList()).stream() @@ -205,7 +202,6 @@ public class TwoFactorAuthConfigController extends BaseController { .collect(Collectors.toList()); } - @ApiOperation(value = "Get platform 2FA settings (getPlatformTwoFaSettings)", notes = "Get platform settings for 2FA. The settings are described for savePlatformTwoFaSettings API method. " + "If 2FA is not configured, then an empty response will be returned." + @@ -260,11 +256,10 @@ public class TwoFactorAuthConfigController extends BaseController { @PostMapping("/settings") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") public PlatformTwoFaSettings savePlatformTwoFaSettings(@Parameter(description = "Settings value", required = true) - @RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException { + @RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException { return twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings); } - @Data public static class TwoFaAccountConfigUpdateRequest { private boolean useByDefault; diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java index 4dd2da547f..e26932ff22 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java @@ -28,7 +28,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.StringUtils; 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.security.model.JwtPair; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; @@ -64,7 +63,6 @@ public class TwoFactorAuthController extends BaseController { private final SystemSecurityService systemSecurityService; private final UserService userService; - @ApiOperation(value = "Request 2FA verification code (requestTwoFaVerificationCode)", notes = "Request 2FA verification code." + NEW_LINE + "To make a request to this endpoint, you need an access token with the scope of PRE_VERIFICATION_TOKEN, " + @@ -92,30 +90,28 @@ public class TwoFactorAuthController extends BaseController { SecurityUser user = getCurrentUser(); boolean verificationSuccess = twoFactorAuthService.checkVerificationCode(user, providerType, verificationCode, true); if (verificationSuccess) { - systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, null); - user = new SecurityUser(userService.findUserById(user.getTenantId(), user.getId()), true, user.getUserPrincipal()); - return tokenFactory.createTokenPair(user); + logLogInAction(servletRequest, user, null); + return createTokenPair(user); } else { - ThingsboardException error = new ThingsboardException("Verification code is incorrect", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, error); + IllegalArgumentException error = new IllegalArgumentException("Verification code is incorrect"); + logLogInAction(servletRequest, user, error); throw error; } } - @ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviders)", notes = "Get the list of 2FA provider infos available for user to use. Example:\n" + - "```\n[\n" + - " {\n \"type\": \"EMAIL\",\n \"default\": true,\n \"contact\": \"ab*****ko@gmail.com\"\n },\n" + - " {\n \"type\": \"TOTP\",\n \"default\": false,\n \"contact\": null\n },\n" + - " {\n \"type\": \"SMS\",\n \"default\": false,\n \"contact\": \"+38********12\"\n }\n" + - "]\n```") + "```\n[\n" + + " {\n \"type\": \"EMAIL\",\n \"default\": true,\n \"contact\": \"ab*****ko@gmail.com\"\n },\n" + + " {\n \"type\": \"TOTP\",\n \"default\": false,\n \"contact\": null\n },\n" + + " {\n \"type\": \"SMS\",\n \"default\": false,\n \"contact\": \"+38********12\"\n }\n" + + "]\n```") @GetMapping("/providers") @PreAuthorize("hasAuthority('PRE_VERIFICATION_TOKEN')") public List getAvailableTwoFaProviders() throws ThingsboardException { SecurityUser user = getCurrentUser(); Optional platformTwoFaSettings = twoFaConfigManager.getPlatformTwoFaSettings(user.getTenantId(), true); - return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user.getId()) + return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user) .map(settings -> settings.getConfigs().values()).orElse(Collections.emptyList()) .stream().map(config -> { String contact = null; @@ -139,6 +135,32 @@ public class TwoFactorAuthController extends BaseController { .collect(Collectors.toList()); } + @ApiOperation(value = "Get regular token pair after successfully configuring 2FA", + notes = "Checks 2FA is configured, returning token pair on success.") + @PostMapping("/login") + @PreAuthorize("hasAuthority('MFA_CONFIGURATION_TOKEN')") + public JwtPair authenticateByTwoFaConfigurationToken(HttpServletRequest servletRequest) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + if (twoFactorAuthService.isTwoFaEnabled(user.getTenantId(), user)) { + logLogInAction(servletRequest, user, null); + return createTokenPair(user); + } else { + IllegalArgumentException error = new IllegalArgumentException("2FA is not configured"); + logLogInAction(servletRequest, user, error); + throw error; + } + } + + private JwtPair createTokenPair(SecurityUser user) { + log.debug("[{}][{}] Creating token pair for user", user.getTenantId(), user.getId()); + user = new SecurityUser(userService.findUserById(user.getTenantId(), user.getId()), true, user.getUserPrincipal()); + return tokenFactory.createTokenPair(user); + } + + private void logLogInAction(HttpServletRequest servletRequest, SecurityUser user, Exception error) { + systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, error); + } + @Data @AllArgsConstructor @Builder diff --git a/application/src/main/java/org/thingsboard/server/controller/UiSettingsController.java b/application/src/main/java/org/thingsboard/server/controller/UiSettingsController.java index fa7004de82..4c820478e9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UiSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UiSettingsController.java @@ -17,11 +17,9 @@ package org.thingsboard.server.controller; 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.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.exception.ThingsboardException; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -37,9 +35,8 @@ public class UiSettingsController extends BaseController { notes = "Get UI help base url used to fetch help assets. " + "The actual value of the base url is configurable in the system configuration file.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/uiSettings/helpBaseUrl", method = RequestMethod.GET) - @ResponseBody - public String getHelpBaseUrl() throws ThingsboardException { + @GetMapping(value = "/uiSettings/helpBaseUrl") + public String getHelpBaseUrl() { return helpBaseUrl; } diff --git a/application/src/main/java/org/thingsboard/server/controller/UsageInfoController.java b/application/src/main/java/org/thingsboard/server/controller/UsageInfoController.java index 14fff9c16c..ba09447388 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UsageInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UsageInfoController.java @@ -18,9 +18,8 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; 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.UsageInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -37,9 +36,9 @@ public class UsageInfoController extends BaseController { private UsageInfoService usageInfoService; @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/usage", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/usage") public UsageInfo getTenantUsageInfo() throws ThingsboardException { return checkNotNull(usageInfoService.getUsageInfo(getCurrentUser().getTenantId())); } + } 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 be0528734a..878a3287bc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -33,9 +34,7 @@ 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.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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; @@ -84,6 +83,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD; import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; @@ -133,8 +135,7 @@ public class UserController extends BaseController { "If the user has the authority of 'TENANT_ADMIN', the server checks that the requested user is owned by the same tenant. " + "If the user has the authority of 'CUSTOMER_USER', the server checks that the requested user is owned by the same customer.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/user/{userId}") public User getUserById( @Parameter(description = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { @@ -150,8 +151,7 @@ public class UserController extends BaseController { "If the user who performs the request has the authority of 'SYS_ADMIN', it is possible to login as any tenant administrator. " + "If the user who performs the request has the authority of 'TENANT_ADMIN', it is possible to login as any customer user. ") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/user/tokenAccessEnabled") public boolean isUserTokenAccessEnabled() { return userTokenAccessEnabled; } @@ -161,8 +161,7 @@ public class UserController extends BaseController { "If the user who performs the request has the authority of 'SYS_ADMIN', it is possible to get the token of any tenant administrator. " + "If the user who performs the request has the authority of 'TENANT_ADMIN', it is possible to get the token of any customer user that belongs to the same tenant. ") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/user/{userId}/token", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/user/{userId}/token") public JwtPair getUserToken( @Parameter(description = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { @@ -189,8 +188,7 @@ public class UserController extends BaseController { "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new User entity." + "\n\nAvailable for users with 'SYS_ADMIN', 'TENANT_ADMIN' or 'CUSTOMER_USER' authority.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/user", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/user") public User saveUser( @Parameter(description = "A JSON value representing the User.", required = true) @RequestBody User user, @@ -206,7 +204,7 @@ public class UserController extends BaseController { @ApiOperation(value = "Send or re-send the activation email", notes = "Force send the activation email to the user. Useful to resend the email if user has accidentally deleted it. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/user/sendActivationMail", method = RequestMethod.POST) + @PostMapping(value = "/user/sendActivationMail") @ResponseStatus(value = HttpStatus.OK) public void sendActivationEmail( @Parameter(description = "Email of the user", required = true) @@ -229,7 +227,6 @@ public class UserController extends BaseController { "The base url for activation link is configurable in the general settings of system administrator. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/user/{userId}/activationLink", produces = "text/plain") - @ResponseBody public String getActivationLink(@Parameter(description = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId, HttpServletRequest request) throws ThingsboardException { @@ -255,7 +252,7 @@ public class UserController extends BaseController { notes = "Deletes the User, it's credentials and all the relations (from and to the User). " + "Referencing non-existing User Id will cause an error. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/user/{userId}") @ResponseStatus(value = HttpStatus.OK) public void deleteUser( @Parameter(description = USER_ID_PARAM_DESCRIPTION) @@ -276,8 +273,7 @@ public class UserController extends BaseController { notes = "Returns a page of users owned by tenant or customer. The scope depends on authority of the user that performs the request." + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/users", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/users", params = {"pageSize", "page"}) public PageData getUsers( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -302,8 +298,7 @@ public class UserController extends BaseController { notes = "Returns page of user data objects. Search is been executed by email, firstName and " + "lastName fields. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/users/info", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/users/info") public PageData findUsersByQuery( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -339,8 +334,7 @@ public class UserController extends BaseController { @ApiOperation(value = "Get Tenant Users (getTenantAdmins)", notes = "Returns a page of users owned by tenant. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}) public PageData getTenantAdmins( @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TENANT_ID) String strTenantId, @@ -363,8 +357,7 @@ public class UserController extends BaseController { @ApiOperation(value = "Get Customer Users (getCustomerUsers)", notes = "Returns a page of users owned by customer. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"}) public PageData getCustomerUsers( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -389,8 +382,7 @@ public class UserController extends BaseController { @ApiOperation(value = "Enable/Disable User credentials (setUserCredentialsEnabled)", notes = "Enables or Disables user credentials. Useful when you would like to block user account without deleting it. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/user/{userId}/userCredentialsEnabled", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/user/{userId}/userCredentialsEnabled") public void setUserCredentialsEnabled( @Parameter(description = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId, @@ -398,7 +390,7 @@ public class UserController extends BaseController { @RequestParam(required = false, defaultValue = "true") boolean userCredentialsEnabled) throws ThingsboardException { checkParameter(USER_ID, strUserId); UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.WRITE); + checkUserId(userId, Operation.WRITE); TenantId tenantId = getCurrentUser().getTenantId(); userService.setUserCredentialsEnabled(tenantId, userId, userCredentialsEnabled); @@ -412,8 +404,7 @@ public class UserController extends BaseController { "Search is been executed by email, firstName and lastName fields. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}) public PageData getUsersForAssign( @Parameter(description = ALARM_ID_PARAM_DESCRIPTION, required = true) @PathVariable("alarmId") String strAlarmId, @@ -491,7 +482,7 @@ public class UserController extends BaseController { notes = "Delete user settings by specifying list of json element xpaths. \n " + "Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/user/settings/{paths}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/user/settings/{paths}") public void deleteUserSettings(@Parameter(description = PATHS) @PathVariable(PATHS) String paths) throws ThingsboardException { checkParameter(USER_ID, paths); @@ -531,7 +522,7 @@ public class UserController extends BaseController { notes = "Delete user settings by specifying list of json element xpaths. \n " + "Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/user/settings/{type}/{paths}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/user/settings/{type}/{paths}") public void deleteUserSettings(@Parameter(description = PATHS) @PathVariable(PATHS) String paths, @Parameter(description = "Settings type, case insensitive, one of: \"general\", \"quick_links\", \"doc_links\" or \"dashboards\".") @@ -555,8 +546,7 @@ public class UserController extends BaseController { @ApiOperation(value = "Report action of User over the dashboard (reportUserDashboardAction)", notes = "Report action of User over the dashboard. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/user/dashboards/{dashboardId}/{action}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/user/dashboards/{dashboardId}/{action}") public UserDashboardsInfo reportUserDashboardAction( @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DashboardController.DASHBOARD_ID) String strDashboardId, @@ -593,6 +583,32 @@ public class UserController extends BaseController { userService.removeMobileSession(user.getTenantId(), mobileToken); } + @ApiOperation(value = "Get Users By Ids (getUsersByIds)", + notes = "Requested users must be owned by tenant or assigned to customer which user is performing the request. ") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/users", params = {"userIds"}) + public List getUsersByIds( + @Parameter(description = "A list of user ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("userIds") Set userUUIDs) throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + List userIds = new ArrayList<>(); + for (UUID userUUID : userUUIDs) { + userIds.add(new UserId(userUUID)); + } + List users = userService.findUsersByTenantIdAndIds(tenantId, userIds); + return filterUsersByReadPermission(users); + } + + private List filterUsersByReadPermission(List users) { + return users.stream().filter(user -> { + try { + return accessControlService.hasPermission(getCurrentUser(), Resource.USER, Operation.READ, user.getId(), user); + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + } + private void checkNotReserved(String strType, UserSettingsType type) throws ThingsboardException { if (type.isReserved()) { throw new ThingsboardException("Settings with type: " + strType + " are reserved for internal use!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); 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 bbcf370158..a519d92e3a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -21,13 +21,13 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; 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.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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.StringUtils; @@ -111,8 +111,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypeInfo/{widgetTypeId}") public WidgetTypeInfo getWidgetTypeInfoById( @Parameter(description = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { @@ -132,8 +131,7 @@ public class WidgetTypeController extends AutoCommitController { "Remove 'id', 'tenantId' rom the request body example (below) to create new Widget Type entity." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/widgetType", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/widgetType") public WidgetTypeDetails saveWidgetType( @Parameter(description = "A JSON value representing the Widget Type Details.", required = true) @RequestBody WidgetTypeDetails widgetTypeDetails, @@ -153,7 +151,7 @@ public class WidgetTypeController extends AutoCommitController { @ApiOperation(value = "Delete widget type (deleteWidgetType)", notes = "Deletes the Widget Type. Referencing non-existing Widget Type Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/widgetType/{widgetTypeId}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/widgetType/{widgetTypeId}") @ResponseStatus(value = HttpStatus.OK) public void deleteWidgetType( @Parameter(description = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) @@ -168,8 +166,7 @@ public class WidgetTypeController extends AutoCommitController { 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 + @GetMapping(value = "/widgetTypes", params = {"pageSize", "page"}) public PageData getWidgetTypes( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -215,8 +212,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}) @Deprecated public List getBundleWidgetTypesByBundleAlias( @Parameter(description = "System or Tenant", required = true) @@ -236,8 +232,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypes", params = {"widgetsBundleId"}) public List getBundleWidgetTypes( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { @@ -248,8 +243,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"}) @Deprecated public List getBundleWidgetTypesDetailsByBundleAlias( @Parameter(description = "System or Tenant", required = true) @@ -269,8 +263,7 @@ public class WidgetTypeController extends AutoCommitController { @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypesDetails)", 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 + @GetMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}) public List getBundleWidgetTypesDetails( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId, @@ -291,8 +284,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypeFqns", params = {"widgetsBundleId"}) public List getBundleWidgetTypeFqns( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { @@ -303,8 +295,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypesInfos", params = {"isSystem", "bundleAlias"}) @Deprecated public List getBundleWidgetTypesInfosByBundleAlias( @Parameter(description = "System or Tenant", required = true) @@ -325,8 +316,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetTypesInfos", params = {"widgetsBundleId", "pageSize", "page"}) public PageData getBundleWidgetTypesInfos( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId, @@ -357,8 +347,7 @@ public class WidgetTypeController extends AutoCommitController { @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 + @GetMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}) @Deprecated public WidgetType getWidgetTypeByBundleAliasAndTypeAlias( @Parameter(description = "System or Tenant", required = true) @@ -382,8 +371,7 @@ public class WidgetTypeController extends AutoCommitController { @ApiOperation(value = "Get Widget Type (getWidgetType)", notes = "Get the Widget Type by FQN. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER, hidden = true) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/widgetType", params = {"fqn"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetType", params = {"fqn"}) public WidgetType getWidgetType( @Parameter(description = "Widget Type fqn", required = true) @RequestParam String fqn) throws ThingsboardException { 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 357c23259a..c2d612f014 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -16,16 +16,19 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +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.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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -48,10 +51,12 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.UUID; 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.NEW_LINE; 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; @@ -79,8 +84,7 @@ public class WidgetsBundleController extends BaseController { @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) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/widgetsBundle/{widgetsBundleId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetsBundle/{widgetsBundleId}") public WidgetsBundle getWidgetsBundleById( @Parameter(description = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetsBundleId") String strWidgetsBundleId, @@ -106,8 +110,7 @@ public class WidgetsBundleController extends BaseController { "Remove 'id', 'tenantId' from the request body example (below) to create new Widgets Bundle entity." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/widgetsBundle") public WidgetsBundle saveWidgetsBundle( @Parameter(description = "A JSON value representing the Widget Bundle.", required = true) @RequestBody WidgetsBundle widgetsBundle) throws Exception { @@ -126,7 +129,7 @@ public class WidgetsBundleController extends BaseController { @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) + @PostMapping(value = "/widgetsBundle/{widgetsBundleId}/widgetTypes") @ResponseStatus(value = HttpStatus.OK) public void updateWidgetsBundleWidgetTypes( @Parameter(description = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @@ -152,7 +155,7 @@ public class WidgetsBundleController extends BaseController { @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) + @PostMapping(value = "/widgetsBundle/{widgetsBundleId}/widgetTypeFqns") @ResponseStatus(value = HttpStatus.OK) public void updateWidgetsBundleWidgetFqns( @Parameter(description = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @@ -169,7 +172,7 @@ public class WidgetsBundleController extends BaseController { @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')") - @RequestMapping(value = "/widgetsBundle/{widgetsBundleId}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/widgetsBundle/{widgetsBundleId}") @ResponseStatus(value = HttpStatus.OK) public void deleteWidgetsBundle( @Parameter(description = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @@ -184,8 +187,7 @@ public class WidgetsBundleController extends BaseController { notes = "Returns a page of Widget Bundle objects available for current user. " + WIDGET_BUNDLE_DESCRIPTION + " " + PAGE_DATA_PARAMETERS + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/widgetsBundles", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetsBundles", params = {"pageSize", "page"}) public PageData getWidgetsBundles( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -223,8 +225,7 @@ public class WidgetsBundleController extends BaseController { @ApiOperation(value = "Get all Widget Bundles (getWidgetsBundles)", notes = "Returns an array of Widget Bundle objects that are available for current user." + WIDGET_BUNDLE_DESCRIPTION + " " + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/widgetsBundles", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetsBundles") public List getWidgetsBundles() throws ThingsboardException { if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); @@ -234,4 +235,19 @@ public class WidgetsBundleController extends BaseController { } } + @ApiOperation(value = "Get Widgets Bundles By Ids (getWidgetsBundlesByIds)", + notes = "Requested widgets bundles must be system level or owned by tenant of the user which is performing the request. " + + NEW_LINE) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/widgetsBundles", params = {"widgetsBundleIds"}) + public List getWidgetsBundlesByIds( + @Parameter(description = "A list of widgets bundle ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("widgetsBundleIds") Set widgetsBundleUUIDs) throws ThingsboardException { + List widgetsBundleIds = new ArrayList<>(); + for (UUID widgetsBundleUUID : widgetsBundleUUIDs) { + widgetsBundleIds.add(new WidgetsBundleId(widgetsBundleUUID)); + } + return widgetsBundleService.findSystemOrTenantWidgetsBundlesByIds(getTenantId(), widgetsBundleIds); + } + } diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardEntitiesLimitExceededResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardEntitiesLimitExceededResponse.java new file mode 100644 index 0000000000..268fae2b1f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardEntitiesLimitExceededResponse.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.v3.oas.annotations.media.Schema; +import org.springframework.http.HttpStatus; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; + +@Schema +public class ThingsboardEntitiesLimitExceededResponse extends ThingsboardErrorResponse { + + private final EntityType entityType; + + private final Long limit; + + protected ThingsboardEntitiesLimitExceededResponse(String message, EntityType entityType, Long limit) { + super(message, ThingsboardErrorCode.ENTITIES_LIMIT_EXCEEDED, HttpStatus.FORBIDDEN); + this.entityType = entityType; + this.limit = limit; + } + + public static ThingsboardEntitiesLimitExceededResponse of(final String message, final EntityType entityType, final Long limit) { + return new ThingsboardEntitiesLimitExceededResponse(message, entityType, limit); + } + + @Schema(description = "Entity type", accessMode = Schema.AccessMode.READ_ONLY) + public EntityType getEntityType() { + return entityType; + } + + @Schema(description = "Limit", accessMode = Schema.AccessMode.READ_ONLY) + public Long getLimit() { + return limit; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java index 96464668b6..28f08ae7ba 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java @@ -64,7 +64,8 @@ public class ThingsboardErrorResponse { "\n\n* `32` - Item not found (HTTP: 404 - Not Found)" + "\n\n* `33` - Too many requests (HTTP: 429 - Too Many Requests)" + "\n\n* `34` - Too many updates (Too many updates over Websocket session)" + - "\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)", + "\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)" + + "\n\n* `41` - Entities limit exceeded (HTTP: 403 - Forbidden)", example = "10", type = "integer", accessMode = Schema.AccessMode.READ_ONLY) public ThingsboardErrorCode getErrorCode() { 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 3dd15b7255..ce7ea1efdd 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -32,6 +32,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.LockedException; import org.springframework.security.core.AuthenticationException; @@ -46,6 +47,7 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import org.springframework.web.util.WebUtils; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.msg.tools.MaxPayloadSizeExceededException; @@ -94,6 +96,7 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS); errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_UPDATES, HttpStatus.TOO_MANY_REQUESTS); errorCodeToStatusMap.put(ThingsboardErrorCode.SUBSCRIPTION_VIOLATION, HttpStatus.FORBIDDEN); + errorCodeToStatusMap.put(ThingsboardErrorCode.ENTITIES_LIMIT_EXCEEDED, HttpStatus.FORBIDDEN); errorCodeToStatusMap.put(ThingsboardErrorCode.VERSION_CONFLICT, HttpStatus.CONFLICT); } @@ -137,23 +140,28 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand try { response.setContentType(MediaType.APPLICATION_JSON_VALUE); - if (exception instanceof ThingsboardException) { - ThingsboardException thingsboardException = (ThingsboardException) exception; + if (exception instanceof ThingsboardException thingsboardException) { if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) { - handleSubscriptionException((ThingsboardException) exception, response); + handleSubscriptionException(thingsboardException, response); } else if (thingsboardException.getErrorCode() == ThingsboardErrorCode.DATABASE) { handleDatabaseException(thingsboardException.getCause(), response); + } else if (thingsboardException.getErrorCode() == ThingsboardErrorCode.ENTITIES_LIMIT_EXCEEDED) { + if (thingsboardException.getCause() instanceof EntitiesLimitExceededException entitiesLimitExceededException) { + handleEntitiesLimitExceededException(entitiesLimitExceededException, response); + } else { + handleEntitiesLimitExceededException(thingsboardException, response); + } } else { - handleThingsboardException((ThingsboardException) exception, response); + handleThingsboardException(thingsboardException, response); } - } else if (exception instanceof TbRateLimitsException) { - handleRateLimitException(response, (TbRateLimitsException) exception); + } else if (exception instanceof TbRateLimitsException rateLimitsException) { + handleRateLimitException(response, rateLimitsException); } else if (exception instanceof AccessDeniedException) { handleAccessDeniedException(response); - } else if (exception instanceof AuthenticationException) { - handleAuthenticationException((AuthenticationException) exception, response); - } else if (exception instanceof MaxPayloadSizeExceededException) { - handleMaxPayloadSizeExceededException(response, (MaxPayloadSizeExceededException) exception); + } else if (exception instanceof AuthenticationException authenticationException) { + handleAuthenticationException(authenticationException, response); + } else if (exception instanceof MaxPayloadSizeExceededException maxPayloadSizeExceededException) { + handleMaxPayloadSizeExceededException(response, maxPayloadSizeExceededException); } else if (exception instanceof DataAccessException e) { handleDatabaseException(e, response); } else { @@ -218,6 +226,20 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand writeResponse(errorResponse, response); } + private void handleEntitiesLimitExceededException(ThingsboardException entitiesLimitExceededException, HttpServletResponse response) throws IOException { + response.setStatus(HttpStatus.FORBIDDEN.value()); + JacksonUtil.writeValue(response.getWriter(), + JacksonUtil.fromBytes(((HttpClientErrorException) entitiesLimitExceededException.getCause()).getResponseBodyAsByteArray(), Object.class)); + } + + private void handleEntitiesLimitExceededException(EntitiesLimitExceededException entitiesLimitExceededException, HttpServletResponse response) throws IOException { + EntityType entityType = entitiesLimitExceededException.getEntityType(); + Long limit = entitiesLimitExceededException.getLimit(); + response.setStatus(HttpStatus.FORBIDDEN.value()); + JacksonUtil.writeValue(response.getWriter(), + ThingsboardEntitiesLimitExceededResponse.of(entitiesLimitExceededException.getMessage(), entityType, limit)); + } + private void handleAccessDeniedException(HttpServletResponse response) throws IOException { response.setStatus(HttpStatus.FORBIDDEN.value()); JacksonUtil.writeValue(response.getWriter(), @@ -238,13 +260,13 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)); } else if (authenticationException instanceof AuthMethodNotSupportedException) { JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); - } else if (authenticationException instanceof UserPasswordExpiredException) { - UserPasswordExpiredException expiredException = (UserPasswordExpiredException) authenticationException; + } else if (authenticationException instanceof UserPasswordExpiredException expiredException) { String resetToken = expiredException.getResetToken(); JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken)); - } else if (authenticationException instanceof UserPasswordNotValidException) { - UserPasswordNotValidException expiredException = (UserPasswordNotValidException) authenticationException; + } else if (authenticationException instanceof UserPasswordNotValidException expiredException) { JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsViolationResponse.of(expiredException.getMessage())); + } else if (authenticationException instanceof CredentialsExpiredException credentialsExpiredException) { + JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsViolationResponse.of(credentialsExpiredException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); } else { JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java new file mode 100644 index 0000000000..8cc0043198 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java @@ -0,0 +1,565 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +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 com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.gson.JsonElement; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.function.TriConsumer; +import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest.Strategy; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; +import org.thingsboard.server.actors.calculatedField.MultipleTbCallback; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.AttributesImmediateOutputStrategy; +import org.thingsboard.server.common.data.cf.configuration.OutputStrategy; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesImmediateOutputStrategy; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +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.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationPathQuery; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntry; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.thingsboard.server.common.data.DataConstants.CF_NAME_METADATA_KEY; +import static org.thingsboard.server.common.data.DataConstants.SCOPE; +import static org.thingsboard.server.common.data.cf.CalculatedFieldType.PROPAGATION; +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED; +import static org.thingsboard.server.dao.util.KvUtils.filterChangedAttr; +import static org.thingsboard.server.dao.util.KvUtils.toTsKvEntryList; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultAttributeEntry; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultTsKvEntry; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformAggMetricArgument; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformAggregationArgument; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformSingleValueArgument; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformTsRollingArgument; + +@Data +@Slf4j +public abstract class AbstractCalculatedFieldProcessingService { + + protected final AttributesService attributesService; + protected final TimeseriesService timeseriesService; + protected final TelemetrySubscriptionService tsSubService; + protected final ApiLimitService apiLimitService; + protected final RelationService relationService; + protected final OwnerService ownerService; + protected final TbClusterService clusterService; + + protected ListeningExecutorService calculatedFieldCallbackExecutor; + + @PostConstruct + public void init() { + calculatedFieldCallbackExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool( + Math.max(4, Runtime.getRuntime().availableProcessors()), getExecutorNamePrefix())); + } + + @PreDestroy + public void stop() { + if (calculatedFieldCallbackExecutor != null) { + calculatedFieldCallbackExecutor.shutdownNow(); + } + } + + protected abstract String getExecutorNamePrefix(); + + protected ListenableFuture> fetchArguments(CalculatedFieldCtx ctx, EntityId entityId, long ts) { + Map> argFutures = switch (ctx.getCfType()) { + case GEOFENCING -> fetchGeofencingCalculatedFieldArguments(ctx, entityId, false, ts); + case SIMPLE, SCRIPT, ALARM, PROPAGATION -> getBaseCalculatedFieldArguments(ctx, entityId, ts); + case RELATED_ENTITIES_AGGREGATION -> fetchRelatedEntitiesAggArguments(ctx, entityId, ts); + case ENTITY_AGGREGATION -> fetchEntityAggArguments(ctx, entityId, ts); + }; + if (ctx.getCfType() == PROPAGATION) { + argFutures.put(PROPAGATION_CONFIG_ARGUMENT, fetchPropagationCalculatedFieldArgument(ctx, entityId)); + } + return Futures.whenAllComplete(argFutures.values()) + .call(() -> resolveArgumentFutures(argFutures), + MoreExecutors.directExecutor()); + } + + private Map> getBaseCalculatedFieldArguments(CalculatedFieldCtx ctx, EntityId entityId, long ts) { + Map> futures = new HashMap<>(); + for (var entry : ctx.getArguments().entrySet()) { + var argEntityId = resolveEntityId(ctx.getTenantId(), entityId, entry.getValue()); + var argValueFuture = fetchArgumentValue(ctx.getTenantId(), argEntityId, entry.getValue(), ts); + futures.put(entry.getKey(), argValueFuture); + } + return futures; + } + + protected EntityId resolveEntityId(TenantId tenantId, EntityId entityId, Argument argument) { + if (argument.getRefEntityId() != null) { + return argument.getRefEntityId(); + } + if (!argument.hasOwnerSource()) { + return entityId; + } + return resolveOwnerArgument(tenantId, entityId); + } + + protected Map resolveArgumentFutures(Map> argFutures) { + return argFutures.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, // Keep the key as is + entry -> resolveArgumentValue(entry.getKey(), entry.getValue()) + )); + } + + protected ArgumentEntry resolveArgumentValue(String key, ListenableFuture future) { + try { + return future.get(); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + throw new RuntimeException("Failed to fetch '" + key + "' argument: " + cause.getMessage(), cause); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to fetch '" + key + "' argument!", e); + } + } + + protected ListenableFuture fetchPropagationCalculatedFieldArgument(CalculatedFieldCtx ctx, EntityId entityId) { + ListenableFuture> propagationEntityIds = fromDynamicSource(ctx.getTenantId(), entityId, ctx.getPropagationArgument()); + return Futures.transform(propagationEntityIds, ArgumentEntry::createPropagationArgument, MoreExecutors.directExecutor()); + } + + protected Map> fetchGeofencingCalculatedFieldArguments(CalculatedFieldCtx ctx, EntityId entityId, boolean dynamicArgumentsOnly, long startTs) { + Map> argFutures = new HashMap<>(); + Set> entries = ctx.getArguments().entrySet(); + if (dynamicArgumentsOnly) { + entries = entries.stream() + .filter(entry -> entry.getValue().hasRelationQuerySource()) + .collect(Collectors.toSet()); + } + for (var entry : entries) { + switch (entry.getKey()) { + case ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY -> + argFutures.put(entry.getKey(), fetchArgumentValue(ctx.getTenantId(), entityId, entry.getValue(), startTs)); + default -> { + var resolvedEntityIdsFuture = resolveGeofencingEntityIds(ctx.getTenantId(), entityId, entry); + argFutures.put(entry.getKey(), Futures.transformAsync(resolvedEntityIdsFuture, resolvedEntityIds -> + fetchGeofencingArgumentValue(ctx.getTenantId(), resolvedEntityIds, entry.getValue(), startTs), MoreExecutors.directExecutor())); + } + } + } + return argFutures; + } + + private Map> fetchRelatedEntitiesAggArguments(CalculatedFieldCtx ctx, EntityId entityId, long ts) { + if (!(ctx.getCalculatedField().getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration config)) { + return Collections.emptyMap(); + } + ListenableFuture> relatedEntitiesFut = resolveRelatedEntities(ctx.getTenantId(), entityId, config.getRelation()); + + return config.getArguments().entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> Futures.transformAsync(relatedEntitiesFut, relatedEntities -> fetchRelatedEntitiesArgumentEntry(ctx.getTenantId(), relatedEntities, entry.getValue(), ts), MoreExecutors.directExecutor()) + )); + } + + private Map> fetchEntityAggArguments(CalculatedFieldCtx ctx, EntityId entityId, long ts) { + if (!(ctx.getCalculatedField().getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration config)) { + return Collections.emptyMap(); + } + return config.getArguments().entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> fetchTimeSeries(ctx.getTenantId(), entityId, entry.getValue(), config.getInterval(), ts) + )); + } + + protected ListenableFuture> resolveRelatedEntities(TenantId tenantId, EntityId entityId, RelationPathLevel relation) { + Predicate filter = entityRelation -> CalculatedField.isSupportedRefEntity(entityRelation.getFrom()) && CalculatedField.isSupportedRefEntity(entityRelation.getTo()); + ListenableFuture> relationsFut = relationService.findFilteredRelationsByPathQueryAsync(tenantId, new EntityRelationPathQuery(entityId, List.of(relation)), filter); + + return Futures.transform(relationsFut, relations -> { + if (relations == null) { + return Collections.emptyList(); + } + + return switch (relation.direction()) { + case FROM -> relations.stream().map(EntityRelation::getTo).toList(); + case TO -> relations.stream().map(EntityRelation::getFrom).toList(); + }; + }, calculatedFieldCallbackExecutor); + } + + private ListenableFuture> resolveGeofencingEntityIds(TenantId tenantId, EntityId entityId, Map.Entry entry) { + Argument value = entry.getValue(); + if (value.getRefEntityId() != null) { + return Futures.immediateFuture(List.of(value.getRefEntityId())); + } + if (!value.hasDynamicSource()) { + return Futures.immediateFuture(List.of(entityId)); + } + return fromDynamicSource(tenantId, entityId, value); + } + + private ListenableFuture> fromDynamicSource(TenantId tenantId, EntityId entityId, Argument value) { + var refDynamicSourceConfiguration = value.getRefDynamicSourceConfiguration(); + return switch (refDynamicSourceConfiguration.getType()) { + case CURRENT_OWNER -> Futures.immediateFuture(List.of(resolveOwnerArgument(tenantId, entityId))); + case RELATION_PATH_QUERY -> { + var configuration = (RelationPathQueryDynamicSourceConfiguration) refDynamicSourceConfiguration; + Predicate filter = entityRelation -> CalculatedField.isSupportedRefEntity(entityRelation.getFrom()) && CalculatedField.isSupportedRefEntity(entityRelation.getTo()); + yield Futures.transform(relationService.findFilteredRelationsByPathQueryAsync(tenantId, configuration.toRelationPathQuery(entityId), filter), + configuration::resolveEntityIds, calculatedFieldCallbackExecutor); + } + }; + } + + private EntityId resolveOwnerArgument(TenantId tenantId, EntityId entityId) { + return ownerService.getOwner(tenantId, entityId); + } + + private ListenableFuture fetchGeofencingArgumentValue(TenantId tenantId, List geofencingEntities, Argument argument, long startTs) { + if (argument.getRefEntityKey().getType() != ArgumentType.ATTRIBUTE) { + throw new IllegalStateException("Unsupported argument key type: " + argument.getRefEntityKey().getType()); + } + var geofencingEntityIdToKvEntryMapFutures = Futures.allAsList(fetchGeofencingEntityIdToKvEntriesFutures(tenantId, geofencingEntities, argument, startTs)); + return Futures.transform(geofencingEntityIdToKvEntryMapFutures, entries -> ArgumentEntry.createGeofencingValueArgument(entries.stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))), MoreExecutors.directExecutor()); + } + + private List>> fetchGeofencingEntityIdToKvEntriesFutures(TenantId tenantId, List geofencingEntities, Argument argument, long startTs) { + return geofencingEntities.stream() + .map(entityId -> { + AttributeScope scope = argument.getRefEntityKey().getScope(); + String key = argument.getRefEntityKey().getKey(); + var attributesFuture = attributesService.find(tenantId, entityId, scope, key); + return Futures.transform(attributesFuture, resultOpt -> + Map.entry(entityId, resultOpt.orElseGet(() -> createDefaultAttributeEntry(argument, startTs))), + calculatedFieldCallbackExecutor); + }).collect(Collectors.toList()); + } + + private ListenableFuture fetchRelatedEntitiesArgumentEntry(TenantId tenantId, List aggEntities, Argument argument, long startTs) { + List>> futures = aggEntities.stream() + .map(entityId -> { + ListenableFuture argumentEntryFut = fetchArgumentValue(tenantId, entityId, argument, startTs); + return Futures.transform(argumentEntryFut, argumentEntry -> Map.entry(entityId, ArgumentEntry.createSingleValueArgument(entityId, argumentEntry)), MoreExecutors.directExecutor()); + }) + .toList(); + + ListenableFuture>> allFutures = Futures.allAsList(futures); + + return Futures.transform(allFutures, + entries -> ArgumentEntry.createAggArgument( + entries.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + ), + MoreExecutors.directExecutor()); + } + + protected ListenableFuture fetchArgumentValue(TenantId tenantId, EntityId entityId, Argument argument, long startTs) { + return switch (argument.getRefEntityKey().getType()) { + case TS_ROLLING -> fetchTsRolling(tenantId, entityId, argument, startTs); + case ATTRIBUTE -> fetchAttribute(tenantId, entityId, argument, startTs); + case TS_LATEST -> fetchTsLatest(tenantId, entityId, argument, startTs); + }; + } + + protected ArgumentEntry fetchMetricDuringInterval(TenantId tenantId, EntityId entityId, String argKey, AggMetric metric, AggIntervalEntry interval) { + AggFunction function = metric.getFunction(); + long intervalMs = interval.getEndTs() - interval.getStartTs(); + BaseReadTsKvQuery query = new BaseReadTsKvQuery(argKey, interval.getStartTs(), interval.getEndTs(), intervalMs, 1, Aggregation.valueOf(function.name())); + ListenableFuture argumentEntryFut = fetchTimeSeriesInternal(tenantId, entityId, query, timeSeries -> transformAggMetricArgument(timeSeries, argKey, metric)); + return resolveArgumentValue(argKey, argumentEntryFut); + } + + private ListenableFuture fetchTimeSeries(TenantId tenantId, EntityId entityId, Argument argument, AggInterval interval, long queryEndTs) { + long intervalStartTs = interval.getCurrentIntervalStartTs(); + long intervalEndTs = interval.getCurrentIntervalEndTs(); + ReadTsKvQuery query = new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), intervalStartTs, queryEndTs, 0, 1, Aggregation.NONE); + return fetchTimeSeriesInternal(tenantId, entityId, query, timeSeries -> transformAggregationArgument(timeSeries, intervalStartTs, intervalEndTs)); + } + + private ListenableFuture fetchTsRolling(TenantId tenantId, EntityId entityId, Argument argument, long queryEndTs) { + long argTimeWindow = argument.getTimeWindow() == 0 ? queryEndTs : argument.getTimeWindow(); + long startInterval = queryEndTs - argTimeWindow; + ReadTsKvQuery query = buildTsRollingQuery(tenantId, argument, startInterval, queryEndTs); + return fetchTimeSeriesInternal(tenantId, entityId, query, tsRolling -> transformTsRollingArgument(tsRolling, query.getLimit(), argTimeWindow)); + } + + private ListenableFuture fetchAttribute(TenantId tenantId, EntityId entityId, Argument argument, long defaultLastUpdateTs) { + log.trace("[{}][{}] Fetching attribute for key {}", tenantId, entityId, argument.getRefEntityKey()); + var attributeOptFuture = attributesService.find(tenantId, entityId, argument.getRefEntityKey().getScope(), argument.getRefEntityKey().getKey()); + + return Futures.transform(attributeOptFuture, attrOpt -> { + log.debug("[{}][{}] Fetched attribute for key {}: {}", tenantId, entityId, argument.getRefEntityKey(), attrOpt); + return transformSingleValueArgument(attrOpt.orElseGet(() -> createDefaultAttributeEntry(argument, defaultLastUpdateTs))); + }, calculatedFieldCallbackExecutor); + } + + private ListenableFuture fetchTsLatest(TenantId tenantId, EntityId entityId, Argument argument, long defaultTs) { + String timeseriesKey = argument.getRefEntityKey().getKey(); + log.trace("[{}][{}] Fetching latest timeseries {}", tenantId, entityId, timeseriesKey); + return Futures.transform(timeseriesService.findLatest(tenantId, entityId, timeseriesKey), result -> { + log.debug("[{}][{}] Fetched latest timeseries {}: {}", tenantId, entityId, timeseriesKey, result); + return transformSingleValueArgument(result.orElseGet(() -> createDefaultTsKvEntry(argument, defaultTs))); + }, calculatedFieldCallbackExecutor); + } + + private ListenableFuture fetchTimeSeriesInternal(TenantId tenantId, EntityId entityId, ReadTsKvQuery query, Function, ArgumentEntry> transformArgument) { + log.trace("[{}][{}] Fetching timeseries for query {}", tenantId, entityId, query); + ListenableFuture> tsRollingFuture = timeseriesService.findAll(tenantId, entityId, List.of(query)); + return Futures.transform(tsRollingFuture, tsRolling -> { + log.debug("[{}][{}] Fetched {} timeseries for query {}", tenantId, entityId, tsRolling == null ? 0 : tsRolling.size(), query); + return transformArgument.apply(tsRolling); + }, calculatedFieldCallbackExecutor); + } + + private ReadTsKvQuery buildTsRollingQuery(TenantId tenantId, Argument argument, long startTs, long endTs) { + long maxDataPoints = apiLimitService.getLimit( + tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); + int argumentLimit = argument.getLimit(); + int limit = argumentLimit == 0 || argumentLimit > maxDataPoints ? (int) maxDataPoints : argumentLimit; + return new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), startTs, endTs, 0, limit, Aggregation.NONE); + } + + protected void handlePropagationResults(PropagationCalculatedFieldResult propagationResult, TbCallback callback, + TriConsumer telemetryResultHandler) { + List propagationEntityIds = propagationResult.getEntityIds(); + if (propagationEntityIds.isEmpty()) { + callback.onSuccess(); + return; + } + if (propagationEntityIds.size() == 1) { + EntityId propagationEntityId = propagationEntityIds.get(0); + telemetryResultHandler.accept(propagationEntityId, propagationResult.getResult(), callback); + return; + } + MultipleTbCallback multipleTbCallback = new MultipleTbCallback(propagationEntityIds.size(), callback); + for (var propagationEntityId : propagationEntityIds) { + telemetryResultHandler.accept(propagationEntityId, propagationResult.getResult(), multipleTbCallback); + } + } + + protected void sendMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbCallback callback, TbMsg msg) { + try { + clusterService.pushMsgToRuleEngine(tenantId, entityId, msg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + log.trace("[{}][{}] Pushed message to rule engine: {} ", tenantId, entityId, msg); + callback.onSuccess(); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push message to rule engine: {}", tenantId, entityId, msg, e); + callback.onFailure(e); + } + } + + protected void saveTelemetryResult(TenantId tenantId, EntityId entityId, String cfName, TelemetryCalculatedFieldResult cfResult, List cfIds, TbCallback callback) { + OutputType type = cfResult.getType(); + JsonElement jsonResult = cfResult.toJsonElement(); + + log.trace("[{}][{}] Saving CF result: {}", tenantId, entityId, jsonResult); + switch (type) { + case ATTRIBUTES -> saveAttributes(tenantId, entityId, jsonResult, cfResult.getOutputStrategy(), cfResult.getScope(), cfName, cfIds, callback); + case TIME_SERIES -> saveTimeSeries(tenantId, entityId, jsonResult, cfResult.getOutputStrategy(), cfIds, System.currentTimeMillis(), callback); + } + } + + private void saveAttributes(TenantId tenantId, EntityId entityId, JsonElement jsonResult, OutputStrategy outputStrategy, AttributeScope scope, String cfName, List cfIds, TbCallback callback) { + if (!(outputStrategy instanceof AttributesImmediateOutputStrategy attOutputStrategy)) { + callback.onFailure(new IllegalArgumentException("Only AttributeImmediateOutputStrategy is supported.")); + } else { + AttributesSaveRequest.Strategy strategy = new Strategy(attOutputStrategy.isSaveAttribute(), attOutputStrategy.isSendWsUpdate(), attOutputStrategy.isProcessCfs()); + List newAttributes = JsonConverter.convertToAttributes(jsonResult); + + if (!attOutputStrategy.isUpdateAttributesOnlyOnValueChange()) { + saveAttributesInternal(tenantId, entityId, scope, cfName, cfIds, newAttributes, strategy, attOutputStrategy.isSendAttributesUpdatedNotification(), callback); + return; + } + + List keys = newAttributes.stream().map(KvEntry::getKey).collect(Collectors.toList()); + ListenableFuture> findFuture = attributesService.find(tenantId, entityId, scope, keys); + + DonAsynchron.withCallback(findFuture, + existingAttributes -> { + List changed = filterChangedAttr(existingAttributes, newAttributes); + if (changed.isEmpty()) { + callback.onSuccess(); + return; + } + saveAttributesInternal(tenantId, entityId, scope, cfName, cfIds, changed, strategy, attOutputStrategy.isSendAttributesUpdatedNotification(), callback); + }, + callback::onFailure, + MoreExecutors.directExecutor()); + } + } + + private void saveAttributesInternal(TenantId tenantId, EntityId entityId, + AttributeScope scope, + String cfName, + List cfIds, + List entries, + AttributesSaveRequest.Strategy strategy, + boolean sendAttributesUpdatedNotification, + TbCallback callback) { + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope) + .entries(entries) + .strategy(strategy) + .previousCalculatedFieldIds(cfIds) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + if (sendAttributesUpdatedNotification) { + sendAttributesUpdatedMsg(tenantId, entityId, scope, cfName, entries); + } + callback.onSuccess(); + log.debug("[{}][{}] Saved CF result: {}", tenantId, entityId, entries); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + log.error("[{}][{}] Failed to save CF result {}", tenantId, entityId, entries, t); + } + }) + .build()); + } + + private void saveTimeSeries(TenantId tenantId, EntityId entityId, JsonElement jsonResult, OutputStrategy outputStrategy, List cfIds, long ts, TbCallback callback) { + if (!(outputStrategy instanceof TimeSeriesImmediateOutputStrategy tsOutputStrategy)) { + callback.onFailure(new IllegalArgumentException("Only TimeSeriesImmediateOutputStrategy is supported.")); + } else { + TimeseriesSaveRequest.Strategy strategy = new TimeseriesSaveRequest.Strategy(tsOutputStrategy.isSaveTimeSeries(), tsOutputStrategy.isSaveLatest(), tsOutputStrategy.isSendWsUpdate(), tsOutputStrategy.isProcessCfs()); + saveTimeSeriesInternal(tenantId, entityId, jsonResult, tsOutputStrategy.getTtl(), cfIds, ts, strategy, callback); + } + } + + private void saveTimeSeriesInternal(TenantId tenantId, EntityId entityId, JsonElement jsonResult, Long ttl, List cfIds, long ts, TimeseriesSaveRequest.Strategy strategy, TbCallback callback) { + Map> tsKvMap = JsonConverter.convertToTelemetry(jsonResult, ts); + if (tsKvMap.isEmpty()) { + callback.onSuccess(); + return; + } + List tsEntries = toTsKvEntryList(tsKvMap); + TimeseriesSaveRequest.Builder builder = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .entries(tsEntries) + .strategy(strategy) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + callback.onSuccess(); + log.debug("[{}][{}] Saved CF result: {}", tenantId, entityId, tsEntries); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + log.error("[{}][{}] Failed to save CF result {}", tenantId, entityId, tsEntries, t); + } + }); + if (ttl != null) { + builder.ttl(ttl); + } + if (cfIds != null && !cfIds.isEmpty()) { + builder.previousCalculatedFieldIds(cfIds); + } + tsSubService.saveTimeseries(builder.build()); + } + + private void sendAttributesUpdatedMsg(TenantId tenantId, EntityId entityId, + AttributeScope scope, + String cfName, + List entries) { + ObjectNode entityNode = JacksonUtil.newObjectNode(); + if (entries != null) { + entries.forEach(attributeKvEntry -> JacksonUtil.addKvEntry(entityNode, attributeKvEntry)); + } + + TbMsg attributesUpdatedMsg = TbMsg.newMsg() + .type(ATTRIBUTES_UPDATED) + .originator(entityId) + .data(JacksonUtil.toString(entityNode)) + .metaData(new TbMsgMetaData(Map.of( + CF_NAME_METADATA_KEY, cfName, + SCOPE, scope.name() + ))) + .build(); + + sendMsgToRuleEngine(tenantId, entityId, TbCallback.EMPTY, attributesUpdatedMsg); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldStateService.java b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldStateService.java index a94c3c2261..4bd162ea91 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldStateService.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.service.cf; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.calculatedField.CalculatedFieldStateRestoreMsg; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.exception.TenantNotFoundException; +import org.thingsboard.server.common.msg.CalculatedFieldStatePartitionRestoreMsg; +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.exception.CalculatedFieldStateException; @@ -37,6 +42,7 @@ import java.util.stream.Collectors; import static org.thingsboard.server.utils.CalculatedFieldUtils.fromProto; import static org.thingsboard.server.utils.CalculatedFieldUtils.toProto; +@Slf4j public abstract class AbstractCalculatedFieldStateService implements CalculatedFieldStateService { @Autowired @@ -56,25 +62,44 @@ public abstract class AbstractCalculatedFieldStateService implements CalculatedF protected abstract void doPersist(CalculatedFieldEntityCtxId stateId, CalculatedFieldStateProto stateMsgProto, TbCallback callback); @Override - public final void removeState(CalculatedFieldEntityCtxId stateId, TbCallback callback) { + public final void deleteState(CalculatedFieldEntityCtxId stateId, TbCallback callback) { doRemove(stateId, callback); } protected abstract void doRemove(CalculatedFieldEntityCtxId stateId, TbCallback callback); - protected void processRestoredState(CalculatedFieldStateProto stateMsg, TbCallback callback) { + protected void processRestoredState(CalculatedFieldStateProto stateMsg, TopicPartitionInfo partition, TbCallback callback) { var id = fromProto(stateMsg.getId()); - var state = fromProto(stateMsg); - processRestoredState(id, state, callback); + if (partition == null) { + try { + partition = actorSystemContext.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, id.tenantId(), id.entityId()); + } catch (TenantNotFoundException e) { + log.debug("Skipping CF state msg for non-existing tenant {}", id.tenantId()); + return; + } + } + var state = fromProto(id, stateMsg); + processRestoredState(id, state, partition, callback); } - protected void processRestoredState(CalculatedFieldEntityCtxId id, CalculatedFieldState state, TbCallback callback) { - actorSystemContext.tell(new CalculatedFieldStateRestoreMsg(id, state, callback)); + protected void processRestoredState(CalculatedFieldEntityCtxId id, CalculatedFieldState state, TopicPartitionInfo partition, TbCallback callback) { + partition = partition.withTopic(DataConstants.CF_STATES_QUEUE_NAME); + actorSystemContext.tellWithHighPriority(new CalculatedFieldStateRestoreMsg(id, state, partition, callback)); } @Override public void restore(QueueKey queueKey, Set partitions) { - stateService.update(queueKey, partitions, null); + stateService.update(queueKey, partitions, new QueueStateService.RestoreCallback() { + @Override + public void onAllPartitionsRestored() { + } + + @Override + public void onPartitionRestored(TopicPartitionInfo partition) { + partition = partition.withTopic(DataConstants.CF_STATES_QUEUE_NAME); + actorSystemContext.tellWithHighPriority(new CalculatedFieldStatePartitionRestoreMsg(partition)); + } + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/cf/AlarmCalculatedFieldResult.java b/application/src/main/java/org/thingsboard/server/service/cf/AlarmCalculatedFieldResult.java new file mode 100644 index 0000000000..5130979a51 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/AlarmCalculatedFieldResult.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +import lombok.Builder; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.action.TbAlarmResult; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +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.util.List; + +@Data +@Builder +@RequiredArgsConstructor +public class AlarmCalculatedFieldResult implements CalculatedFieldResult { + + private final TbAlarmResult alarmResult; + + @Override + public TbMsg toTbMsg(EntityId entityId, String cfName, List cfIds) { + TbMsgType msgType; + TbMsgMetaData metaData = new TbMsgMetaData(); + if (alarmResult.isCreated()) { + msgType = TbMsgType.ALARM_CREATED; + metaData.putValue(DataConstants.IS_NEW_ALARM, Boolean.TRUE.toString()); + } else if (alarmResult.isUpdated()) { + msgType = TbMsgType.ALARM_UPDATED; + metaData.putValue(DataConstants.IS_EXISTING_ALARM, Boolean.TRUE.toString()); + } else if (alarmResult.isSeverityUpdated()) { + msgType = TbMsgType.ALARM_SEVERITY_UPDATED; + metaData.putValue(DataConstants.IS_EXISTING_ALARM, Boolean.TRUE.toString()); + metaData.putValue(DataConstants.IS_SEVERITY_UPDATED_ALARM, Boolean.TRUE.toString()); + } else { + msgType = TbMsgType.ALARM_CLEAR; + metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString()); + } + if (alarmResult.getConditionRepeats() != null) { + metaData.putValue(DataConstants.ALARM_CONDITION_REPEATS, String.valueOf(alarmResult.getConditionRepeats())); + } + if (alarmResult.getConditionDuration() != null) { + metaData.putValue(DataConstants.ALARM_CONDITION_DURATION, String.valueOf(alarmResult.getConditionDuration())); + } + + return TbMsg.newMsg() + .type(msgType) + .originator(entityId) + .data(JacksonUtil.toString(alarmResult.getAlarm())) + .metaData(metaData) + .build(); + } + + @Override + public String stringValue() { + return alarmResult != null ? JacksonUtil.toString(alarmResult) : null; + } + + @Override + public boolean isEmpty() { + return alarmResult == null; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldCache.java b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldCache.java index 720a1b48b0..75da5c5d7a 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldCache.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldCache.java @@ -17,12 +17,17 @@ package org.thingsboard.server.service.cf; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldLink; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.service.cf.ctx.state.CalculatedFieldCtx; import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; public interface CalculatedFieldCache { @@ -36,10 +41,28 @@ public interface CalculatedFieldCache { List getCalculatedFieldCtxsByEntityId(EntityId entityId); + Stream getCalculatedFieldCtxsByType(CalculatedFieldType cfType); + + boolean hasCalculatedFields(TenantId tenantId, EntityId entityId, Predicate filter); + void addCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId); void updateCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId); void evict(CalculatedFieldId calculatedFieldId); + void handleTenantProfileUpdate(TenantProfileId tenantProfileId); + + EntityId getProfileId(TenantId tenantId, EntityId entityId); + + Set getDynamicEntities(TenantId tenantId, EntityId entityId); + + void updateOwnerEntity(TenantId tenantId, EntityId entityId); + + void addOwnerEntity(TenantId tenantId, EntityId entityId); + + void evictEntity(EntityId entityId); + + void evictOwner(EntityId owner); + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldProcessingService.java index b0678875d6..1f264d118e 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldProcessingService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.cf; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.actors.calculatedField.CalculatedFieldTelemetryMsg; import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -25,18 +26,28 @@ import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; -import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; import java.util.List; import java.util.Map; +import java.util.Optional; public interface CalculatedFieldProcessingService { - ListenableFuture fetchStateFromDb(CalculatedFieldCtx ctx, EntityId entityId); + ListenableFuture> fetchArguments(CalculatedFieldCtx ctx, EntityId entityId); + + Map fetchDynamicArgsFromDb(CalculatedFieldCtx ctx, EntityId entityId); + + Optional fetchPropagationArgumentFromDb(CalculatedFieldCtx ctx, EntityId entityId); + + List fetchRelatedEntities(CalculatedFieldCtx ctx, EntityId entityId); Map fetchArgsFromDb(TenantId tenantId, EntityId entityId, Map arguments); - void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, CalculatedFieldResult calculationResult, List cfIds, TbCallback callback); + void processResult(TenantId tenantId, EntityId entityId, String cfName, CalculatedFieldResult result, List cfIds, TbCallback callback); + + ArgumentEntry fetchMetricDuringInterval(TenantId tenantId, EntityId entityId, String argKey, AggMetric metric, AggIntervalEntry interval); void pushMsgToLinks(CalculatedFieldTelemetryMsg msg, List linkedCalculatedFields, TbCallback callback); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java index f48d4737be..48d6f5a6b7 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java @@ -15,23 +15,25 @@ */ package org.thingsboard.server.service.cf; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.Data; -import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.data.cf.configuration.OutputType; - -@Data -public final class CalculatedFieldResult { - - private final OutputType type; - private final AttributeScope scope; - private final JsonNode result; - - public boolean isEmpty() { - return result == null || result.isMissingNode() || result.isNull() || - (result.isObject() && result.isEmpty()) || - (result.isArray() && result.isEmpty()) || - (result.isTextual() && result.asText().isEmpty()); +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.msg.TbMsg; + +import java.util.List; +import java.util.Objects; + +public interface CalculatedFieldResult { + + TbMsg toTbMsg(EntityId entityId, String cfName, List cfIds); + + String stringValue(); + + boolean isEmpty(); + + default JsonElement toJsonElement() { + return JsonParser.parseString(Objects.requireNonNull(stringValue())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldStateService.java b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldStateService.java index abddca594d..d084b05bdf 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldStateService.java @@ -33,7 +33,7 @@ public interface CalculatedFieldStateService { void persistState(CalculatedFieldEntityCtxId stateId, CalculatedFieldState state, TbCallback callback) throws CalculatedFieldStateException; - void removeState(CalculatedFieldEntityCtxId stateId, TbCallback callback); + void deleteState(CalculatedFieldEntityCtxId stateId, TbCallback callback); void restore(QueueKey queueKey, Set partitions); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java index 1eba2c4549..ea55c2b60d 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java @@ -19,28 +19,41 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; 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.springframework.util.ConcurrentReferenceHashMap; -import org.thingsboard.script.api.tbel.TbelInvokeService; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldLink; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CalculatedFieldId; +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.page.PageDataIterable; import org.thingsboard.server.dao.cf.CalculatedFieldService; -import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.profile.TbAssetProfileCache; +import org.thingsboard.server.service.profile.TbDeviceProfileCache; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Predicate; +import java.util.stream.Stream; @Service @Slf4j @@ -50,8 +63,12 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { private final ConcurrentReferenceHashMap calculatedFieldFetchLocks = new ConcurrentReferenceHashMap<>(); private final CalculatedFieldService calculatedFieldService; - private final TbelInvokeService tbelInvokeService; - private final ApiLimitService apiLimitService; + private final TbAssetProfileCache assetProfileCache; + private final TbDeviceProfileCache deviceProfileCache; + private final TbTenantProfileCache tenantProfileCache; + @Lazy + private final ActorSystemContext systemContext; + private final OwnerService ownerService; private final ConcurrentMap calculatedFields = new ConcurrentHashMap<>(); private final ConcurrentMap> entityIdCalculatedFields = new ConcurrentHashMap<>(); @@ -59,6 +76,8 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { private final ConcurrentMap> entityIdCalculatedFieldLinks = new ConcurrentHashMap<>(); private final ConcurrentMap calculatedFieldsCtx = new ConcurrentHashMap<>(); + private final ConcurrentMap> ownerEntities = new ConcurrentHashMap<>(); + @Value("${queue.calculated_fields.init_fetch_pack_size:50000}") @Getter private int initFetchPackSize; @@ -69,19 +88,17 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { cfs.forEach(cf -> { if (cf != null) { calculatedFields.putIfAbsent(cf.getId(), cf); + List links = cf.getConfiguration().buildCalculatedFieldLinks(cf.getTenantId(), cf.getEntityId(), cf.getId()); + calculatedFieldLinks.put(cf.getId(), new CopyOnWriteArrayList<>(links)); } }); calculatedFields.values().forEach(cf -> { entityIdCalculatedFields.computeIfAbsent(cf.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(cf); }); - PageDataIterable cfls = new PageDataIterable<>(calculatedFieldService::findAllCalculatedFieldLinks, initFetchPackSize); - cfls.forEach(link -> { - calculatedFieldLinks.computeIfAbsent(link.getCalculatedFieldId(), id -> new CopyOnWriteArrayList<>()).add(link); - }); calculatedFieldLinks.values().stream() .flatMap(List::stream) .forEach(link -> - entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(link) + entityIdCalculatedFieldLinks.computeIfAbsent(link.entityId(), id -> new CopyOnWriteArrayList<>()).add(link) ); } @@ -111,7 +128,7 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { if (ctx == null) { CalculatedField calculatedField = getCalculatedField(calculatedFieldId); if (calculatedField != null) { - ctx = new CalculatedFieldCtx(calculatedField, tbelInvokeService, apiLimitService); + ctx = new CalculatedFieldCtx(calculatedField, systemContext); calculatedFieldsCtx.put(calculatedFieldId, ctx); log.debug("[{}] Put calculated field ctx into cache: {}", calculatedFieldId, ctx); } @@ -134,6 +151,38 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { .toList(); } + @Override + public Stream getCalculatedFieldCtxsByType(CalculatedFieldType cfType) { + return calculatedFields.values().stream() + .filter(cf -> cfType.equals(cf.getType())) + .map(cf -> getCalculatedFieldCtx(cf.getId())); + } + + @Override + public boolean hasCalculatedFields(TenantId tenantId, EntityId entityId, Predicate filter) { + List entityCfs = getCalculatedFieldCtxsByEntityId(entityId); + for (CalculatedFieldCtx ctx : entityCfs) { + if (filter.test(ctx)) { + return true; + } + } + + return hasCalculatedFieldsByProfile(tenantId, entityId, filter); + } + + public boolean hasCalculatedFieldsByProfile(TenantId tenantId, EntityId entityId, Predicate filter) { + EntityId profileId = getProfileId(tenantId, entityId); + if (profileId != null) { + List profileCfs = getCalculatedFieldCtxsByEntityId(profileId); + for (CalculatedFieldCtx ctx : profileCfs) { + if (filter.test(ctx)) { + return true; + } + } + } + return false; + } + @Override public void addCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId) { Lock lock = getFetchLock(calculatedFieldId); @@ -179,10 +228,68 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { log.debug("[{}] evict calculated field links from cache: {}", calculatedFieldId, oldCalculatedField); calculatedFieldsCtx.remove(calculatedFieldId); log.debug("[{}] evict calculated field ctx from cache: {}", calculatedFieldId, oldCalculatedField); - entityIdCalculatedFieldLinks.forEach((entityId, calculatedFieldLinks) -> calculatedFieldLinks.removeIf(link -> link.getCalculatedFieldId().equals(calculatedFieldId))); + entityIdCalculatedFieldLinks.forEach((entityId, calculatedFieldLinks) -> calculatedFieldLinks.removeIf(link -> link.calculatedFieldId().equals(calculatedFieldId))); log.debug("[{}] evict calculated field links from cached links by entity id: {}", calculatedFieldId, oldCalculatedField); } + @Override + public void handleTenantProfileUpdate(TenantProfileId tenantProfileId) { + calculatedFieldsCtx.values().stream() + .filter(ctx -> { + TenantProfile tenantProfile = tenantProfileCache.get(ctx.getTenantId()); + return tenantProfile != null && tenantProfileId.equals(tenantProfile.getId()); + }) + .forEach(CalculatedFieldCtx::setTenantProfileProperties); + } + + @Override + public EntityId getProfileId(TenantId tenantId, EntityId entityId) { + HasId profile = switch (entityId.getEntityType()) { + case ASSET -> assetProfileCache.get(tenantId, (AssetId) entityId); + case DEVICE -> deviceProfileCache.get(tenantId, (DeviceId) entityId); + default -> null; + }; + return profile != null ? profile.getId() : null; + } + + @Override + public Set getDynamicEntities(TenantId tenantId, EntityId entityId) { + if (entityId != null && entityId.getEntityType().isOneOf(EntityType.CUSTOMER, EntityType.TENANT)) { + return getOwnedEntities(tenantId, entityId); + } + return Collections.emptySet(); + } + + @Override + public void addOwnerEntity(TenantId tenantId, EntityId entityId) { + EntityId owner = ownerService.getOwner(tenantId, entityId); + getOwnedEntities(tenantId, owner).add(entityId); + } + + @Override + public void updateOwnerEntity(TenantId tenantId, EntityId entityId) { + evictEntity(entityId); + addOwnerEntity(tenantId, entityId); + } + + @Override + public void evictEntity(EntityId entityId) { + ownerEntities.values().forEach(entities -> entities.remove(entityId)); + } + + @Override + public void evictOwner(EntityId owner) { + ownerEntities.remove(owner); + } + + private Set getOwnedEntities(TenantId tenantId, EntityId ownerId) { + return ownerEntities.computeIfAbsent(ownerId, owner -> { + Set entities = ConcurrentHashMap.newKeySet(); + entities.addAll(ownerService.getOwnedEntities(tenantId, ownerId)); + return entities; + }); + } + private Lock getFetchLock(CalculatedFieldId id) { return calculatedFieldFetchLocks.computeIfAbsent(id, __ -> new ReentrantLock()); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java index c0cb4d0bab..7082c1d58a 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java @@ -15,38 +15,26 @@ */ package org.thingsboard.server.service.cf; -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 jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.actors.calculatedField.CalculatedFieldTelemetryMsg; import org.thingsboard.server.actors.calculatedField.MultipleTbCallback; 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.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; -import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.ReadTsKvQuery; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.msg.TbMsgType; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -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.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldLinkedTelemetryMsgProto; @@ -61,123 +49,133 @@ import org.thingsboard.server.queue.util.TbRuleEngineComponent; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; -import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; -import org.thingsboard.server.service.cf.ctx.state.TsRollingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; -import static org.thingsboard.server.common.data.DataConstants.SCOPE; -import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultAttributeEntry; -import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultTsKvEntry; -import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createStateByType; -import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformSingleValueArgument; +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; import static org.thingsboard.server.utils.CalculatedFieldUtils.toProto; @TbRuleEngineComponent @Service @Slf4j -@RequiredArgsConstructor -public class DefaultCalculatedFieldProcessingService implements CalculatedFieldProcessingService { +public class DefaultCalculatedFieldProcessingService extends AbstractCalculatedFieldProcessingService implements CalculatedFieldProcessingService { - private final AttributesService attributesService; - private final TimeseriesService timeseriesService; - private final TbClusterService clusterService; - private final ApiLimitService apiLimitService; private final PartitionService partitionService; - private ListeningExecutorService calculatedFieldCallbackExecutor; + public DefaultCalculatedFieldProcessingService(AttributesService attributesService, + TimeseriesService timeseriesService, + ApiLimitService apiLimitService, + RelationService relationService, + OwnerService ownerService, + TbClusterService clusterService, + TelemetrySubscriptionService tsSubService, + PartitionService partitionService) { + super(attributesService, timeseriesService, tsSubService, apiLimitService, relationService, ownerService, clusterService); + this.partitionService = partitionService; + } + + @Override + protected String getExecutorNamePrefix() { + return "calculated-field-callback"; + } - @PostConstruct - public void init() { - calculatedFieldCallbackExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool( - Math.max(4, Runtime.getRuntime().availableProcessors()), "calculated-field-callback")); + @Override + public ListenableFuture> fetchArguments(CalculatedFieldCtx ctx, EntityId entityId) { + return super.fetchArguments(ctx, entityId, System.currentTimeMillis()); + } + + @Override + public Map fetchDynamicArgsFromDb(CalculatedFieldCtx ctx, EntityId entityId) { + return ctx.getCfType() == CalculatedFieldType.GEOFENCING ? + resolveArgumentFutures(fetchGeofencingCalculatedFieldArguments(ctx, entityId, true, System.currentTimeMillis())) : + Collections.emptyMap(); } - @PreDestroy - public void stop() { - if (calculatedFieldCallbackExecutor != null) { - calculatedFieldCallbackExecutor.shutdownNow(); + @Override + public Optional fetchPropagationArgumentFromDb(CalculatedFieldCtx ctx, EntityId entityId) { + if (ctx.getCfType() != CalculatedFieldType.PROPAGATION) { + return Optional.empty(); } + return Optional.of((PropagationArgumentEntry) + resolveArgumentValue(PROPAGATION_CONFIG_ARGUMENT, fetchPropagationCalculatedFieldArgument(ctx, entityId))); } @Override - public ListenableFuture fetchStateFromDb(CalculatedFieldCtx ctx, EntityId entityId) { - Map> argFutures = new HashMap<>(); - for (var entry : ctx.getArguments().entrySet()) { - var argEntityId = entry.getValue().getRefEntityId() != null ? entry.getValue().getRefEntityId() : entityId; - var argValueFuture = fetchKvEntry(ctx.getTenantId(), argEntityId, entry.getValue()); - argFutures.put(entry.getKey(), argValueFuture); + public List fetchRelatedEntities(CalculatedFieldCtx ctx, EntityId entityId) { + try { + if (ctx.getCalculatedField().getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration config) { + return resolveRelatedEntities(ctx.getTenantId(), entityId, config.getRelation()).get(); + } + return Collections.emptyList(); + } catch (ExecutionException | InterruptedException e) { + Throwable cause = e.getCause(); + throw new RuntimeException("Failed to fetch related entities for entity [" + entityId + "]: " + cause.getMessage(), cause); } - return Futures.whenAllComplete(argFutures.values()).call(() -> { - var result = createStateByType(ctx); - result.updateState(ctx, argFutures.entrySet().stream() - .collect(Collectors.toMap( - Entry::getKey, // Keep the key as is - entry -> { - try { - // Resolve the future to get the value - return entry.getValue().get(); - } catch (ExecutionException | InterruptedException e) { - throw new RuntimeException("Error getting future result for key: " + entry.getKey(), e); - } - } - ))); - return result; - }, calculatedFieldCallbackExecutor); } @Override public Map fetchArgsFromDb(TenantId tenantId, EntityId entityId, Map arguments) { Map> argFutures = new HashMap<>(); for (var entry : arguments.entrySet()) { - var argEntityId = entry.getValue().getRefEntityId() != null ? entry.getValue().getRefEntityId() : entityId; - var argValueFuture = fetchKvEntry(tenantId, argEntityId, entry.getValue()); + if (entry.getValue().hasRelationQuerySource()) { + continue; + } + var argEntityId = resolveEntityId(tenantId, entityId, entry.getValue()); + var argValueFuture = fetchArgumentValue(tenantId, argEntityId, entry.getValue(), System.currentTimeMillis()); argFutures.put(entry.getKey(), argValueFuture); } - return argFutures.entrySet().stream() - .collect(Collectors.toMap( - Entry::getKey, // Keep the key as is - entry -> { - try { - // Resolve the future to get the value - return entry.getValue().get(); - } catch (ExecutionException | InterruptedException e) { - throw new RuntimeException("Error getting future result for key: " + entry.getKey(), e); - } - } - )); + return resolveArgumentFutures(argFutures); } @Override - public void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, CalculatedFieldResult calculatedFieldResult, List cfIds, TbCallback callback) { - try { - OutputType type = calculatedFieldResult.getType(); - TbMsgType msgType = OutputType.ATTRIBUTES.equals(type) ? TbMsgType.POST_ATTRIBUTES_REQUEST : TbMsgType.POST_TELEMETRY_REQUEST; - TbMsgMetaData md = OutputType.ATTRIBUTES.equals(type) ? new TbMsgMetaData(Map.of(SCOPE, calculatedFieldResult.getScope().name())) : TbMsgMetaData.EMPTY; - TbMsg msg = TbMsg.newMsg().type(msgType).originator(entityId).previousCalculatedFieldIds(cfIds).metaData(md).data(calculatedFieldResult.getResult().toString()).build(); - clusterService.pushMsgToRuleEngine(tenantId, entityId, msg, new TbQueueCallback() { - @Override - public void onSuccess(TbQueueMsgMetadata metadata) { - callback.onSuccess(); - log.trace("[{}][{}] Pushed message to rule engine: {} ", tenantId, entityId, msg); - } + public ArgumentEntry fetchMetricDuringInterval(TenantId tenantId, EntityId entityId, String argKey, AggMetric metric, AggIntervalEntry interval) { + return super.fetchMetricDuringInterval(tenantId, entityId, argKey, metric, interval); + } - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }); - } catch (Exception e) { - log.warn("[{}][{}] Failed to push message to rule engine. CalculatedFieldResult: {}", tenantId, entityId, calculatedFieldResult, e); - callback.onFailure(e); + public void processResult(TenantId tenantId, EntityId entityId, String cfName, CalculatedFieldResult result, List cfIds, TbCallback callback) { + if (result instanceof AlarmCalculatedFieldResult) { + sendMsgToRuleEngine(tenantId, entityId, callback, result.toTbMsg(entityId, cfName, cfIds)); + return; } + TelemetryCalculatedFieldResult telemetryResult = result instanceof TelemetryCalculatedFieldResult telemetryRes + ? telemetryRes : ((PropagationCalculatedFieldResult) result).getResult(); + switch (telemetryResult.getOutputStrategy().getType()) { + case IMMEDIATE -> processImmediately(tenantId, entityId, cfName, result, cfIds, callback); + case RULE_CHAIN -> pushMsgToRuleEngine(tenantId, entityId, cfName, result, cfIds, callback); + } + } + + private void processImmediately(TenantId tenantId, EntityId entityId, String cfName, CalculatedFieldResult result, List cfIds, TbCallback callback) { + if (result instanceof TelemetryCalculatedFieldResult telemetryResult) { + saveTelemetryResult(tenantId, entityId, cfName, telemetryResult, cfIds, callback); + return; + } + if (result instanceof PropagationCalculatedFieldResult propagationResult) { + handlePropagationResults(propagationResult, callback, + (entity, res, cb) -> saveTelemetryResult(tenantId, entity, cfName, res, cfIds, cb)); + return; + } + callback.onSuccess(); + } + + private void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, String cfName, CalculatedFieldResult result, List cfIds, TbCallback callback) { + if (result instanceof PropagationCalculatedFieldResult propagationResult) { + handlePropagationResults(propagationResult, callback, + (entity, res, cb) -> sendMsgToRuleEngine(tenantId, entity, cb, res.toTbMsg(entity, cfName, cfIds))); + return; + } + + sendMsgToRuleEngine(tenantId, entityId, callback, result.toTbMsg(entityId, cfName, cfIds)); } @Override @@ -233,34 +231,6 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP return builder.build(); } - private ListenableFuture fetchKvEntry(TenantId tenantId, EntityId entityId, Argument argument) { - return switch (argument.getRefEntityKey().getType()) { - case TS_ROLLING -> fetchTsRolling(tenantId, entityId, argument); - case ATTRIBUTE -> Futures.transform( - attributesService.find(tenantId, entityId, argument.getRefEntityKey().getScope(), argument.getRefEntityKey().getKey()), - result -> transformSingleValueArgument(result.orElseGet(() -> createDefaultAttributeEntry(argument, System.currentTimeMillis()))), - calculatedFieldCallbackExecutor); - case TS_LATEST -> Futures.transform( - timeseriesService.findLatest(tenantId, entityId, argument.getRefEntityKey().getKey()), - result -> transformSingleValueArgument(result.orElseGet(() -> createDefaultTsKvEntry(argument, System.currentTimeMillis()))), - calculatedFieldCallbackExecutor); - }; - } - - private ListenableFuture fetchTsRolling(TenantId tenantId, EntityId entityId, Argument argument) { - long currentTime = System.currentTimeMillis(); - long timeWindow = argument.getTimeWindow() == 0 ? System.currentTimeMillis() : argument.getTimeWindow(); - long startTs = currentTime - timeWindow; - long maxDataPoints = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); - int argumentLimit = argument.getLimit(); - int limit = argumentLimit == 0 || argumentLimit > maxDataPoints ? (int) maxDataPoints : argument.getLimit(); - - ReadTsKvQuery query = new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), startTs, currentTime, 0, limit, Aggregation.NONE); - ListenableFuture> tsRollingFuture = timeseriesService.findAll(tenantId, entityId, List.of(query)); - - return Futures.transform(tsRollingFuture, tsRolling -> tsRolling == null ? new TsRollingArgumentEntry(limit, timeWindow) : ArgumentEntry.createTsRollingArgument(tsRolling, limit, timeWindow), calculatedFieldCallbackExecutor); - } - private static class TbCallbackWrapper implements TbQueueCallback { private final TbCallback callback; @@ -277,6 +247,7 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP public void onFailure(Throwable t) { callback.onFailure(t); } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java index a816eb104c..da79e41e5f 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java @@ -25,10 +25,11 @@ import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.CalculatedFieldId; -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; @@ -36,7 +37,12 @@ import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; 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.EntityRelationPathQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import org.thingsboard.server.common.util.ProtoUtils; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.gen.transport.TransportProtos.AttributeScopeProto; import org.thingsboard.server.gen.transport.TransportProtos.AttributeValueProto; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto; @@ -45,13 +51,9 @@ import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; -import org.thingsboard.server.service.profile.TbAssetProfileCache; -import org.thingsboard.server.service.profile.TbDeviceProfileCache; import java.util.Collections; -import java.util.EnumSet; import java.util.List; -import java.util.Set; import java.util.UUID; import java.util.function.Predicate; import java.util.function.Supplier; @@ -74,14 +76,9 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS } }; - private final TbAssetProfileCache assetProfileCache; - private final TbDeviceProfileCache deviceProfileCache; private final CalculatedFieldCache calculatedFieldCache; private final TbClusterService clusterService; - - private static final Set supportedReferencedEntities = EnumSet.of( - EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.TENANT - ); + private final RelationService relationService; @Override public void pushRequestToQueue(TimeseriesSaveRequest request, TimeseriesSaveResult result, FutureCallback callback) { @@ -91,6 +88,8 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS checkEntityAndPushToQueue(tenantId, entityId, cf -> cf.matches(entries), cf -> cf.linkMatches(entityId, entries), + cf -> cf.dynamicSourceMatches(request.getEntries()), + cf -> cf.relatedEntityMatches(entries), () -> toCalculatedFieldTelemetryMsgProto(request, result), callback); } @@ -108,6 +107,8 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS checkEntityAndPushToQueue(tenantId, entityId, cf -> cf.matches(entries, scope), cf -> cf.linkMatches(entityId, entries, scope), + cf -> cf.dynamicSourceMatches(request.getEntries(), request.getScope()), + cf -> cf.relatedEntityMatches(entries, scope), () -> toCalculatedFieldTelemetryMsgProto(request, result), callback); } @@ -124,6 +125,8 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS checkEntityAndPushToQueue(tenantId, entityId, cf -> cf.matchesKeys(result, scope), cf -> cf.linkMatchesAttrKeys(entityId, result, scope), + cf -> cf.matchesDynamicSourceKeys(result, request.getScope()), + cf -> cf.matchesRelatedEntityKeys(result, scope), () -> toCalculatedFieldTelemetryMsgProto(request, result), callback); } @@ -134,16 +137,21 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS checkEntityAndPushToQueue(tenantId, entityId, cf -> cf.matchesKeys(result), cf -> cf.linkMatchesTsKeys(entityId, result), + cf -> cf.matchesDynamicSourceKeys(result), + cf -> cf.matchesRelatedEntityKeys(result), () -> toCalculatedFieldTelemetryMsgProto(request, result), callback); } private void checkEntityAndPushToQueue(TenantId tenantId, EntityId entityId, - Predicate mainEntityFilter, Predicate linkedEntityFilter, + Predicate mainEntityFilter, + Predicate linkedEntityFilter, + Predicate dynamicSourceFilter, + Predicate relatedEntityFilter, Supplier msg, FutureCallback callback) { if (EntityType.TENANT.equals(entityId.getEntityType())) { tenantId = (TenantId) entityId; } - boolean send = checkEntityForCalculatedFields(tenantId, entityId, mainEntityFilter, linkedEntityFilter); + boolean send = checkEntityForCalculatedFields(tenantId, entityId, mainEntityFilter, linkedEntityFilter, dynamicSourceFilter, relatedEntityFilter); if (send) { ToCalculatedFieldMsg calculatedFieldMsg = msg.get(); clusterService.pushMsgToCalculatedFields(tenantId, entityId, calculatedFieldMsg, wrap(callback)); @@ -154,44 +162,63 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS } } - private boolean checkEntityForCalculatedFields(TenantId tenantId, EntityId entityId, Predicate filter, Predicate linkedEntityFilter) { - if (!supportedReferencedEntities.contains(entityId.getEntityType())) { + private boolean checkEntityForCalculatedFields(TenantId tenantId, EntityId entityId, Predicate filter, Predicate linkedEntityFilter, Predicate dynamicSourceFilter, Predicate relatedEntityFilter) { + if (!CalculatedField.isSupportedRefEntity(entityId)) { return false; } - List entityCfs = calculatedFieldCache.getCalculatedFieldCtxsByEntityId(entityId); - for (CalculatedFieldCtx ctx : entityCfs) { - if (filter.test(ctx)) { - return true; - } - } - EntityId profileId = getProfileId(tenantId, entityId); - if (profileId != null) { - List profileCfs = calculatedFieldCache.getCalculatedFieldCtxsByEntityId(profileId); - for (CalculatedFieldCtx ctx : profileCfs) { - if (filter.test(ctx)) { - return true; - } - } + if (calculatedFieldCache.hasCalculatedFields(tenantId, entityId, filter)) { + return true; } List links = calculatedFieldCache.getCalculatedFieldLinksByEntityId(entityId); for (CalculatedFieldLink link : links) { - CalculatedFieldCtx ctx = calculatedFieldCache.getCalculatedFieldCtx(link.getCalculatedFieldId()); + CalculatedFieldCtx ctx = calculatedFieldCache.getCalculatedFieldCtx(link.calculatedFieldId()); if (ctx != null && linkedEntityFilter.test(ctx)) { return true; } } - return false; - } + for (EntityId dynamicEntity : calculatedFieldCache.getDynamicEntities(tenantId, entityId)) { + if (calculatedFieldCache.getCalculatedFieldCtxsByEntityId(dynamicEntity).stream().anyMatch(dynamicSourceFilter)) { + return true; + } + EntityId dynamicEntityProfileId = calculatedFieldCache.getProfileId(tenantId, dynamicEntity); + if (calculatedFieldCache.getCalculatedFieldCtxsByEntityId(dynamicEntityProfileId).stream().anyMatch(dynamicSourceFilter)) { + return true; + } + } + + boolean hasMatchesEntityAggCfs = calculatedFieldCache.getCalculatedFieldCtxsByType(CalculatedFieldType.ENTITY_AGGREGATION).anyMatch(filter); + if (hasMatchesEntityAggCfs) { + return true; + } + + List relatedEntitiesAggregationCfs = calculatedFieldCache.getCalculatedFieldCtxsByType(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) + .filter(relatedEntityFilter) + .toList(); + for (CalculatedFieldCtx cfCtx : relatedEntitiesAggregationCfs) { + if (cfCtx.getCalculatedField().getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration aggConfig) { + RelationPathLevel relation = aggConfig.getRelation(); + EntitySearchDirection inverseDirection = switch (relation.direction()) { + case FROM -> EntitySearchDirection.TO; + case TO -> EntitySearchDirection.FROM; + }; + RelationPathLevel inverseRelation = new RelationPathLevel(inverseDirection, relation.relationType()); + List byRelationPathQuery = relationService.findByRelationPathQuery(tenantId, new EntityRelationPathQuery(entityId, List.of(inverseRelation))); + if (!byRelationPathQuery.isEmpty()) { + EntityId cfEntityId = cfCtx.getEntityId(); + for (EntityRelation entityRelation : byRelationPathQuery) { + EntityId relatedId = (inverseDirection == EntitySearchDirection.FROM) ? entityRelation.getTo() : entityRelation.getFrom(); + if (cfEntityId.equals(relatedId) || cfEntityId.equals(calculatedFieldCache.getProfileId(tenantId, relatedId))) { + return true; + } + } + } + } + } - private EntityId getProfileId(TenantId tenantId, EntityId entityId) { - return switch (entityId.getEntityType()) { - case ASSET -> assetProfileCache.get(tenantId, (AssetId) entityId).getId(); - case DEVICE -> deviceProfileCache.get(tenantId, (DeviceId) entityId).getId(); - default -> null; - }; + return false; } private ToCalculatedFieldMsg toCalculatedFieldTelemetryMsgProto(TimeseriesSaveRequest request, TimeseriesSaveResult result) { @@ -305,6 +332,7 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS public void onFailure(Throwable t) { callback.onFailure(t); } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/OwnerService.java b/application/src/main/java/org/thingsboard/server/service/cf/OwnerService.java new file mode 100644 index 0000000000..5cd913531b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/OwnerService.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.DeviceInfo; +import org.thingsboard.server.common.data.DeviceInfoFilter; +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.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.page.PageDataIterable; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; + +import java.util.HashSet; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class OwnerService { + + private final DeviceService deviceService; + private final AssetService assetService; + private final CustomerService customerService; + + public EntityId getOwner(TenantId tenantId, EntityId entityId) { + return switch (entityId.getEntityType()) { + case DEVICE -> deviceService.findDeviceById(tenantId, (DeviceId) entityId).getOwnerId(); + case ASSET -> assetService.findAssetById(tenantId, (AssetId) entityId).getOwnerId(); + case CUSTOMER -> tenantId; + default -> throw new UnsupportedOperationException(); + }; + } + + public Set getOwnedEntities(TenantId tenantId, EntityId ownerId) { + Set ownedEntities = new HashSet<>(); + if (EntityType.CUSTOMER.equals(ownerId.getEntityType())) { + PageDataIterable deviceIdInfos = new PageDataIterable<>(pageLink -> deviceService.findDeviceInfosByFilter(DeviceInfoFilter.builder().tenantId(tenantId).customerId((CustomerId) ownerId).build(), pageLink), 1000); + deviceIdInfos.forEach(deviceInfo -> ownedEntities.add(deviceInfo.getId())); + + PageDataIterable assets = new PageDataIterable<>(pageLink -> assetService.findAssetsByTenantIdAndCustomerId(tenantId, (CustomerId) ownerId, pageLink), 1000); + assets.forEach(asset -> ownedEntities.add(asset.getId())); + } else if (EntityType.TENANT.equals(ownerId.getEntityType())) { + PageDataIterable deviceIdInfos = new PageDataIterable<>(pageLink -> deviceService.findDeviceInfosByFilter(DeviceInfoFilter.builder().tenantId((TenantId) ownerId).customerId(new CustomerId(CustomerId.NULL_UUID)).build(), pageLink), 1000); + deviceIdInfos.forEach(deviceInfo -> ownedEntities.add(deviceInfo.getId())); + + PageDataIterable assets = new PageDataIterable<>(pageLink -> assetService.findAssetsByTenantIdAndCustomerId((TenantId) ownerId, new CustomerId(CustomerId.NULL_UUID), pageLink), 1000); + assets.forEach(asset -> ownedEntities.add(asset.getId())); + + PageDataIterable customers = new PageDataIterable<>(pageLink -> customerService.findCustomersByTenantId((TenantId) ownerId, pageLink), 1000); + customers.forEach(customer -> ownedEntities.add(customer.getId())); + } + return ownedEntities; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/PropagationCalculatedFieldResult.java b/application/src/main/java/org/thingsboard/server/service/cf/PropagationCalculatedFieldResult.java new file mode 100644 index 0000000000..2beaf999b6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/PropagationCalculatedFieldResult.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.util.CollectionsUtil; +import org.thingsboard.server.common.msg.TbMsg; + +import java.util.List; + +@Data +@Builder +public final class PropagationCalculatedFieldResult implements CalculatedFieldResult { + + private final List entityIds; + private final TelemetryCalculatedFieldResult result; + + @Override + public TbMsg toTbMsg(EntityId entityId, String cfName, List cfIds) { + return result.toTbMsg(entityId, cfName, cfIds); + } + + @Override + public String stringValue() { + return result.stringValue(); + } + + @Override + public boolean isEmpty() { + return CollectionsUtil.isEmpty(entityIds) || result.isEmpty(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/TelemetryCalculatedFieldResult.java b/application/src/main/java/org/thingsboard/server/service/cf/TelemetryCalculatedFieldResult.java new file mode 100644 index 0000000000..497921d834 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/TelemetryCalculatedFieldResult.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.cf.configuration.OutputStrategy; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +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.util.List; + +import static org.thingsboard.server.common.data.DataConstants.CF_NAME_METADATA_KEY; +import static org.thingsboard.server.common.data.DataConstants.SCOPE; + +@Data +@Builder +public final class TelemetryCalculatedFieldResult implements CalculatedFieldResult { + + private final OutputType type; + private final AttributeScope scope; + private final OutputStrategy outputStrategy; + private final JsonNode result; + + public static final TelemetryCalculatedFieldResult EMPTY = TelemetryCalculatedFieldResult.builder().result(null).build(); + + @Override + public TbMsg toTbMsg(EntityId entityId, String cfName, List cfIds) { + TbMsgType msgType = switch (type) { + case ATTRIBUTES -> TbMsgType.POST_ATTRIBUTES_REQUEST; + case TIME_SERIES -> TbMsgType.POST_TELEMETRY_REQUEST; + }; + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue(CF_NAME_METADATA_KEY, cfName); + if (OutputType.ATTRIBUTES == type) { + metaData.putValue(SCOPE, scope.name()); + } + return TbMsg.newMsg() + .type(msgType) + .originator(entityId) + .previousCalculatedFieldIds(cfIds) + .data(stringValue()) + .metaData(metaData) + .build(); + } + + @Override + public String stringValue() { + return result == null ? null : result.toString(); + } + + @Override + public boolean isEmpty() { + return result == null || result.isMissingNode() || result.isNull() || + (result.isObject() && result.isEmpty()) || + (result.isArray() && result.isEmpty()) || + (result.isTextual() && result.asText().isEmpty()); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java index 5fe1fee31e..492f004c89 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java @@ -18,11 +18,19 @@ package org.thingsboard.server.service.cf.ctx.state; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; import java.util.List; +import java.util.Map; @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -31,7 +39,11 @@ import java.util.List; ) @JsonSubTypes({ @JsonSubTypes.Type(value = SingleValueArgumentEntry.class, name = "SINGLE_VALUE"), - @JsonSubTypes.Type(value = TsRollingArgumentEntry.class, name = "TS_ROLLING") + @JsonSubTypes.Type(value = TsRollingArgumentEntry.class, name = "TS_ROLLING"), + @JsonSubTypes.Type(value = GeofencingArgumentEntry.class, name = "GEOFENCING"), + @JsonSubTypes.Type(value = PropagationArgumentEntry.class, name = "PROPAGATION"), + @JsonSubTypes.Type(value = RelatedEntitiesArgumentEntry.class, name = "RELATED_ENTITIES"), + @JsonSubTypes.Type(value = EntityAggregationArgumentEntry.class, name = "ENTITY_AGGREGATION") }) public interface ArgumentEntry { @@ -40,10 +52,14 @@ public interface ArgumentEntry { Object getValue(); - boolean updateEntry(ArgumentEntry entry); + boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx); boolean isEmpty(); + default JsonNode jsonValue() { + return JacksonUtil.valueToTree(toTbelCfArg()); + } + TbelCfArg toTbelCfArg(); boolean isForceResetPrevious(); @@ -54,8 +70,24 @@ public interface ArgumentEntry { return new SingleValueArgumentEntry(kvEntry); } + static ArgumentEntry createSingleValueArgument(EntityId entityId, ArgumentEntry argumentEntry) { + return new SingleValueArgumentEntry(entityId, argumentEntry); + } + static ArgumentEntry createTsRollingArgument(List kvEntries, int limit, long timeWindow) { return new TsRollingArgumentEntry(kvEntries, limit, timeWindow); } + static ArgumentEntry createGeofencingValueArgument(Map entityIdkvEntryMap) { + return new GeofencingArgumentEntry(entityIdkvEntryMap); + } + + static ArgumentEntry createPropagationArgument(List entityIds) { + return new PropagationArgumentEntry(entityIds); + } + + static ArgumentEntry createAggArgument(Map entityIdkvEntryMap) { + return new RelatedEntitiesArgumentEntry(entityIdkvEntryMap, false); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntryType.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntryType.java index 22cec4252e..16a7a20216 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntryType.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntryType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.service.cf.ctx.state; public enum ArgumentEntryType { - SINGLE_VALUE, TS_ROLLING + SINGLE_VALUE, TS_ROLLING, GEOFENCING, PROPAGATION, RELATED_ENTITIES, ENTITY_AGGREGATION } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index 5b9ed588df..8bb78c29dc 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -15,44 +15,64 @@ */ package org.thingsboard.server.service.cf.ctx.state; -import lombok.AllArgsConstructor; -import lombok.Data; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Getter; +import lombok.Setter; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationArgumentEntry; import org.thingsboard.server.utils.CalculatedFieldUtils; +import java.io.Closeable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import static org.thingsboard.server.utils.CalculatedFieldUtils.toSingleValueArgumentProto; +@Getter +public abstract class BaseCalculatedFieldState implements CalculatedFieldState, Closeable { -@Data -@AllArgsConstructor -public abstract class BaseCalculatedFieldState implements CalculatedFieldState { - - static final long DEFAULT_LAST_UPDATE_TS = -1L; + public static final long DEFAULT_LAST_UPDATE_TS = -1L; + protected final EntityId entityId; + protected CalculatedFieldCtx ctx; + protected TbActorRef actorCtx; protected List requiredArguments; - protected Map arguments; + + protected Map arguments = new HashMap<>(); protected boolean sizeExceedsLimit; + protected ReadinessStatus readinessStatus; - public BaseCalculatedFieldState(List requiredArguments) { - this.requiredArguments = requiredArguments; - this.arguments = new HashMap<>(); + @Setter + private TopicPartitionInfo partition; + + public BaseCalculatedFieldState(EntityId entityId) { + this.entityId = entityId; } - public BaseCalculatedFieldState() { - this(new ArrayList<>(), new HashMap<>(), false); + @Override + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + this.ctx = ctx; + this.actorCtx = actorCtx; + this.requiredArguments = ctx.getArgNames(); + this.readinessStatus = checkReadiness(); } @Override - public boolean updateState(CalculatedFieldCtx ctx, Map argumentValues) { - if (arguments == null) { - arguments = new HashMap<>(); - } + public void init(boolean restored) { + } - boolean stateUpdated = false; + @Override + public Map update(Map argumentValues, CalculatedFieldCtx ctx) { + Map updatedArguments = null; for (Map.Entry entry : argumentValues.entrySet()) { String key = entry.getKey(); @@ -64,26 +84,48 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { boolean entryUpdated; if (existingEntry == null || newEntry.isForceResetPrevious()) { - validateNewEntry(newEntry); - arguments.put(key, newEntry); + validateNewEntry(key, newEntry); + if (existingEntry instanceof RelatedEntitiesArgumentEntry || + existingEntry instanceof EntityAggregationArgumentEntry) { + updateEntry(existingEntry, newEntry, ctx); + } else { + arguments.put(key, newEntry); + } entryUpdated = true; } else { - entryUpdated = existingEntry.updateEntry(newEntry); + entryUpdated = updateEntry(existingEntry, newEntry, ctx); } if (entryUpdated) { - stateUpdated = true; + if (updatedArguments == null) { + updatedArguments = new HashMap<>(argumentValues.size()); + } + updatedArguments.put(key, newEntry); } } - return stateUpdated; + if (updatedArguments == null) { + return Collections.emptyMap(); + } + readinessStatus = checkReadiness(); + return updatedArguments; + } + + protected boolean updateEntry(ArgumentEntry existingEntry, ArgumentEntry newEntry, CalculatedFieldCtx ctx) { + return existingEntry.updateEntry(newEntry, ctx); + } + + @Override + public void reset() { // must reset everything dependent on arguments + requiredArguments = null; + arguments.clear(); + sizeExceedsLimit = false; } @Override public boolean isReady() { - return arguments.keySet().containsAll(requiredArguments) && - arguments.values().stream().noneMatch(ArgumentEntry::isEmpty); + return readinessStatus.ready(); } @Override @@ -95,19 +137,26 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { } @Override - public void checkArgumentSize(String name, ArgumentEntry entry, CalculatedFieldCtx ctx) { - if (entry instanceof TsRollingArgumentEntry) { - return; + public void close() { + } + + protected void validateNewEntry(String key, ArgumentEntry newEntry) { + } + + protected ObjectNode toResultNode(ObjectNode valuesNode) { + if (ctx.getOutput().getType() == OutputType.ATTRIBUTES || !ctx.isUseLatestTs()) { + return valuesNode; } - if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { - if (ctx.getMaxSingleValueArgumentSize() > 0 && toSingleValueArgumentProto(name, singleValueArgumentEntry).getSerializedSize() > ctx.getMaxSingleValueArgumentSize()) { - throw new IllegalArgumentException("Single value size exceeds the maximum allowed limit. The argument will not be used for calculation."); - } + long latestTs = getLatestTimestamp(); + if (latestTs == DEFAULT_LAST_UPDATE_TS) { + return valuesNode; } + ObjectNode resultNode = JacksonUtil.newObjectNode(); + resultNode.put("ts", latestTs); + resultNode.set("values", valuesNode); + return resultNode; } - protected abstract void validateNewEntry(ArgumentEntry newEntry); - public long getLatestTimestamp() { long latestTs = DEFAULT_LAST_UPDATE_TS; @@ -125,12 +174,37 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { } else if (!single.isDefaultValue()) { latestTs = Math.max(latestTs, single.getTs()); } - } else if (entry instanceof TsRollingArgumentEntry rolling) { - latestTs = Math.max(latestTs, rolling.getLatestTs()); + } else if (entry instanceof HasLatestTs hasLatestTsEntry) { + latestTs = Math.max(latestTs, hasLatestTsEntry.getLatestTs()); } } return latestTs; } + protected ReadinessStatus checkReadiness() { + if (arguments == null) { + return ReadinessStatus.from(requiredArguments); + } + List emptyArguments = null; + for (String requiredArgumentKey : requiredArguments) { + ArgumentEntry argumentEntry = arguments.get(requiredArgumentKey); + if (argumentEntry == null || argumentEntry.isEmpty()) { + if (emptyArguments == null) { + emptyArguments = new ArrayList<>(); + } + emptyArguments.add(requiredArgumentKey); + } + } + return ReadinessStatus.from(emptyArguments); + } + + @Override + public JsonNode getArgumentsJson() { + return JacksonUtil.valueToTree(arguments.entrySet().stream() + .filter(entry -> !entry.getValue().isEmpty()) + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().jsonValue())) + ); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index 373e2f588f..373480e161 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -15,130 +15,396 @@ */ package org.thingsboard.server.service.cf.ctx.state; +import com.google.common.util.concurrent.ListenableFuture; import lombok.Data; +import lombok.extern.slf4j.Slf4j; import net.objecthunter.exp4j.Expression; -import net.objecthunter.exp4j.ExpressionBuilder; import org.mvel2.MVEL; +import org.thingsboard.common.util.ExpressionUtils; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.script.api.tbel.TbelCfCtx; +import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; import org.thingsboard.script.api.tbel.TbelInvokeService; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.actors.calculatedField.CalculatedFieldReevaluateMsg; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ExpressionBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.HasUseLatestTsConfig; import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunctionInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.BasicKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.util.ProtoUtils; -import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.util.TimeUtils; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto; +import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; +import org.thingsboard.server.service.cf.ctx.state.geofencing.ScheduledRefreshSupported; +import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; +import java.io.Closeable; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -import static org.thingsboard.common.util.ExpressionFunctionsUtil.userDefinedFunctions; +import static org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState.DEFAULT_LAST_UPDATE_TS; @Data -public class CalculatedFieldCtx { +@Slf4j +public class CalculatedFieldCtx implements Closeable { + + public static final long DISABLED_INTERVAL_VALUE = -1L; private CalculatedField calculatedField; private CalculatedFieldId cfId; + private String cfName; private TenantId tenantId; private EntityId entityId; private CalculatedFieldType cfType; private final Map arguments; private final Map> mainEntityArguments; private final Map>> linkedEntityArguments; + private final Map> dynamicEntityArguments; + private final Map> relatedEntityArguments; private final List argNames; private Output output; private String expression; private boolean useLatestTs; + + private long lastReevaluationTs; + + private ActorSystemContext systemContext; private TbelInvokeService tbelInvokeService; - private CalculatedFieldScriptEngine calculatedFieldScriptEngine; - private ThreadLocal customExpression; + private RelationService relationService; + private AlarmSubscriptionService alarmService; + private CalculatedFieldProcessingService cfProcessingService; + + private Map tbelExpressions; + private Map> simpleExpressions; private boolean initialized; - private long maxDataPointsPerRollingArg; private long maxStateSize; private long maxSingleValueArgumentSize; + private long intermediateAggregationIntervalMillis; + + private boolean cfHasRelationPathQuerySource; + private List mainEntityGeofencingArgumentNames; + private List linkedEntityAndCurrentOwnerGeofencingArgumentNames; + private List relatedEntityArgumentNames; + + private long scheduledUpdateIntervalMillis; + private long cfCheckReevaluationIntervalMillis; + private long alarmReevaluationIntervalMillis; + private long maxRelatedEntitiesPerCfArgument; + private long minScheduledUpdateIntervalMillis; + private long minDeduplicationIntervalMillis; + + private Argument propagationArgument; + private boolean applyExpressionForResolvedArguments; - public CalculatedFieldCtx(CalculatedField calculatedField, TbelInvokeService tbelInvokeService, ApiLimitService apiLimitService) { + public CalculatedFieldCtx(CalculatedField calculatedField, + ActorSystemContext systemContext) { this.calculatedField = calculatedField; this.cfId = calculatedField.getId(); + this.cfName = calculatedField.getName(); this.tenantId = calculatedField.getTenantId(); this.entityId = calculatedField.getEntityId(); this.cfType = calculatedField.getType(); - CalculatedFieldConfiguration configuration = calculatedField.getConfiguration(); - this.arguments = configuration.getArguments(); + this.arguments = new HashMap<>(); this.mainEntityArguments = new HashMap<>(); this.linkedEntityArguments = new HashMap<>(); - for (Map.Entry entry : arguments.entrySet()) { - var refId = entry.getValue().getRefEntityId(); - var refKey = entry.getValue().getRefEntityKey(); - if (refId == null || refId.equals(calculatedField.getEntityId())) { - mainEntityArguments.compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); - } else { - linkedEntityArguments.computeIfAbsent(refId, key -> new HashMap<>()) - .compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); + this.dynamicEntityArguments = new HashMap<>(); + this.relatedEntityArguments = new HashMap<>(); + this.argNames = new ArrayList<>(); + this.mainEntityGeofencingArgumentNames = new ArrayList<>(); + this.linkedEntityAndCurrentOwnerGeofencingArgumentNames = new ArrayList<>(); + this.relatedEntityArgumentNames = new ArrayList<>(); + this.output = calculatedField.getConfiguration().getOutput(); + if (calculatedField.getConfiguration() instanceof ArgumentsBasedCalculatedFieldConfiguration argBasedConfig) { + this.arguments.putAll(argBasedConfig.getArguments()); + for (Map.Entry entry : arguments.entrySet()) { + var refId = entry.getValue().getRefEntityId(); + var refKey = entry.getValue().getRefEntityKey(); + if (refId == null) { + if (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION.equals(cfType)) { + relatedEntityArguments.compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); + cfHasRelationPathQuerySource = true; + continue; + } + if (entry.getValue().hasRelationQuerySource()) { + cfHasRelationPathQuerySource = true; + continue; + } + if (entry.getValue().hasOwnerSource()) { + dynamicEntityArguments.compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); + } else { + mainEntityArguments.compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); + } + } else if (refId.equals(calculatedField.getEntityId())) { + mainEntityArguments.compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); + } else { + linkedEntityArguments.computeIfAbsent(refId, key -> new HashMap<>()) + .compute(refKey, (key, existingNames) -> CollectionsUtil.addToSet(existingNames, entry.getKey())); + } + } + this.argNames.addAll(arguments.keySet()); + this.relatedEntityArgumentNames = relatedEntityArguments.values().stream() + .flatMap(Set::stream) + .collect(Collectors.toList()); + if (argBasedConfig instanceof ExpressionBasedCalculatedFieldConfiguration expressionBasedConfig) { + this.expression = expressionBasedConfig.getExpression(); + } + if (calculatedField.getConfiguration() instanceof GeofencingCalculatedFieldConfiguration geofencingConfig) { + geofencingConfig.getZoneGroups().forEach((zoneGroupName, config) -> { + if (config.isCfEntitySource(entityId)) { + mainEntityGeofencingArgumentNames.add(zoneGroupName); + return; + } + if (config.isLinkedCfEntitySource(entityId) || config.hasCurrentOwnerSource()) { + linkedEntityAndCurrentOwnerGeofencingArgumentNames.add(zoneGroupName); + } + }); } + if (calculatedField.getConfiguration() instanceof PropagationCalculatedFieldConfiguration propagationConfig) { + propagationArgument = propagationConfig.toPropagationArgument(); + applyExpressionForResolvedArguments = propagationConfig.isApplyExpressionToResolvedArguments(); + cfHasRelationPathQuerySource = true; + } + } + if (calculatedField.getConfiguration() instanceof ScheduledUpdateSupportedCalculatedFieldConfiguration scheduledConfig) { + this.scheduledUpdateIntervalMillis = scheduledConfig.isScheduledUpdateEnabled() ? TimeUnit.SECONDS.toMillis(scheduledConfig.getScheduledUpdateInterval()) : DISABLED_INTERVAL_VALUE; + } + if (calculatedField.getConfiguration() instanceof HasUseLatestTsConfig hasUseLatestTsConfig) { + this.useLatestTs = hasUseLatestTsConfig.isUseLatestTs(); } - this.argNames = new ArrayList<>(arguments.keySet()); - this.output = configuration.getOutput(); - this.expression = configuration.getExpression(); - this.useLatestTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) configuration).isUseLatestTs(); - this.tbelInvokeService = tbelInvokeService; + this.systemContext = systemContext; + this.tbelInvokeService = systemContext.getTbelInvokeService(); + this.relationService = systemContext.getRelationService(); + this.alarmService = systemContext.getAlarmService(); + this.cfProcessingService = systemContext.getCalculatedFieldProcessingService(); - this.maxDataPointsPerRollingArg = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); - this.maxStateSize = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxStateSizeInKBytes) * 1024; - this.maxSingleValueArgumentSize = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxSingleValueArgumentSizeInKBytes) * 1024; + setTenantProfileProperties(); + } + + public boolean requiresScheduledReevaluation() { + long now = System.currentTimeMillis(); + if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration entityAggregationConfig) { + Watermark watermark = entityAggregationConfig.getWatermark(); + if (watermark != null && watermark.getDuration() > 0) { + return true; + } + if (lastReevaluationTs == 0) { + lastReevaluationTs = now; + return true; + } + if (entityAggregationConfig.isProduceIntermediateResult()) { + if (now - lastReevaluationTs >= intermediateAggregationIntervalMillis) { + lastReevaluationTs = now; + return true; + } + } + ZonedDateTime lastReevaluationTime = TimeUtils.toZonedDateTime(lastReevaluationTs, entityAggregationConfig.getInterval().getZoneId()); + long previousIntervalEndTs = entityAggregationConfig.getInterval().getDateTimeIntervalEndTs(lastReevaluationTime); + if (now >= previousIntervalEndTs) { + lastReevaluationTs = now; + return true; + } + } + boolean requiresScheduledReevaluation = calculatedField.getConfiguration().requiresScheduledReevaluation(); + if (calculatedField.getConfiguration() instanceof AlarmCalculatedFieldConfiguration) { + if (requiresScheduledReevaluation) { + if (now - lastReevaluationTs >= alarmReevaluationIntervalMillis) { + lastReevaluationTs = now; + return true; + } + return false; + } + } + return requiresScheduledReevaluation; } public void init() { - if (CalculatedFieldType.SCRIPT.equals(cfType)) { - try { - this.calculatedFieldScriptEngine = initEngine(tenantId, expression, tbelInvokeService); + switch (cfType) { + case SCRIPT -> { + initTbelExpression(expression); initialized = true; - } catch (Exception e) { - initialized = false; - throw new RuntimeException("Failed to initialize CF context. The script expression is invalid. Please check for syntax errors or unsupported functions.", e); } - } else { - if (isValidExpression(expression)) { - this.customExpression = ThreadLocal.withInitial(() -> - new ExpressionBuilder(expression) - .functions(userDefinedFunctions) - .implicitMultiplication(true) - .variables(this.arguments.keySet()) - .build() - ); + case GEOFENCING -> initialized = true; + case SIMPLE -> { + initSimpleExpression(expression); + initialized = true; + } + case ALARM -> { + AlarmCalculatedFieldConfiguration configuration = (AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration(); + configuration.getAllRules().map(rule -> rule.getValue().getCondition().getExpression()) + .forEach(expression -> { + if (expression instanceof TbelAlarmConditionExpression tbelExpression) { + initTbelExpression(tbelExpression.getExpression()); + } + }); + initialized = true; + } + case PROPAGATION -> { + if (applyExpressionForResolvedArguments) { + initTbelExpression(expression); + } initialized = true; + } + case RELATED_ENTITIES_AGGREGATION -> { + RelatedEntitiesAggregationCalculatedFieldConfiguration configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) calculatedField.getConfiguration(); + configuration.getMetrics().forEach((key, metric) -> { + if (metric.getInput() instanceof AggFunctionInput functionInput) { + initTbelExpression(functionInput.getFunction()); + } + String filter = metric.getFilter(); + if (filter != null && !filter.isEmpty()) { + initTbelExpression(filter); + } + }); + initialized = true; + } + case ENTITY_AGGREGATION -> initialized = true; + } + } + + public void setTenantProfileProperties() { + TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenantId); + if (tenantProfile == null) { + throw new IllegalStateException("Tenant Profile not found for tenant: " + tenantId); + } + tenantProfile.getProfileConfiguration().ifPresent(config -> { + this.maxStateSize = config.getMaxStateSizeInKBytes() * 1024L; + this.maxSingleValueArgumentSize = config.getMaxSingleValueArgumentSizeInKBytes() * 1024L; + this.intermediateAggregationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getIntermediateAggregationIntervalInSecForCF()); + this.cfCheckReevaluationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getCfReevaluationCheckInterval()); + this.alarmReevaluationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getAlarmsReevaluationInterval()); + this.maxRelatedEntitiesPerCfArgument = config.getMaxRelatedEntitiesToReturnPerCfArgument(); + this.minScheduledUpdateIntervalMillis = TimeUnit.SECONDS.toMillis(config.getMinAllowedScheduledUpdateIntervalInSecForCF()); + this.minDeduplicationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getMinAllowedDeduplicationIntervalInSecForCF()); + }); + } + + public double evaluateSimpleExpression(Expression expression, CalculatedFieldState state) { + for (Map.Entry entry : state.getArguments().entrySet()) { + try { + BasicKvEntry kvEntry = ((SingleValueArgumentEntry) entry.getValue()).getKvEntryValue(); + double value = switch (kvEntry.getDataType()) { + case LONG -> kvEntry.getLongValue().map(Long::doubleValue).orElseThrow(); + case DOUBLE -> kvEntry.getDoubleValue().orElseThrow(); + case BOOLEAN -> kvEntry.getBooleanValue().map(b -> b ? 1.0 : 0.0).orElseThrow(); + case STRING, JSON -> Double.parseDouble(kvEntry.getValueAsString()); + }; + expression.setVariable(entry.getKey(), value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Argument '" + entry.getKey() + "' is not a number."); + } + } + return expression.evaluate(); + } + + public ListenableFuture evaluateTbelExpression(String expression, CalculatedFieldState state) { + return evaluateTbelExpression(tbelExpressions.get(expression), state.getArguments(), state.getLatestTimestamp()); + } + + public ListenableFuture evaluateTbelExpression(CalculatedFieldScriptEngine expression, CalculatedFieldState state) { + return evaluateTbelExpression(expression, state.getArguments(), state.getLatestTimestamp()); + } + + public ListenableFuture evaluateTbelExpression(String expression, Map entries, long latestTimestamp) { + return evaluateTbelExpression(tbelExpressions.get(expression), entries, latestTimestamp); + } + + public ListenableFuture evaluateTbelExpression(CalculatedFieldScriptEngine expression, Map entries, long latestTimestamp) { + Map arguments = new LinkedHashMap<>(); + List args = new ArrayList<>(argNames.size() + 1); + args.add(new Object()); // first element is a ctx, but we will set it later; + for (String argName : argNames) { + var arg = toTbelArgument(argName, entries); + arguments.put(argName, arg); + if (arg instanceof TbelCfSingleValueArg svArg) { + args.add(svArg.getValue()); } else { - initialized = false; - throw new RuntimeException("Failed to initialize CF context. The expression has invalid syntax or unknown variables. Ensure all mathematical operators are correct."); + args.add(arg); } } + args.set(0, new TbelCfCtx(arguments, latestTimestamp)); + + return expression.executeScriptAsync(args.toArray()); + } + + public ScheduledFuture scheduleReevaluation(long delayMs, TbActorRef actorCtx) { + log.debug("[{}] Scheduling CF reevaluation in {} ms", cfId, delayMs); + return systemContext.scheduleMsgWithDelay(actorCtx, new CalculatedFieldReevaluateMsg(tenantId, this), delayMs); + } + + private TbelCfArg toTbelArgument(String key, Map arguments) { + return arguments.get(key).toTbelCfArg(); } - public void stop() { - if (calculatedFieldScriptEngine != null) { - calculatedFieldScriptEngine.destroy(); + private void initTbelExpression(String expression) { + if (tbelExpressions == null) { + tbelExpressions = new HashMap<>(); + } else if (tbelExpressions.containsKey(expression)) { + return; } - if (customExpression != null) { - customExpression.remove(); + try { + CalculatedFieldScriptEngine engine = initEngine(tenantId, expression, tbelInvokeService); + tbelExpressions.put(expression, engine); + } catch (Exception e) { + initialized = false; + throw new RuntimeException("Failed to initialize CF context. The script expression is invalid. Please check for syntax errors or unsupported functions.", e); + } + } + + private void initSimpleExpression(String expression) { + if (simpleExpressions == null) { + simpleExpressions = new HashMap<>(); + } else if (simpleExpressions.containsKey(expression)) { + return; + } + if (isValidExpression(expression)) { + ThreadLocal compiledExpression = ThreadLocal.withInitial(() -> + ExpressionUtils.createExpression(expression, this.arguments.keySet()) + ); + simpleExpressions.put(expression, compiledExpression); + } else { + initialized = false; + throw new RuntimeException("Failed to initialize CF context. The expression has invalid syntax or unknown variables. Ensure all mathematical operators are correct."); } } @@ -185,6 +451,14 @@ public class CalculatedFieldCtx { return map != null && matchesTimeSeries(map, values); } + public boolean dynamicSourceMatches(List values) { + return matchesTimeSeries(dynamicEntityArguments, values); + } + + public boolean dynamicSourceMatches(List values, AttributeScope scope) { + return matchesAttributes(dynamicEntityArguments, values, scope); + } + private boolean matchesAttributes(Map> argMap, List values, AttributeScope scope) { if (argMap.isEmpty() || values.isEmpty()) { return false; @@ -228,6 +502,14 @@ public class CalculatedFieldCtx { return matchesTimeSeriesKeys(mainEntityArguments, keys); } + public boolean matchesDynamicSourceKeys(List keys, AttributeScope scope) { + return matchesAttributesKeys(dynamicEntityArguments, keys, scope); + } + + public boolean matchesDynamicSourceKeys(List keys) { + return matchesTimeSeriesKeys(dynamicEntityArguments, keys); + } + private boolean matchesAttributesKeys(Map> argMap, List keys, AttributeScope scope) { if (argMap.isEmpty() || keys.isEmpty()) { return false; @@ -274,6 +556,60 @@ public class CalculatedFieldCtx { return map != null && matchesTimeSeriesKeys(map, keys); } + public boolean relatedEntityMatches(List values) { + return matchesTimeSeries(relatedEntityArguments, values); + } + + public boolean relatedEntityMatches(List values, AttributeScope scope) { + return matchesAttributes(relatedEntityArguments, values, scope); + } + + public boolean matchesRelatedEntityKeys(List keys, AttributeScope scope) { + return matchesAttributesKeys(relatedEntityArguments, keys, scope); + } + + public boolean matchesRelatedEntityKeys(List keys) { + return matchesTimeSeriesKeys(relatedEntityArguments, keys); + } + + public boolean relatedEntityMatches(CalculatedFieldTelemetryMsgProto proto) { + if (!proto.getTsDataList().isEmpty()) { + List updatedTelemetry = proto.getTsDataList().stream() + .map(ProtoUtils::fromProto) + .toList(); + return relatedEntityMatches(updatedTelemetry); + } else if (!proto.getAttrDataList().isEmpty()) { + AttributeScope scope = AttributeScope.valueOf(proto.getScope().name()); + List updatedTelemetry = proto.getAttrDataList().stream() + .map(ProtoUtils::fromProto) + .toList(); + return relatedEntityMatches(updatedTelemetry, scope); + } else if (!proto.getRemovedTsKeysList().isEmpty()) { + return matchesRelatedEntityKeys(proto.getRemovedTsKeysList()); + } else { + return matchesRelatedEntityKeys(proto.getRemovedAttrKeysList(), AttributeScope.valueOf(proto.getScope().name())); + } + } + + public boolean dynamicSourceMatches(CalculatedFieldTelemetryMsgProto proto) { + if (!proto.getTsDataList().isEmpty()) { + List updatedTelemetry = proto.getTsDataList().stream() + .map(ProtoUtils::fromProto) + .toList(); + return dynamicSourceMatches(updatedTelemetry); + } else if (!proto.getAttrDataList().isEmpty()) { + AttributeScope scope = AttributeScope.valueOf(proto.getScope().name()); + List updatedTelemetry = proto.getAttrDataList().stream() + .map(ProtoUtils::fromProto) + .toList(); + return dynamicSourceMatches(updatedTelemetry, scope); + } else if (!proto.getRemovedTsKeysList().isEmpty()) { + return matchesDynamicSourceKeys(proto.getRemovedTsKeysList()); + } else { + return matchesDynamicSourceKeys(proto.getRemovedAttrKeysList(), AttributeScope.valueOf(proto.getScope().name())); + } + } + public boolean linkMatches(EntityId entityId, CalculatedFieldTelemetryMsgProto proto) { if (!proto.getTsDataList().isEmpty()) { List updatedTelemetry = proto.getTsDataList().stream() @@ -293,24 +629,195 @@ public class CalculatedFieldCtx { } } + public Map> getLinkedAndDynamicArgs(EntityId entityId) { + var argNames = new HashMap>(); + var linkedArgNames = linkedEntityArguments.get(entityId); + if (linkedArgNames != null && !linkedArgNames.isEmpty()) { + argNames.putAll(linkedArgNames); + } + if (dynamicEntityArguments != null && !dynamicEntityArguments.isEmpty()) { + argNames.putAll(dynamicEntityArguments); + } + return argNames; + } + public CalculatedFieldEntityCtxId toCalculatedFieldEntityCtxId() { return new CalculatedFieldEntityCtxId(tenantId, cfId, entityId); } - public boolean hasOtherSignificantChanges(CalculatedFieldCtx other) { - boolean expressionChanged = !expression.equals(other.expression); - boolean outputChanged = !output.equals(other.output); - return expressionChanged || outputChanged; + public boolean hasRefreshContextOnlyChanges(CalculatedFieldCtx other) { // has changes that do not require state recalculation + if (output != null) { + var thisOutputStrategy = output.getStrategy(); + var otherOutputStrategy = other.getCalculatedField().getConfiguration().getOutput().getStrategy(); + if (thisOutputStrategy.hasRefreshContextOnlyChanges(otherOutputStrategy)) { + return true; + } + } + + if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration thisConfig + && other.getCalculatedField().getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration otherConfig) { + if (thisConfig.isProduceIntermediateResult() != otherConfig.isProduceIntermediateResult()) { + return true; + } + } + + return false; + } + + public boolean hasContextOnlyChanges(CalculatedFieldCtx other) { // has changes that do not require state reinit and will be picked up by the state on the fly + if (calculatedField.getConfiguration() instanceof ExpressionBasedCalculatedFieldConfiguration && !Objects.equals(expression, other.expression)) { + return true; + } + if (output != null && output.hasContextOnlyChanges(other.output)) { + return true; + } + if (calculatedField.getConfiguration() instanceof SimpleCalculatedFieldConfiguration thisConfig + && other.calculatedField.getConfiguration() instanceof SimpleCalculatedFieldConfiguration otherConfig + && thisConfig.isUseLatestTs() != otherConfig.isUseLatestTs()) { + return true; + } + if (cfType == CalculatedFieldType.ALARM) { + if (!calculatedField.getName().equals(other.getCalculatedField().getName())) { + return true; + } + + var thisConfig = (AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration(); + var otherConfig = (AlarmCalculatedFieldConfiguration) other.getCalculatedField().getConfiguration(); + if (!thisConfig.rulesEqual(otherConfig, AlarmRule::equals)) { + // if the rules have any changes not tracked by hasStateChanges + return true; + } + if (!thisConfig.propagationSettingsEqual(otherConfig)) { + return true; + } + } + if (scheduledUpdateIntervalMillis != other.scheduledUpdateIntervalMillis) { + return true; + } + if (calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration thisConfig + && other.getCalculatedField().getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration otherConfig + && (thisConfig.getDeduplicationIntervalInSec() != otherConfig.getDeduplicationIntervalInSec() + || !thisConfig.getMetrics().equals(otherConfig.getMetrics()) + || thisConfig.isUseLatestTs() != otherConfig.isUseLatestTs())) { + return true; + } + if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration thisConfig + && other.getCalculatedField().getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration otherConfig) { + boolean metricsChanged = !Objects.equals(thisConfig.getMetrics(), otherConfig.getMetrics()); + boolean watermarkChanged = !Objects.equals(thisConfig.getWatermark(), otherConfig.getWatermark()); + if (metricsChanged || watermarkChanged) { + return true; + } + } + return false; } public boolean hasStateChanges(CalculatedFieldCtx other) { - boolean typeChanged = !cfType.equals(other.cfType); - boolean argumentsChanged = !arguments.equals(other.arguments); - return typeChanged || argumentsChanged; + if (!arguments.equals(other.arguments)) { + return true; + } + if (cfType == CalculatedFieldType.ALARM) { + var thisConfig = (AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration(); + var otherConfig = (AlarmCalculatedFieldConfiguration) other.getCalculatedField().getConfiguration(); + if (!thisConfig.rulesEqual(otherConfig, (thisRule, otherRule) -> { + return thisRule.getCondition().getType() == otherRule.getCondition().getType(); + })) { + // reinitializing only if the rule list changed, or if a condition type changed for any rule + return true; + } + } + if (cfType == CalculatedFieldType.PROPAGATION && !propagationArgument.equals(other.propagationArgument)) { + return true; + } + if (hasGeofencingZoneGroupConfigurationChanges(other)) { + return true; + } + if (hasRelatedEntitiesAggregationConfigurationChanges(other)) { + return true; + } + if (hasEntityAggregationConfigurationChanges(other)) { + return true; + } + return false; + } + + private boolean hasGeofencingZoneGroupConfigurationChanges(CalculatedFieldCtx other) { + if (calculatedField.getConfiguration() instanceof GeofencingCalculatedFieldConfiguration thisConfig + && other.calculatedField.getConfiguration() instanceof GeofencingCalculatedFieldConfiguration otherConfig) { + return !thisConfig.getZoneGroups().equals(otherConfig.getZoneGroups()); + } + return false; + } + + private boolean hasRelatedEntitiesAggregationConfigurationChanges(CalculatedFieldCtx other) { + if (calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration thisConfig + && other.calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration otherConfig) { + return !thisConfig.getRelation().equals(otherConfig.getRelation()); + } + return false; + } + + private boolean hasEntityAggregationConfigurationChanges(CalculatedFieldCtx other) { + if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration thisConfig + && other.calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration otherConfig) { + return !thisConfig.getInterval().equals(otherConfig.getInterval()); + } + return false; + } + + private boolean isScheduledUpdateDisabled() { + return scheduledUpdateIntervalMillis == DISABLED_INTERVAL_VALUE; + } + + public boolean hasRelatedEntities() { + return cfHasRelationPathQuerySource; + } + + public boolean shouldFetchRelatedEntities(CalculatedFieldState state) { + if (!cfHasRelationPathQuerySource) { + return false; + } + if (isScheduledUpdateDisabled()) { + return false; + } + if (!(state instanceof ScheduledRefreshSupported scheduledRefreshSupported)) { + return false; + } + if (scheduledRefreshSupported.getLastScheduledRefreshTs() == DEFAULT_LAST_UPDATE_TS) { + return true; + } + return scheduledRefreshSupported.getLastScheduledRefreshTs() < System.currentTimeMillis() - Math.max(scheduledUpdateIntervalMillis, minScheduledUpdateIntervalMillis); + } + + @Override + public void close() { + try { + if (tbelExpressions != null) { + tbelExpressions.values().forEach(CalculatedFieldScriptEngine::destroy); + } + if (simpleExpressions != null) { + simpleExpressions.values().forEach(ThreadLocal::remove); + } + } catch (Exception e) { + log.warn("Failed to stop {}", this, e); + } } public String getSizeExceedsLimitMessage() { return "State size exceeds limit of " + (maxStateSize / 1024) + "Kb!"; } + public boolean hasCurrentOwnerSourceArguments() { + return !dynamicEntityArguments.isEmpty(); + } + + @Override + public String toString() { + return "CalculatedFieldCtx{" + + "cfId=" + cfId + + ", cfType=" + cfType + + ", entityId=" + entityId + + '}'; + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java index 8667d1e585..7be39c0136 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java @@ -17,42 +17,69 @@ package org.thingsboard.server.service.cf.ctx.state; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.util.CollectionsUtil; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.service.cf.CalculatedFieldResult; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.alarm.AlarmCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; +import java.io.Closeable; import java.util.List; import java.util.Map; -@JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.PROPERTY, - property = "type" -) +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; +import static org.thingsboard.server.utils.CalculatedFieldUtils.toSingleValueArgumentProto; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ - @JsonSubTypes.Type(value = SimpleCalculatedFieldState.class, name = "SIMPLE"), - @JsonSubTypes.Type(value = ScriptCalculatedFieldState.class, name = "SCRIPT"), + @Type(value = SimpleCalculatedFieldState.class, name = "SIMPLE"), + @Type(value = ScriptCalculatedFieldState.class, name = "SCRIPT"), + @Type(value = GeofencingCalculatedFieldState.class, name = "GEOFENCING"), + @Type(value = AlarmCalculatedFieldState.class, name = "ALARM"), + @Type(value = PropagationCalculatedFieldState.class, name = "PROPAGATION"), + @Type(value = RelatedEntitiesAggregationCalculatedFieldState.class, name = "RELATED_ENTITIES_AGGREGATION"), + @Type(value = EntityAggregationCalculatedFieldState.class, name = "ENTITY_AGGREGATION") }) -public interface CalculatedFieldState { +public interface CalculatedFieldState extends Closeable { @JsonIgnore CalculatedFieldType getType(); + EntityId getEntityId(); + Map getArguments(); + JsonNode getArgumentsJson(); + long getLatestTimestamp(); - void setRequiredArguments(List requiredArguments); + void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx); + + void init(boolean restored); - boolean updateState(CalculatedFieldCtx ctx, Map argumentValues); + Map update(Map arguments, CalculatedFieldCtx ctx); - ListenableFuture performCalculation(CalculatedFieldCtx ctx); + void reset(); + + ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) throws Exception; @JsonIgnore boolean isReady(); + ReadinessStatus getReadinessStatus(); + boolean isSizeExceedsLimit(); @JsonIgnore @@ -60,8 +87,50 @@ public interface CalculatedFieldState { return !isSizeExceedsLimit(); } + TopicPartitionInfo getPartition(); + + void setPartition(TopicPartitionInfo partition); + void checkStateSize(CalculatedFieldEntityCtxId ctxId, long maxStateSize); - void checkArgumentSize(String name, ArgumentEntry entry, CalculatedFieldCtx ctx); + default void checkArgumentSize(String name, ArgumentEntry entry, CalculatedFieldCtx ctx) { + if (entry instanceof TsRollingArgumentEntry || entry instanceof GeofencingArgumentEntry) { + return; + } + if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { + if (ctx.getMaxSingleValueArgumentSize() > 0 && toSingleValueArgumentProto(name, singleValueArgumentEntry).getSerializedSize() > ctx.getMaxSingleValueArgumentSize()) { + throw new IllegalArgumentException("Single value size exceeds the maximum allowed limit. The argument will not be used for calculation."); + } + } + } + + record ReadinessStatus(boolean ready, String errorMsg) { + + private static final String MISSING_REQUIRED_ARGUMENTS_ERROR = "Required arguments are missing: "; + private static final String MISSING_PROPAGATION_TARGETS_ERROR = "No entities found via 'Propagation path to related entities'. " + + "Verify the configured relation type and direction."; + private static final String MISSING_PROPAGATION_TARGETS_AND_ARGUMENTS_ERROR = MISSING_PROPAGATION_TARGETS_ERROR + " Missing arguments to propagate: "; + public static final String MISSING_AGGREGATION_ENTITIES_ERROR = "No entities found via 'Aggregation path to related entities'. " + + "Verify the configured relation type and direction."; + public static final ReadinessStatus READY = new ReadinessStatus(true, null); + + public static ReadinessStatus notReady(String errorMsg) { + return new ReadinessStatus(false, errorMsg); + } + + public static ReadinessStatus from(List emptyOrMissingArguments) { + if (CollectionsUtil.isEmpty(emptyOrMissingArguments)) { + return ReadinessStatus.READY; + } + boolean propagationCtxIsEmpty = emptyOrMissingArguments.remove(PROPAGATION_CONFIG_ARGUMENT); + if (!propagationCtxIsEmpty) { + return notReady(MISSING_REQUIRED_ARGUMENTS_ERROR + String.join(", ", emptyOrMissingArguments)); + } + if (emptyOrMissingArguments.isEmpty()) { + return notReady(MISSING_PROPAGATION_TARGETS_ERROR); + } + return notReady(MISSING_PROPAGATION_TARGETS_AND_ARGUMENTS_ERROR + String.join(", ", emptyOrMissingArguments)); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java new file mode 100644 index 0000000000..7d5bc47cc0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +public interface HasEntityLimit { + + default void checkEntityLimit(int currentEntitiesCount, CalculatedFieldCtx ctx) { + if (currentEntitiesCount >= ctx.getMaxRelatedEntitiesPerCfArgument()) { + throw new IllegalArgumentException( + "Exceeded the maximum allowed related entities per argument '" + + ctx.getMaxRelatedEntitiesPerCfArgument() + "'. Increase the limit in the tenant profile configuration." + ); + } + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmStateUpdateResult.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasLatestTs.java similarity index 82% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmStateUpdateResult.java rename to application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasLatestTs.java index 442abc9a7d..a0f3b39689 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmStateUpdateResult.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasLatestTs.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.rule.engine.profile; +package org.thingsboard.server.service.cf.ctx.state; -enum AlarmStateUpdateResult { +public interface HasLatestTs { - NONE, CREATED, UPDATED, SEVERITY_UPDATED, CLEARED; + long getLatestTs(); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java index a3bab92487..93ade0238e 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java @@ -99,9 +99,9 @@ public class KafkaCalculatedFieldStateService extends AbstractCalculatedFieldSta try { if (msg.getValue() != null) { - processRestoredState(msg.getValue(), callback); + processRestoredState(msg.getValue(), consumerKey.partition(), callback); } else { - processRestoredState(getStateId(msg.getHeaders()), null, callback); + processRestoredState(getStateId(msg.getHeaders()), null, consumerKey.partition(), callback); } } catch (Throwable t) { callback.onFailure(t); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/RocksDBCalculatedFieldStateService.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/RocksDBCalculatedFieldStateService.java index 1ef386430b..5f49907816 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/RocksDBCalculatedFieldStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/RocksDBCalculatedFieldStateService.java @@ -69,7 +69,7 @@ public class RocksDBCalculatedFieldStateService extends AbstractCalculatedFieldS log.error("Failed to parse CalculatedFieldStateProto for key {}", key, e); return; } - processRestoredState(stateMsg, new TbCallback() { + processRestoredState(stateMsg, null, new TbCallback() { @Override public void onSuccess() {} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java index 24f3acd4a7..aa97d30325 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java @@ -15,68 +15,55 @@ */ package org.thingsboard.server.service.cf.ctx.state; -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.Data; -import lombok.NoArgsConstructor; +import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.script.api.tbel.TbelCfArg; -import org.thingsboard.script.api.tbel.TbelCfCtx; -import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -@Data @Slf4j -@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) public class ScriptCalculatedFieldState extends BaseCalculatedFieldState { - public ScriptCalculatedFieldState(List requiredArguments) { - super(requiredArguments); - } + protected CalculatedFieldScriptEngine tbelExpression; - @Override - public CalculatedFieldType getType() { - return CalculatedFieldType.SCRIPT; + public ScriptCalculatedFieldState(EntityId entityId) { + super(entityId); } @Override - protected void validateNewEntry(ArgumentEntry newEntry) { + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + super.setCtx(ctx, actorCtx); + this.tbelExpression = ctx.getTbelExpressions().get(ctx.getExpression()); } @Override - public ListenableFuture performCalculation(CalculatedFieldCtx ctx) { - Map arguments = new LinkedHashMap<>(); - List args = new ArrayList<>(ctx.getArgNames().size() + 1); - args.add(new Object()); // first element is a ctx, but we will set it later; - for (String argName : ctx.getArgNames()) { - var arg = toTbelArgument(argName); - arguments.put(argName, arg); - if (arg instanceof TbelCfSingleValueArg svArg) { - args.add(svArg.getValue()); - } else { - args.add(arg); - } - } - args.set(0, new TbelCfCtx(arguments, getLatestTimestamp())); - ListenableFuture resultFuture = ctx.getCalculatedFieldScriptEngine().executeJsonAsync(args.toArray()); + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) { + ListenableFuture resultFuture = ctx.evaluateTbelExpression(tbelExpression, this); Output output = ctx.getOutput(); return Futures.transform(resultFuture, - result -> new CalculatedFieldResult(output.getType(), output.getScope(), result), + result -> TelemetryCalculatedFieldResult.builder() + .outputStrategy(output.getStrategy()) + .type(output.getType()) + .scope(output.getScope()) + .result(JacksonUtil.valueToTree(result)) + .build(), MoreExecutors.directExecutor() ); } - private TbelCfArg toTbelArgument(String key) { - return arguments.get(key).toTbelCfArg(); + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.SCRIPT; } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java index 0d018f5def..7f21f8d2e7 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java @@ -19,80 +19,51 @@ 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 lombok.Data; -import lombok.NoArgsConstructor; +import lombok.EqualsAndHashCode; +import net.objecthunter.exp4j.Expression; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.script.api.tbel.TbUtils; +import org.thingsboard.common.util.NumberUtils; +import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; -import org.thingsboard.server.common.data.kv.BasicKvEntry; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; -import java.util.List; import java.util.Map; -@Data -@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { - public SimpleCalculatedFieldState(List requiredArguments) { - super(requiredArguments); - } + private ThreadLocal expression; - @Override - public CalculatedFieldType getType() { - return CalculatedFieldType.SIMPLE; + public SimpleCalculatedFieldState(EntityId entityId) { + super(entityId); } @Override - protected void validateNewEntry(ArgumentEntry newEntry) { - if (newEntry instanceof TsRollingArgumentEntry) { - throw new IllegalArgumentException("Rolling argument entry is not supported for simple calculated fields."); - } + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + super.setCtx(ctx, actorCtx); + this.expression = ctx.getSimpleExpressions().get(ctx.getExpression()); } @Override - public ListenableFuture performCalculation(CalculatedFieldCtx ctx) { - var expr = ctx.getCustomExpression().get(); - - for (Map.Entry entry : this.arguments.entrySet()) { - try { - BasicKvEntry kvEntry = ((SingleValueArgumentEntry) entry.getValue()).getKvEntryValue(); - double value = switch (kvEntry.getDataType()) { - case LONG -> kvEntry.getLongValue().map(Long::doubleValue).orElseThrow(); - case DOUBLE -> kvEntry.getDoubleValue().orElseThrow(); - case BOOLEAN -> kvEntry.getBooleanValue().map(b -> b ? 1.0 : 0.0).orElseThrow(); - case STRING, JSON -> Double.parseDouble(kvEntry.getValueAsString()); - }; - expr.setVariable(entry.getKey(), value); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Argument '" + entry.getKey() + "' is not a number."); - } - } - - double expressionResult = expr.evaluate(); + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) { + double expressionResult = ctx.evaluateSimpleExpression(expression.get(), this); Output output = ctx.getOutput(); - Object result = formatResult(expressionResult, output.getDecimalsByDefault()); - JsonNode outputResult = createResultJson(ctx, result); + Object result = NumberUtils.roundResult(expressionResult, output.getDecimalsByDefault()); + JsonNode outputResult = createResultJson(output.getName(), result); - return Futures.immediateFuture(new CalculatedFieldResult(output.getType(), output.getScope(), outputResult)); - } - - private Object formatResult(double expressionResult, Integer decimals) { - if (decimals == null) { - return expressionResult; - } - if (decimals.equals(0)) { - return TbUtils.toInt(expressionResult); - } - return TbUtils.toFixed(expressionResult, decimals); + return Futures.immediateFuture(TelemetryCalculatedFieldResult.builder() + .outputStrategy(output.getStrategy()) + .type(output.getType()) + .scope(output.getScope()) + .result(outputResult) + .build()); } - private JsonNode createResultJson(CalculatedFieldCtx ctx, Object result) { - Output output = ctx.getOutput(); - String outputName = output.getName(); + private JsonNode createResultJson(String outputName, Object result) { ObjectNode valuesNode = JacksonUtil.newObjectNode(); if (result instanceof Double doubleValue) { valuesNode.put(outputName, doubleValue); @@ -101,17 +72,20 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { } else { valuesNode.set(outputName, JacksonUtil.valueToTree(result)); } - if (output.getType() == OutputType.ATTRIBUTES || !ctx.isUseLatestTs()) { - return valuesNode; - } - long latestTs = getLatestTimestamp(); - if (latestTs == DEFAULT_LAST_UPDATE_TS) { - return valuesNode; + return toResultNode(valuesNode); + } + + @Override + protected void validateNewEntry(String key, ArgumentEntry newEntry) { + if (newEntry instanceof TsRollingArgumentEntry) { + throw new IllegalArgumentException("Unsupported argument type detected for argument: " + key + ". " + + "Rolling argument entry is not supported for simple calculated fields."); } - ObjectNode resultNode = JacksonUtil.newObjectNode(); - resultNode.put("ts", latestTs); - resultNode.set("values", valuesNode); - return resultNode; + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.SIMPLE; } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java index 37ed0a5e5d..14433136f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java @@ -19,9 +19,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.type.TypeReference; import lombok.AllArgsConstructor; import lombok.Data; +import org.springframework.lang.Nullable; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; @@ -39,17 +41,39 @@ public class SingleValueArgumentEntry implements ArgumentEntry { public static final Long DEFAULT_VERSION = -1L; - private long ts; - private BasicKvEntry kvEntryValue; - private Long version; + @Nullable + protected EntityId entityId; - private boolean forceResetPrevious; + protected long ts; + protected BasicKvEntry kvEntryValue; + protected Long version; + + protected boolean forceResetPrevious; public SingleValueArgumentEntry() { this.ts = DEFAULT_LAST_UPDATE_TS; this.version = DEFAULT_VERSION; } + public SingleValueArgumentEntry(EntityId entityId, ArgumentEntry entry) { + this(entry); + this.entityId = entityId; + } + + public SingleValueArgumentEntry(ArgumentEntry entry) { + if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { + this.ts = singleValueArgumentEntry.ts; + this.kvEntryValue = singleValueArgumentEntry.kvEntryValue; + this.version = singleValueArgumentEntry.version; + this.forceResetPrevious = singleValueArgumentEntry.forceResetPrevious; + } + } + + public SingleValueArgumentEntry(EntityId entityId, TsKvProto entry) { + this(entry); + this.entityId = entityId; + } + public SingleValueArgumentEntry(TsKvProto entry) { this.ts = entry.getTs(); if (entry.hasVersion()) { @@ -58,6 +82,11 @@ public class SingleValueArgumentEntry implements ArgumentEntry { this.kvEntryValue = ProtoUtils.fromProto(entry.getKv()); } + public SingleValueArgumentEntry(EntityId entityId, AttributeValueProto entry) { + this(entry); + this.entityId = entityId; + } + public SingleValueArgumentEntry(AttributeValueProto entry) { this.ts = entry.getLastUpdateTs(); if (entry.hasVersion()) { @@ -66,6 +95,11 @@ public class SingleValueArgumentEntry implements ArgumentEntry { this.kvEntryValue = ProtoUtils.basicKvEntryFromProto(entry); } + public SingleValueArgumentEntry(EntityId entityId, KvEntry entry) { + this(entry); + this.entityId = entityId; + } + public SingleValueArgumentEntry(KvEntry entry) { if (entry instanceof TsKvEntry tsKvEntry) { this.ts = tsKvEntry.getTs(); @@ -77,6 +111,11 @@ public class SingleValueArgumentEntry implements ArgumentEntry { this.kvEntryValue = ProtoUtils.basicKvEntryFromKvEntry(entry); } + public SingleValueArgumentEntry(EntityId entityId, long ts, BasicKvEntry kvEntryValue, Long version) { + this(ts, kvEntryValue, version); + this.entityId = entityId; + } + public SingleValueArgumentEntry(long ts, BasicKvEntry kvEntryValue, Long version) { this.ts = ts; this.kvEntryValue = kvEntryValue; @@ -100,6 +139,9 @@ public class SingleValueArgumentEntry implements ArgumentEntry { @Override public TbelCfArg toTbelCfArg() { + if (isEmpty()) { + return new TbelCfSingleValueArg(ts, null); + } Object value = kvEntryValue.getValue(); if (kvEntryValue instanceof JsonDataEntry) { try { @@ -117,7 +159,7 @@ public class SingleValueArgumentEntry implements ArgumentEntry { } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof SingleValueArgumentEntry singleValueEntry) { if (singleValueEntry.getTs() < this.ts) { return false; diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java index 34cd0de4b2..07b65fabce 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java @@ -37,7 +37,7 @@ import static org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldSta @NoArgsConstructor @AllArgsConstructor @Slf4j -public class TsRollingArgumentEntry implements ArgumentEntry { +public class TsRollingArgumentEntry implements ArgumentEntry, HasLatestTs { private Integer limit; private Long timeWindow; @@ -100,7 +100,7 @@ public class TsRollingArgumentEntry implements ArgumentEntry { } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof TsRollingArgumentEntry tsRollingEntry) { updateTsRollingEntry(tsRollingEntry); } else if (entry instanceof SingleValueArgumentEntry singleValueEntry) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java new file mode 100644 index 0000000000..5c5858e3bc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java @@ -0,0 +1,329 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation; + +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 lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunctionInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.aggregation.function.AggEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.ScheduledRefreshSupported; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ScheduledFuture; +import java.util.stream.Collectors; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx.DISABLED_INTERVAL_VALUE; +import static org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState.ReadinessStatus.MISSING_AGGREGATION_ENTITIES_ERROR; + +@Slf4j +public class RelatedEntitiesAggregationCalculatedFieldState extends BaseCalculatedFieldState implements ScheduledRefreshSupported { + + @Setter + @Getter + private long lastArgsRefreshTs = DEFAULT_LAST_UPDATE_TS; + @Setter + @Getter + private long lastMetricsEvalTs = DEFAULT_LAST_UPDATE_TS; + private long lastRelatedEntitiesRefreshTs = DEFAULT_LAST_UPDATE_TS; + private long deduplicationIntervalMs = DISABLED_INTERVAL_VALUE; + private Map metrics; + + private ScheduledFuture reevaluationFuture; + + private EntityService entityService; + + public RelatedEntitiesAggregationCalculatedFieldState(EntityId entityId) { + super(entityId); + } + + @Override + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + super.setCtx(ctx, actorCtx); + var configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + metrics = configuration.getMetrics(); + deduplicationIntervalMs = SECONDS.toMillis(configuration.getDeduplicationIntervalInSec()); + entityService = ctx.getSystemContext().getEntityService(); + } + + @Override + public void init(boolean restored) { + super.init(restored); + if (restored) { + scheduleReevaluation(); + } + } + + @Override + public void close() { + super.close(); + if (reevaluationFuture != null) { + reevaluationFuture.cancel(true); + reevaluationFuture = null; + } + } + + @Override + public void reset() { // must reset everything dependent on arguments + super.reset(); + resetScheduledRefreshTs(); + lastArgsRefreshTs = DEFAULT_LAST_UPDATE_TS; + lastMetricsEvalTs = DEFAULT_LAST_UPDATE_TS; + metrics = null; + } + + @Override + public void resetScheduledRefreshTs() { + lastRelatedEntitiesRefreshTs = DEFAULT_LAST_UPDATE_TS; + } + + @Override + public long getLastScheduledRefreshTs() { + return lastRelatedEntitiesRefreshTs; + } + + @Override + public void updateScheduledRefreshTs() { + lastRelatedEntitiesRefreshTs = System.currentTimeMillis(); + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.RELATED_ENTITIES_AGGREGATION; + } + + @Override + public Map update(Map argumentValues, CalculatedFieldCtx ctx) { + lastArgsRefreshTs = System.currentTimeMillis(); + return super.update(argumentValues, ctx); + } + + public List checkRelatedEntities(List relatedEntities) { + Map> entityInputs = prepareInputs(); + findOutdatedEntities(entityInputs, relatedEntities).forEach(this::cleanupEntityData); + updateScheduledRefreshTs(); + return findMissingEntities(entityInputs, relatedEntities); + } + + private List findMissingEntities(Map> entityInputs, List relatedEntities) { + List missing = new ArrayList<>(); + relatedEntities.forEach(entityId -> { + if (!entityInputs.containsKey(entityId)) { + missing.add(entityId); + log.warn("[{}] Missing related entity inputs for {}", ctx.getCfId(), entityId); + } + }); + return missing; + } + + private List findOutdatedEntities(Map> entityInputs, List relatedEntities) { + List outdated = new ArrayList<>(); + entityInputs.keySet().forEach(entityId -> { + if (!relatedEntities.contains(entityId)) { + outdated.add(entityId); + log.warn("[{}] CF state keeps outdated related entity {}", ctx.getCfId(), entityId); + } + }); + return outdated; + } + + public Map updateEntityData(Map fetchedArgs) { + lastMetricsEvalTs = DEFAULT_LAST_UPDATE_TS; + return update(fetchedArgs, ctx); + } + + public void cleanupEntityData(EntityId relatedEntityId) { + arguments.values().forEach(argEntry -> { + RelatedEntitiesArgumentEntry aggEntry = (RelatedEntitiesArgumentEntry) argEntry; + aggEntry.getEntityInputs().remove(relatedEntityId); + }); + lastMetricsEvalTs = DEFAULT_LAST_UPDATE_TS; + lastArgsRefreshTs = System.currentTimeMillis(); + readinessStatus = checkReadiness(); + } + + public void scheduleReevaluation() { + ScheduledFuture future = ctx.scheduleReevaluation(getEnforcedDeduplicationIntervalMillis(), actorCtx); + if (future != null) { + reevaluationFuture = future; + } + } + + @Override + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) throws Exception { + boolean cfUpdated = updatedArgs != null && updatedArgs.isEmpty(); + if (shouldRecalculate() || cfUpdated) { + Output output = ctx.getOutput(); + ObjectNode aggResult = aggregateMetrics(output); + lastMetricsEvalTs = System.currentTimeMillis(); + scheduleReevaluation(); + return Futures.immediateFuture(TelemetryCalculatedFieldResult.builder() + .outputStrategy(output.getStrategy()) + .type(output.getType()) + .scope(output.getScope()) + .result(toResultNode(aggResult)) + .build()); + } else { + return Futures.immediateFuture(TelemetryCalculatedFieldResult.EMPTY); + } + } + + private boolean shouldRecalculate() { + boolean intervalPassed = lastMetricsEvalTs <= System.currentTimeMillis() - getEnforcedDeduplicationIntervalMillis(); + boolean argsUpdatedDuringInterval = lastArgsRefreshTs > lastMetricsEvalTs; + return intervalPassed && argsUpdatedDuringInterval; + } + + private long getEnforcedDeduplicationIntervalMillis() { + return Math.max(deduplicationIntervalMs, ctx.getMinDeduplicationIntervalMillis()); + } + + private Map> prepareInputs() { + Map> inputs = new HashMap<>(); + for (Map.Entry argEntry : arguments.entrySet()) { + String key = argEntry.getKey(); + RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry = (RelatedEntitiesArgumentEntry) argEntry.getValue(); + relatedEntitiesArgumentEntry.getEntityInputs().forEach((entityId, argumentEntry) -> { + inputs.computeIfAbsent(entityId, k -> new HashMap<>()).put(key, argumentEntry); + }); + } + return inputs; + } + + private ObjectNode aggregateMetrics(Output output) throws Exception { + ObjectNode aggResult = JacksonUtil.newObjectNode(); + Map> inputs = prepareInputs(); + for (Entry entry : metrics.entrySet()) { + String metricKey = entry.getKey(); + AggMetric metric = entry.getValue(); + + AggEntry aggMetricEntry = AggEntry.createAggFunction(metric.getFunction()); + aggregateMetric(metric, aggMetricEntry, inputs); + aggMetricEntry.result(output.getDecimalsByDefault()).ifPresent(result -> { + aggResult.set(metricKey, JacksonUtil.valueToTree(result)); + }); + } + return aggResult; + } + + private void aggregateMetric(AggMetric metric, AggEntry aggEntry, Map> inputs) throws Exception { + for (Map entityInputs : inputs.values()) { + if (applyAggregation(metric.getFilter(), entityInputs)) { + Object arg = resolveAggregationInput(metric.getInput(), entityInputs); + if (arg != null) { + aggEntry.update(arg); + } + } + } + } + + private boolean applyAggregation(String filter, Map entityInputs) throws Exception { + if (filter == null || filter.isEmpty()) { + return true; + } else { + Object filterResult = ctx.evaluateTbelExpression(filter, entityInputs, getLatestTimestamp()).get(); + return filterResult instanceof Boolean booleanResult && booleanResult; + } + } + + private Object resolveAggregationInput(AggInput aggInput, Map entityInputs) throws Exception { + if (aggInput instanceof AggFunctionInput functionInput) { + return ctx.evaluateTbelExpression(functionInput.getFunction(), entityInputs, getLatestTimestamp()).get(); + } else { + String inputKey = ((AggKeyInput) aggInput).getKey(); + return entityInputs.get(inputKey).getValue(); + } + } + + @Override + public JsonNode getArgumentsJson() { + Map> inputs = prepareInputs(); + Map entityIdEntityInfos = entityService.fetchEntityInfos(ctx.getTenantId(), null, inputs.keySet()); + List entitiesArguments = new ArrayList<>(); + inputs.forEach((entityId, entityArguments) -> { + EntityInfo entityInfo = entityIdEntityInfos.get(entityId); + if (entityInfo != null) { + JsonNode entityArgumentsJson = JacksonUtil.valueToTree(entityArguments.entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().jsonValue()))); + entitiesArguments.add(new EntityArgument(entityInfo, entityArgumentsJson)); + } + }); + return JacksonUtil.valueToTree(new RelatedEntitiesArgument(ArgumentEntryType.RELATED_ENTITIES, entitiesArguments)); + } + + record RelatedEntitiesArgument(ArgumentEntryType type, List entitiesArguments) {} + + record EntityArgument(EntityInfo entity, JsonNode entityArguments) {} + + @Override + protected ReadinessStatus checkReadiness() { + if (arguments == null) { + return ReadinessStatus.notReady(MISSING_AGGREGATION_ENTITIES_ERROR); + } + for (String requiredArgumentKey : requiredArguments) { + ArgumentEntry argumentEntry = arguments.get(requiredArgumentKey); + if (argumentEntry == null || argumentEntry.isEmpty()) { + return ReadinessStatus.notReady(MISSING_AGGREGATION_ENTITIES_ERROR); + } + if (argumentEntry instanceof RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry) { + try { + checkConstraintByDirection(relatedEntitiesArgumentEntry); + } catch (Exception e) { + return ReadinessStatus.notReady(e.getMessage()); + } + } + } + return ReadinessStatus.READY; + } + + public void checkConstraintByDirection(RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry) { + if (ctx.getCalculatedField().getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration config) { + if (EntitySearchDirection.TO == config.getRelation().direction()) { + if (relatedEntitiesArgumentEntry.getEntityInputs().size() > 1) { + throw new IllegalArgumentException("More than one related entity is not supported for relation direction 'TO'. Found: " + relatedEntitiesArgumentEntry.getEntityInputs().size() + "."); + } + } + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java new file mode 100644 index 0000000000..9cd40e4250 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java @@ -0,0 +1,106 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.script.api.tbel.TbelCfRelatedEntitiesArgumentValue; +import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.HasEntityLimit; +import org.thingsboard.server.service.cf.ctx.state.HasLatestTs; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; + +import java.util.Map; +import java.util.stream.Collectors; + +import static org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState.DEFAULT_LAST_UPDATE_TS; + +@Data +@AllArgsConstructor +public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs, HasEntityLimit { + + private final Map entityInputs; + + private boolean forceResetPrevious; + + @Override + public ArgumentEntryType getType() { + return ArgumentEntryType.RELATED_ENTITIES; + } + + @Override + public Object getValue() { + return entityInputs; + } + + @Override + public long getLatestTs() { + long latestTs = DEFAULT_LAST_UPDATE_TS; + for (ArgumentEntry entry : entityInputs.values()) { + if (entry instanceof SingleValueArgumentEntry single) { + if (!single.isDefaultValue()) { + latestTs = Math.max(latestTs, single.getTs()); + } + } + } + return latestTs; + } + + @Override + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { + if (entry instanceof RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry) { + checkEntityLimit(entityInputs.size(), ctx); + entityInputs.putAll(relatedEntitiesArgumentEntry.entityInputs); + } else if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { + if (entry.isForceResetPrevious()) { + checkEntityLimit(entityInputs.size(), ctx); + entityInputs.put(singleValueArgumentEntry.getEntityId(), singleValueArgumentEntry); + } else { + ArgumentEntry argumentEntry = entityInputs.get(singleValueArgumentEntry.getEntityId()); + if (argumentEntry != null) { + argumentEntry.updateEntry(singleValueArgumentEntry, ctx); + } else { + checkEntityLimit(entityInputs.size(), ctx); + entityInputs.put(singleValueArgumentEntry.getEntityId(), singleValueArgumentEntry); + } + } + } else { + throw new IllegalArgumentException("Unsupported argument entry type for aggregation argument entry: " + entry.getType()); + } + return true; + } + + @Override + public boolean isEmpty() { + return entityInputs.isEmpty(); + } + + @Override + public TbelCfArg toTbelCfArg() { + var inputs = entityInputs.entrySet().stream() + .collect(Collectors.toMap( + e -> e.getKey().getId(), + e -> (TbelCfSingleValueArg) e.getValue().toTbelCfArg() + )); + return new TbelCfRelatedEntitiesArgumentValue(inputs); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/AggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/AggEntry.java new file mode 100644 index 0000000000..79bf1bdf25 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/AggEntry.java @@ -0,0 +1,58 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; + +import java.util.Optional; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = AvgAggEntry.class, name = "AVG"), + @JsonSubTypes.Type(value = CountAggEntry.class, name = "COUNT"), + @JsonSubTypes.Type(value = CountUniqueAggEntry.class, name = "COUNT_UNIQUE"), + @JsonSubTypes.Type(value = MaxAggEntry.class, name = "MAX"), + @JsonSubTypes.Type(value = MinAggEntry.class, name = "MIN"), + @JsonSubTypes.Type(value = SumAggEntry.class, name = "SUM") +}) +public interface AggEntry { + + @JsonIgnore + AggFunction getType(); + + void update(Object value); + + Optional result(Integer precision); + + static AggEntry createAggFunction(AggFunction function) { + return switch (function) { + case MIN -> new MinAggEntry(); + case MAX -> new MaxAggEntry(); + case SUM -> new SumAggEntry(); + case AVG -> new AvgAggEntry(); + case COUNT -> new CountAggEntry(); + case COUNT_UNIQUE -> new CountUniqueAggEntry(); + }; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/AvgAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/AvgAggEntry.java new file mode 100644 index 0000000000..5ee6548f29 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/AvgAggEntry.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.common.util.NumberUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class AvgAggEntry extends BaseAggEntry { + + private BigDecimal sum = BigDecimal.ZERO; + private long count = 0L; + + @Override + protected void doUpdate(double value) { + if (value != 0.0) { + sum = sum.add(BigDecimal.valueOf(value)); + } + this.count++; + } + + @Override + protected Object prepareResult(Integer precision) { + double result = sum.divide(BigDecimal.valueOf(count), RoundingMode.HALF_UP).doubleValue(); + return NumberUtils.roundResult(result, precision); + } + + @Override + public AggFunction getType() { + return AggFunction.AVG; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/BaseAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/BaseAggEntry.java new file mode 100644 index 0000000000..471eb5555d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/BaseAggEntry.java @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import java.util.Optional; + +public abstract class BaseAggEntry implements AggEntry { + + private boolean hasResult = false; + + @Override + public void update(Object value) { + doUpdate(extractDoubleValue(value)); + hasResult = true; + } + + @Override + public Optional result(Integer precision) { + if (hasResult) { + hasResult = false; + return Optional.of(prepareResult(precision)); + } else { + return Optional.empty(); + } + } + + protected abstract void doUpdate(double value); + + protected abstract Object prepareResult(Integer precision); + + protected double extractDoubleValue(Object value) { + try { + if (value instanceof Number number) { + return number.doubleValue(); + } + return Double.parseDouble(value.toString()); + } catch (Exception e) { + throw new NumberFormatException("Cannot parse value " + value.toString()); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/CountAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/CountAggEntry.java new file mode 100644 index 0000000000..1b0514fb9b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/CountAggEntry.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; + +import java.util.Optional; + +public class CountAggEntry implements AggEntry { + + private long count = 0L; + + @Override + public void update(Object value) { + count++; + } + + @Override + public Optional result(Integer precision) { + return Optional.of(count); + } + + @Override + public AggFunction getType() { + return AggFunction.COUNT; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/CountUniqueAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/CountUniqueAggEntry.java new file mode 100644 index 0000000000..76209ae042 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/CountUniqueAggEntry.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +public class CountUniqueAggEntry implements AggEntry { + + private final Set items = new HashSet<>(); + + @Override + public void update(Object value) { + if (value != null) { + items.add(String.valueOf(value)); + } + } + + @Override + public Optional result(Integer precision) { + return Optional.of(items.size()); + } + + @Override + public AggFunction getType() { + return AggFunction.COUNT_UNIQUE; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/MaxAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/MaxAggEntry.java new file mode 100644 index 0000000000..f1054bb326 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/MaxAggEntry.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.common.util.NumberUtils; + +public class MaxAggEntry extends BaseAggEntry { + + private double max = Double.MIN_VALUE; + + @Override + protected void doUpdate(double value) { + if (value > max) { + max = value; + } + } + + @Override + protected Object prepareResult(Integer precision) { + return NumberUtils.roundResult(max, precision); + } + + @Override + public AggFunction getType() { + return AggFunction.MAX; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/MinAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/MinAggEntry.java new file mode 100644 index 0000000000..fc4257dfcb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/MinAggEntry.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.common.util.NumberUtils; + +public class MinAggEntry extends BaseAggEntry { + + private double min = Double.MAX_VALUE; + + @Override + protected void doUpdate(double value) { + if (value < min) { + min = value; + } + } + + @Override + protected Object prepareResult(Integer precision) { + return NumberUtils.roundResult(min, precision); + } + + @Override + public AggFunction getType() { + return AggFunction.MIN; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/SumAggEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/SumAggEntry.java new file mode 100644 index 0000000000..9908757e56 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/function/SumAggEntry.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.function; + +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.common.util.NumberUtils; + +import java.math.BigDecimal; + +public class SumAggEntry extends BaseAggEntry { + + private BigDecimal sum = BigDecimal.ZERO; + + @Override + protected void doUpdate(double value) { + if (value != 0.0) { + sum = sum.add(BigDecimal.valueOf(value)); + } + } + + @Override + protected Object prepareResult(Integer precision) { + return NumberUtils.roundResult(sum.doubleValue(), precision); + } + + @Override + public AggFunction getType() { + return AggFunction.SUM; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/AggIntervalEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/AggIntervalEntry.java new file mode 100644 index 0000000000..5b7b28fe1e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/AggIntervalEntry.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.single; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class AggIntervalEntry { + + private Long startTs; + private Long endTs; + + public boolean belongsToInterval(long ts) { + return ts >= startTs && ts < endTs; + } + + public long getIntervalDuration() { + return endTs - startTs; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/AggIntervalEntryStatus.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/AggIntervalEntryStatus.java new file mode 100644 index 0000000000..a6cb8766d5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/AggIntervalEntryStatus.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.single; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AggIntervalEntryStatus { + + private long lastArgsRefreshTs = -1; + + private long lastMetricsEvalTs = -1; + + public AggIntervalEntryStatus(long lastArgsRefreshTs) { + this.lastArgsRefreshTs = lastArgsRefreshTs; + } + + public boolean intervalPassed(long checkInterval) { + return lastMetricsEvalTs <= System.currentTimeMillis() - checkInterval; + } + + @JsonIgnore + public boolean argsUpdated() { + return lastArgsRefreshTs > -1; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java new file mode 100644 index 0000000000..b59c5d4742 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java @@ -0,0 +1,129 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.single; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; + +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Data +public class EntityAggregationArgumentEntry implements ArgumentEntry { + + private Map aggIntervals; + + private boolean forceResetPrevious; + + public EntityAggregationArgumentEntry(Map aggIntervals) { + this.aggIntervals = aggIntervals; + } + + @Override + public ArgumentEntryType getType() { + return ArgumentEntryType.ENTITY_AGGREGATION; + } + + @Override + public Object getValue() { + return aggIntervals; + } + + @Override + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { + if (entry instanceof EntityAggregationArgumentEntry entityAggEntry) { + aggIntervals.putAll(entityAggEntry.getAggIntervals()); + return true; + } else if (entry instanceof SingleValueArgumentEntry singleValueArgEntry) { + long entryTs = singleValueArgEntry.getTs(); + long now = System.currentTimeMillis(); + if (updateExistingIntervals(singleValueArgEntry, entryTs, now)) { + return true; + } + return createNewInterval(entryTs, now, ctx); + } + return false; + } + + private boolean updateExistingIntervals(SingleValueArgumentEntry entry, long entryTs, long now) { + boolean updated = false; + + for (Map.Entry aggIntervalEntry : aggIntervals.entrySet()) { + AggIntervalEntry interval = aggIntervalEntry.getKey(); + AggIntervalEntryStatus status = aggIntervalEntry.getValue(); + if (entry.isForceResetPrevious()) { + status.setLastArgsRefreshTs(now); + updated = true; + continue; + } + if (interval.belongsToInterval(entryTs)) { + status.setLastArgsRefreshTs(now); + return true; + } + } + + return updated; + } + + private boolean createNewInterval(long entryTs, long now, CalculatedFieldCtx ctx) { + if (!(ctx.getCalculatedField().getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration config)) { + return false; + } + AggInterval interval = config.getInterval(); + Watermark watermark = config.getWatermark(); + long watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration()); + + ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(entryTs), interval.getZoneId()); + + long startTs = interval.getDateTimeIntervalStartTs(zdt); + long endTs = interval.getDateTimeIntervalEndTs(zdt); + + if (now - endTs > watermarkDuration) { + return false; + } + + AggIntervalEntry newInterval = new AggIntervalEntry(startTs, endTs); + aggIntervals.computeIfAbsent(newInterval, i -> new AggIntervalEntryStatus(now)); + return true; + } + + @Override + public boolean isEmpty() { + return aggIntervals.isEmpty(); + } + + @Override + public JsonNode jsonValue() { + return JacksonUtil.valueToTree(aggIntervals); + } + + @Override + public TbelCfArg toTbelCfArg() { + return null; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java new file mode 100644 index 0000000000..c945868576 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java @@ -0,0 +1,389 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.aggregation.single; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.common.util.DebugModeUtil; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; +import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; +import org.thingsboard.common.util.NumberUtils; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +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.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultMetricArgumentEntry; + +public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldState { + + private AggInterval interval; + private long watermarkDuration; + private Map metrics; + + private boolean produceIntermediateResult; + + private EntityAggregationDebugArgumentsTracker debugTracker; + + private CalculatedFieldProcessingService cfProcessingService; + + private long now; + + public EntityAggregationCalculatedFieldState(EntityId entityId) { + super(entityId); + } + + @Override + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + super.setCtx(ctx, actorCtx); + this.cfProcessingService = ctx.getCfProcessingService(); + var configuration = (EntityAggregationCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + Watermark watermark = configuration.getWatermark(); + watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration()); + interval = configuration.getInterval(); + metrics = configuration.getMetrics(); + produceIntermediateResult = configuration.isProduceIntermediateResult(); + } + + @Override + public void init(boolean restored) { + super.init(restored); + if (restored) { + fillMissingIntervals(); + } + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.ENTITY_AGGREGATION; + } + + @Override + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) throws Exception { + createIntervalIfNotExist(); + now = System.currentTimeMillis(); + + if (DebugModeUtil.isDebugFailuresAvailable(ctx.getCalculatedField())) { + if (debugTracker == null) { + debugTracker = new EntityAggregationDebugArgumentsTracker(new HashMap<>()); + } else { + debugTracker.reset(); + } + debugTracker.recordUpdatedArgs(updatedArgs, arguments); + } + + Map> results = new HashMap<>(); + List expiredIntervals = new ArrayList<>(); + getIntervals().forEach((intervalEntry, argIntervalStatuses) -> { + processInterval(intervalEntry, argIntervalStatuses, expiredIntervals, results); + }); + removeExpiredIntervals(expiredIntervals); + + Output output = ctx.getOutput(); + ArrayNode result = toResult(results, output.getDecimalsByDefault()); + if (result.isEmpty()) { + return Futures.immediateFuture(TelemetryCalculatedFieldResult.EMPTY); + } + return Futures.immediateFuture(TelemetryCalculatedFieldResult.builder() + .outputStrategy(output.getStrategy()) + .type(output.getType()) + .scope(output.getScope()) + .result(result) + .build()); + } + + @Override + public Map update(Map argumentValues, CalculatedFieldCtx ctx) { + createIntervalIfNotExist(); + return super.update(argumentValues, ctx); + } + + private void removeExpiredIntervals(List expiredIntervals) { + expiredIntervals.forEach(expiredInterval -> { + arguments.values().stream() + .map(EntityAggregationArgumentEntry.class::cast) + .forEach(arg -> arg.getAggIntervals().remove(expiredInterval)); + }); + } + + private void createIntervalIfNotExist() { + AggIntervalEntry currentInterval = new AggIntervalEntry(interval.getCurrentIntervalStartTs(), interval.getCurrentIntervalEndTs()); + arguments.forEach((argName, argumentEntry) -> { + var entityAggEntry = (EntityAggregationArgumentEntry) argumentEntry; + entityAggEntry.getAggIntervals().computeIfAbsent(currentInterval, current -> new AggIntervalEntryStatus()); + }); + } + + private void fillMissingIntervals() { + long now = System.currentTimeMillis(); + ZoneId zoneId = interval.getZoneId(); + long currentIntervalEndTs = interval.getCurrentIntervalEndTs(); + long watermarkThresholdTs = now - watermarkDuration; + + Map> intervals = getIntervals(); + AggIntervalEntry lastIntervalEntry = intervals.keySet().stream().max(Comparator.comparing(AggIntervalEntry::getEndTs)).orElse(null); + if (lastIntervalEntry == null) { + return; + } + + ZonedDateTime nextStart = Instant.ofEpochMilli(lastIntervalEntry.getEndTs()).atZone(zoneId); + ZonedDateTime nextEnd = interval.getNextIntervalStart(nextStart); + + while (nextEnd.toInstant().toEpochMilli() <= currentIntervalEndTs) { + long nextStartTs = nextStart.toInstant().toEpochMilli(); + long nextEndTs = nextEnd.toInstant().toEpochMilli(); + + if (nextEndTs < watermarkThresholdTs) { + nextStart = nextEnd; + nextEnd = interval.getNextIntervalStart(nextStart); + continue; + } + + AggIntervalEntry missing = new AggIntervalEntry(nextStartTs, nextEndTs); + + arguments.forEach((argName, argumentEntry) -> { + var entityAggEntry = (EntityAggregationArgumentEntry) argumentEntry; + AggIntervalEntryStatus intervalEntryStatus = new AggIntervalEntryStatus(System.currentTimeMillis()); + entityAggEntry.getAggIntervals().computeIfAbsent(missing, missingInterval -> intervalEntryStatus); + }); + + nextStart = nextEnd; + nextEnd = interval.getNextIntervalStart(nextStart); + } + } + + private Map> getIntervals() { + Map> intervals = new HashMap<>(); + arguments.forEach((argName, entry) -> { + var argEntry = (EntityAggregationArgumentEntry) entry; + argEntry.getAggIntervals().forEach((intervalEntry, status) -> + intervals.computeIfAbsent(intervalEntry, i -> new HashMap<>()).put(argName, status) + ); + }); + return intervals; + } + + private void processInterval(AggIntervalEntry intervalEntry, + Map args, + List expiredIntervals, + Map> results) { + long startTs = intervalEntry.getStartTs(); + long endTs = intervalEntry.getEndTs(); + + if (now - endTs > watermarkDuration) { + handleExpiredInterval(intervalEntry, args, results); + expiredIntervals.add(intervalEntry); + } else if (now - startTs >= intervalEntry.getIntervalDuration()) { + handleActiveInterval(ctx.getCfCheckReevaluationIntervalMillis(), intervalEntry, args, results); + if (watermarkDuration == 0) { + expiredIntervals.add(intervalEntry); + } + } else if (produceIntermediateResult) { + handleActiveInterval(ctx.getIntermediateAggregationIntervalMillis(), intervalEntry, args, results); + } + } + + private void handleExpiredInterval(AggIntervalEntry intervalEntry, + Map args, + Map> results) { + args.forEach((argName, argEntryIntervalStatus) -> { + if (argEntryIntervalStatus.getLastArgsRefreshTs() > argEntryIntervalStatus.getLastMetricsEvalTs()) { + argEntryIntervalStatus.setLastMetricsEvalTs(System.currentTimeMillis()); + processArgument(intervalEntry, argName, false, results); + } else if (argEntryIntervalStatus.getLastMetricsEvalTs() == DEFAULT_LAST_UPDATE_TS) { + argEntryIntervalStatus.setLastMetricsEvalTs(System.currentTimeMillis()); + processArgument(intervalEntry, argName, true, results); + } + }); + } + + private void handleActiveInterval(long cfCheckInterval, + AggIntervalEntry intervalEntry, + Map args, + Map> results) { + args.forEach((argName, argEntryIntervalStatus) -> { + if (argEntryIntervalStatus.intervalPassed(cfCheckInterval)) { + if (argEntryIntervalStatus.argsUpdated()) { + argEntryIntervalStatus.setLastMetricsEvalTs(now); + argEntryIntervalStatus.setLastArgsRefreshTs(DEFAULT_LAST_UPDATE_TS); + processArgument(intervalEntry, argName, false, results); + } else if (argEntryIntervalStatus.getLastMetricsEvalTs() == DEFAULT_LAST_UPDATE_TS) { + argEntryIntervalStatus.setLastMetricsEvalTs(now); + processArgument(intervalEntry, argName, true, results); + } + } + }); + } + + private void processArgument(AggIntervalEntry intervalEntry, + String argName, + boolean useDefault, + Map> results) { + Set metrics = findMetrics(argName); + if (!metrics.isEmpty()) { + metrics.forEach(metricName -> { + AggMetric metric = this.metrics.get(metricName); + String argKey = ctx.getArguments().get(argName).getRefEntityKey().getKey(); + ArgumentEntry metricEntry = useDefault + ? createDefaultMetricArgumentEntry(argKey, metric) + : cfProcessingService.fetchMetricDuringInterval(ctx.getTenantId(), entityId, argKey, metric, intervalEntry); + if (!metricEntry.isEmpty()) { + results.computeIfAbsent(intervalEntry, i -> new HashMap<>()).put(metricName, metricEntry); + } + }); + } + } + + private Set findMetrics(String argName) { + return metrics.entrySet().stream() + .filter(e -> ((AggKeyInput) e.getValue().getInput()).getKey().equals(argName)) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } + + protected ArrayNode toResult(Map> results, Integer precision) { + ArrayNode result = JacksonUtil.newArrayNode(); + results.forEach((interval, args) -> { + ObjectNode metricsNode = JacksonUtil.newObjectNode(); + for (Map.Entry entry : args.entrySet()) { + String metricName = entry.getKey(); + ArgumentEntry argumentEntry = entry.getValue(); + if (!argumentEntry.isEmpty()) { + Object resultValue = argumentEntry.getValue() instanceof Number number + ? NumberUtils.roundResult(number.doubleValue(), precision) + : argumentEntry.getValue(); + metricsNode.put(metricName, JacksonUtil.toString(resultValue)); + } + } + if (!metricsNode.isEmpty()) { + ObjectNode resultNode = JacksonUtil.newObjectNode(); + resultNode.put("ts", interval.getStartTs()); + resultNode.set("values", metricsNode); + result.add(resultNode); + + if (DebugModeUtil.isDebugFailuresAvailable(ctx.getCalculatedField())) { + if (debugTracker != null) { + debugTracker.addInterval(interval); + } + } + } + }); + return result; + } + + @Override + public JsonNode getArgumentsJson() { + if (debugTracker == null) { + return null; + } + EntityAggregationDebugArguments debugArguments = debugTracker.toDebugArguments(); + return debugArguments == null ? null : JacksonUtil.valueToTree(debugArguments); + } + + @Override + public boolean isReady() { + return true; + } + + record EntityAggregationDebugArgumentsTracker(Map> processedIntervals) { + + public void reset() { + processedIntervals.clear(); + } + + public void addInterval(AggIntervalEntry interval) { + processedIntervals.computeIfAbsent(interval, k -> new HashMap<>()); + } + + public void recordUpdatedArgs(Map updatedArgs, Map arguments) { + if (updatedArgs != null && !updatedArgs.isEmpty()) { + updatedArgs.forEach((argName, argEntry) -> { + ArgumentEntry argumentEntry = arguments.get(argName); + if (argumentEntry instanceof EntityAggregationArgumentEntry entityAggEntry && argEntry instanceof SingleValueArgumentEntry singleEntry) { + entityAggEntry.getAggIntervals().forEach((aggIntervalEntry, aggIntervalEntryStatus) -> { + boolean match = singleEntry.isForceResetPrevious() || aggIntervalEntry.belongsToInterval(singleEntry.getTs()); + if (match) { + recordArg(aggIntervalEntry, argName, singleEntry.toTbelCfArg()); + } + }); + } + }); + } + } + + public void recordArg(AggIntervalEntry interval, String argName, TbelCfArg value) { + processedIntervals.computeIfAbsent(interval, k -> new HashMap<>()).put(argName, value); + } + + public EntityAggregationDebugArguments toDebugArguments() { + if (processedIntervals.isEmpty()) { + return null; + } + return EntityAggregationDebugArguments.toDebugArguments(processedIntervals); + } + + } + + record EntityAggregationDebugArguments(List processedIntervals) { + + public static EntityAggregationDebugArguments toDebugArguments(Map> processedIntervals) { + List result = new ArrayList<>(); + processedIntervals.forEach((interval, args) -> { + result.add(new IntervalDebugArgument(interval.getStartTs(), interval.getEndTs(), args)); + }); + return new EntityAggregationDebugArguments(result); + } + + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + record IntervalDebugArgument(Long intervalStartTs, Long intervalEndTs, JsonNode updatedArguments) { + + public IntervalDebugArgument(Long intervalStartTs, Long intervalEndTs, Map updatedArguments) { + this(intervalStartTs, intervalEndTs, updatedArguments == null || updatedArguments.isEmpty() ? null : JacksonUtil.valueToTree(updatedArguments)); + } + + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java new file mode 100644 index 0000000000..1719c95f7a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java @@ -0,0 +1,586 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.alarm; + +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 lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.KvUtil; +import org.thingsboard.rule.engine.action.TbAlarmResult; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.common.data.StringUtils; +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.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionType; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionFilter; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.SimpleAlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.BooleanFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.ComplexFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.KeyFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NoDataFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.StringFilterPredicate; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.service.cf.AlarmCalculatedFieldResult; +import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; + +import java.util.Comparator; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +import static org.thingsboard.server.common.data.StringUtils.equalsAny; +import static org.thingsboard.server.common.data.StringUtils.splitByCommaWithoutQuotes; +import static org.thingsboard.server.service.cf.ctx.state.alarm.AlarmEvalResult.Cause.NEW_EVENT; +import static org.thingsboard.server.service.cf.ctx.state.alarm.AlarmEvalResult.Cause.SCHEDULED_REEVALUATION; +import static org.thingsboard.server.service.cf.ctx.state.alarm.AlarmEvalResult.Status.FALSE; +import static org.thingsboard.server.service.cf.ctx.state.alarm.AlarmEvalResult.Status.NOT_YET_TRUE; +import static org.thingsboard.server.service.cf.ctx.state.alarm.AlarmEvalResult.Status.TRUE; + +@EqualsAndHashCode(callSuper = true) +@Slf4j +public class AlarmCalculatedFieldState extends BaseCalculatedFieldState { + + private AlarmCalculatedFieldConfiguration configuration; + private String alarmType; + + @Getter + private final Map createRuleStates = new TreeMap<>(Comparator.comparing(Enum::ordinal)); + @Getter + @Setter + private AlarmRuleState clearRuleState; + + @Getter + private Alarm currentAlarm; + private boolean initialFetchDone; + + public AlarmCalculatedFieldState(EntityId entityId) { + super(entityId); + } + + @Override + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + super.setCtx(ctx, actorCtx); + this.configuration = getConfiguration(ctx); + this.alarmType = ctx.getCalculatedField().getName(); + + Map createRules = configuration.getCreateRules(); + createRules.forEach((severity, rule) -> { + AlarmRuleState ruleState = createRuleStates.get(severity); + if (ruleState != null) { + ruleState.setAlarmRule(rule); + } + }); + AlarmRule clearRule = configuration.getClearRule(); + if (clearRule != null && clearRuleState != null) { + clearRuleState.setAlarmRule(clearRule); + } + + if (currentAlarm != null && !currentAlarm.getType().equals(alarmType)) { + currentAlarm = null; + initialFetchDone = false; + } + } + + @Override + public void init(boolean restored) { + super.init(restored); + AtomicBoolean reevalNeeded = new AtomicBoolean(false); + Map createRules = configuration.getCreateRules(); + for (AlarmSeverity severity : AlarmSeverity.values()) { + AlarmRule rule = createRules.get(severity); + if (rule != null) { + createRuleStates.compute(severity, (__, ruleState) -> { + return initRuleState(severity, rule, ruleState, reevalNeeded); + }); + } else { + AlarmRuleState state = createRuleStates.remove(severity); + if (state != null) { + clearState(state); + } + } + } + + AlarmRule clearRule = configuration.getClearRule(); + if (clearRule != null) { + clearRuleState = initRuleState(null, clearRule, clearRuleState, reevalNeeded); + } else { + if (clearRuleState != null) { + clearState(clearRuleState); + clearRuleState = null; + } + } + log.debug("Initialized create rule states {} and clear rule state {} for {}. Restored: {}, reeval needed: {}", + createRuleStates, clearRuleState, configuration, restored, reevalNeeded); + + if (reevalNeeded.get()) { + initCurrentAlarm(ctx); + createOrClearAlarms(state -> { + if (state.getCondition().getType() == AlarmConditionType.DURATION) { + AlarmEvalResult evalResult = state.reeval(System.currentTimeMillis(), ctx); + if (evalResult.getStatus() == TRUE || evalResult.getStatus() == NOT_YET_TRUE) { + ScheduledFuture future = ctx.scheduleReevaluation(evalResult.getLeftDuration(), actorCtx); + if (future != null) { + state.setDurationCheckFuture(future); + } + } + } + return AlarmEvalResult.NOT_YET_TRUE; + }, ctx); + } + } + + private AlarmRuleState initRuleState(AlarmSeverity severity, AlarmRule rule, AlarmRuleState ruleState, AtomicBoolean reevalNeeded) { + if (ruleState == null) { + ruleState = new AlarmRuleState(severity, rule, this); + } else { + // when restored + ruleState.setAlarmRule(rule); + ruleState.setActive(null); + AlarmCondition condition = rule.getCondition(); + if (condition.hasSchedule() || (condition.getType() == AlarmConditionType.DURATION && !ruleState.isEmpty())) { + reevalNeeded.set(true); + } + } + return ruleState; + } + + @Override + public void reset() { + super.reset(); + configuration = null; + } + + @Override + public void close() { + super.close(); + for (AlarmRuleState state : createRuleStates.values()) { + clearState(state); + } + clearState(clearRuleState); + } + + @Override + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) { + initCurrentAlarm(ctx); + TbAlarmResult result = createOrClearAlarms(state -> { + if (updatedArgs != null) { + boolean newEvent = !updatedArgs.isEmpty(); + AlarmEvalResult evalResult = state.eval(newEvent, ctx); + if (evalResult.getStatus() == NOT_YET_TRUE && evalResult.getLeftDuration() > 0) { + long leftDuration = evalResult.getLeftDuration(); + ScheduledFuture future = ctx.scheduleReevaluation(leftDuration, actorCtx); + if (future != null) { + state.setDurationCheckFuture(future); + } + } + return evalResult.withCause(NEW_EVENT); + } else { + return state.reeval(System.currentTimeMillis(), ctx).withCause(SCHEDULED_REEVALUATION); + } + }, ctx); + return Futures.immediateFuture(AlarmCalculatedFieldResult.builder() + .alarmResult(result) + .build()); + } + + @Override + protected boolean updateEntry(ArgumentEntry existingArgumentEntry, ArgumentEntry newArgumentEntry, CalculatedFieldCtx ctx) { + if (!(existingArgumentEntry instanceof SingleValueArgumentEntry existingEntry) || + !(newArgumentEntry instanceof SingleValueArgumentEntry newEntry)) { + return super.updateEntry(existingArgumentEntry, newArgumentEntry, ctx); + } + if (newEntry.getTs() < existingEntry.getTs()) { + if (existingEntry.isDefaultValue()) { + existingEntry.setTs(newEntry.getTs()); + } + } + return super.updateEntry(existingEntry, newEntry, ctx); + } + + public void processAlarmAction(Alarm alarm, ActionType action) { + switch (action) { + case ALARM_ACK -> processAlarmAck(alarm); + case ALARM_CLEAR -> processAlarmClear(alarm); + case ALARM_DELETE -> processAlarmDelete(alarm); + } + } + + private void processAlarmClear(Alarm alarm) { + currentAlarm = null; + createRuleStates.values().forEach(this::clearState); + clearState(clearRuleState); + } + + private void processAlarmAck(Alarm alarm) { + currentAlarm.setAcknowledged(alarm.isAcknowledged()); + currentAlarm.setAckTs(alarm.getAckTs()); + } + + private void processAlarmDelete(Alarm alarm) { + processAlarmClear(alarm); + } + + private TbAlarmResult createOrClearAlarms(Function evalFunction, + CalculatedFieldCtx ctx) { + AlarmEvalResult evalResult = null; + AlarmRuleState resultState = null; + AlarmRuleState.StateInfo resultStateInfo = null; + + for (AlarmRuleState state : createRuleStates.values()) { + evalResult = evalFunction.apply(state); + log.debug("Evaluated create rule {} with args {}. Result: {}", state, arguments, evalResult); + if (evalResult.getStatus() == TRUE) { + resultState = state; + break; + } else if (evalResult.getStatus() == FALSE) { + clearState(state); + } + } + + TbAlarmResult result = null; + if (resultState != null) { + result = calculateAlarmResult(resultState, evalResult, ctx); + resultStateInfo = resultState.getStateInfo(); + log.debug("Alarm result for state {}: {}", resultState, result); + clearState(clearRuleState); + } else if (currentAlarm != null && clearRuleState != null) { + evalResult = evalFunction.apply(clearRuleState); + log.debug("Evaluated clear rule {} with args {}. Result: {}", clearRuleState, arguments, evalResult); + if (evalResult.getStatus() == TRUE) { + resultStateInfo = clearRuleState.getStateInfo(); + clearState(clearRuleState); + for (AlarmRuleState state : createRuleStates.values()) { + clearState(state); + } + AlarmApiCallResult clearResult = ctx.getAlarmService().clearAlarm( + ctx.getTenantId(), currentAlarm.getId(), System.currentTimeMillis(), createDetails(clearRuleState), false + ); + if (clearResult.isCleared()) { + result = TbAlarmResult.builder() + .isCleared(true) + .alarm(clearResult.getAlarm()) + .build(); + resultState = clearRuleState; + } + currentAlarm = null; + } else if (evalResult.getStatus() == FALSE) { + clearState(clearRuleState); + } + } + if (result != null && resultState != null) { + result.setConditionRepeats(resultStateInfo.eventCount()); + result.setConditionDuration(resultStateInfo.duration()); + } + return result; + } + + private void clearState(AlarmRuleState state) { + if (state != null) { + log.debug("Clearing rule state {}", state); + state.clear(); + } + } + + private void initCurrentAlarm(CalculatedFieldCtx ctx) { + if (!initialFetchDone) { + Alarm alarm = ctx.getAlarmService().findLatestActiveByOriginatorAndType(ctx.getTenantId(), entityId, alarmType); + if (alarm != null && !alarm.getStatus().isCleared()) { + currentAlarm = alarm; + } + initialFetchDone = true; + } + } + + private TbAlarmResult calculateAlarmResult(AlarmRuleState ruleState, AlarmEvalResult evalResult, CalculatedFieldCtx ctx) { + AlarmSeverity severity = ruleState.getSeverity(); + if (currentAlarm != null) { + AlarmSeverity oldSeverity = currentAlarm.getSeverity(); + if (severity.ordinal() > oldSeverity.ordinal()) { + log.trace("Skipping alarm update for result state {} for eval result {} because severity is decreased", ruleState, evalResult); + return null; + } + if (severity.ordinal() == oldSeverity.ordinal() && evalResult.getCause() == SCHEDULED_REEVALUATION) { + log.trace("Skipping alarm update for result state {} for eval result {}", ruleState, evalResult); + return null; + } + + currentAlarm.setEndTs(System.currentTimeMillis()); + currentAlarm.setDetails(createDetails(ruleState)); + currentAlarm.setSeverity(severity); + AlarmApiCallResult result = ctx.getAlarmService().updateAlarm(AlarmUpdateRequest.fromAlarm(currentAlarm)); + currentAlarm = result.getAlarm(); + return TbAlarmResult.fromAlarmResult(result); + } else { + var newAlarm = new Alarm(); + newAlarm.setType(alarmType); + newAlarm.setAcknowledged(false); + newAlarm.setCleared(false); + newAlarm.setSeverity(severity); + long startTs = getLatestTimestamp(); + long currentTime = System.currentTimeMillis(); + if (startTs <= 0L || startTs > currentTime) { + startTs = currentTime; + } + newAlarm.setStartTs(startTs); + newAlarm.setEndTs(startTs); + newAlarm.setDetails(createDetails(ruleState)); + newAlarm.setOriginator(entityId); + newAlarm.setTenantId(ctx.getTenantId()); + newAlarm.setPropagate(configuration.isPropagate()); + newAlarm.setPropagateToOwner(configuration.isPropagateToOwner()); + newAlarm.setPropagateToTenant(configuration.isPropagateToTenant()); + if (configuration.getPropagateRelationTypes() != null) { + newAlarm.setPropagateRelationTypes(configuration.getPropagateRelationTypes()); + } + AlarmApiCallResult result = ctx.getAlarmService().createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(newAlarm)); + currentAlarm = result.getAlarm(); + return TbAlarmResult.fromAlarmResult(result); + } + } + + private JsonNode createDetails(AlarmRuleState ruleState) { + JsonNode alarmDetails; + String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails(); + DashboardId dashboardId = ruleState.getAlarmRule().getDashboardId(); + + if (StringUtils.isNotEmpty(alarmDetailsStr) || dashboardId != null) { + ObjectNode newDetails = JacksonUtil.newObjectNode(); + if (StringUtils.isNotEmpty(alarmDetailsStr)) { + for (Map.Entry entry : arguments.entrySet()) { + String key = entry.getKey(); + ArgumentEntry value = entry.getValue(); + alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", key), String.valueOf(value.getValue())); + } + newDetails.put("data", alarmDetailsStr); + } + if (dashboardId != null) { + newDetails.put("dashboardId", dashboardId.getId().toString()); + } + alarmDetails = newDetails; + } else if (currentAlarm != null) { + alarmDetails = currentAlarm.getDetails(); + } else { + alarmDetails = JacksonUtil.newObjectNode(); + } + + return alarmDetails; + } + + @SneakyThrows + public boolean eval(AlarmConditionExpression expression, CalculatedFieldCtx ctx) { + if (expression instanceof TbelAlarmConditionExpression tbelExpression) { + Object result = ctx.evaluateTbelExpression(tbelExpression.getExpression(), this).get(); + if (result instanceof Boolean booleanResult) { + return booleanResult; + } else { + throw new IllegalStateException("Condition expression returned non-boolean value: '" + result + "'"); + } + } else { + SimpleAlarmConditionExpression simpleExpression = (SimpleAlarmConditionExpression) expression; + ComplexOperation operation = simpleExpression.getOperation(); + if (operation == null) { + operation = ComplexOperation.AND; + } + return switch (operation) { + case AND -> simpleExpression.getFilters().stream() + .allMatch(filter -> eval(getArgument(filter.getArgument()), filter)); + case OR -> simpleExpression.getFilters().stream() + .anyMatch(filter -> eval(getArgument(filter.getArgument()), filter)); + }; + } + } + + private boolean eval(SingleValueArgumentEntry argument, AlarmConditionFilter filter) { + ComplexOperation operation = filter.getOperation(); + if (operation == null) { + operation = ComplexOperation.AND; + } + return switch (operation) { + case AND -> filter.getPredicates().stream() + .allMatch(predicate -> eval(argument, predicate)); + case OR -> filter.getPredicates().stream() + .anyMatch(predicate -> eval(argument, predicate)); + }; + } + + private boolean eval(SingleValueArgumentEntry argument, KeyFilterPredicate predicate) { + return switch (predicate.getType()) { + case STRING -> evalStrPredicate(argument, (StringFilterPredicate) predicate); + case NUMERIC -> evalNumPredicate(argument, (NumericFilterPredicate) predicate); + case BOOLEAN -> evalBooleanPredicate(argument, (BooleanFilterPredicate) predicate); + case NO_DATA -> evalNoDataPredicate(argument, (NoDataFilterPredicate) predicate); + case COMPLEX -> evalComplexPredicate(argument, (ComplexFilterPredicate) predicate); + }; + } + + private boolean evalComplexPredicate(SingleValueArgumentEntry argument, ComplexFilterPredicate complexPredicate) { + return switch (complexPredicate.getOperation()) { + case OR -> { + for (KeyFilterPredicate predicate : complexPredicate.getPredicates()) { + if (eval(argument, predicate)) { + yield true; + } + } + yield false; + } + case AND -> { + for (KeyFilterPredicate predicate : complexPredicate.getPredicates()) { + if (!eval(argument, predicate)) { + yield false; + } + } + yield true; + } + }; + } + + private boolean evalBooleanPredicate(SingleValueArgumentEntry argument, BooleanFilterPredicate predicate) { + Boolean value = KvUtil.getBoolValue(argument.getKvEntryValue()); + if (value == null) { + return false; + } + Boolean predicateValue = resolveValue(predicate.getValue(), KvUtil::getBoolValue); + if (predicateValue == null) { + return false; + } + return switch (predicate.getOperation()) { + case EQUAL -> value.equals(predicateValue); + case NOT_EQUAL -> !value.equals(predicateValue); + }; + } + + private boolean evalNumPredicate(SingleValueArgumentEntry argument, NumericFilterPredicate predicate) { + Double value = KvUtil.getDoubleValue(argument.getKvEntryValue()); + if (value == null) { + return false; + } + Double predicateValue = resolveValue(predicate.getValue(), KvUtil::getDoubleValue); + if (predicateValue == null) { + return false; + } + return switch (predicate.getOperation()) { + case NOT_EQUAL -> !value.equals(predicateValue); + case EQUAL -> value.equals(predicateValue); + case GREATER -> value > predicateValue; + case GREATER_OR_EQUAL -> value >= predicateValue; + case LESS -> value < predicateValue; + case LESS_OR_EQUAL -> value <= predicateValue; + }; + } + + private boolean evalStrPredicate(SingleValueArgumentEntry argument, StringFilterPredicate predicate) { + String value = KvUtil.getStringValue(argument.getKvEntryValue()); + if (value == null) { + return false; + } + String predicateValue = resolveValue(predicate.getValue(), KvUtil::getStringValue); + if (predicateValue == null) { + return false; + } + if (predicate.isIgnoreCase()) { + value = value.toLowerCase(); + predicateValue = predicateValue.toLowerCase(); + } + return switch (predicate.getOperation()) { + case CONTAINS -> value.contains(predicateValue); + case EQUAL -> value.equals(predicateValue); + case STARTS_WITH -> value.startsWith(predicateValue); + case ENDS_WITH -> value.endsWith(predicateValue); + case NOT_EQUAL -> !value.equals(predicateValue); + case NOT_CONTAINS -> !value.contains(predicateValue); + case IN -> equalsAny(value, splitByCommaWithoutQuotes(predicateValue)); + case NOT_IN -> !equalsAny(value, splitByCommaWithoutQuotes(predicateValue)); + }; + } + + private boolean evalNoDataPredicate(SingleValueArgumentEntry argument, NoDataFilterPredicate predicate) { + long passedMs = System.currentTimeMillis() - argument.getTs(); + long duration = resolveValue(predicate.getDuration(), KvUtil::getLongValue); + if (duration > 0) { + long requiredDuration = predicate.getUnit().toMillis(duration); + log.trace("[{}] No data for argument {} during {} ms, required duration: {} ms", ctx, argument, passedMs, requiredDuration); + return passedMs >= requiredDuration; + } else { + return false; + } + } + + protected T resolveValue(AlarmConditionValue conditionValue, Function mapper) { + T value = conditionValue.getStaticValue(); + if (value == null) { + String argument = conditionValue.getDynamicValueArgument(); + SingleValueArgumentEntry entry = getArgument(argument); + value = mapper.apply(entry.getKvEntryValue()); + if (value == null) { + throw new IllegalArgumentException("No proper value found for argument " + argument); + } + } + return value; + } + + protected SingleValueArgumentEntry getArgument(String key) { + SingleValueArgumentEntry entry = (SingleValueArgumentEntry) arguments.get(key); + if (entry == null) { + throw new IllegalArgumentException("Argument '" + key + "' is missing"); + } + return entry; + } + + private AlarmCalculatedFieldConfiguration getConfiguration(CalculatedFieldCtx ctx) { + return (AlarmCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + } + + @Override + protected void validateNewEntry(String key, ArgumentEntry newEntry) { + if (!(newEntry instanceof SingleValueArgumentEntry)) { + throw new IllegalArgumentException("Only single value arguments supported"); + } + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.ALARM; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmEvalResult.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmEvalResult.java new file mode 100644 index 0000000000..805af09907 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmEvalResult.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.alarm; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +public class AlarmEvalResult { + + public static final AlarmEvalResult TRUE = new AlarmEvalResult(Status.TRUE); + public static final AlarmEvalResult FALSE = new AlarmEvalResult(Status.FALSE); + public static final AlarmEvalResult NOT_YET_TRUE = new AlarmEvalResult(Status.NOT_YET_TRUE); + public static final AlarmEvalResult EMPTY = new AlarmEvalResult(null); + + private final Status status; + private final long leftDuration; + private final long leftEvents; + private Cause cause; + + public AlarmEvalResult(Status status) { + this(status, 0, 0); + } + + public static AlarmEvalResult notYetTrue(long leftEvents, long leftDuration) { + return new AlarmEvalResult(Status.NOT_YET_TRUE, leftDuration, leftEvents); + } + + public AlarmEvalResult withCause(Cause cause) { + this.cause = cause; + return this; + } + + public enum Status { + FALSE, NOT_YET_TRUE, TRUE; + } + + public enum Cause { + NEW_EVENT, SCHEDULED_REEVALUATION; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java new file mode 100644 index 0000000000..19c48272cc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java @@ -0,0 +1,360 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.alarm; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.KvUtil; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionType; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; +import org.thingsboard.server.common.data.alarm.rule.condition.DurationAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.RepeatingAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSchedule; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmScheduleType; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AnyTimeSchedule; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.CustomTimeSchedule; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.CustomTimeScheduleItem; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.SpecificTimeSchedule; +import org.thingsboard.server.common.msg.tools.SchedulerUtils; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Optional; +import java.util.concurrent.ScheduledFuture; + +import static org.thingsboard.server.service.cf.ctx.state.alarm.AlarmEvalResult.Status.TRUE; + +@Data +@Slf4j +public class AlarmRuleState { + + private final AlarmSeverity severity; + private AlarmRule alarmRule; + private AlarmCalculatedFieldState state; + + private AlarmCondition condition; + + private long eventCount; + private long firstEventTs; // when duration condition started + private long lastCheckTs; + private transient long duration; + private ScheduledFuture durationCheckFuture; + private Boolean active; + + public AlarmRuleState(AlarmSeverity severity, AlarmRule alarmRule, AlarmCalculatedFieldState state) { + this.severity = severity; + if (alarmRule != null) { + setAlarmRule(alarmRule); + } + this.state = state; + } + + public AlarmEvalResult eval(boolean newEvent, CalculatedFieldCtx ctx) { // on event or config change + long ts = newEvent ? state.getLatestTimestamp() : System.currentTimeMillis(); + active = isActive(ts); + if (!active) { + return AlarmEvalResult.FALSE; + } + return doEval(newEvent, ctx); + } + + public AlarmEvalResult reeval(long ts, CalculatedFieldCtx ctx) { // on scheduled duration check or periodic re-eval for rules with schedule + boolean active = isActive(ts); + switch (condition.getType()) { + case SIMPLE, REPEATING -> { + boolean activeChanged = this.active == null || active != this.active; + this.active = active; + if (!active) { + return AlarmEvalResult.EMPTY; + } + + if ((condition.hasSchedule() && activeChanged) || + condition.getExpression().requiresScheduledReevaluation()) { + AlarmEvalResult result = doEval(false, ctx); + if (result.getStatus() == TRUE) { + return result; + } else { + return AlarmEvalResult.EMPTY; + } + } + } + case DURATION -> { + if (!active) { + return AlarmEvalResult.FALSE; + } + long requiredDuration = getRequiredDurationInMs(); + if (requiredDuration > 0 && firstEventTs > 0 && ts > firstEventTs) { + duration = ts - firstEventTs; + long prevDuration = lastCheckTs - firstEventTs; + lastCheckTs = ts; + + long leftDuration = requiredDuration - duration; + if (leftDuration <= 0) { + if (prevDuration >= requiredDuration) { + // already evaluated as true on previous check, no need to update alarm + return AlarmEvalResult.EMPTY; + } + return AlarmEvalResult.TRUE; + } else { + return AlarmEvalResult.notYetTrue(0, leftDuration); + } + } + } + } + return AlarmEvalResult.EMPTY; + } + + public AlarmEvalResult doEval(boolean newEvent, CalculatedFieldCtx ctx) { + return switch (condition.getType()) { + case SIMPLE -> evalSimple(ctx); + case DURATION -> evalDuration(ctx); + case REPEATING -> evalRepeating(newEvent, ctx); + }; + } + + private AlarmEvalResult evalSimple(CalculatedFieldCtx ctx) { + return eval(condition.getExpression(), ctx) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE; + } + + private AlarmEvalResult evalRepeating(boolean newEvent, CalculatedFieldCtx ctx) { + if (eval(condition.getExpression(), ctx)) { + if (newEvent) { + eventCount++; + } + long requiredRepeats = getIntValue(((RepeatingAlarmCondition) condition).getCount()); + if (requiredRepeats > 0) { + long leftRepeats = requiredRepeats - eventCount; + return leftRepeats <= 0 ? AlarmEvalResult.TRUE : AlarmEvalResult.notYetTrue(leftRepeats, 0); + } else { + return AlarmEvalResult.NOT_YET_TRUE; + } + } else { + return AlarmEvalResult.FALSE; + } + } + + private AlarmEvalResult evalDuration(CalculatedFieldCtx ctx) { + if (eval(condition.getExpression(), ctx)) { + long ts = System.currentTimeMillis(); + if (firstEventTs <= 0) { + firstEventTs = state.getLatestTimestamp(); + } + lastCheckTs = ts; + + long requiredDuration = getRequiredDurationInMs(); + if (requiredDuration > 0 && firstEventTs > 0 && ts > firstEventTs) { + duration = ts - firstEventTs; + long leftDuration = requiredDuration - duration; + if (leftDuration <= 0) { + return AlarmEvalResult.TRUE; + } else { + return AlarmEvalResult.notYetTrue(0, leftDuration); + } + } else { + return AlarmEvalResult.NOT_YET_TRUE; + } + } else { + return AlarmEvalResult.FALSE; + } + } + + private boolean isActive(long eventTs) { + if (condition.getSchedule() == null) { + return true; + } + AlarmSchedule schedule = state.resolveValue(condition.getSchedule(), entry -> Optional.ofNullable(KvUtil.getStringValue(entry)) + .map(this::parseSchedule).orElse(null)); + boolean active = switch (schedule.getType()) { + case ANY_TIME -> true; + case SPECIFIC_TIME -> isActiveSpecific((SpecificTimeSchedule) schedule, eventTs); + case CUSTOM -> isActiveCustom((CustomTimeSchedule) schedule, eventTs); + }; + log.trace("Alarm rule active = {} for schedule {}", active, schedule); + return active; + } + + private boolean isActiveSpecific(SpecificTimeSchedule schedule, long eventTs) { + ZoneId zoneId = SchedulerUtils.getZoneId(schedule.getTimezone()); + ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(eventTs), zoneId); + if (schedule.getDaysOfWeek().size() != 7) { + int dayOfWeek = zdt.getDayOfWeek().getValue(); + if (!schedule.getDaysOfWeek().contains(dayOfWeek)) { + return false; + } + } + long endsOn = schedule.getEndsOn(); + if (endsOn == 0) { + // 24 hours in milliseconds + endsOn = 86400000; + } + + return isActive(eventTs, zoneId, zdt, schedule.getStartsOn(), endsOn); + } + + private boolean isActiveCustom(CustomTimeSchedule schedule, long eventTs) { + ZoneId zoneId = SchedulerUtils.getZoneId(schedule.getTimezone()); + ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(eventTs), zoneId); + int dayOfWeek = zdt.toLocalDate().getDayOfWeek().getValue(); + for (CustomTimeScheduleItem item : schedule.getItems()) { + if (item.getDayOfWeek() == dayOfWeek) { + if (item.isEnabled()) { + long endsOn = item.getEndsOn(); + if (endsOn == 0) { + // 24 hours in milliseconds + endsOn = 86400000; + } + return isActive(eventTs, zoneId, zdt, item.getStartsOn(), endsOn); + } else { + return false; + } + } + } + return false; + } + + private boolean isActive(long eventTs, ZoneId zoneId, ZonedDateTime zdt, long startsOn, long endsOn) { + long startOfDay = zdt.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli(); + long msFromStartOfDay = eventTs - startOfDay; + if (startsOn <= endsOn) { + return startsOn <= msFromStartOfDay && endsOn > msFromStartOfDay; + } else { + return startsOn < msFromStartOfDay || (0 < msFromStartOfDay && msFromStartOfDay < endsOn); + } + } + + public void clear() { + clearRepeatingConditionState(); + clearDurationConditionState(); + } + + private void clearRepeatingConditionState() { + eventCount = 0L; + } + + private void clearDurationConditionState() { + firstEventTs = 0L; + lastCheckTs = 0L; + duration = 0L; + if (durationCheckFuture != null) { + durationCheckFuture.cancel(true); + durationCheckFuture = null; + } + } + + public void setDurationCheckFuture(ScheduledFuture durationCheckFuture) { + if (this.durationCheckFuture != null) { + log.warn("Setting new duration check future while previous is not null for state {}", this, new RuntimeException("stacktrace")); + this.durationCheckFuture.cancel(true); + } + this.durationCheckFuture = durationCheckFuture; + } + + public boolean isEmpty() { + return eventCount == 0L && firstEventTs == 0L && lastCheckTs == 0L && durationCheckFuture == null; + } + + private AlarmSchedule parseSchedule(String str) { + ObjectNode json = (ObjectNode) JacksonUtil.toJsonNode(str); + if (json.isEmpty()) { + return new AnyTimeSchedule(); // only if valid json, fail otherwise + } + + if (!json.hasNonNull("type")) { + // deducting the schedule type + AlarmScheduleType type; + if (json.hasNonNull("daysOfWeek")) { + type = AlarmScheduleType.SPECIFIC_TIME; + } else if (json.hasNonNull("items")) { + type = AlarmScheduleType.CUSTOM; + } else { + throw new IllegalArgumentException("Failed to parse alarm schedule from '" + str + "'"); + } + json.put("type", type.name()); + } + + return JacksonUtil.treeToValue(json, AlarmSchedule.class); + } + + private Integer getIntValue(AlarmConditionValue value) { + return state.resolveValue(value, entry -> Optional.ofNullable(KvUtil.getLongValue(entry)).map(Long::intValue).orElse(null)); + } + + private long getRequiredDurationInMs() { + DurationAlarmCondition durationCondition = (DurationAlarmCondition) condition; + return durationCondition.getUnit().toMillis(state.resolveValue(durationCondition.getValue(), KvUtil::getLongValue)); + } + + private boolean eval(AlarmConditionExpression expression, CalculatedFieldCtx ctx) { + return state.eval(expression, ctx); + } + + public void setAlarmRule(AlarmRule alarmRule) { + this.alarmRule = alarmRule; + this.condition = alarmRule.getCondition(); + + // clearing state for other condition types (possibly left from a previous condition type) + switch (condition.getType()) { + case SIMPLE -> { + clearRepeatingConditionState(); + clearDurationConditionState(); + } + case REPEATING -> { + clearDurationConditionState(); + } + case DURATION -> { + clearRepeatingConditionState(); + } + } + } + + public StateInfo getStateInfo() { + if (condition.getType() == AlarmConditionType.REPEATING) { + return new StateInfo(eventCount, null); + } else if (condition.getType() == AlarmConditionType.DURATION) { + return new StateInfo(null, duration); + } else { + return StateInfo.EMPTY; + } + } + + @Override + public String toString() { + return "AlarmRuleState{" + + "severity=" + severity + + ", condition=" + condition + + ", eventCount=" + eventCount + + ", firstEventTs=" + firstEventTs + + ", lastCheckTs=" + lastCheckTs + + ", duration=" + duration + + ", durationCheckFuture=" + durationCheckFuture + + '}'; + } + + public record StateInfo(Long eventCount, Long duration) { + + static final StateInfo EMPTY = new StateInfo(null, null); + + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java new file mode 100644 index 0000000000..f73d8825f0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.geofencing; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.script.api.tbel.TbelCfGeofencingArg; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.util.ProtoUtils; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.HasLatestTs; + +import java.util.Map; +import java.util.stream.Collectors; + +import static org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState.DEFAULT_LAST_UPDATE_TS; + +@Data +@Slf4j +public class GeofencingArgumentEntry implements ArgumentEntry, HasLatestTs { + + private Map zoneStates; + + private boolean forceResetPrevious; + + public GeofencingArgumentEntry() { + } + + public GeofencingArgumentEntry(EntityId entityId, TransportProtos.AttributeValueProto entry) { + this(Map.of(entityId, ProtoUtils.fromProto(entry))); + } + + public GeofencingArgumentEntry(Map entityIdkvEntryMap) { + this.zoneStates = toZones(entityIdkvEntryMap); + } + + @Override + public ArgumentEntryType getType() { + return ArgumentEntryType.GEOFENCING; + } + + @Override + public Object getValue() { + return zoneStates; + } + + @Override + public long getLatestTs() { + return zoneStates.values().stream() + .mapToLong(GeofencingZoneState::getTs).max().orElse(DEFAULT_LAST_UPDATE_TS); + } + + @Override + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { + if (!(entry instanceof GeofencingArgumentEntry geofencingArgumentEntry)) { + throw new IllegalArgumentException("Unsupported argument entry type for geofencing argument entry: " + entry.getType()); + } + if (geofencingArgumentEntry.isEmpty()) { + zoneStates.clear(); + return true; + } + boolean updated = false; + for (var zoneEntry : geofencingArgumentEntry.getZoneStates().entrySet()) { + if (updateZone(zoneEntry)) { + updated = true; + } + } + return updated; + } + + @Override + public boolean isEmpty() { + return zoneStates == null || zoneStates.isEmpty(); + } + + @Override + public TbelCfArg toTbelCfArg() { + return new TbelCfGeofencingArg(zoneStates); + } + + private Map toZones(Map entityIdKvEntryMap) { + return entityIdKvEntryMap.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + entry -> new GeofencingZoneState(entry.getKey(), entry.getValue()))); + } + + private boolean updateZone(Map.Entry zoneEntry) { + EntityId zoneId = zoneEntry.getKey(); + GeofencingZoneState newZoneState = zoneEntry.getValue(); + + GeofencingZoneState existingZoneState = zoneStates.get(zoneId); + if (existingZoneState == null) { + zoneStates.put(zoneId, newZoneState); + return true; + } + if (newZoneState.getPerimeterDefinition() == null) { + zoneStates.remove(zoneId); + return true; + } + return existingZoneState.update(newZoneState); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java new file mode 100644 index 0000000000..ea669998bd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java @@ -0,0 +1,207 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.geofencing; + +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.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.geo.Coordinates; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingTransitionEvent; +import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus.INSIDE; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus.OUTSIDE; + +@Slf4j +@EqualsAndHashCode(callSuper = true) +public class GeofencingCalculatedFieldState extends BaseCalculatedFieldState implements ScheduledRefreshSupported { + + private long lastDynamicArgumentsRefreshTs = DEFAULT_LAST_UPDATE_TS; + + public GeofencingCalculatedFieldState(EntityId entityId) { + super(entityId); + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.GEOFENCING; + } + + @Override + protected void validateNewEntry(String key, ArgumentEntry newEntry) { + switch (key) { + case ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY -> { + if (!(newEntry instanceof SingleValueArgumentEntry)) { + throw new IllegalArgumentException("Unsupported argument entry type for " + key + " argument: " + newEntry.getType() + ". " + + "Only SINGLE_VALUE type is allowed."); + } + } + default -> { + if (!(newEntry instanceof GeofencingArgumentEntry)) { + throw new IllegalArgumentException("Unsupported argument entry type for " + key + " argument: " + newEntry.getType() + ". " + + "Only GEOFENCING type is allowed."); + } + } + } + } + + @Override + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) { + double latitude = (double) arguments.get(ENTITY_ID_LATITUDE_ARGUMENT_KEY).getValue(); + double longitude = (double) arguments.get(ENTITY_ID_LONGITUDE_ARGUMENT_KEY).getValue(); + Coordinates entityCoordinates = new Coordinates(latitude, longitude); + + var geofencingCfg = (GeofencingCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + Map zoneGroups = geofencingCfg.getZoneGroups(); + + ObjectNode valuesNode = JacksonUtil.newObjectNode(); + List> relationFutures = new ArrayList<>(); + + getGeofencingArguments().forEach((argumentKey, argumentEntry) -> { + ZoneGroupConfiguration zoneGroupCfg = zoneGroups.get(argumentKey); + if (zoneGroupCfg == null) { + throw new RuntimeException("Zone group configuration is missing for the: " + entityId); + } + boolean createRelationsWithMatchedZones = zoneGroupCfg.isCreateRelationsWithMatchedZones(); + List zoneResults = new ArrayList<>(argumentEntry.getZoneStates().size()); + argumentEntry.getZoneStates().forEach((zoneId, zoneState) -> { + boolean firstEval = zoneState.getLastPresence() == null; + GeofencingEvalResult eval = zoneState.evaluate(entityCoordinates); + zoneResults.add(eval); + if (!createRelationsWithMatchedZones) { + return; + } + GeofencingTransitionEvent transitionEvent = eval.transition(); + if (transitionEvent == null) { + if (!firstEval) { + return; + } + transitionEvent = eval.status() == INSIDE ? + GeofencingTransitionEvent.ENTERED : + GeofencingTransitionEvent.LEFT; + } + EntityRelation relation = switch (zoneGroupCfg.getDirection()) { + case TO -> new EntityRelation(zoneId, entityId, zoneGroupCfg.getRelationType()); + case FROM -> new EntityRelation(entityId, zoneId, zoneGroupCfg.getRelationType()); + }; + ListenableFuture f = switch (transitionEvent) { + case ENTERED -> ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), relation); + case LEFT -> ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), relation); + }; + relationFutures.add(f); + }); + updateValuesNode(argumentKey, zoneResults, zoneGroupCfg.getReportStrategy(), valuesNode); + }); + + OutputType outputType = ctx.getOutput().getType(); + var result = TelemetryCalculatedFieldResult.builder() + .outputStrategy(ctx.getOutput().getStrategy()) + .type(outputType) + .scope(ctx.getOutput().getScope()) + .result(toResultNode(valuesNode)) + .build(); + if (relationFutures.isEmpty()) { + return Futures.immediateFuture(result); + } + return Futures.whenAllComplete(relationFutures).call(() -> result, MoreExecutors.directExecutor()); + } + + @Override + public void reset() { + super.reset(); + resetScheduledRefreshTs(); + } + + @Override + public void resetScheduledRefreshTs() { + lastDynamicArgumentsRefreshTs = DEFAULT_LAST_UPDATE_TS; + } + + @Override + public long getLastScheduledRefreshTs() { + return lastDynamicArgumentsRefreshTs; + } + + @Override + public void updateScheduledRefreshTs() { + lastDynamicArgumentsRefreshTs = System.currentTimeMillis(); + } + + private Map getGeofencingArguments() { + return arguments.entrySet() + .stream() + .filter(entry -> entry.getValue().getType().equals(ArgumentEntryType.GEOFENCING)) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> (GeofencingArgumentEntry) entry.getValue())); + } + + private void updateValuesNode(String argumentKey, List zoneResults, GeofencingReportStrategy geofencingReportStrategy, ObjectNode resultNode) { + GeofencingEvalResult aggregationResult = aggregateZoneGroup(zoneResults); + final String eventKey = argumentKey + "Event"; + final String statusKey = argumentKey + "Status"; + switch (geofencingReportStrategy) { + case REPORT_TRANSITION_EVENTS_ONLY -> addTransitionEventIfExists(resultNode, aggregationResult, eventKey); + case REPORT_PRESENCE_STATUS_ONLY -> resultNode.put(statusKey, aggregationResult.status().name()); + case REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS -> { + addTransitionEventIfExists(resultNode, aggregationResult, eventKey); + resultNode.put(statusKey, aggregationResult.status().name()); + } + } + } + + private GeofencingEvalResult aggregateZoneGroup(List zoneResults) { + boolean nowInside = zoneResults.stream().anyMatch(r -> INSIDE.equals(r.status())); + boolean prevInside = zoneResults.stream() + .anyMatch(r -> GeofencingTransitionEvent.LEFT.equals(r.transition()) || r.transition() == null && r.status() == INSIDE); + GeofencingTransitionEvent transition = null; + if (!prevInside && nowInside) { + transition = GeofencingTransitionEvent.ENTERED; + } else if (prevInside && !nowInside) { + transition = GeofencingTransitionEvent.LEFT; + } + return new GeofencingEvalResult(transition, nowInside ? INSIDE : OUTSIDE); + } + + private void addTransitionEventIfExists(ObjectNode resultNode, GeofencingEvalResult aggregationResult, String eventKey) { + if (aggregationResult.transition() != null) { + resultNode.put(eventKey, aggregationResult.transition().name()); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingEvalResult.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingEvalResult.java new file mode 100644 index 0000000000..e0530f621d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingEvalResult.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.geofencing; + +import jakarta.annotation.Nullable; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingTransitionEvent; + +public record GeofencingEvalResult(@Nullable GeofencingTransitionEvent transition, + GeofencingPresenceStatus status) { +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingZoneState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingZoneState.java new file mode 100644 index 0000000000..cd61509c18 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingZoneState.java @@ -0,0 +1,106 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.geofencing; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.geo.Coordinates; +import org.thingsboard.common.util.geo.PerimeterDefinition; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingTransitionEvent; +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.util.ProtoUtils; +import org.thingsboard.server.gen.transport.TransportProtos.GeofencingZoneProto; + +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus.INSIDE; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus.OUTSIDE; + +@Data +public class GeofencingZoneState { + + private final EntityId zoneId; + + private long ts; + private Long version; + private PerimeterDefinition perimeterDefinition; + + @EqualsAndHashCode.Exclude + private GeofencingPresenceStatus lastPresence; + + public GeofencingZoneState(EntityId zoneId, KvEntry entry) { + this.zoneId = zoneId; + if (!(entry instanceof AttributeKvEntry attributeKvEntry)) { + throw new IllegalArgumentException("Invalid perimeter data source for zone with id: " + zoneId + ". Perimeter definition must be stored as attribute!"); + } + this.ts = attributeKvEntry.getLastUpdateTs(); + this.version = attributeKvEntry.getVersion(); + if (entry.getValueAsString() == null) { + throw new IllegalArgumentException("Perimeter attribute '" + entry.getKey() + "' not found for Zone with id: " + zoneId); + } + this.perimeterDefinition = JacksonUtil.fromString(entry.getValueAsString(), PerimeterDefinition.class, + "Invalid perimeter definition format for Zone with id: " + zoneId + ". Failed to parse attribute '" + entry.getKey() + "'"); + } + + public GeofencingZoneState(GeofencingZoneProto proto) { + this.zoneId = ProtoUtils.fromProto(proto.getZoneId()); + this.ts = proto.getTs(); + this.version = proto.getVersion(); + this.perimeterDefinition = JacksonUtil.fromString(proto.getPerimeterDefinition(), PerimeterDefinition.class); + if (proto.hasInside()) { + this.lastPresence = proto.getInside() ? INSIDE : OUTSIDE; + } + } + + public boolean update(GeofencingZoneState newZoneState) { + if (newZoneState.getTs() <= this.ts) { + return false; + } + Long newVersion = newZoneState.getVersion(); + if (newVersion == null || this.version == null || newVersion > this.version) { + this.ts = newZoneState.getTs(); + this.version = newVersion; + this.perimeterDefinition = newZoneState.getPerimeterDefinition(); + this.lastPresence = null; + return true; + } + return false; + } + + public GeofencingEvalResult evaluate(Coordinates entityCoordinates) { + boolean nowInside = perimeterDefinition.checkMatches(entityCoordinates); + + GeofencingPresenceStatus status = nowInside ? INSIDE : OUTSIDE; + + // first evaluation + if (this.lastPresence == null) { + this.lastPresence = status; + return new GeofencingEvalResult(null, status); + } + // State changed + if (this.lastPresence != status) { + this.lastPresence = status; + GeofencingTransitionEvent transition = (status == GeofencingPresenceStatus.INSIDE) ? + GeofencingTransitionEvent.ENTERED : GeofencingTransitionEvent.LEFT; + return new GeofencingEvalResult(transition, status); + } + // State unchanged + return new GeofencingEvalResult(null, status); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/ScheduledRefreshSupported.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/ScheduledRefreshSupported.java new file mode 100644 index 0000000000..4a532ea03d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/ScheduledRefreshSupported.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.geofencing; + +public interface ScheduledRefreshSupported { + + void resetScheduledRefreshTs(); + + long getLastScheduledRefreshTs(); + + void updateScheduledRefreshTs(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java new file mode 100644 index 0000000000..8fdc105168 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java @@ -0,0 +1,118 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.propagation; + +import lombok.Data; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.script.api.tbel.TbelCfPropagationArg; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.HasEntityLimit; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Data +public class PropagationArgumentEntry implements ArgumentEntry, HasEntityLimit { + + private Set entityIds; + private transient List added; + private transient EntityId removed; + + private transient boolean forceResetPrevious; + private transient boolean syncWithDb; + + public PropagationArgumentEntry() { + this.entityIds = new HashSet<>(); + this.added = null; + this.removed = null; + } + + public PropagationArgumentEntry(List entityIds) { + this.entityIds = new HashSet<>(entityIds); + } + + @Override + public ArgumentEntryType getType() { + return ArgumentEntryType.PROPAGATION; + } + + @Override + public Object getValue() { + return entityIds; + } + + @Override + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { + if (!(entry instanceof PropagationArgumentEntry updated)) { + throw new IllegalArgumentException("Unsupported argument entry type for propagation argument entry: " + entry.getType()); + } + if (updated.getAdded() != null) { + return checkAdded(updated.getAdded(), ctx); + } + if (updated.getRemoved() != null) { + return entityIds.remove(updated.getRemoved()); + } + if (updated.isSyncWithDb()) { + Set dbEntityIds = updated.getEntityIds(); + if (dbEntityIds.isEmpty()) { + if (entityIds.isEmpty()) { + return false; + } + entityIds.clear(); + return true; + } + boolean retained = entityIds.retainAll(dbEntityIds); + boolean added = checkAdded(dbEntityIds, ctx); + return retained || added; + } + if (updated.isEmpty()) { + entityIds.clear(); + return true; + } + entityIds = updated.getEntityIds(); + return true; + } + + private boolean checkAdded(Collection updatedIds, CalculatedFieldCtx ctx) { + for (EntityId id : updatedIds) { + checkEntityLimit(entityIds.size(), ctx); + if (entityIds.add(id)) { + if (added == null) { + added = new ArrayList<>(); + } + added.add(id); + } + } + return added != null && !added.isEmpty(); + } + + @Override + public boolean isEmpty() { + return entityIds.isEmpty(); + } + + @Override + public TbelCfArg toTbelCfArg() { + return new TbelCfPropagationArg(entityIds); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java new file mode 100644 index 0000000000..7ab0a51af3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java @@ -0,0 +1,177 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state.propagation; + +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.common.util.JacksonUtil; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.util.CollectionsUtil; +import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; +import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.service.cf.PropagationCalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.ScriptCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; + +@Slf4j +public class PropagationCalculatedFieldState extends ScriptCalculatedFieldState { + + private CalculatedFieldProcessingService cfProcessingService; + + public PropagationCalculatedFieldState(EntityId entityId) { + super(entityId); + } + + @Override + public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { + this.ctx = ctx; + this.actorCtx = actorCtx; + this.cfProcessingService = ctx.getCfProcessingService(); + this.requiredArguments = new ArrayList<>(ctx.getArgNames()); + requiredArguments.add(PROPAGATION_CONFIG_ARGUMENT); + this.readinessStatus = checkReadiness(); + if (ctx.isApplyExpressionForResolvedArguments()) { + this.tbelExpression = ctx.getTbelExpressions().get(ctx.getExpression()); + } + } + + @Override + public void init(boolean restored) { + super.init(restored); + if (restored) { + cfProcessingService.fetchPropagationArgumentFromDb(ctx, entityId).ifPresent(fromDb -> { + fromDb.setSyncWithDb(true); + var updatedArgs = update(Map.of(PROPAGATION_CONFIG_ARGUMENT, fromDb), ctx); + if (updatedArgs.isEmpty()) { + return; + } + log.warn("[{}][{}] Propagation argument was out of sync during state restore and was reconciled with DB.", ctx.getCfId(), entityId); + var updatedPropagationArgument = (PropagationArgumentEntry) arguments.get(PROPAGATION_CONFIG_ARGUMENT); + if (updatedPropagationArgument.getAdded() != null) { + log.warn("[{}][{}] New propagation entities were detected during restore. Scheduling reevaluation for new entities...", ctx.getCfId(), entityId); + ctx.scheduleReevaluation(0L, actorCtx); + } + }); + } + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.PROPAGATION; + } + + @Override + public ListenableFuture performCalculation(Map updatedArgs, CalculatedFieldCtx ctx) { + ArgumentEntry argumentEntry = arguments.get(PROPAGATION_CONFIG_ARGUMENT); + if (!(argumentEntry instanceof PropagationArgumentEntry propagationArgumentEntry)) { + return Futures.immediateFuture(PropagationCalculatedFieldResult.builder().build()); + } + boolean newEntityAdded = propagationArgumentEntry.getAdded() != null; + List entityIds; + if (newEntityAdded) { + entityIds = propagationArgumentEntry.getAdded(); + propagationArgumentEntry.setAdded(null); + } else { + if (propagationArgumentEntry.getEntityIds().isEmpty()) { + return Futures.immediateFuture(PropagationCalculatedFieldResult.builder().build()); + } + entityIds = List.copyOf(propagationArgumentEntry.getEntityIds()); + } + if (ctx.isApplyExpressionForResolvedArguments()) { + return Futures.transform(super.performCalculation(updatedArgs, ctx), telemetryCfResult -> + PropagationCalculatedFieldResult.builder() + .entityIds(entityIds) + .result((TelemetryCalculatedFieldResult) telemetryCfResult) + .build(), + MoreExecutors.directExecutor()); + } + if (newEntityAdded || CollectionsUtil.isEmpty(updatedArgs)) { + updatedArgs = arguments; + } + return Futures.immediateFuture(PropagationCalculatedFieldResult.builder() + .entityIds(entityIds) + .result(toTelemetryResult(ctx, updatedArgs)) + .build()); + } + + @Override + protected ReadinessStatus checkReadiness() { + if (ctx.isApplyExpressionForResolvedArguments() || arguments == null) { + return super.checkReadiness(); + } + boolean propagationNotEmpty = false; + boolean hasOtherNonEmpty = false; + List emptyArguments = null; + for (String requiredArgumentKey : requiredArguments) { + ArgumentEntry argumentEntry = arguments.get(requiredArgumentKey); + if (argumentEntry == null || argumentEntry.isEmpty()) { + if (emptyArguments == null) { + emptyArguments = new ArrayList<>(); + } + emptyArguments.add(requiredArgumentKey); + } else if (PROPAGATION_CONFIG_ARGUMENT.equals(requiredArgumentKey)) { + propagationNotEmpty = true; + } else { + hasOtherNonEmpty = true; + } + } + if (propagationNotEmpty && hasOtherNonEmpty) { + return ReadinessStatus.READY; + } + return ReadinessStatus.from(emptyArguments); + } + + private TelemetryCalculatedFieldResult toTelemetryResult(CalculatedFieldCtx ctx, Map updatedArgs) { + Output output = ctx.getOutput(); + TelemetryCalculatedFieldResult.TelemetryCalculatedFieldResultBuilder telemetryCfBuilder = + TelemetryCalculatedFieldResult.builder() + .outputStrategy(output.getStrategy()) + .type(output.getType()) + .scope(output.getScope()); + ObjectNode valuesNode = JacksonUtil.newObjectNode(); + updatedArgs.forEach((outputKey, argumentEntry) -> { + if (argumentEntry instanceof PropagationArgumentEntry) { + return; + } + if (argumentEntry instanceof SingleValueArgumentEntry singleArgumentEntry) { + if (!singleArgumentEntry.isEmpty()) { + JacksonUtil.addKvEntry(valuesNode, singleArgumentEntry.getKvEntryValue(), outputKey); + } + return; + } + throw new IllegalArgumentException("Unsupported argument type: " + argumentEntry.getType() + " detected for argument: " + outputKey + ". " + + "Only Latest telemetry or Attribute arguments supported for 'Arguments Only' propagation mode!"); + }); + telemetryCfBuilder.result(toResultNode(valuesNode)); + return telemetryCfBuilder.build(); + } + +} 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 fbb3591bb5..a3b9ba2810 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 @@ -24,6 +24,7 @@ import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; +import org.thingsboard.server.dao.ai.AiModelService; import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetProfileService; @@ -59,6 +60,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.ai.AiModelProcessor; import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmProcessor; import org.thingsboard.server.service.edge.rpc.processor.alarm.comment.AlarmCommentProcessor; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessor; @@ -73,6 +75,7 @@ import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgePr import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainMetadataEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.telemetry.TelemetryEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.user.UserProcessor; import org.thingsboard.server.service.edge.rpc.sync.EdgeRequestsService; import org.thingsboard.server.service.executors.GrpcCallbackExecutorService; @@ -203,7 +206,6 @@ public class EdgeContextComponent { @Autowired private Optional statsCounterService; - // processors @Autowired private AlarmProcessor alarmProcessor; @@ -261,6 +263,15 @@ public class EdgeContextComponent { @Autowired private CalculatedFieldProcessor calculatedFieldProcessor; + @Autowired + private AiModelService aiModelService; + + @Autowired + private AiModelProcessor aiModelProcessor; + + @Autowired + private UserProcessor userProcessor; + public EdgeProcessor getProcessor(EdgeEventType edgeEventType) { EdgeProcessor processor = processorMap.get(edgeEventType); if (processor == null) { 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 index a01bfdc009..1e737df4e4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -113,7 +113,7 @@ public class EdgeEventSourcingListener { return; } try { - if (EntityType.TENANT == entityType || EntityType.EDGE == entityType || EntityType.AI_MODEL == entityType) { + if (EntityType.TENANT == entityType || EntityType.EDGE == entityType) { return; } log.trace("[{}] DeleteEntityEvent called: {}", tenantId, event); @@ -227,7 +227,7 @@ public class EdgeEventSourcingListener { break; case TENANT: return !event.getCreated(); - case API_USAGE_STATE, EDGE, AI_MODEL: + case API_USAGE_STATE, EDGE: return false; case DOMAIN: if (entity instanceof Domain domain) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java index 2417931e0b..9ecfd2be08 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java @@ -21,11 +21,13 @@ 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.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.action.TbSaveToCustomCassandraTableNode; +import org.thingsboard.rule.engine.ai.TbAiNode; import org.thingsboard.rule.engine.aws.lambda.TbAwsLambdaNode; import org.thingsboard.rule.engine.rest.TbSendRestApiCallReplyNode; import org.thingsboard.rule.engine.telemetry.TbCalculatedFieldsNode; @@ -44,14 +46,19 @@ 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.User; +import org.thingsboard.server.common.data.ai.AiModel; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.domain.DomainInfo; 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.id.AiModelId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CalculatedFieldId; @@ -74,6 +81,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; 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; @@ -86,6 +94,8 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.common.transport.util.JsonUtils; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; @@ -124,11 +134,16 @@ import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j public class EdgeMsgConstructorUtils { @@ -153,6 +168,14 @@ public class EdgeMsgConstructorUtils { ); public static final Map> EXCLUDED_NODES_BY_EDGE_VERSION = Map.of( + EdgeVersion.V_4_1_0, + Set.of( + TbAiNode.class.getName() + ), + EdgeVersion.V_4_0_0, + Set.of( + TbAiNode.class.getName() + ), EdgeVersion.V_3_9_0, Set.of( TbCalculatedFieldsNode.class.getName() @@ -249,12 +272,40 @@ public class EdgeMsgConstructorUtils { return DeviceCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(deviceCredentials)).build(); } - public static DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { - return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) + public static DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { + String entity = getEntityAndFixLwm2mBootstrapShortServerId(deviceProfile, edgeVersion); + return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(entity) .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()).build(); } + public static String getEntityAndFixLwm2mBootstrapShortServerId(DeviceProfile deviceProfile, EdgeVersion edgeVersion) { + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + if (!(transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) || edgeVersion.getNumber() >= EdgeVersion.V_4_3_0.getNumber()) { + return JacksonUtil.toString(deviceProfile); + } + JsonNode jsonNode = JacksonUtil.valueToTree(deviceProfile); + JsonNode profileDataNode = jsonNode.get("profileData"); + if (profileDataNode != null && profileDataNode.has("transportConfiguration")) { + JsonNode transportConfigNode = profileDataNode.get("transportConfiguration"); + JsonNode bootstrapNode = transportConfigNode.get("bootstrap"); + if (bootstrapNode != null && bootstrapNode.isArray()) { + for (JsonNode bootstrapServerNode : bootstrapNode) { + if (bootstrapServerNode.isObject()) { + ObjectNode serverObjectNode = (ObjectNode) bootstrapServerNode; + JsonNode isBootstrapNode = serverObjectNode.get("bootstrapServerIs"); + boolean isBootstrapServer = isBootstrapNode != null && isBootstrapNode.asBoolean(false); + JsonNode shortServerIdNode = serverObjectNode.get("shortServerId"); + if (isBootstrapServer && (shortServerIdNode == null || shortServerIdNode.isNull())) { + serverObjectNode.put("shortServerId", 0); + } + } + } + } + } + return JacksonUtil.toString(jsonNode); + } + public static DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId) { return DeviceProfileUpdateMsg.newBuilder() .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) @@ -518,7 +569,7 @@ public class EdgeMsgConstructorUtils { .setEntityIdMSB(entityId.getId().getMostSignificantBits()) .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) .setEntityType(entityId.getEntityType().name()); - long ts = getTs(entityData.getAsJsonObject()); + long ts = extractTs(entityData.getAsJsonObject()); switch (actionType) { case TIMESERIES_UPDATED: try { @@ -571,8 +622,8 @@ public class EdgeMsgConstructorUtils { return builder.build(); } - private static long getTs(JsonObject data) { - if (data.get("ts") != null && !data.get("ts").isJsonNull()) { + private static long extractTs(JsonObject data) { + if (data.has("ts") && data.get("ts").isJsonPrimitive()) { return data.getAsJsonPrimitive("ts").getAsLong(); } return System.currentTimeMillis(); @@ -654,4 +705,146 @@ public class EdgeMsgConstructorUtils { .setIdLSB(calculatedFieldId.getId().getLeastSignificantBits()).build(); } + public static AiModelUpdateMsg constructAiModelUpdatedMsg(UpdateMsgType msgType, AiModel aiModel) { + return AiModelUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(aiModel)) + .setIdMSB(aiModel.getId().getId().getMostSignificantBits()) + .setIdLSB(aiModel.getId().getId().getLeastSignificantBits()).build(); + } + + public static AiModelUpdateMsg constructAiModelDeleteMsg(AiModelId aiModelId) { + return AiModelUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(aiModelId.getId().getMostSignificantBits()) + .setIdLSB(aiModelId.getId().getLeastSignificantBits()).build(); + } + + public static List mergeAndFilterDownlinkDuplicates(List edgeEvents) { + try { + edgeEvents = removeDownlinkDuplicates(edgeEvents); + + List attrUpdateMsgs = new ArrayList<>(); + for (EdgeEvent edgeEvent : edgeEvents) { + if (EdgeEventActionType.ATTRIBUTES_UPDATED.equals(edgeEvent.getAction())) { + attrUpdateMsgs.add(new AttrUpdateMsg(edgeEvent.getEntityId(), edgeEvent.getBody())); + } + } + Map> latestTsByEntityAndKey = computeLatestTsByEntityAndKey(attrUpdateMsgs); + + List result = new ArrayList<>(); + for (EdgeEvent edgeEvent : edgeEvents) { + if (!EdgeEventActionType.ATTRIBUTES_UPDATED.equals(edgeEvent.getAction())) { + result.add(edgeEvent); + continue; + } + + Map latestByKey = latestTsByEntityAndKey.get(edgeEvent.getEntityId()); + JsonNode filteredBody = filterAttributesBody(edgeEvent.getBody(), latestByKey); + if (filteredBody == null) { + continue; + } + + result.add(createFilteredEdgeEvent(edgeEvent, filteredBody)); + } + + result.sort(Comparator.comparingLong(EdgeEvent::getSeqId)); + return result; + } catch (Exception e) { + log.info("Can't merge downlink duplicates. Sending downlinks without merge. Original edgeEvents [{}]", edgeEvents, e); + return edgeEvents; + } + } + + private static AttrsTs extractAttributes(JsonNode body) { + if (body == null) { + return new AttrsTs(0L, List.of()); + } + String bodyStr = JacksonUtil.toString(body); + var jsonObject = JsonParser.parseString(bodyStr).getAsJsonObject(); + if (!jsonObject.has("ts")) { + return new AttrsTs(0L, List.of()); + } + long ts = jsonObject.get("ts").getAsLong(); + var kv = jsonObject.getAsJsonObject("kv"); + List attrs = JsonConverter.convertToAttributes( + JsonUtils.getJsonObject( + JsonConverter.convertToAttributesProto(kv).getKvList() + ), ts); + return new AttrsTs(ts, attrs); + } + + private static JsonNode filterAttributesBody(JsonNode body, Map latestByKey) { + if (body == null) { + return null; + } + String bodyStr = JacksonUtil.toString(body); + JsonObject jsonObject = JsonParser.parseString(bodyStr).getAsJsonObject(); + if (jsonObject.has("ts") && latestByKey != null && !latestByKey.isEmpty()) { + long ts = jsonObject.get("ts").getAsLong(); + JsonObject kv = jsonObject.getAsJsonObject("kv"); + for (Iterator> it = kv.entrySet().iterator(); it.hasNext(); ) { + Map.Entry e = it.next(); + Long latestTs = latestByKey.get(e.getKey()); + if (latestTs == null || !latestTs.equals(ts)) { + it.remove(); + } + } + if (kv.isEmpty()) { + return null; + } + } + return JacksonUtil.toJsonNode(jsonObject.toString()); + } + + private static Map> computeLatestTsByEntityAndKey(List attrUpdateMsgs) { + Map> latestTsByEntityAndKey = new HashMap<>(); + for (AttrUpdateMsg attrUpdateMsg : attrUpdateMsgs) { + UUID entityId = attrUpdateMsg.entityId(); + AttrsTs attrsTs = extractAttributes(attrUpdateMsg.body()); + Map map = latestTsByEntityAndKey.computeIfAbsent(entityId, id -> new HashMap<>()); + long ts = attrsTs.ts(); + for (AttributeKvEntry attr : attrsTs.attrs()) { + map.merge(attr.getKey(), ts, Math::max); + } + } + return latestTsByEntityAndKey; + } + + private static EdgeEvent createFilteredEdgeEvent(EdgeEvent edgeEvent, JsonNode filteredBody) { + EdgeEvent filtered = new EdgeEvent(); + filtered.setSeqId(edgeEvent.getSeqId()); + filtered.setTenantId(edgeEvent.getTenantId()); + filtered.setEdgeId(edgeEvent.getEdgeId()); + filtered.setAction(edgeEvent.getAction()); + filtered.setEntityId(edgeEvent.getEntityId()); + filtered.setUid(edgeEvent.getUid()); + filtered.setType(edgeEvent.getType()); + filtered.setBody(filteredBody); + return filtered; + } + + private static List removeDownlinkDuplicates(List edgeEvents) { + Set seen = new HashSet<>(); + return edgeEvents.stream() + .filter(e -> seen.add(new EventKey( + e.getTenantId(), + e.getAction(), + e.getEntityId(), + e.getType().name(), + (e.getBody() != null ? e.getBody().toString() : "null")))) + .collect(Collectors.toList()); + } + + private record EventKey(TenantId tenantId, + EdgeEventActionType action, + UUID entityId, + String type, + String body) { + } + + private record AttrsTs(long ts, List attrs) { + } + + private record AttrUpdateMsg(UUID entityId, JsonNode body) { + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/RelatedEdgesSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/RelatedEdgesSourcingListener.java index e9512504c9..a46a204cee 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/RelatedEdgesSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/RelatedEdgesSourcingListener.java @@ -54,16 +54,18 @@ public class RelatedEdgesSourcingListener { @TransactionalEventListener(fallbackExecution = true) public void handleEvent(ActionEntityEvent event) { - executorService.submit(() -> { - log.trace("[{}] ActionEntityEvent called: {}", event.getTenantId(), event); - try { - switch (event.getActionType()) { - case ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE -> relatedEdgesService.publishRelatedEdgeIdsEvictEvent(event.getTenantId(), event.getEntityId()); - } - } catch (Exception e) { - log.error("[{}] failed to process ActionEntityEvent: {}", event.getTenantId(), event, e); + switch (event.getActionType()) { + case ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE -> { + executorService.submit(() -> { + log.trace("[{}] ActionEntityEvent called: {}", event.getTenantId(), event); + try { + relatedEdgesService.publishRelatedEdgeIdsEvictEvent(event.getTenantId(), event.getEntityId()); + } catch (Exception e) { + log.error("[{}] failed to process ActionEntityEvent: {}", event.getTenantId(), event, e); + } + }); } - }); + } } @TransactionalEventListener( diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventStorageSettings.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventStorageSettings.java index 915e2613a8..b7153a4918 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventStorageSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventStorageSettings.java @@ -29,4 +29,6 @@ public class EdgeEventStorageSettings { private long noRecordsSleepInterval; @Value("${edges.storage.sleep_between_batches}") private long sleepIntervalBetweenBatches; + @Value("${edges.storage.misordering_compensation_millis:60000}") + private long misorderingCompensationMillis; } 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 03228c52d4..76070bec53 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 @@ -706,7 +706,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i !kafkaSession.getConsumer().getConsumer().isStopped(); } return false; - } } 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 fb420d080a..2b026f207f 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 @@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; import org.thingsboard.server.dao.edge.stats.EdgeStatsKey; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; @@ -83,6 +84,8 @@ import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg; import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg; import org.thingsboard.server.service.edge.EdgeContextComponent; import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils; @@ -100,6 +103,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; @@ -121,6 +125,7 @@ public abstract class EdgeGrpcSession implements Closeable { private final EdgeSessionState sessionState = new EdgeSessionState(); private final ReentrantLock downlinkMsgLock = new ReentrantLock(); + private final Lock sequenceDependencyLock = new ReentrantLock(); protected EdgeContextComponent ctx; protected Edge edge; @@ -301,7 +306,8 @@ public abstract class EdgeGrpcSession implements Closeable { if (isConnected() && !pageData.getData().isEmpty()) { if (fetcher instanceof GeneralEdgeEventFetcher) { long queueSize = pageData.getTotalElements() - ((long) pageLink.getPageSize() * pageLink.getPage()); - ctx.getStatsCounterService().ifPresent(statsCounterService -> statsCounterService.setDownlinkMsgsLag(edge.getTenantId(), edge.getId(), queueSize)); + ctx.getStatsCounterService().ifPresent(statsCounterService -> + statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_LAG, tenantId, edge.getId(), queueSize)); } log.trace("[{}][{}][{}] event(s) are going to be processed.", tenantId, edge.getId(), pageData.getData().size()); List downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData()); @@ -499,7 +505,8 @@ public abstract class EdgeGrpcSession implements Closeable { ctx.getRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg) .error("Failed to deliver messages after " + MAX_DOWNLINK_ATTEMPTS + " attempts").build()); - ctx.getStatsCounterService().ifPresent(statsCounterService -> statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_PERMANENTLY_FAILED, edge.getTenantId(), edge.getId(), copy.size())); + ctx.getStatsCounterService().ifPresent(statsCounterService -> + statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_PERMANENTLY_FAILED, edge.getTenantId(), edge.getId(), copy.size())); stopCurrentSendDownlinkMsgsTask(false); } } else { @@ -539,7 +546,8 @@ public abstract class EdgeGrpcSession implements Closeable { try { if (msg.getSuccess()) { sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId()); - ctx.getStatsCounterService().ifPresent(statsCounterService -> statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_PUSHED, edge.getTenantId(), edge.getId(), 1)); + ctx.getStatsCounterService().ifPresent(statsCounterService -> + statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_PUSHED, edge.getTenantId(), edge.getId(), 1)); log.debug("[{}][{}][{}] Msg has been processed successfully! Msg Id: [{}], Msg: {}", tenantId, edge.getId(), sessionId, msg.getDownlinkMsgId(), msg); } else { log.debug("[{}][{}][{}] Msg processing failed! Msg Id: [{}], Error msg: {}", tenantId, edge.getId(), sessionId, msg.getDownlinkMsgId(), msg.getErrorMsg()); @@ -589,7 +597,8 @@ public abstract class EdgeGrpcSession implements Closeable { previousStartSeqId, false, Integer.toUnsignedLong(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()), - ctx.getEdgeEventService()); + ctx.getEdgeEventService(), + ctx.getEdgeEventStorageSettings().getMisorderingCompensationMillis()); log.trace("[{}][{}] starting processing edge events, previousStartTs = {}, previousStartSeqId = {}", tenantId, edge.getId(), previousStartTs, previousStartSeqId); Futures.addCallback(startProcessingEdgeEvents(fetcher), new FutureCallback<>() { @@ -645,7 +654,8 @@ public abstract class EdgeGrpcSession implements Closeable { protected List convertToDownlinkMsgsPack(List edgeEvents) { List result = new ArrayList<>(); - for (EdgeEvent edgeEvent : edgeEvents) { + List filtered = EdgeMsgConstructorUtils.mergeAndFilterDownlinkDuplicates(edgeEvents); + for (EdgeEvent edgeEvent : filtered) { log.trace("[{}][{}] converting edge event to downlink msg [{}]", tenantId, edge.getId(), edgeEvent); DownlinkMsg downlinkMsg = null; try { @@ -807,7 +817,8 @@ public abstract class EdgeGrpcSession implements Closeable { } } highPriorityQueue.add(edgeEvent); - ctx.getStatsCounterService().ifPresent(statsCounterService -> statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_ADDED, edge.getTenantId(), edgeEvent.getEdgeId(), 1)); + ctx.getStatsCounterService().ifPresent(statsCounterService -> + statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_ADDED, edge.getTenantId(), edgeEvent.getEdgeId(), 1)); } protected ListenableFuture> processUplinkMsg(UplinkMsg uplinkMsg) { @@ -933,6 +944,31 @@ public abstract class EdgeGrpcSession implements Closeable { result.add(ctx.getCalculatedFieldProcessor().processCalculatedFieldMsgFromEdge(edge.getTenantId(), edge, calculatedFieldUpdateMsg)); } } + if (uplinkMsg.getAiModelUpdateMsgCount() > 0) { + for (AiModelUpdateMsg aiModelUpdateMsg : uplinkMsg.getAiModelUpdateMsgList()) { + result.add(ctx.getAiModelProcessor().processAiModelMsgFromEdge(edge.getTenantId(), edge, aiModelUpdateMsg)); + } + } + if (uplinkMsg.getUserUpdateMsgCount() > 0) { + for (UserUpdateMsg userUpdateMsg : uplinkMsg.getUserUpdateMsgList()) { + sequenceDependencyLock.lock(); + try { + result.add(ctx.getUserProcessor().processUserMsgFromEdge(edge.getTenantId(), edge, userUpdateMsg)); + } finally { + sequenceDependencyLock.unlock(); + } + } + } + if (uplinkMsg.getUserCredentialsUpdateMsgCount() > 0) { + for (UserCredentialsUpdateMsg userCredentialsUpdateMsg : uplinkMsg.getUserCredentialsUpdateMsgList()) { + sequenceDependencyLock.lock(); + try { + result.add(ctx.getUserProcessor().processUserCredentialsMsgFromEdge(edge.getTenantId(), edge, userCredentialsUpdateMsg)); + } finally { + sequenceDependencyLock.unlock(); + } + } + } } catch (Exception e) { String failureMsg = String.format("Can't process uplink msg [%s] from edge", uplinkMsg); log.trace("[{}][{}] Can't process uplink msg [{}]", tenantId, edge.getId(), uplinkMsg, e); 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 bf3c94ca44..55f61ed54f 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 @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.edge.EdgeContextComponent; import org.thingsboard.server.service.edge.rpc.fetch.AdminSettingsEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.AiModelEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.AssetProfilesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher; @@ -64,6 +65,12 @@ public class EdgeSyncCursor { fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService())); fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService())); fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService())); + fetchers.add(new OAuth2EdgeEventFetcher(ctx.getDomainService())); + 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 AiModelEdgeEventFetcher(ctx.getAiModelService())); } Customer publicCustomer = ctx.getCustomerService().findPublicCustomer(edge.getTenantId()); if (publicCustomer != null) { @@ -84,14 +91,10 @@ public class EdgeSyncCursor { fetchers.add(new NotificationTemplateEdgeEventFetcher(ctx.getNotificationTemplateService())); fetchers.add(new NotificationTargetEdgeEventFetcher(ctx.getNotificationTargetService())); fetchers.add(new NotificationRuleEdgeEventFetcher(ctx.getNotificationRuleService())); - 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())); + // sync device profiles twice to update software and hardware fields fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService())); fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService())); - fetchers.add(new OAuth2EdgeEventFetcher(ctx.getDomainService())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeEventService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeEventService.java index 0f52af6c4d..e9ba942afb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeEventService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeEventService.java @@ -52,7 +52,8 @@ public class KafkaEdgeEventService extends BaseEdgeEventService { TopicPartitionInfo tpi = topicService.getEdgeEventNotificationsTopic(edgeEvent.getTenantId(), edgeEvent.getEdgeId()); ToEdgeEventNotificationMsg msg = ToEdgeEventNotificationMsg.newBuilder().setEdgeEventMsg(ProtoUtils.toProto(edgeEvent)).build(); producerProvider.getTbEdgeEventsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); - statsCounterService.ifPresent(statsCounterService -> statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_ADDED, edgeEvent.getTenantId(), edgeEvent.getEdgeId(), 1)); + statsCounterService.ifPresent(statsCounterService -> + statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_ADDED, edgeEvent.getTenantId(), edgeEvent.getEdgeId(), 1)); return Futures.immediateFuture(null); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AiModelEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AiModelEdgeEventFetcher.java new file mode 100644 index 0000000000..f5ce48f696 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AiModelEdgeEventFetcher.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ai.AiModel; +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.ai.AiModelService; + +@AllArgsConstructor +@Slf4j +public class AiModelEdgeEventFetcher extends BasePageableEdgeEventFetcher { + + private final AiModelService aiModelService; + + @Override + PageData fetchEntities(TenantId tenantId, Edge edge, PageLink pageLink) { + return aiModelService.findAiModelsByTenantId(tenantId, pageLink); + } + + @Override + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, AiModel aiModel) { + return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.AI_MODEL, + EdgeEventActionType.ADDED, aiModel.getId(), null); + } + +} 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 3c8dc6ead6..e5dcc4410f 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 @@ -26,13 +26,9 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.edge.EdgeEventService; -import java.util.concurrent.TimeUnit; - @AllArgsConstructor @Slf4j public class GeneralEdgeEventFetcher implements EdgeEventFetcher { - // Subtract from queueStartTs to ensure no data is lost due to potential misordering of edge events by created_time. - private static final long MISORDERING_COMPENSATION_MILLIS = TimeUnit.SECONDS.toMillis(60); private final Long queueStartTs; private Long seqIdStart; @@ -40,6 +36,10 @@ public class GeneralEdgeEventFetcher implements EdgeEventFetcher { private boolean seqIdNewCycleStarted; private Long maxReadRecordsCount; private final EdgeEventService edgeEventService; + // Subtract from queueStartTs to compensate for possible misalignment between `created_time` and `seqId`. + // This ensures early events with lower seqId are not skipped due to partitioning by `created_time`. + // See: edge_event is partitioned by created_time but sorted by seqId during retrieval. + private final long misorderingCompensationMillis; @Override public PageLink getPageLink(int pageSize) { @@ -48,7 +48,7 @@ public class GeneralEdgeEventFetcher implements EdgeEventFetcher { 0, null, null, - queueStartTs > 0 ? queueStartTs - MISORDERING_COMPENSATION_MILLIS : 0, + queueStartTs > 0 ? queueStartTs - misorderingCompensationMillis : 0, System.currentTimeMillis()); } 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 5cfd16aa90..cc77a41f6a 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 @@ -19,12 +19,17 @@ 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.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.HasCustomerId; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasVersion; +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; @@ -37,6 +42,7 @@ import org.thingsboard.server.common.data.id.EdgeId; 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.HasId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -61,6 +67,7 @@ import org.thingsboard.server.service.edge.EdgeContextComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.state.DefaultDeviceStateService; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -139,7 +146,7 @@ public abstract class BaseEdgeProcessor implements EdgeProcessor { UPDATED_COMMENT, DELETED -> true; default -> switch (type) { case ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_CHAIN_METADATA, USER, CUSTOMER, TENANT, TENANT_PROFILE, - WIDGETS_BUNDLE, WIDGET_TYPE, ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION, CALCULATED_FIELD, NOTIFICATION_TEMPLATE, + WIDGETS_BUNDLE, WIDGET_TYPE, ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION, CALCULATED_FIELD, AI_MODEL, NOTIFICATION_TEMPLATE, NOTIFICATION_TARGET, NOTIFICATION_RULE -> true; default -> false; }; @@ -348,6 +355,29 @@ public abstract class BaseEdgeProcessor implements EdgeProcessor { edgeCtx.getRelationService().saveRelation(tenantId, relation); } + protected > void pushEntityEventToRuleEngine(TenantId tenantId, T entity, TbMsgType msgType) { + pushEntityEventToRuleEngine(tenantId, null, entity, msgType); + } + + protected > void pushEntityEventToRuleEngine(TenantId tenantId, Edge edge, T entity, TbMsgType msgType) { + try { + String entityAsString = JacksonUtil.toString(entity); + CustomerId customerId = getCustomerId(entity); + TbMsgMetaData tbMsgMetaData = edge == null ? TbMsgMetaData.EMPTY : getEdgeActionTbMsgMetaData(edge, customerId); + + pushEntityEventToRuleEngine(tenantId, entity.getId(), customerId, msgType, entityAsString, tbMsgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push entity action for {} to rule engine: {}", tenantId, entity.getId(), entity.getId().getEntityType(), msgType.name(), e); + } + } + + private > CustomerId getCustomerId(T entity) { + if (entity instanceof HasCustomerId hasCustomer) { + return hasCustomer.getCustomerId(); + } + return null; + } + protected TbMsgMetaData getEdgeActionTbMsgMetaData(Edge edge, CustomerId customerId) { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("edgeId", edge.getId().toString()); @@ -381,4 +411,26 @@ public abstract class BaseEdgeProcessor implements EdgeProcessor { }); } + protected boolean isSaveRequired(HasVersion current, HasVersion updated) { + updated.setVersion(null); + return !updated.equals(current); + } + + protected > Optional generateUniqueNameIfDuplicateExists( + TenantId tenantId, I entityId, E entity, @Nullable E entityWithSameName) { + + if (entityWithSameName == null || entityWithSameName.getId().equals(entityId)) { + return Optional.empty(); + } + String currentName = entity.getName(); + String newEntityName = generateRandomAlphabeticString(currentName); + + log.warn("[{}] Entity with name '{}' already exists (id={}). Renaming to '{}'", tenantId, currentName, entityWithSameName.getId(), newEntityName); + return Optional.of(newEntityName); + } + + protected static String generateRandomAlphabeticString(String prefix) { + return prefix + "_" + StringUtils.randomAlphabetic(15); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/AiModelEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/AiModelEdgeProcessor.java new file mode 100644 index 0000000000..afb44d4607 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/AiModelEdgeProcessor.java @@ -0,0 +1,115 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ai; + +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.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.ai.AiModel; +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.AiModelId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.exception.DataValidationException; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; +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.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils; + +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@Component +@TbCoreComponent +public class AiModelEdgeProcessor extends BaseAiModelProcessor implements AiModelProcessor { + + @Override + public ListenableFuture processAiModelMsgFromEdge(TenantId tenantId, Edge edge, AiModelUpdateMsg aiModelUpdateMsg) { + AiModelId aiModelId = new AiModelId(new UUID(aiModelUpdateMsg.getIdMSB(), aiModelUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + return switch (aiModelUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE, ENTITY_UPDATED_RPC_MESSAGE -> { + processAiModel(tenantId, aiModelId, aiModelUpdateMsg, edge); + yield Futures.immediateFuture(null); + } + case ENTITY_DELETED_RPC_MESSAGE -> { + deleteAiModel(tenantId, edge, aiModelId); + yield Futures.immediateFuture(null); + } + default -> handleUnsupportedMsgType(aiModelUpdateMsg.getMsgType()); + }; + } catch (DataValidationException e) { + return Futures.immediateFailedFuture(e); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + @Override + public DownlinkMsg convertEdgeEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + AiModelId aiModelId = new AiModelId(edgeEvent.getEntityId()); + switch (edgeEvent.getAction()) { + case ADDED, UPDATED -> { + Optional aiModel = edgeCtx.getAiModelService().findAiModelById(edgeEvent.getTenantId(), aiModelId); + if (aiModel.isPresent()) { + UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); + AiModelUpdateMsg aiModelUpdateMsg = EdgeMsgConstructorUtils.constructAiModelUpdatedMsg(msgType, aiModel.get()); + return DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addAiModelUpdateMsg(aiModelUpdateMsg) + .build(); + } + } + case DELETED -> { + AiModelUpdateMsg aiModelUpdateMsg = EdgeMsgConstructorUtils.constructAiModelDeleteMsg(aiModelId); + return DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addAiModelUpdateMsg(aiModelUpdateMsg) + .build(); + } + } + return null; + } + + private void processAiModel(TenantId tenantId, AiModelId aiModelId, AiModelUpdateMsg aiModelUpdateMsg, Edge edge) { + Pair resultPair = super.saveOrUpdateAiModel(tenantId, aiModelId, aiModelUpdateMsg); + Boolean created = resultPair.getFirst(); + if (created) { + Optional aiModel = edgeCtx.getAiModelService().findAiModelById(tenantId, aiModelId); + aiModel.ifPresent(model -> pushEntityEventToRuleEngine(tenantId, edge, model, TbMsgType.ENTITY_CREATED)); + } + Boolean aiModelNameUpdated = resultPair.getSecond(); + if (aiModelNameUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.AI_MODEL, EdgeEventActionType.UPDATED, aiModelId, null); + } + } + + @Override + public EdgeEventType getEdgeEventType() { + return EdgeEventType.AI_MODEL; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/AiModelProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/AiModelProcessor.java new file mode 100644 index 0000000000..c8ae278608 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/AiModelProcessor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ai; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface AiModelProcessor extends EdgeProcessor { + + ListenableFuture processAiModelMsgFromEdge(TenantId tenantId, Edge edge, AiModelUpdateMsg aiModelUpdateMsg); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/BaseAiModelProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/BaseAiModelProcessor.java new file mode 100644 index 0000000000..ccc54f75d9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ai/BaseAiModelProcessor.java @@ -0,0 +1,91 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ai; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +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.StringUtils; +import org.thingsboard.server.common.data.ai.AiModel; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.AiModelId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +import java.util.Optional; + +@Slf4j +public abstract class BaseAiModelProcessor extends BaseEdgeProcessor { + + @Autowired + private DataValidator aiModelValidator; + + protected Pair saveOrUpdateAiModel(TenantId tenantId, AiModelId aiModelId, AiModelUpdateMsg aiModelUpdateMsg) { + boolean isCreated = false; + boolean isNameUpdated = false; + try { + AiModel aiModel = JacksonUtil.fromString(aiModelUpdateMsg.getEntity(), AiModel.class, true); + if (aiModel == null) { + throw new RuntimeException("[{" + tenantId + "}] aiModelUpdateMsg {" + aiModelUpdateMsg + " } cannot be converted to aiModel"); + } + + Optional aiModelById = edgeCtx.getAiModelService().findAiModelById(tenantId, aiModelId); + if (aiModelById.isEmpty()) { + aiModel.setCreatedTime(Uuids.unixTimestamp(aiModelId.getId())); + isCreated = true; + aiModel.setId(null); + } else { + aiModel.setId(aiModelId); + } + + String aiModelName = aiModel.getName(); + Optional aiModelByName = edgeCtx.getAiModelService().findAiModelByTenantIdAndName(aiModel.getTenantId(), aiModelName); + if (aiModelByName.isPresent() && !aiModelByName.get().getId().equals(aiModelId)) { + aiModelName = aiModelName + "_" + StringUtils.randomAlphabetic(15); + log.warn("[{}] aiModel with name {} already exists. Renaming aiModel name to {}", + tenantId, aiModel.getName(), aiModelByName.get().getName()); + isNameUpdated = true; + } + aiModel.setName(aiModelName); + + aiModelValidator.validate(aiModel, AiModel::getTenantId); + + if (isCreated) { + aiModel.setId(aiModelId); + } + + edgeCtx.getAiModelService().save(aiModel, false); + } catch (Exception e) { + log.error("[{}] Failed to process aiModel update msg [{}]", tenantId, aiModelUpdateMsg, e); + throw e; + } + return Pair.of(isCreated, isNameUpdated); + } + + protected void deleteAiModel(TenantId tenantId, Edge edge, AiModelId aiModelId) { + Optional aiModel = edgeCtx.getAiModelService().findAiModelById(tenantId, aiModelId); + if (aiModel.isPresent()) { + edgeCtx.getAiModelService().deleteByTenantIdAndId(tenantId, aiModelId); + pushEntityEventToRuleEngine(tenantId, edge, aiModel.get(), TbMsgType.ENTITY_DELETED); + } + } + +} 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 638ed9693a..bfdc39313d 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 @@ -77,7 +77,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR_RPC_MESSAGE: Alarm alarmToClear = edgeCtx.getAlarmService().findAlarmById(tenantId, alarmId); if (alarmToClear != null) { - edgeCtx.getAlarmService().clearAlarm(tenantId, alarmId, alarm.getClearTs(), alarm.getDetails()); + edgeCtx.getAlarmService().clearAlarm(tenantId, alarmId, alarm.getClearTs(), alarm.getDetails(), true); } break; case ENTITY_DELETED_RPC_MESSAGE: 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 a004f211c4..03f765cfb7 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 @@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.id.CustomerId; 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.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; @@ -55,21 +55,17 @@ public class AssetEdgeProcessor extends BaseAssetProcessor implements AssetProce try { edgeSynchronizationManager.getEdgeId().set(edge.getId()); - switch (assetUpdateMsg.getMsgType()) { - case ENTITY_CREATED_RPC_MESSAGE: - case ENTITY_UPDATED_RPC_MESSAGE: + return switch (assetUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE, ENTITY_UPDATED_RPC_MESSAGE -> { saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, edge); - return Futures.immediateFuture(null); - case ENTITY_DELETED_RPC_MESSAGE: - Asset assetToDelete = edgeCtx.getAssetService().findAssetById(tenantId, assetId); - if (assetToDelete != null) { - edgeCtx.getAssetService().unassignAssetFromEdge(tenantId, assetId, edge.getId()); - } - return Futures.immediateFuture(null); - case UNRECOGNIZED: - default: - return handleUnsupportedMsgType(assetUpdateMsg.getMsgType()); - } + yield Futures.immediateFuture(null); + } + case ENTITY_DELETED_RPC_MESSAGE -> { + deleteAsset(tenantId, edge, assetId); + yield Futures.immediateFuture(null); + } + default -> handleUnsupportedMsgType(assetUpdateMsg.getMsgType()); + }; } catch (DataValidationException e) { if (e.getMessage().contains("limit reached")) { log.warn("[{}] Number of allowed asset violated {}", tenantId, assetUpdateMsg, e); @@ -97,14 +93,8 @@ public class AssetEdgeProcessor extends BaseAssetProcessor implements AssetProce } private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetId assetId) { - try { - Asset asset = edgeCtx.getAssetService().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); - } + Asset asset = edgeCtx.getAssetService().findAssetById(tenantId, assetId); + pushEntityEventToRuleEngine(tenantId, edge, asset, TbMsgType.ENTITY_CREATED); } @Override 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 index 53c9f34b8a..cf160abf08 100644 --- 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 @@ -19,11 +19,12 @@ 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.StringUtils; 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.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @@ -50,22 +51,15 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { } else { asset.setId(assetId); } - String assetName = asset.getName(); - Asset assetByName = edgeCtx.getAssetService().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; + if (isSaveRequired(assetById, asset)) { + assetNameUpdated = updateAssetNameIfDuplicateExists(tenantId, assetId, asset); + setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg); + assetValidator.validate(asset, Asset::getTenantId); + if (created) { + asset.setId(assetId); + } + edgeCtx.getAssetService().saveAsset(asset, false); } - asset.setName(assetName); - setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg); - - assetValidator.validate(asset, Asset::getTenantId); - if (created) { - asset.setId(assetId); - } - edgeCtx.getAssetService().saveAsset(asset, false); } catch (Exception e) { log.error("[{}] Failed to process asset update msg [{}]", tenantId, assetUpdateMsg, e); throw e; @@ -75,6 +69,27 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { return Pair.of(created, assetNameUpdated); } + private boolean updateAssetNameIfDuplicateExists(TenantId tenantId, AssetId assetId, Asset asset) { + Asset assetByName = edgeCtx.getAssetService().findAssetByTenantIdAndName(tenantId, asset.getName()); + + return generateUniqueNameIfDuplicateExists(tenantId, assetId, asset, assetByName).map(uniqueName -> { + asset.setName(uniqueName); + return true; + }).orElse(false); + } + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg); + protected void deleteAsset(TenantId tenantId, AssetId assetId) { + deleteAsset(tenantId, null, assetId); + } + + protected void deleteAsset(TenantId tenantId, Edge edge, AssetId assetId) { + Asset assetById = edgeCtx.getAssetService().findAssetById(tenantId, assetId); + if (assetById != null) { + edgeCtx.getAssetService().deleteAsset(tenantId, assetId); + pushEntityEventToRuleEngine(tenantId, edge, assetById, TbMsgType.ENTITY_DELETED); + } + } + } 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 index e11b809955..74c02fa667 100644 --- 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 @@ -33,7 +33,7 @@ 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.msg.TbMsgMetaData; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.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; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/BaseCalculatedFieldProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/BaseCalculatedFieldProcessor.java index ddb7d65d9c..9d6b44e661 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/BaseCalculatedFieldProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/BaseCalculatedFieldProcessor.java @@ -53,7 +53,7 @@ public abstract class BaseCalculatedFieldProcessor extends BaseEdgeProcessor { } String calculatedFieldName = calculatedField.getName(); - CalculatedField calculatedFieldByName = edgeCtx.getCalculatedFieldService().findByEntityIdAndName(calculatedField.getEntityId(), calculatedFieldName); + CalculatedField calculatedFieldByName = edgeCtx.getCalculatedFieldService().findByEntityIdAndTypeAndName(calculatedField.getEntityId(), calculatedField.getType(), calculatedFieldName); if (calculatedFieldByName != null && !calculatedFieldByName.getId().equals(calculatedFieldId)) { calculatedFieldName = calculatedFieldName + "_" + StringUtils.randomAlphabetic(15); log.warn("[{}] calculatedField with name {} already exists. Renaming calculatedField name to {}", diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java index c7e092acaf..5f11abb478 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java @@ -25,6 +25,7 @@ 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.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -36,7 +37,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; 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.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.CalculatedFieldUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; @@ -47,6 +48,9 @@ import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils; import java.util.UUID; +import static org.thingsboard.server.common.data.cf.CalculatedFieldType.SCRIPT; +import static org.thingsboard.server.common.data.cf.CalculatedFieldType.SIMPLE; + @Slf4j @Component @TbCoreComponent @@ -91,7 +95,7 @@ public class CalculatedFieldEdgeProcessor extends BaseCalculatedFieldProcessor i switch (edgeEvent.getAction()) { case ADDED, UPDATED -> { CalculatedField calculatedField = edgeCtx.getCalculatedFieldService().findById(edgeEvent.getTenantId(), calculatedFieldId); - if (calculatedField != null) { + if (calculatedField != null && isValidCfToSend(calculatedField.getType(), edgeVersion)) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); CalculatedFieldUpdateMsg calculatedFieldUpdateMsg = EdgeMsgConstructorUtils.constructCalculatedFieldUpdatedMsg(msgType, calculatedField); return DownlinkMsg.newBuilder() @@ -111,6 +115,10 @@ public class CalculatedFieldEdgeProcessor extends BaseCalculatedFieldProcessor i return null; } + private boolean isValidCfToSend(CalculatedFieldType type, EdgeVersion edgeVersion) { + return edgeVersion.getNumber() >= EdgeVersion.V_4_3_0.getNumber() || (type == SIMPLE || type == SCRIPT); + } + @Override public EdgeEventType getEdgeEventType() { return EdgeEventType.CALCULATED_FIELD; 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 index 91b7c5b4bd..5c0c6c70e9 100644 --- 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 @@ -20,9 +20,11 @@ import org.springframework.beans.factory.annotation.Autowired; 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.edge.Edge; 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.common.data.msg.TbMsgType; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @@ -56,15 +58,14 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { dashboard.setAssignedCustomers(dashboardById.getAssignedCustomers()); } - dashboardValidator.validate(dashboard, Dashboard::getTenantId); - if (created) { - dashboard.setId(dashboardId); + if (isSaveRequired(dashboardById, dashboard)) { + dashboardValidator.validate(dashboard, Dashboard::getTenantId); + if (created) { + dashboard.setId(dashboardId); + } + dashboard = edgeCtx.getDashboardService().saveDashboard(dashboard, false); } - - Dashboard savedDashboard = edgeCtx.getDashboardService().saveDashboard(dashboard, false); - - updateDashboardAssignments(tenantId, customerId, dashboardById, savedDashboard, newAssignedCustomers); - + updateDashboardAssignments(tenantId, customerId, dashboardById, dashboard, newAssignedCustomers); return created; } @@ -100,6 +101,18 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { } } + protected void deleteDashboard(TenantId tenantId, DashboardId dashboardId) { + deleteDashboard(tenantId, null, dashboardId); + } + + protected void deleteDashboard(TenantId tenantId, Edge edge, DashboardId dashboardId) { + Dashboard dashboardById = edgeCtx.getDashboardService().findDashboardById(tenantId, dashboardId); + if (dashboardById != null) { + edgeCtx.getDashboardService().deleteDashboard(tenantId, dashboardId); + pushEntityEventToRuleEngine(tenantId, edge, dashboardById, TbMsgType.ENTITY_DELETED); + } + } + protected abstract Set filterNonExistingCustomers(TenantId tenantId, CustomerId customerId, Set currentAssignedCustomers, Set newAssignedCustomers); } 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 1e33f88333..b227334b29 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 @@ -19,7 +19,6 @@ 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; @@ -30,8 +29,7 @@ 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.common.data.msg.TbMsgType; -import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.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; @@ -62,10 +60,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor implements Da saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, edge); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: - Dashboard dashboardToDelete = edgeCtx.getDashboardService().findDashboardById(tenantId, dashboardId); - if (dashboardToDelete != null) { - edgeCtx.getDashboardService().unassignDashboardFromEdge(tenantId, dashboardId, edge.getId()); - } + deleteDashboard(tenantId, edge, dashboardId); return Futures.immediateFuture(null); case UNRECOGNIZED: default: @@ -93,14 +88,8 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor implements Da } private void pushDashboardCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DashboardId dashboardId) { - try { - Dashboard dashboard = edgeCtx.getDashboardService().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); - } + Dashboard dashboard = edgeCtx.getDashboardService().findDashboardById(tenantId, dashboardId); + pushEntityEventToRuleEngine(tenantId, edge, dashboard, TbMsgType.ENTITY_CREATED); } @Override 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 04b18d6774..e4449f9816 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 @@ -20,10 +20,11 @@ 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.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.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; @@ -52,23 +53,17 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { } else { device.setId(deviceId); } - String deviceName = device.getName(); - Device deviceByName = edgeCtx.getDeviceService().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); - setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg); + if (isSaveRequired(deviceById, device)) { + deviceNameUpdated = updateDeviceNameIfDuplicateExists(tenantId, deviceId, device); + setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg); - deviceValidator.validate(device, Device::getTenantId); - if (created) { - device.setId(deviceId); + deviceValidator.validate(device, Device::getTenantId); + if (created) { + device.setId(deviceId); + } + Device savedDevice = edgeCtx.getDeviceService().saveDevice(device, false); + edgeCtx.getClusterService().onDeviceUpdated(savedDevice, created ? null : device); } - Device savedDevice = edgeCtx.getDeviceService().saveDevice(device, false); - edgeCtx.getClusterService().onDeviceUpdated(savedDevice, created ? null : device); } catch (Exception e) { log.error("[{}] Failed to process device update msg [{}]", tenantId, deviceUpdateMsg, e); throw e; @@ -78,6 +73,15 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { return Pair.of(created, deviceNameUpdated); } + private boolean updateDeviceNameIfDuplicateExists(TenantId tenantId, DeviceId deviceId, Device device) { + Device deviceByName = edgeCtx.getDeviceService().findDeviceByTenantIdAndName(tenantId, device.getName()); + + return generateUniqueNameIfDuplicateExists(tenantId, deviceId, device, deviceByName).map(uniqueName -> { + device.setName(uniqueName); + return true; + }).orElse(false); + } + protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); if (deviceCredentials == null) { @@ -110,4 +114,15 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg); + protected void deleteDevice(TenantId tenantId, DeviceId deviceId) { + deleteDevice(tenantId, null, deviceId); + } + + protected void deleteDevice(TenantId tenantId, Edge edge, DeviceId deviceId) { + Device deviceById = edgeCtx.getDeviceService().findDeviceById(tenantId, deviceId); + if (deviceById != null) { + edgeCtx.getDeviceService().deleteDevice(tenantId, deviceId); + pushEntityEventToRuleEngine(tenantId, edge, deviceById, TbMsgType.ENTITY_DELETED); + } + } } 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 dab3efd911..920b86208d 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 @@ -44,7 +44,7 @@ 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.rpc.FromDeviceRpcResponseActorMsg; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; @@ -76,10 +76,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor implements DevicePr saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, edge); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: - Device deviceToDelete = edgeCtx.getDeviceService().findDeviceById(tenantId, deviceId); - if (deviceToDelete != null) { - edgeCtx.getDeviceService().unassignDeviceFromEdge(tenantId, deviceId, edge.getId()); - } + deleteDevice(tenantId, edge, deviceId); return Futures.immediateFuture(null); case UNRECOGNIZED: default: @@ -125,14 +122,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor implements DevicePr } private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceId deviceId) { - try { - Device device = edgeCtx.getDeviceService().findDeviceById(tenantId, deviceId); - 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); - } + Device device = edgeCtx.getDeviceService().findDeviceById(tenantId, deviceId); + pushEntityEventToRuleEngine(tenantId, edge, device, TbMsgType.ENTITY_CREATED); } @Override @@ -246,7 +237,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor implements DevicePr if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { DeviceProfile deviceProfile = edgeCtx.getDeviceProfileService().findDeviceProfileById(edgeEvent.getTenantId(), device.getDeviceProfileId()); - builder.addDeviceProfileUpdateMsg(EdgeMsgConstructorUtils.constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); + builder.addDeviceProfileUpdateMsg(EdgeMsgConstructorUtils.constructDeviceProfileUpdatedMsg(msgType, deviceProfile, edgeVersion)); } return builder.build(); } 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 index b13614053f..1827302525 100644 --- 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 @@ -33,7 +33,7 @@ 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.msg.TbMsgMetaData; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.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; @@ -102,7 +102,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor imple DeviceProfile deviceProfile = edgeCtx.getDeviceProfileService().findDeviceProfileById(edgeEvent.getTenantId(), deviceProfileId); if (deviceProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - DeviceProfileUpdateMsg deviceProfileUpdateMsg = EdgeMsgConstructorUtils.constructDeviceProfileUpdatedMsg(msgType, deviceProfile); + DeviceProfileUpdateMsg deviceProfileUpdateMsg = EdgeMsgConstructorUtils.constructDeviceProfileUpdatedMsg(msgType, deviceProfile, edgeVersion); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) @@ -141,5 +141,4 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor imple public EdgeEventType getEdgeEventType() { return EdgeEventType.DEVICE_PROFILE; } - } 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 index a6d821c2cd..8fdedc106f 100644 --- 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 @@ -21,9 +21,13 @@ import org.springframework.data.util.Pair; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.StringUtils; +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.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @@ -48,25 +52,39 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { } else { entityView.setId(entityViewId); } - String entityViewName = entityView.getName(); - EntityView entityViewByName = edgeCtx.getEntityViewService().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); + if (isSaveRequired(entityViewById, entityView)) { + entityViewNameUpdated = updateEntityViewNameIfDuplicateExists(tenantId, entityViewId, entityView); + setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg); - entityViewValidator.validate(entityView, EntityView::getTenantId); - if (created) { - entityView.setId(entityViewId); + entityViewValidator.validate(entityView, EntityView::getTenantId); + if (created) { + entityView.setId(entityViewId); + } + edgeCtx.getEntityViewService().saveEntityView(entityView, false); } - edgeCtx.getEntityViewService().saveEntityView(entityView, false); return Pair.of(created, entityViewNameUpdated); } + private boolean updateEntityViewNameIfDuplicateExists(TenantId tenantId, EntityViewId entityViewId, EntityView entityView) { + EntityView entityViewByName = edgeCtx.getEntityViewService().findEntityViewByTenantIdAndName(tenantId, entityView.getName()); + + return generateUniqueNameIfDuplicateExists(tenantId, entityViewId, entityView, entityViewByName).map(uniqueName -> { + entityView.setName(uniqueName); + return true; + }).orElse(false); + } + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg); + protected void deleteEntityView(TenantId tenantId, EntityViewId entityViewId) { + deleteEntityView(tenantId, null, entityViewId); + } + + protected void deleteEntityView(TenantId tenantId, Edge edge, EntityViewId entityViewId) { + EntityView entityViewById = edgeCtx.getEntityViewService().findEntityViewById(tenantId, entityViewId); + if (entityViewById != null) { + edgeCtx.getEntityViewService().deleteEntityView(tenantId, entityViewId); + pushEntityEventToRuleEngine(tenantId, edge, entityViewById, TbMsgType.ENTITY_DELETED); + } + } } 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 f91591a079..8e4c9587ac 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 @@ -32,7 +32,7 @@ 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.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; @@ -54,21 +54,17 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor implements try { edgeSynchronizationManager.getEdgeId().set(edge.getId()); - switch (entityViewUpdateMsg.getMsgType()) { - case ENTITY_CREATED_RPC_MESSAGE: - case ENTITY_UPDATED_RPC_MESSAGE: + return switch (entityViewUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE, ENTITY_UPDATED_RPC_MESSAGE -> { saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, edge); - return Futures.immediateFuture(null); - case ENTITY_DELETED_RPC_MESSAGE: - EntityView entityViewToDelete = edgeCtx.getEntityViewService().findEntityViewById(tenantId, entityViewId); - if (entityViewToDelete != null) { - edgeCtx.getEntityViewService().unassignEntityViewFromEdge(tenantId, entityViewId, edge.getId()); - } - return Futures.immediateFuture(null); - case UNRECOGNIZED: - default: - return handleUnsupportedMsgType(entityViewUpdateMsg.getMsgType()); - } + yield Futures.immediateFuture(null); + } + case ENTITY_DELETED_RPC_MESSAGE -> { + deleteEntityView(tenantId, edge, entityViewId); + yield Futures.immediateFuture(null); + } + default -> handleUnsupportedMsgType(entityViewUpdateMsg.getMsgType()); + }; } catch (DataValidationException e) { if (e.getMessage().contains("limit reached")) { log.warn("[{}] Number of allowed entity views violated {}", tenantId, entityViewUpdateMsg, e); @@ -96,14 +92,8 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor implements } private void pushEntityViewCreatedEventToRuleEngine(TenantId tenantId, Edge edge, EntityViewId entityViewId) { - try { - EntityView entityView = edgeCtx.getEntityViewService().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); - } + EntityView entityView = edgeCtx.getEntityViewService().findEntityViewById(tenantId, entityViewId); + pushEntityEventToRuleEngine(tenantId, edge, entityView, TbMsgType.ENTITY_CREATED); } @Override 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 index 5246f7a730..fe203349f4 100644 --- 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 @@ -27,7 +27,7 @@ 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.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; 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 1a304dfe83..86ae1e93c4 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 @@ -31,7 +31,7 @@ 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.msg.TbMsgMetaData; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.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.RuleChainMetadataUpdateMsg; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/BaseUserProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/BaseUserProcessor.java new file mode 100644 index 0000000000..f3c710ffed --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/BaseUserProcessor.java @@ -0,0 +1,159 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.user; + +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.StringUtils; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.edge.Edge; +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.msg.TbMsgType; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +public abstract class BaseUserProcessor extends BaseEdgeProcessor { + + @Autowired + private DataValidator userValidator; + + protected Pair saveOrUpdateUser(TenantId tenantId, UserId userId, UserUpdateMsg userUpdateMsg) { + boolean isCreated = false; + boolean userEmailUpdated = false; + + try { + User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + if (user == null) { + throw new IllegalArgumentException(String.format("[%s] Failed to parse User from UserUpdateMsg: %s", tenantId, userUpdateMsg)); + } + + User userById = edgeCtx.getUserService().findUserById(tenantId, userId); + if (userById == null) { + isCreated = true; + user.setId(null); + } else { + user.setId(userId); + } + if (isSaveRequired(userById, user)) { + userEmailUpdated = updateUserEmailIfDuplicateExists(tenantId, userId, user); + setCustomerId(tenantId, isCreated ? null : userById.getCustomerId(), user, userUpdateMsg); + + userValidator.validate(user, User::getTenantId); + + if (isCreated) { + user.setId(userId); + } + + edgeCtx.getUserService().saveUser(tenantId, user, false); + } + } catch (Exception e) { + log.error("[{}] Failed to process user update msg [{}]", tenantId, userUpdateMsg, e); + throw e; + } + + return Pair.of(isCreated, userEmailUpdated); + } + + private boolean updateUserEmailIfDuplicateExists(TenantId tenantId, UserId userId, User user) { + String email = user.getEmail(); + User userByEmail = edgeCtx.getUserService().findUserByTenantIdAndEmail(tenantId, email); + + if (userByEmail != null && !userByEmail.getId().equals(user.getId())) { + String[] splitEmail = email.split("@"); + String newEmail = splitEmail[0] + "_" + StringUtils.randomAlphanumeric(15) + "@" + splitEmail[1]; + log.warn("[{}] User with email {} already exists. Renaming User email to {}", tenantId, user.getEmail(), newEmail); + user.setEmail(newEmail); + return true; + } + return false; + } + + protected void deleteUserAndPushEntityDeletedEventToRuleEngine(TenantId tenantId, UserId userId) { + deleteUserAndPushEntityDeletedEventToRuleEngine(tenantId, userId, null); + } + + protected void deleteUserAndPushEntityDeletedEventToRuleEngine(TenantId tenantId, UserId userId, Edge edge) { + User removedUser = deleteUser(tenantId, userId); + if (removedUser == null) { + return; + } + CustomerId userCustomerId = removedUser.getCustomerId(); + String userAsString = JacksonUtil.toString(removedUser); + TbMsgMetaData msgMetaData = edge == null ? new TbMsgMetaData() : getEdgeActionTbMsgMetaData(edge, userCustomerId); + addRemovedUserMetadata(msgMetaData, removedUser); + pushEntityEventToRuleEngine(tenantId, userId, userCustomerId, TbMsgType.ENTITY_DELETED, userAsString, msgMetaData); + } + + private User deleteUser(TenantId tenantId, UserId userId) { + User userById = edgeCtx.getUserService().findUserById(tenantId, userId); + if (userById == null) { + log.trace("[{}] User with id {} does not exist", tenantId, userId); + return null; + } + edgeCtx.getUserService().deleteUser(tenantId, userById); + return userById; + } + + protected void updateUserCredentials(TenantId tenantId, UserCredentialsUpdateMsg updateMsg) { + UserCredentials userCredentials = JacksonUtil.fromString(updateMsg.getEntity(), UserCredentials.class, true); + if (userCredentials == null) { + throw new IllegalArgumentException(String.format("[%s] Failed to parse UserCredentials from updateMsg: %s", tenantId, updateMsg)); + } + User user = edgeCtx.getUserService().findUserById(tenantId, userCredentials.getUserId()); + if (user == null) { + log.warn("[{}] Can't find user by id [{}] skipping credentials update. UserCredentialsUpdateMsg [{}]", + tenantId, userCredentials.getUserId(), updateMsg); + return; + } + log.debug("[{}] Updating user credentials for user [{}]. New credentials Id [{}], enabled [{}]", + tenantId, user.getName(), userCredentials.getId(), userCredentials.isEnabled()); + try { + UserCredentials userCredentialsByUserId = edgeCtx.getUserService().findUserCredentialsByUserId(tenantId, user.getId()); + if (userCredentialsByUserId != null && !userCredentialsByUserId.getId().equals(userCredentials.getId())) { + edgeCtx.getUserService().deleteUserCredentials(tenantId, userCredentialsByUserId); + } + edgeCtx.getUserService().saveUserCredentials(tenantId, userCredentials, false); + } catch (Exception e) { + log.error("[{}] Can't update user credentials for user [{}], userCredentialsUpdateMsg [{}]", + tenantId, user.getName(), updateMsg, e); + throw new RuntimeException(e); + } + } + + private void addRemovedUserMetadata(TbMsgMetaData metaData, User removedUser) { + metaData.putValue("userId", removedUser.getId().toString()); + metaData.putValue("userName", removedUser.getName()); + metaData.putValue("userEmail", removedUser.getEmail()); + if (removedUser.getFirstName() != null) { + metaData.putValue("userFirstName", removedUser.getFirstName()); + } + if (removedUser.getLastName() != null) { + metaData.putValue("userLastName", removedUser.getLastName()); + } + } + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, User user, UserUpdateMsg userUpdateMsg); + +} 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 f4b0ff039a..027b1225c4 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,26 +15,110 @@ */ package org.thingsboard.server.service.edge.rpc.processor.user; +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.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.User; +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.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.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.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils; -import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +import java.util.UUID; @Slf4j @Component @TbCoreComponent -public class UserEdgeProcessor extends BaseEdgeProcessor { +public class UserEdgeProcessor extends BaseUserProcessor implements UserProcessor { + + @Override + public ListenableFuture processUserMsgFromEdge(TenantId tenantId, Edge edge, UserUpdateMsg userUpdateMsg) { + log.trace("[{}] executing processUserMsgFromEdge [{}] from edge [{}]", tenantId, userUpdateMsg, edge.getId()); + UserId userId = new UserId(new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + return switch (userUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE, ENTITY_UPDATED_RPC_MESSAGE -> { + saveOrUpdateUser(tenantId, userId, userUpdateMsg, edge); + yield Futures.immediateFuture(null); + } + case ENTITY_DELETED_RPC_MESSAGE -> { + deleteUserAndPushEntityDeletedEventToRuleEngine(tenantId, userId, edge); + yield Futures.immediateFuture(null); + } + default -> handleUnsupportedMsgType(userUpdateMsg.getMsgType()); + }; + } catch (DataValidationException e) { + if (e.getMessage().contains("limit reached")) { + log.warn("[{}] Number of allowed users violated {}", tenantId, userUpdateMsg, e); + return Futures.immediateFuture(null); + } else { + return Futures.immediateFailedFuture(e); + } + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + @Override + public ListenableFuture processUserCredentialsMsgFromEdge(TenantId tenantId, Edge edge, UserCredentialsUpdateMsg userCredentialsUpdateMsg) { + log.debug("[{}] Executing processUserCredentialsMsgFromEdge, userCredentialsUpdateMsg [{}]", tenantId, userCredentialsUpdateMsg); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + super.updateUserCredentials(tenantId, userCredentialsUpdateMsg); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + return Futures.immediateFuture(null); + } + + private void saveOrUpdateUser(TenantId tenantId, UserId userId, UserUpdateMsg userUpdateMsg, Edge edge) { + Pair resultPair = super.saveOrUpdateUser(tenantId, userId, userUpdateMsg); + boolean isCreated = resultPair.getFirst(); + if (isCreated) { + createRelationFromEdge(tenantId, edge.getId(), userId); + pushUserCreatedEventToRuleEngine(tenantId, edge, userId); + } + + boolean userEmailUpdated = resultPair.getSecond(); + + if (userEmailUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER, EdgeEventActionType.UPDATED, userId, null); + } + } + + private void pushUserCreatedEventToRuleEngine(TenantId tenantId, Edge edge, UserId userId) { + try { + User user = edgeCtx.getUserService().findUserById(tenantId, userId); + if (user != null) { + String userAsString = JacksonUtil.toString(user); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, user.getCustomerId()); + pushEntityEventToRuleEngine(tenantId, userId, user.getCustomerId(), TbMsgType.ENTITY_CREATED, userAsString, msgMetaData); + } + } catch (Exception e) { + log.warn("[{}][{}] Failed to push user action to rule engine: {}", tenantId, userId, TbMsgType.ENTITY_CREATED.name(), e); + } + } @Override public DownlinkMsg convertEdgeEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { @@ -48,7 +132,7 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addUserUpdateMsg(EdgeMsgConstructorUtils.constructUserUpdatedMsg(msgType, user)); UserCredentials userCredentialsByUserId = edgeCtx.getUserService().findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); - if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { + if (userCredentialsByUserId != null) { builder.addUserCredentialsUpdateMsg(EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentialsByUserId)); } return builder.build(); @@ -62,11 +146,10 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { } case CREDENTIALS_UPDATED -> { UserCredentials userCredentialsByUserId = edgeCtx.getUserService().findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); - if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { - UserCredentialsUpdateMsg userCredentialsUpdateMsg = EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentialsByUserId); + if (userCredentialsByUserId != null) { return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) + .addUserCredentialsUpdateMsg(EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentialsByUserId)) .build(); } } @@ -79,4 +162,10 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { return EdgeEventType.USER; } + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, User user, UserUpdateMsg userUpdateMsg) { + CustomerId customerUUID = user.getCustomerId() != null ? user.getCustomerId() : customerId; + user.setCustomerId(customerUUID); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserProcessor.java new file mode 100644 index 0000000000..9e6a289386 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserProcessor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.user; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface UserProcessor extends EdgeProcessor { + + ListenableFuture processUserMsgFromEdge(TenantId tenantId, Edge edge, UserUpdateMsg userUpdateMsg); + + ListenableFuture processUserCredentialsMsgFromEdge(TenantId tenantId, Edge edge, UserCredentialsUpdateMsg userCredentialsUpdateMsg); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/stats/EdgeStatsService.java b/application/src/main/java/org/thingsboard/server/service/edge/stats/EdgeStatsService.java index 26917f845d..3458d8f010 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/stats/EdgeStatsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/stats/EdgeStatsService.java @@ -54,7 +54,7 @@ import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_P import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_TMP_FAILED; @TbCoreComponent -@ConditionalOnProperty(prefix = "edges.stats", name = "enabled", havingValue = "true", matchIfMissing = false) +@ConditionalOnProperty(prefix = "edges.stats", name = "enabled", havingValue = "true") @RequiredArgsConstructor @Service @Slf4j @@ -70,7 +70,6 @@ public class EdgeStatsService { @Value("${edges.stats.report-interval-millis:600000}") private long reportIntervalMillis; - @Scheduled( fixedDelayString = "${edges.stats.report-interval-millis:600000}", initialDelayString = "${edges.stats.report-interval-millis:600000}" @@ -80,13 +79,13 @@ public class EdgeStatsService { long now = System.currentTimeMillis(); long ts = now - (now % reportIntervalMillis); - Map countersByEdge = statsCounterService.getCounterByEdge(); - Map lagByEdgeId = kafkaAdmin.isPresent() ? getEdgeLagByEdgeId(countersByEdge) : Collections.emptyMap(); - Map countersByEdgeSnapshot = new HashMap<>(statsCounterService.getCounterByEdge()); + Map countersByEdgeSnapshot = new HashMap<>(statsCounterService.getMsgCountersByEdge()); + boolean isKafkaStats = kafkaAdmin.isPresent(); + Map lagByEdgeId = isKafkaStats ? getLagByEdgeId(countersByEdgeSnapshot) : Collections.emptyMap(); countersByEdgeSnapshot.forEach((edgeId, counters) -> { TenantId tenantId = counters.getTenantId(); - if (kafkaAdmin.isPresent()) { + if (isKafkaStats) { counters.getMsgsLag().set(lagByEdgeId.getOrDefault(edgeId, 0L)); } List statsEntries = List.of( @@ -102,7 +101,7 @@ public class EdgeStatsService { }); } - private Map getEdgeLagByEdgeId(Map countersByEdge) { + private Map getLagByEdgeId(Map countersByEdge) { Map edgeToTopicMap = countersByEdge.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, diff --git a/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java b/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java index a7c1fc67b0..38fbd94b16 100644 --- a/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java @@ -73,6 +73,7 @@ import org.thingsboard.server.queue.util.AfterStartUp; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -150,12 +151,10 @@ public class DefaultEdqsService implements EdqsService { executor.submit(() -> { try { EdqsSyncState syncState = getSyncState(); - if (edqsSyncService.isSyncNeeded() || syncState == null || syncState.getStatus() != EdqsSyncStatus.FINISHED) { - if (hashPartitionService.isSystemPartitionMine(ServiceType.TB_CORE)) { - processSystemRequest(ToCoreEdqsRequest.builder() - .syncRequest(new EdqsSyncRequest()) - .build()); - } + if (edqsSyncService.isSyncNeeded() || syncState == null) { + requestEdqsSync(new EdqsSyncRequest()); + } else if (syncState.getStatus() != EdqsSyncStatus.FINISHED) { + requestEdqsSync(new EdqsSyncRequest(syncState.getObjectTypes())); } else { // only if topic/RocksDB is not empty and sync is finished onSyncStatusUpdate(EdqsSyncStatus.FINISHED); @@ -166,11 +165,19 @@ public class DefaultEdqsService implements EdqsService { }); } + private void requestEdqsSync(EdqsSyncRequest syncRequest) { + if (hashPartitionService.isSystemPartitionMine(ServiceType.TB_CORE)) { + processSystemRequest(ToCoreEdqsRequest.builder() + .syncRequest(syncRequest) + .build()); + } + } + @Override public void processSystemRequest(ToCoreEdqsRequest request) { log.info("Processing system request {}", request); if (request.getSyncRequest() != null) { - saveSyncState(EdqsSyncStatus.REQUESTED); + saveSyncState(EdqsSyncStatus.REQUESTED, request.getSyncRequest().getObjectTypes()); } broadcast(ToCoreEdqsMsg.builder() .syncRequest(request.getSyncRequest()) @@ -198,7 +205,8 @@ public class DefaultEdqsService implements EdqsService { onSyncStatusUpdate(msg.getSyncStatus()); } - if (msg.getSyncRequest() != null) { + EdqsSyncRequest syncRequest = msg.getSyncRequest(); + if (syncRequest != null) { syncLock.lock(); try { EdqsSyncState syncState = getSyncState(); @@ -209,15 +217,15 @@ public class DefaultEdqsService implements EdqsService { return; } } - saveSyncState(EdqsSyncStatus.STARTED); + saveSyncState(EdqsSyncStatus.STARTED, syncRequest.getObjectTypes()); - edqsSyncService.sync(); + edqsSyncService.sync(syncRequest); saveSyncState(EdqsSyncStatus.FINISHED); broadcastSyncStatusUpdate(EdqsSyncStatus.FINISHED); } catch (Exception e) { log.error("Failed to complete sync", e); - saveSyncState(EdqsSyncStatus.FAILED); + saveSyncState(EdqsSyncStatus.FAILED, syncRequest.getObjectTypes()); broadcastSyncStatusUpdate(EdqsSyncStatus.FAILED); } finally { syncLock.unlock(); @@ -369,7 +377,12 @@ public class DefaultEdqsService implements EdqsService { @SneakyThrows private void saveSyncState(EdqsSyncStatus status) { - EdqsSyncState state = new EdqsSyncState(status); + saveSyncState(status, null); + } + + @SneakyThrows + private void saveSyncState(EdqsSyncStatus status, Set objectTypes) { + EdqsSyncState state = new EdqsSyncState(status, objectTypes); log.info("New EDQS sync state: {}", state); attributesService.save(TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID, AttributeScope.SERVER_SCOPE, new BaseAttributeKvEntry( new JsonDataEntry("edqsSyncState", JacksonUtil.toString(state)), @@ -383,11 +396,4 @@ public class DefaultEdqsService implements EdqsService { eventsProducer.stop(); } - @Data - @AllArgsConstructor - @NoArgsConstructor - private static class EdqsSyncState { - private EdqsSyncStatus status; - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java b/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java index c4afbbb2ea..e6713fc9d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java +++ b/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.ObjectType; import org.thingsboard.server.common.data.edqs.AttributeKv; import org.thingsboard.server.common.data.edqs.EdqsEventType; import org.thingsboard.server.common.data.edqs.EdqsObject; +import org.thingsboard.server.common.data.edqs.EdqsSyncRequest; import org.thingsboard.server.common.data.edqs.Entity; import org.thingsboard.server.common.data.edqs.LatestTsKv; import org.thingsboard.server.common.data.edqs.fields.EntityFields; @@ -83,21 +84,38 @@ public abstract class EdqsSyncService { public abstract boolean isSyncNeeded(); - public void sync() { + public void sync(EdqsSyncRequest syncRequest) { log.info("Synchronizing data to EDQS"); long startTs = System.currentTimeMillis(); counters.clear(); - syncTenantEntities(); - syncRelations(); - loadKeyDictionary(); - syncAttributes(); - syncLatestTimeseries(); + if (syncRequest.getObjectTypes() != null && !syncRequest.getObjectTypes().isEmpty()) { + log.info("Sync request for entity types: {}", syncRequest.getObjectTypes()); + for (ObjectType objectType : syncRequest.getObjectTypes()) { + syncObjectType(objectType); + } + } else { + log.info("Sync request for all entity types"); + syncTenantEntities(); + syncRelations(); + loadKeyDictionary(); + syncAttributes(); + syncLatestTimeseries(); + } counters.clear(); log.info("Finished synchronizing data to EDQS in {} ms", (System.currentTimeMillis() - startTs)); } + private void syncObjectType(ObjectType objectType) { + switch (objectType) { + case RELATION -> syncRelations(); + case ATTRIBUTE_KV -> syncAttributes(); + case LATEST_TS_KV -> syncLatestTimeseries(); + default -> syncTenantEntity(objectType); + } + } + private void process(TenantId tenantId, ObjectType type, EdqsObject object) { AtomicInteger counter = counters.computeIfAbsent(type, t -> new AtomicInteger()); if (counter.incrementAndGet() % 10000 == 0) { @@ -108,26 +126,30 @@ public abstract class EdqsSyncService { private void syncTenantEntities() { for (ObjectType type : edqsTenantTypes) { - log.info("Synchronizing {} entities to EDQS", type); - long ts = System.currentTimeMillis(); - EntityType entityType = type.toEntityType(); - Dao dao = entityDaoRegistry.getDao(entityType); - UUID lastId = UUID.fromString("00000000-0000-0000-0000-000000000000"); - while (true) { - var batch = dao.findNextBatch(lastId, entityBatchSize); - if (batch.isEmpty()) { - break; - } - for (EntityFields entityFields : batch) { - TenantId tenantId = TenantId.fromUUID(entityFields.getTenantId()); - entityInfoMap.put(entityFields.getId(), new EntityIdInfo(entityType, tenantId)); - process(tenantId, type, new Entity(entityType, entityFields)); - } - EntityFields lastRecord = batch.get(batch.size() - 1); - lastId = lastRecord.getId(); + syncTenantEntity(type); + } + } + + private void syncTenantEntity(ObjectType type) { + log.info("Synchronizing {} entities to EDQS", type); + long ts = System.currentTimeMillis(); + EntityType entityType = type.toEntityType(); + Dao dao = entityDaoRegistry.getDao(entityType); + UUID lastId = UUID.fromString("00000000-0000-0000-0000-000000000000"); + while (true) { + var batch = dao.findNextBatch(lastId, entityBatchSize); + if (batch.isEmpty()) { + break; + } + for (EntityFields entityFields : batch) { + TenantId tenantId = TenantId.fromUUID(entityFields.getTenantId()); + entityInfoMap.put(entityFields.getId(), new EntityIdInfo(entityType, tenantId)); + process(tenantId, type, new Entity(entityType, entityFields)); } - log.info("Finished synchronizing {} entities to EDQS in {} ms", type, (System.currentTimeMillis() - ts)); + EntityFields lastRecord = batch.get(batch.size() - 1); + lastId = lastRecord.getId(); } + log.info("Finished synchronizing {} entities to EDQS in {} ms", type, (System.currentTimeMillis() - ts)); } private void syncRelations() { diff --git a/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncState.java b/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncState.java new file mode 100644 index 0000000000..9ea2f7cc8d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncState.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.edqs; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.ObjectType; +import org.thingsboard.server.common.data.edqs.EdqsState.EdqsSyncStatus; + +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EdqsSyncState { + private EdqsSyncStatus status; + private Set objectTypes; +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index 0669c32bdd..5f4a6d29ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -22,8 +22,10 @@ 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.rule.engine.api.JobManager; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageState; +import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; @@ -31,9 +33,11 @@ import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; 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.asset.Asset; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.DeviceId; @@ -44,6 +48,7 @@ import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.NotificationRequest; 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; @@ -53,13 +58,18 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.server.common.util.ProtoUtils; 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.tenant.TenantService; +import org.thingsboard.server.gen.transport.TransportProtos.EntityActionEventProto; +import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.rule.engine.api.JobManager; +import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.service.cf.CalculatedFieldCache; import java.util.Set; @@ -72,6 +82,7 @@ public class EntityStateSourcingListener { private final TbClusterService tbClusterService; private final EdgeSynchronizationManager edgeSynchronizationManager; private final JobManager jobManager; + private final CalculatedFieldCache calculatedFieldCache; @PostConstruct public void init() { @@ -99,7 +110,7 @@ public class EntityStateSourcingListener { case ASSET -> { onAssetUpdate(event.getEntity(), event.getOldEntity()); } - case ASSET_PROFILE, ENTITY_VIEW, NOTIFICATION_RULE -> { + case ASSET_PROFILE, ENTITY_VIEW, NOTIFICATION_RULE, USER -> { tbClusterService.broadcastEntityStateChangeEvent(tenantId, entityId, lifecycleEvent); } case RULE_CHAIN -> { @@ -140,6 +151,9 @@ public class EntityStateSourcingListener { case JOB -> { onJobUpdate((Job) event.getEntity()); } + case CUSTOMER -> { + tbClusterService.onCustomerUpdated((Customer) event.getEntity(), (Customer) event.getOldEntity()); + } default -> { } } @@ -153,7 +167,7 @@ public class EntityStateSourcingListener { return; } EntityType entityType = entityId.getEntityType(); - if (!tenantId.isSysTenantId() && entityType != EntityType.TENANT && !tenantService.tenantExists(tenantId)) { + if (entityType != EntityType.TENANT && !tenantExists(tenantId)) { log.debug("[{}] Ignoring DeleteEntityEvent because tenant does not exist: {}", tenantId, event); return; } @@ -164,7 +178,7 @@ public class EntityStateSourcingListener { Asset asset = (Asset) event.getEntity(); tbClusterService.onAssetDeleted(tenantId, asset, null); } - case ASSET_PROFILE, ENTITY_VIEW, CUSTOMER, EDGE, NOTIFICATION_RULE -> { + case ASSET_PROFILE, ENTITY_VIEW, CUSTOMER, EDGE, NOTIFICATION_RULE, USER -> { tbClusterService.broadcastEntityStateChangeEvent(tenantId, entityId, ComponentLifecycleEvent.DELETED); } case NOTIFICATION_REQUEST -> { @@ -216,18 +230,59 @@ public class EntityStateSourcingListener { @TransactionalEventListener(fallbackExecution = true) public void handleEvent(ActionEntityEvent event) { - log.trace("[{}] ActionEntityEvent called: {}", event.getTenantId(), event); - if (ActionType.CREDENTIALS_UPDATED.equals(event.getActionType()) && - EntityType.DEVICE.equals(event.getEntityId().getEntityType()) - && event.getEntity() instanceof DeviceCredentials) { - tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(event.getTenantId(), - (DeviceId) event.getEntityId(), (DeviceCredentials) event.getEntity()), null); - } else if (ActionType.ASSIGNED_TO_TENANT.equals(event.getActionType()) && event.getEntity() instanceof Device device) { - Tenant tenant = JacksonUtil.fromString(event.getBody(), Tenant.class); - if (tenant != null) { - tbClusterService.onDeviceAssignedToTenant(tenant.getId(), device); + TenantId tenantId = event.getTenantId(); + log.trace("[{}] ActionEntityEvent called: {}", tenantId, event); + switch (event.getActionType()) { + case CREDENTIALS_UPDATED -> { + if (event.getEntityId().getEntityType() == EntityType.DEVICE && event.getEntity() instanceof DeviceCredentials deviceCredentials) { + tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, + (DeviceId) event.getEntityId(), deviceCredentials), null); + } else if (event.getEntityId().getEntityType() == EntityType.USER) { + tbClusterService.broadcastEntityStateChangeEvent(event.getTenantId(), event.getEntityId(), ComponentLifecycleEvent.UPDATED); + + } + } + case ASSIGNED_TO_TENANT -> { + if (event.getEntity() instanceof Device device) { + Tenant tenant = JacksonUtil.fromString(event.getBody(), Tenant.class); + if (tenant != null) { + tbClusterService.onDeviceAssignedToTenant(tenant.getId(), device); + } + pushAssignedFromNotification(tenant, tenantId, device); + } + } + case ALARM_ACK, ALARM_CLEAR, ALARM_DELETE -> { + if (event.getActionType() == ActionType.ALARM_DELETE && !tenantExists(tenantId)) { + return; + } + Alarm alarm = (Alarm) event.getEntity(); + if (calculatedFieldCache.hasCalculatedFields(tenantId, alarm.getOriginator(), ctx -> ctx.getCfType() == CalculatedFieldType.ALARM)) { + ToCalculatedFieldMsg msg = ToCalculatedFieldMsg.newBuilder() + .setEventMsg(toProto(event)) + .build(); + tbClusterService.pushMsgToCalculatedFields(tenantId, alarm.getOriginator(), msg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) {} + + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to push alarm event to CF queue: {}", tenantId, event, t); + } + }); + } + } + } + } + + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(RelationActionEvent relationEvent) { + EntityRelation relation = relationEvent.getRelation(); + if (CalculatedField.isSupportedRefEntity(relation.getFrom()) && CalculatedField.isSupportedRefEntity(relation.getTo())) { + if (relationEvent.getActionType() == ActionType.RELATION_ADD_OR_UPDATE) { + tbClusterService.onRelationUpdated(relationEvent.getTenantId(), relation, TbQueueCallback.EMPTY); + } else if (relationEvent.getActionType() == ActionType.RELATION_DELETED) { + tbClusterService.onRelationDeleted(relationEvent.getTenantId(), relation, TbQueueCallback.EMPTY); } - pushAssignedFromNotification(tenant, event.getTenantId(), device); } } @@ -338,6 +393,10 @@ public class EntityStateSourcingListener { } } + private boolean tenantExists(TenantId tenantId) { + return tenantId.isSysTenantId() || tenantService.tenantExists(tenantId); + } + private TbMsgMetaData getMetaDataForAssignedFrom(Tenant tenant) { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("assignedFromTenantId", tenant.getId().getId().toString()); @@ -345,4 +404,13 @@ public class EntityStateSourcingListener { return metaData; } + private EntityActionEventProto toProto(ActionEntityEvent event) { + return EntityActionEventProto.newBuilder() + .setTenantId(ProtoUtils.toProto(event.getTenantId())) + .setEntityId(ProtoUtils.toProto(event.getEntityId())) + .setAction(event.getActionType().name()) + .setEntity(event.getEntity() != null ? JacksonUtil.toString(event.getEntity()) : "") + .build(); + } + } 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 0fb43a4511..1daab60990 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 @@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.COMMENT_DELETED; + @Service @AllArgsConstructor public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService { @@ -59,9 +61,10 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem if (alarmComment.getType() == AlarmCommentType.OTHER) { alarmComment.setType(AlarmCommentType.SYSTEM); alarmComment.setUserId(null); - alarmComment.setComment(JacksonUtil.newObjectNode().put("text", - String.format("User %s deleted his comment", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))); + alarmComment.setComment(JacksonUtil.newObjectNode() + .put("text", String.format(COMMENT_DELETED.getText(), user.getTitle())) + .put("subtype", COMMENT_DELETED.name()) + .put("userName", user.getTitle())); AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment)); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), ActionType.DELETED_COMMENT, user, savedAlarmComment); } else { 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 2a494e9dfa..448fc19852 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 @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmCommentSubType; import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -39,9 +40,17 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.ACKED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.ASSIGNED_TO_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.CLEARED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.UNASSIGNED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.UNASSIGNED_FROM_DELETED_USER; + @Service @AllArgsConstructor @Slf4j @@ -102,8 +111,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - String systemComment = String.format("Alarm was acknowledged by user %s", user.getTitle()); - addSystemAlarmComment(alarmInfo, user, "ACK", systemComment); + addSystemAlarmComment(alarmInfo, user, ACKED_BY_USER,"userName", user.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user); } else { @@ -125,8 +133,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isCleared()) { - String systemComment = String.format("Alarm was cleared by user %s", user.getTitle()); - addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment); + addSystemAlarmComment(alarmInfo, user, CLEARED_BY_USER, "userName", user.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user); } else { @@ -144,8 +151,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { AlarmAssignee assignee = alarmInfo.getAssignee(); - String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), assignee.getTitle()); - addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId()); + addSystemAlarmComment(alarmInfo, user, ASSIGNED_TO_USER, "userName", user.getTitle(), "assigneeName", assignee.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user); } else { @@ -162,8 +168,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - String systemComment = String.format("Alarm was unassigned by user %s", user.getTitle()); - addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment); + addSystemAlarmComment(alarmInfo, user, UNASSIGNED_BY_USER, "userName", user.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); } else { @@ -182,8 +187,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb continue; } if (result.isModified()) { - String comment = String.format("Alarm was unassigned because user %s - was deleted", userTitle); - addSystemAlarmComment(result.getAlarm(), null, "ASSIGN", comment); + addSystemAlarmComment(result.getAlarm(), null, UNASSIGNED_FROM_DELETED_USER, "userName", userTitle); logEntityActionService.logEntityAction(result.getAlarm().getTenantId(), result.getAlarm().getOriginator(), result.getAlarm(), result.getAlarm().getCustomerId(), ActionType.ALARM_UNASSIGNED, null); } } @@ -214,20 +218,24 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb return ts > 0 ? ts : System.currentTimeMillis(); } - private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText) { - addSystemAlarmComment(alarm, user, subType, commentText, null); + private void addSystemAlarmComment(Alarm alarm, User user, AlarmCommentSubType subType, String param, String value) { + Map params = new LinkedHashMap<>(1); + params.put(param, value); + addSystemAlarmComment(alarm, user, subType, params); + } + + private void addSystemAlarmComment(Alarm alarm, User user, AlarmCommentSubType subType, String param, String value, String param2, String value2) { + Map params = new LinkedHashMap<>(2); + params.put(param, value); + params.put(param2, value2); + addSystemAlarmComment(alarm, user, subType, params); } - private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText, UserId assigneeId) { + private void addSystemAlarmComment(Alarm alarm, User user, AlarmCommentSubType subType, Map params) { 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()); - } + commentNode.put("text", String.format(subType.getText(), params.values().toArray())) + .put("subtype", subType.name()); + params.forEach(commentNode::put); AlarmComment alarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) 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 40e85aa987..32e2fedaab 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 @@ -20,6 +20,7 @@ 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.NameConflictStrategy; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.audit.ActionType; @@ -40,10 +41,15 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb @Override public Asset save(Asset asset, User user) throws Exception { + return save(asset, NameConflictStrategy.DEFAULT, user); + } + + @Override + public Asset save(Asset asset, NameConflictStrategy nameConflictStrategy, User user) throws Exception { ActionType actionType = asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = asset.getTenantId(); try { - Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); + Asset savedAsset = checkNotNull(assetService.saveAsset(asset, nameConflictStrategy)); autoCommit(user, savedAsset.getId()); logEntityActionService.logEntityAction(tenantId, savedAsset.getId(), savedAsset, asset.getCustomerId(), actionType, user); 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 2d8a5affd0..7ffd5500f3 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 @@ -16,6 +16,7 @@ package org.thingsboard.server.service.entitiy.asset; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; @@ -27,6 +28,8 @@ public interface TbAssetService { Asset save(Asset asset, User user) throws Exception; + Asset save(Asset asset, NameConflictStrategy nameConflictStrategy, User user) throws Exception; + 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/cf/DefaultTbCalculatedFieldService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/cf/DefaultTbCalculatedFieldService.java index fde88bf084..f175c46706 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/cf/DefaultTbCalculatedFieldService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/cf/DefaultTbCalculatedFieldService.java @@ -22,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; @@ -33,7 +34,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.security.model.SecurityUser; -import java.util.Optional; +import java.util.Set; @TbCoreComponent @Service @@ -52,7 +53,7 @@ public class DefaultTbCalculatedFieldService extends AbstractTbEntityService imp CalculatedField existingCf = calculatedFieldService.findById(tenantId, calculatedField.getId()); checkForEntityChange(existingCf, calculatedField); } - checkEntityExistence(tenantId, calculatedField.getEntityId()); + checkEntity(tenantId, calculatedField.getEntityId(), calculatedField.getType()); CalculatedField savedCalculatedField = checkNotNull(calculatedFieldService.save(calculatedField)); logEntityActionService.logEntityAction(tenantId, savedCalculatedField.getId(), savedCalculatedField, actionType, user); return savedCalculatedField; @@ -68,10 +69,9 @@ public class DefaultTbCalculatedFieldService extends AbstractTbEntityService imp } @Override - public PageData findAllByTenantIdAndEntityId(EntityId entityId, SecurityUser user, PageLink pageLink) { - TenantId tenantId = user.getTenantId(); - checkEntityExistence(tenantId, entityId); - return calculatedFieldService.findAllCalculatedFieldsByEntityId(tenantId, entityId, pageLink); + public PageData findByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, CalculatedFieldType type, PageLink pageLink) { + checkEntity(tenantId, entityId, type); + return calculatedFieldService.findCalculatedFieldsByEntityId(tenantId, entityId, type, pageLink); } @Override @@ -95,11 +95,15 @@ public class DefaultTbCalculatedFieldService extends AbstractTbEntityService imp } } - private void checkEntityExistence(TenantId tenantId, EntityId entityId) { - switch (entityId.getEntityType()) { - case ASSET, DEVICE, ASSET_PROFILE, DEVICE_PROFILE -> Optional.ofNullable(entityService.fetchEntity(tenantId, entityId)) - .orElseThrow(() -> new IllegalArgumentException(entityId.getEntityType().getNormalName() + " with id [" + entityId.getId() + "] does not exist.")); - default -> throw new IllegalArgumentException("Entity type '" + entityId.getEntityType() + "' does not support calculated fields."); + private void checkEntity(TenantId tenantId, EntityId entityId, CalculatedFieldType type) { + EntityType entityType = entityId.getEntityType(); + Set supportedTypes = CalculatedField.SUPPORTED_ENTITIES.get(entityType); + if (supportedTypes == null || supportedTypes.isEmpty()) { + throw new IllegalArgumentException("Entity type '" + entityType + "' does not support calculated fields"); + } else if (type != null && !supportedTypes.contains(type)) { + throw new IllegalArgumentException("Entity type '" + entityType + "' does not support '" + type + "' calculated fields"); + } else if (entityService.fetchEntity(tenantId, entityId).isEmpty()) { + throw new IllegalArgumentException(entityType.getNormalName() + " with id [" + entityId.getId() + "] does not exist."); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/cf/TbCalculatedFieldService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/cf/TbCalculatedFieldService.java index 25b58c1d39..5fbb96e636 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/cf/TbCalculatedFieldService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/cf/TbCalculatedFieldService.java @@ -16,9 +16,11 @@ package org.thingsboard.server.service.entitiy.cf; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.service.security.model.SecurityUser; @@ -29,7 +31,7 @@ public interface TbCalculatedFieldService { CalculatedField findById(CalculatedFieldId calculatedFieldId, SecurityUser user); - PageData findAllByTenantIdAndEntityId(EntityId entityId, SecurityUser user, PageLink pageLink); + PageData findByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, CalculatedFieldType type, PageLink pageLink); void delete(CalculatedField calculatedField, SecurityUser user); 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 4988880f4d..6dc1db024d 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 @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; @@ -32,10 +33,15 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements @Override public Customer save(Customer customer, SecurityUser user) throws Exception { + return save(customer, NameConflictStrategy.DEFAULT, user); + } + + @Override + public Customer save(Customer customer, NameConflictStrategy nameConflictStrategy, SecurityUser user) throws Exception { ActionType actionType = customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = customer.getTenantId(); try { - Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer)); + Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer, nameConflictStrategy)); autoCommit(user, savedCustomer.getId()); logEntityActionService.logEntityAction(tenantId, savedCustomer.getId(), savedCustomer, null, actionType, user); return savedCustomer; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java index 63f49783a3..7571947b8c 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java @@ -16,8 +16,12 @@ package org.thingsboard.server.service.entitiy.customer; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.service.entitiy.SimpleTbEntityService; +import org.thingsboard.server.service.security.model.SecurityUser; public interface TbCustomerService extends SimpleTbEntityService { + Customer save(Customer customer, NameConflictStrategy nameConflictStrategy, SecurityUser user) throws Exception; + } 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 9f637b3beb..f0ead91eb3 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 @@ -25,6 +25,7 @@ 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; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -56,10 +57,15 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T @Override public Device save(Device device, String accessToken, User user) throws Exception { + return save(device, accessToken, NameConflictStrategy.DEFAULT, user); + } + + @Override + public Device save(Device device, String accessToken, NameConflictStrategy nameConflictStrategy, User user) throws Exception { ActionType actionType = device.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = device.getTenantId(); try { - Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); + Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken, nameConflictStrategy)); autoCommit(user, savedDevice.getId()); logEntityActionService.logEntityAction(tenantId, savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), actionType, user); @@ -73,10 +79,15 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T @Override public Device saveDeviceWithCredentials(Device device, DeviceCredentials credentials, User user) throws ThingsboardException { + return saveDeviceWithCredentials(device, credentials, NameConflictStrategy.DEFAULT, user); + } + + @Override + public Device saveDeviceWithCredentials(Device device, DeviceCredentials credentials, NameConflictStrategy nameConflictStrategy, User user) throws ThingsboardException { ActionType actionType = device.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = device.getTenantId(); try { - Device savedDevice = checkNotNull(deviceService.saveDeviceWithCredentials(device, credentials)); + Device savedDevice = checkNotNull(deviceService.saveDeviceWithCredentials(device, credentials, nameConflictStrategy)); logEntityActionService.logEntityAction(tenantId, savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), actionType, user); 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 63743d30b3..453d4de1b1 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 @@ -18,6 +18,7 @@ package org.thingsboard.server.service.entitiy.device; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.Edge; @@ -33,8 +34,12 @@ public interface TbDeviceService { Device save(Device device, String accessToken, User user) throws Exception; + Device save(Device device, String accessToken, NameConflictStrategy nameConflictStrategy, User user) throws Exception; + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials, User user) throws ThingsboardException; + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials, NameConflictStrategy nameConflictStrategy, User user) throws ThingsboardException; + 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/entity/relation/DefaultTbEntityRelationService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java index 8777c82940..9bfda157fc 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 @@ -16,7 +16,6 @@ package org.thingsboard.server.service.entitiy.entity.relation; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -33,7 +32,6 @@ import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @Service @TbCoreComponent @AllArgsConstructor -@Slf4j public class DefaultTbEntityRelationService extends AbstractTbEntityService implements TbEntityRelationService { private final RelationService relationService; @@ -71,7 +69,7 @@ public class DefaultTbEntityRelationService extends AbstractTbEntityService impl } @Override - public void deleteCommonRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException { + public void deleteCommonRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) { try { relationService.deleteEntityCommonRelations(tenantId, entityId); logEntityActionService.logEntityAction(tenantId, entityId, null, customerId, ActionType.RELATIONS_DELETED, user); @@ -81,4 +79,5 @@ public class DefaultTbEntityRelationService extends AbstractTbEntityService impl throw e; } } + } 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 7e74383f8c..822c5603b5 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 @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; @@ -80,10 +81,15 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen @Override public EntityView save(EntityView entityView, EntityView existingEntityView, User user) throws Exception { + return save(entityView, existingEntityView, NameConflictStrategy.DEFAULT, user); + } + + @Override + public EntityView save(EntityView entityView, EntityView existingEntityView, NameConflictStrategy nameConflictStrategy, User user) throws Exception { ActionType actionType = entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = entityView.getTenantId(); try { - EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); + EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView, nameConflictStrategy)); this.updateEntityViewAttributes(tenantId, savedEntityView, existingEntityView, user); autoCommit(user, savedEntityView.getId()); logEntityActionService.logEntityAction(savedEntityView.getTenantId(), savedEntityView.getId(), savedEntityView, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/TbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/TbEntityViewService.java index aaf9e65453..0cb789d1db 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/TbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/TbEntityViewService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.entitiy.entityview; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -33,6 +34,8 @@ public interface TbEntityViewService extends ComponentLifecycleListener { EntityView save(EntityView entityView, EntityView existingEntityView, User user) throws Exception; + EntityView save(EntityView entityView, EntityView existingEntityView, NameConflictStrategy nameConflictStrategy, User user) throws Exception; + void updateEntityViewAttributes(TenantId tenantId, EntityView savedEntityView, EntityView oldEntityView, User user) throws ThingsboardException; void delete(EntityView entity, User user) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java index c002838db2..468aa80e6c 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java @@ -30,7 +30,8 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti // This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.). // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release. - private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.1.0"); + // TODO: don't check the "patch" number, since upgrade is not required for patch releases + private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.3.0.0"); private final ProjectInfo projectInfo; private final JdbcTemplate jdbcTemplate; 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 4864f53e17..965d7105a9 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 @@ -49,17 +49,25 @@ 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.device.profile.AlarmCondition; -import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; -import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; -import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; -import org.thingsboard.server.common.data.device.profile.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; +import org.thingsboard.server.common.data.alarm.rule.condition.SimpleAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionFilter; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.SimpleAlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.BooleanFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.debug.DebugSettings; 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.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; -import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -74,12 +82,7 @@ import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.query.BooleanFilterPredicate; -import org.thingsboard.server.common.data.query.DynamicValue; -import org.thingsboard.server.common.data.query.DynamicValueSourceType; import org.thingsboard.server.common.data.query.EntityKeyValueType; -import org.thingsboard.server.common.data.query.FilterPredicateValue; -import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; @@ -94,12 +97,13 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceConnectivityConfiguration; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.mobile.MobileAppDao; import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTargetService; @@ -117,7 +121,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.List; -import java.util.TreeMap; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -155,6 +159,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { private final MobileAppDao mobileAppDao; private final NotificationSettingsService notificationSettingsService; private final NotificationTargetService notificationTargetService; + private final CalculatedFieldService calculatedFieldService; @Autowired private BCryptPasswordEncoder passwordEncoder; @@ -306,8 +311,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { if (invalidSignKey) { log.warn("WARNING: {}. A new JWT Signing Key has been added automatically. " + - "You can change the JWT Signing Key using the Web UI: " + - "Navigate to \"System settings -> Security settings\" while logged in as a System Administrator.", warningMessage); + "You can change the JWT Signing Key using the Web UI: " + + "Navigate to \"System settings -> Security settings\" while logged in as a System Administrator.", warningMessage); jwtSettings.setTokenSigningKey(generateRandomKey()); jwtSettingsService.saveJwtSettings(jwtSettings); @@ -319,9 +324,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { .filter(mobileApp -> !validateKeyLength(mobileApp.getAppSecret())) .forEach(mobileApp -> { log.warn("WARNING: The App secret is shorter than 512 bits, which is a security risk. " + - "A new Application Secret has been added automatically for Mobile Application [{}]. " + - "You can change the Application Secret using the Web UI: " + - "Navigate to \"Security settings -> OAuth2 -> Mobile applications\" while logged in as a System Administrator.", mobileApp.getPkgName()); + "A new Application Secret has been added automatically for Mobile Application [{}]. " + + "You can change the Application Secret using the Web UI: " + + "Navigate to \"Security settings -> OAuth2 -> Mobile applications\" while logged in as a System Administrator.", mobileApp.getPkgName()); mobileApp.setAppSecret(generateRandomKey()); mobileAppDao.save(TenantId.SYS_TENANT_ID, mobileApp); }); @@ -372,11 +377,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { createDevice(demoTenant.getId(), customerB.getId(), defaultDeviceProfile.getId(), "Test Device B1", "B1_TEST_TOKEN", null); createDevice(demoTenant.getId(), customerC.getId(), defaultDeviceProfile.getId(), "Test Device C1", "C1_TEST_TOKEN", null); - createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "DHT11 Demo Device", "DHT11_DEMO_TOKEN", "Demo device that is used in sample " + - "applications that upload data from DHT11 temperature and humidity sensor"); + createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "DHT11 Demo Device", "DHT11_DEMO_TOKEN", + "Demo device that is used in sample applications that upload data from DHT11 temperature and humidity sensor"); - createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + - "Raspberry Pi GPIO control sample application"); + createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", + "Demo device that is used in Raspberry Pi GPIO control sample application"); DeviceProfile thermostatDeviceProfile = new DeviceProfile(); thermostatDeviceProfile.setTenantId(demoTenant.getId()); @@ -398,110 +403,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { deviceProfileData.setProvisionConfiguration(provisionConfiguration); thermostatDeviceProfile.setProfileData(deviceProfileData); - DeviceProfileAlarm highTemperature = new DeviceProfileAlarm(); - highTemperature.setId("highTemperatureAlarmID"); - highTemperature.setAlarmType("High Temperature"); - AlarmRule temperatureRule = new AlarmRule(); - AlarmCondition temperatureCondition = new AlarmCondition(); - temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); - - AlarmConditionFilter temperatureAlarmFlagAttributeFilter = new AlarmConditionFilter(); - temperatureAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperatureAlarmFlag")); - temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); - BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); - temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); - temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); - temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate); - - AlarmConditionFilter temperatureTimeseriesFilter = new AlarmConditionFilter(); - temperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); - temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); - temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); - FilterPredicateValue temperatureTimeseriesPredicateValue = - new FilterPredicateValue<>(25.0, null, - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "temperatureAlarmThreshold")); - temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue); - temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate); - temperatureCondition.setCondition(Arrays.asList(temperatureAlarmFlagAttributeFilter, temperatureTimeseriesFilter)); - temperatureRule.setAlarmDetails("Current temperature = ${temperature}"); - temperatureRule.setCondition(temperatureCondition); - highTemperature.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule))); - - AlarmRule clearTemperatureRule = new AlarmRule(); - AlarmCondition clearTemperatureCondition = new AlarmCondition(); - clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); - - AlarmConditionFilter clearTemperatureTimeseriesFilter = new AlarmConditionFilter(); - clearTemperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); - clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); - clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); - FilterPredicateValue clearTemperatureTimeseriesPredicateValue = - new FilterPredicateValue<>(25.0, null, - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "temperatureAlarmThreshold")); - - clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue); - clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate); - clearTemperatureCondition.setCondition(Collections.singletonList(clearTemperatureTimeseriesFilter)); - clearTemperatureRule.setCondition(clearTemperatureCondition); - clearTemperatureRule.setAlarmDetails("Current temperature = ${temperature}"); - highTemperature.setClearRule(clearTemperatureRule); - - DeviceProfileAlarm lowHumidity = new DeviceProfileAlarm(); - lowHumidity.setId("lowHumidityAlarmID"); - lowHumidity.setAlarmType("Low Humidity"); - AlarmRule humidityRule = new AlarmRule(); - AlarmCondition humidityCondition = new AlarmCondition(); - humidityCondition.setSpec(new SimpleAlarmConditionSpec()); - - AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter(); - humidityAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "humidityAlarmFlag")); - humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); - BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate(); - humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); - humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); - humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate); - - AlarmConditionFilter humidityTimeseriesFilter = new AlarmConditionFilter(); - humidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity")); - humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); - humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); - FilterPredicateValue humidityTimeseriesPredicateValue = - new FilterPredicateValue<>(60.0, null, - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "humidityAlarmThreshold")); - humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue); - humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate); - humidityCondition.setCondition(Arrays.asList(humidityAlarmFlagAttributeFilter, humidityTimeseriesFilter)); - - humidityRule.setCondition(humidityCondition); - humidityRule.setAlarmDetails("Current humidity = ${humidity}"); - lowHumidity.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule))); - - AlarmRule clearHumidityRule = new AlarmRule(); - AlarmCondition clearHumidityCondition = new AlarmCondition(); - clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); - - AlarmConditionFilter clearHumidityTimeseriesFilter = new AlarmConditionFilter(); - clearHumidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity")); - clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); - clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); - FilterPredicateValue clearHumidityTimeseriesPredicateValue = - new FilterPredicateValue<>(60.0, null, - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "humidityAlarmThreshold")); - - clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue); - clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate); - clearHumidityCondition.setCondition(Collections.singletonList(clearHumidityTimeseriesFilter)); - clearHumidityRule.setCondition(clearHumidityCondition); - clearHumidityRule.setAlarmDetails("Current humidity = ${humidity}"); - lowHumidity.setClearRule(clearHumidityRule); - - deviceProfileData.setAlarms(Arrays.asList(highTemperature, lowHumidity)); - DeviceProfile savedThermostatDeviceProfile = deviceProfileService.saveDeviceProfile(thermostatDeviceProfile); + createAlarmRules(demoTenant.getId(), savedThermostatDeviceProfile.getId()); DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); @@ -526,6 +429,136 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { installScripts.createDefaultTenantDashboards(demoTenant.getId(), null); } + private void createAlarmRules(TenantId tenantId, DeviceProfileId deviceProfileId) { + CalculatedField highTemperature = new CalculatedField(); + highTemperature.setName("High Temperature"); + highTemperature.setType(CalculatedFieldType.ALARM); + highTemperature.setTenantId(tenantId); + highTemperature.setEntityId(deviceProfileId); + highTemperature.setDebugSettings(DebugSettings.all()); + AlarmCalculatedFieldConfiguration highTemperatureConfig = new AlarmCalculatedFieldConfiguration(); + highTemperature.setConfiguration(highTemperatureConfig); + + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + Argument temperatureThresholdArgument = new Argument(); + temperatureThresholdArgument.setRefEntityKey(new ReferencedEntityKey("temperatureAlarmThreshold", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + temperatureThresholdArgument.setDefaultValue("25"); + Argument temperatureAlarmFlagArgument = new Argument(); + temperatureAlarmFlagArgument.setRefEntityKey(new ReferencedEntityKey("temperatureAlarmFlag", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + highTemperatureConfig.setArguments(Map.of( + "temperature", temperatureArgument, + "temperatureAlarmThreshold", temperatureThresholdArgument, + "temperatureAlarmFlag", temperatureAlarmFlagArgument + )); + + AlarmRule temperatureRule = new AlarmRule(); + SimpleAlarmCondition temperatureCondition = new SimpleAlarmCondition(); + + AlarmConditionFilter temperatureAlarmFlagFilter = new AlarmConditionFilter(); + temperatureAlarmFlagFilter.setArgument("temperatureAlarmFlag"); + temperatureAlarmFlagFilter.setValueType(EntityKeyValueType.BOOLEAN); + BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); + temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); + temperatureAlarmFlagAttributePredicate.setValue(new AlarmConditionValue<>(Boolean.TRUE, null)); + temperatureAlarmFlagFilter.setPredicates(List.of(temperatureAlarmFlagAttributePredicate)); + + AlarmConditionFilter temperatureFilter = new AlarmConditionFilter(); + temperatureFilter.setArgument("temperature"); + temperatureFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate temperatureFilterPredicate = new NumericFilterPredicate(); + temperatureFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + temperatureFilterPredicate.setValue(new AlarmConditionValue<>(null, "temperatureAlarmThreshold")); + temperatureFilter.setPredicates(List.of(temperatureFilterPredicate)); + temperatureCondition.setExpression(new SimpleAlarmConditionExpression(List.of(temperatureAlarmFlagFilter, temperatureFilter), ComplexOperation.AND)); + temperatureRule.setCondition(temperatureCondition); + temperatureRule.setAlarmDetails("Current temperature = ${temperature}"); + highTemperatureConfig.setCreateRules(Map.of( + AlarmSeverity.MAJOR, temperatureRule + )); + + AlarmRule clearTemperatureRule = new AlarmRule(); + SimpleAlarmCondition clearTemperatureCondition = new SimpleAlarmCondition(); + + AlarmConditionFilter clearTemperatureFilter = new AlarmConditionFilter(); + clearTemperatureFilter.setArgument("temperature"); + clearTemperatureFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate clearTemperatureFilterPredicate = new NumericFilterPredicate(); + clearTemperatureFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); + clearTemperatureFilterPredicate.setValue(new AlarmConditionValue<>(null, "temperatureAlarmThreshold")); + clearTemperatureFilter.setPredicates(List.of(clearTemperatureFilterPredicate)); + clearTemperatureCondition.setExpression(new SimpleAlarmConditionExpression(List.of(clearTemperatureFilter), ComplexOperation.AND)); + clearTemperatureRule.setCondition(clearTemperatureCondition); + clearTemperatureRule.setAlarmDetails("Current temperature = ${temperature}"); + highTemperatureConfig.setClearRule(clearTemperatureRule); + + calculatedFieldService.save(highTemperature); + + CalculatedField lowHumidity = new CalculatedField(); + lowHumidity.setName("Low Humidity"); + lowHumidity.setType(CalculatedFieldType.ALARM); + lowHumidity.setTenantId(tenantId); + lowHumidity.setEntityId(deviceProfileId); + lowHumidity.setDebugSettings(DebugSettings.all()); + AlarmCalculatedFieldConfiguration lowHumidityConfig = new AlarmCalculatedFieldConfiguration(); + lowHumidity.setConfiguration(lowHumidityConfig); + + Argument humidityArgument = new Argument(); + humidityArgument.setRefEntityKey(new ReferencedEntityKey("humidity", ArgumentType.TS_LATEST, null)); + Argument humidityThresholdArgument = new Argument(); + humidityThresholdArgument.setRefEntityKey(new ReferencedEntityKey("humidityAlarmThreshold", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + humidityThresholdArgument.setDefaultValue("60"); + Argument humidityAlarmFlagArgument = new Argument(); + humidityAlarmFlagArgument.setRefEntityKey(new ReferencedEntityKey("humidityAlarmFlag", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + lowHumidityConfig.setArguments(Map.of( + "humidity", humidityArgument, + "humidityAlarmThreshold", humidityThresholdArgument, + "humidityAlarmFlag", humidityAlarmFlagArgument + )); + + AlarmRule humidityRule = new AlarmRule(); + SimpleAlarmCondition humidityCondition = new SimpleAlarmCondition(); + + AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter(); + humidityAlarmFlagAttributeFilter.setArgument("humidityAlarmFlag"); + humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); + BooleanFilterPredicate humidityAlarmFlagPredicate = new BooleanFilterPredicate(); + humidityAlarmFlagPredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); + humidityAlarmFlagPredicate.setValue(new AlarmConditionValue<>(Boolean.TRUE, null)); + humidityAlarmFlagAttributeFilter.setPredicates(List.of(humidityAlarmFlagPredicate)); + + AlarmConditionFilter humidityFilter = new AlarmConditionFilter(); + humidityFilter.setArgument("humidity"); + humidityFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate humidityFilterPredicate = new NumericFilterPredicate(); + humidityFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); + humidityFilterPredicate.setValue(new AlarmConditionValue<>(null, "humidityAlarmThreshold")); + humidityFilter.setPredicates(List.of(humidityFilterPredicate)); + humidityCondition.setExpression(new SimpleAlarmConditionExpression(List.of(humidityAlarmFlagAttributeFilter, humidityFilter), ComplexOperation.AND)); + humidityRule.setCondition(humidityCondition); + humidityRule.setAlarmDetails("Current humidity = ${humidity}"); + lowHumidityConfig.setCreateRules(Map.of( + AlarmSeverity.MINOR, humidityRule + )); + + AlarmRule clearHumidityRule = new AlarmRule(); + SimpleAlarmCondition clearHumidityCondition = new SimpleAlarmCondition(); + + AlarmConditionFilter clearHumidityFilter = new AlarmConditionFilter(); + clearHumidityFilter.setArgument("humidity"); + clearHumidityFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate clearHumidityFilterPredicate = new NumericFilterPredicate(); + clearHumidityFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); + clearHumidityFilterPredicate.setValue(new AlarmConditionValue<>(null, "humidityAlarmThreshold")); + clearHumidityFilter.setPredicates(List.of(clearHumidityFilterPredicate)); + clearHumidityCondition.setExpression(new SimpleAlarmConditionExpression(List.of(clearHumidityFilter), ComplexOperation.AND)); + clearHumidityRule.setCondition(clearHumidityCondition); + clearHumidityRule.setAlarmDetails("Current humidity = ${humidity}"); + lowHumidityConfig.setClearRule(clearHumidityRule); + + calculatedFieldService.save(lowHumidity); + } + @Override public void loadSystemWidgets() throws Exception { installScripts.loadSystemWidgets(); @@ -609,6 +642,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { public void onFailure(Throwable t) { log.warn("[{}] Failed to update attribute [{}] with value [{}]", deviceId, key, value, t); } + } private void addTsCallback(ListenableFuture saveFuture, final FutureCallback callback) { 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 364fcb47dd..96f58707b5 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 @@ -39,7 +39,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.resource.ResourceService; @@ -406,7 +406,8 @@ public class InstallScripts { Path resourcesDir = Path.of(getDataDir(), RESOURCES_DIR); loadSystemResources(resourcesDir.resolve("images"), ResourceType.IMAGE, null); - loadSystemResources(resourcesDir.resolve("js_modules"), ResourceType.JS_MODULE, ResourceSubType.EXTENSION); + loadSystemResources(resourcesDir.resolve("js_extensions"), ResourceType.JS_MODULE, ResourceSubType.EXTENSION); + loadSystemResources(resourcesDir.resolve("js_modules"), ResourceType.JS_MODULE, ResourceSubType.MODULE); loadSystemResources(resourcesDir.resolve("dashboards"), ResourceType.DASHBOARD, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/ProjectInfo.java b/application/src/main/java/org/thingsboard/server/service/install/ProjectInfo.java index 44f73f00a8..128ca5578a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/ProjectInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/install/ProjectInfo.java @@ -19,14 +19,17 @@ import lombok.RequiredArgsConstructor; import org.springframework.boot.info.BuildProperties; import org.springframework.stereotype.Component; +import java.util.Optional; + @Component @RequiredArgsConstructor public class ProjectInfo { - private final BuildProperties buildProperties; + private final Optional buildProperties; public String getProjectVersion() { - return buildProperties.getVersion().replaceAll("[^\\d.]", ""); + return buildProperties.orElseThrow(() -> new IllegalStateException("Build properties are missing. Please rebuild the project with maven")) + .getVersion().replaceAll("[^\\d.]", ""); } public String getProductType() { 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 6bdcb1b0a2..2eb8df92fe 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 @@ -15,8 +15,6 @@ */ 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; @@ -24,12 +22,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.query.DynamicValue; -import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.RuleNodeClassInfo; @@ -129,60 +124,6 @@ public class DefaultDataUpdateService implements DataUpdateService { return ruleNodeIds; } - boolean convertDeviceProfileForVersion330(JsonNode profileData) { - boolean isUpdated = false; - if (profileData.has("alarms") && !profileData.get("alarms").isNull()) { - JsonNode alarms = profileData.get("alarms"); - for (JsonNode alarm : alarms) { - if (alarm.has("createRules")) { - JsonNode createRules = alarm.get("createRules"); - for (AlarmSeverity severity : AlarmSeverity.values()) { - if (createRules.has(severity.name())) { - JsonNode spec = createRules.get(severity.name()).get("condition").get("spec"); - if (convertDeviceProfileAlarmRulesForVersion330(spec)) { - isUpdated = true; - } - } - } - } - if (alarm.has("clearRule") && !alarm.get("clearRule").isNull()) { - JsonNode spec = alarm.get("clearRule").get("condition").get("spec"); - if (convertDeviceProfileAlarmRulesForVersion330(spec)) { - isUpdated = true; - } - } - } - } - return isUpdated; - } - - boolean convertDeviceProfileAlarmRulesForVersion330(JsonNode spec) { - if (spec != null) { - if (spec.has("type") && spec.get("type").asText().equals("DURATION")) { - if (spec.has("value")) { - long value = spec.get("value").asLong(); - var predicate = new FilterPredicateValue<>( - value, null, new DynamicValue<>(null, null, false) - ); - ((ObjectNode) spec).remove("value"); - ((ObjectNode) spec).putPOJO("predicate", predicate); - return true; - } - } else if (spec.has("type") && spec.get("type").asText().equals("REPEATING")) { - if (spec.has("count")) { - int count = spec.get("count").asInt(); - var predicate = new FilterPredicateValue<>( - count, null, new DynamicValue<>(null, null, false) - ); - ((ObjectNode) spec).remove("count"); - ((ObjectNode) spec).putPOJO("predicate", predicate); - return true; - } - } - } - return false; - } - public static boolean getEnv(String name, boolean defaultValue) { String env = System.getenv(name); if (env == null) { 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 98b696125d..585130501f 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 @@ -62,6 +62,7 @@ public class AlarmAssignmentTriggerProcessor implements NotificationRuleTriggerP .alarmType(alarmInfo.getType()) .alarmOriginator(alarmInfo.getOriginator()) .alarmOriginatorName(alarmInfo.getOriginatorName()) + .alarmOriginatorLabel(alarmInfo.getOriginatorLabel()) .alarmSeverity(alarmInfo.getSeverity()) .alarmStatus(alarmInfo.getStatus()) .alarmCustomerId(alarmInfo.getCustomerId()) 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 4ae158c6e7..155e8e4e3c 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 @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.NameLabelAndCustomerDetails; 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.AlarmCommentTrigger; @@ -57,11 +58,14 @@ public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProc @Override public RuleOriginatedNotificationInfo constructNotificationInfo(AlarmCommentTrigger trigger) { Alarm alarm = trigger.getAlarm(); - String originatorName; + String originatorName, originatorLabel; if (alarm instanceof AlarmInfo) { originatorName = ((AlarmInfo) alarm).getOriginatorName(); + originatorLabel = ((AlarmInfo) alarm).getOriginatorLabel(); } else { - originatorName = entityService.fetchEntityName(trigger.getTenantId(), alarm.getOriginator()).orElse(""); + var infoOpt = entityService.fetchNameLabelAndCustomerDetails(trigger.getTenantId(), alarm.getOriginator()); + originatorName = infoOpt.map(NameLabelAndCustomerDetails::getName).orElse(null); + originatorLabel = infoOpt.map(NameLabelAndCustomerDetails::getLabel).orElse(null); } return AlarmCommentNotificationInfo.builder() .comment(trigger.getComment().getComment().get("text").asText()) @@ -73,6 +77,7 @@ public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProc .alarmType(alarm.getType()) .alarmOriginator(alarm.getOriginator()) .alarmOriginatorName(originatorName) + .alarmOriginatorLabel(originatorLabel) .alarmSeverity(alarm.getSeverity()) .alarmStatus(alarm.getStatus()) .alarmCustomerId(alarm.getCustomerId()) 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 e91b1002f7..992d86d83a 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 @@ -15,7 +15,9 @@ */ package org.thingsboard.server.service.notification.rule.trigger; +import com.fasterxml.jackson.databind.JsonNode; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -28,6 +30,9 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Alarm import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.ClearRule; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import java.util.HashMap; +import java.util.Map; + import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; @@ -106,15 +111,27 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor toDetailsTemplateMap(JsonNode details) { + Map infoMap = JacksonUtil.toFlatMap(details); + Map result = new HashMap<>(); + for (Map.Entry entry : infoMap.entrySet()) { + String key = "details." + entry.getKey(); + result.put(key, entry.getValue()); + } + return result; + } + @Override public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ALARM; diff --git a/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java b/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java index 38efff3d39..4d76989a92 100644 --- a/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java +++ b/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java @@ -15,24 +15,18 @@ */ package org.thingsboard.server.service.query; -import com.fasterxml.jackson.databind.node.ArrayNode; -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.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.KvUtil; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; +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.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -40,6 +34,7 @@ import org.thingsboard.server.common.data.page.PageData; 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.common.data.query.AvailableEntityKeys; import org.thingsboard.server.common.data.query.ComplexFilterPredicate; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.EntityCountQuery; @@ -56,16 +51,13 @@ import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.sql.query.EntityKeyMapping; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; -import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -73,9 +65,10 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.function.Consumer; import java.util.stream.Collectors; +import static com.google.common.util.concurrent.Futures.immediateFuture; + @Service @Slf4j @TbCoreComponent @@ -138,20 +131,12 @@ public class DefaultEntityQueryService implements EntityQueryService { } private void resolveDynamicValue(DynamicValue dynamicValue, SecurityUser user, FilterPredicateType predicateType) { - EntityId entityId; - switch (dynamicValue.getSourceType()) { - case CURRENT_TENANT: - entityId = user.getTenantId(); - break; - case CURRENT_CUSTOMER: - entityId = user.getCustomerId(); - break; - case CURRENT_USER: - entityId = user.getId(); - break; - default: - throw new RuntimeException("Not supported operation for source type: {" + dynamicValue.getSourceType() + "}"); - } + EntityId entityId = switch (dynamicValue.getSourceType()) { + case CURRENT_TENANT -> user.getTenantId(); + case CURRENT_CUSTOMER -> user.getCustomerId(); + case CURRENT_USER -> user.getId(); + default -> throw new RuntimeException("Not supported operation for source type: {" + dynamicValue.getSourceType() + "}"); + }; try { Optional valueOpt = attributesService.find(user.getTenantId(), entityId, @@ -242,101 +227,51 @@ public class DefaultEntityQueryService implements EntityQueryService { } @Override - public DeferredResult getKeysByQuery(SecurityUser securityUser, TenantId tenantId, EntityDataQuery query, - boolean isTimeseries, boolean isAttributes, String attributesScope) { - final DeferredResult response = new DeferredResult<>(); + public ListenableFuture getKeysByQuery(SecurityUser securityUser, TenantId tenantId, EntityDataQuery query, + boolean isTimeseries, boolean isAttributes, AttributeScope scope) { if (!isAttributes && !isTimeseries) { - replyWithEmptyResponse(response); - return response; + return immediateFuture(AvailableEntityKeys.none()); } - List ids = this.findEntityDataByQuery(securityUser, query).getData().stream() + List ids = findEntityDataByQuery(securityUser, query).getData().stream() .map(EntityData::getEntityId) - .collect(Collectors.toList()); + .toList(); if (ids.isEmpty()) { - replyWithEmptyResponse(response); - return response; + return immediateFuture(AvailableEntityKeys.none()); } Set types = ids.stream().map(EntityId::getEntityType).collect(Collectors.toSet()); - final ListenableFuture> timeseriesKeysFuture; - final ListenableFuture> attributesKeysFuture; + ListenableFuture> timeseriesKeysFuture; + ListenableFuture> attributesKeysFuture; if (isTimeseries) { - timeseriesKeysFuture = dbCallbackExecutor.submit(() -> timeseriesService.findAllKeysByEntityIds(tenantId, ids)); + timeseriesKeysFuture = timeseriesService.findAllKeysByEntityIdsAsync(tenantId, ids); } else { - timeseriesKeysFuture = null; + timeseriesKeysFuture = immediateFuture(Collections.emptyList()); } if (isAttributes) { Map> typesMap = ids.stream().collect(Collectors.groupingBy(EntityId::getEntityType)); List>> futures = new ArrayList<>(typesMap.size()); - typesMap.forEach((type, entityIds) -> futures.add(dbCallbackExecutor.submit(() -> attributesService.findAllKeysByEntityIds(tenantId, entityIds, attributesScope)))); + typesMap.forEach((type, entityIds) -> futures.add(dbCallbackExecutor.submit(() -> attributesService.findAllKeysByEntityIds(tenantId, entityIds, scope)))); attributesKeysFuture = Futures.transform(Futures.allAsList(futures), lists -> { if (CollectionUtils.isEmpty(lists)) { return Collections.emptyList(); } - return lists.stream().flatMap(List::stream).distinct().sorted().collect(Collectors.toList()); - }, dbCallbackExecutor); - } else { - attributesKeysFuture = null; - } - - if (isTimeseries && isAttributes) { - Futures.whenAllComplete(timeseriesKeysFuture, attributesKeysFuture).run(() -> { - try { - replyWithResponse(response, types, timeseriesKeysFuture.get(), attributesKeysFuture.get()); - } catch (Exception e) { - log.error("Failed to fetch timeseries and attributes keys!", e); - AccessValidator.handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR); - } + return lists.stream().flatMap(List::stream).distinct().sorted().toList(); }, dbCallbackExecutor); - } else if (isTimeseries) { - addCallback(timeseriesKeysFuture, keys -> replyWithResponse(response, types, keys, null), - error -> { - log.error("Failed to fetch timeseries keys!", error); - AccessValidator.handleError(error, response, HttpStatus.INTERNAL_SERVER_ERROR); - }); } else { - addCallback(attributesKeysFuture, keys -> replyWithResponse(response, types, null, keys), - error -> { - log.error("Failed to fetch attributes keys!", error); - AccessValidator.handleError(error, response, HttpStatus.INTERNAL_SERVER_ERROR); - }); + attributesKeysFuture = immediateFuture(Collections.emptyList()); } - return response; - } - - private void replyWithResponse(DeferredResult response, Set types, List timeseriesKeys, List attributesKeys) { - ObjectNode json = JacksonUtil.newObjectNode(); - addItemsToArrayNode(json.putArray("entityTypes"), types); - addItemsToArrayNode(json.putArray("timeseries"), timeseriesKeys); - addItemsToArrayNode(json.putArray("attribute"), attributesKeys); - response.setResult(new ResponseEntity<>(json, HttpStatus.OK)); - } - private void replyWithEmptyResponse(DeferredResult response) { - replyWithResponse(response, Collections.emptySet(), Collections.emptyList(), Collections.emptyList()); - } - - private void addItemsToArrayNode(ArrayNode arrayNode, Collection collection) { - if (!CollectionUtils.isEmpty(collection)) { - collection.forEach(item -> arrayNode.add(item.toString())); - } - } - - private void addCallback(ListenableFuture> future, Consumer> success, Consumer error) { - Futures.addCallback(future, new FutureCallback>() { - @Override - public void onSuccess(@Nullable List keys) { - success.accept(keys); - } - - @Override - public void onFailure(Throwable t) { - error.accept(t); - } - }, dbCallbackExecutor); + return Futures.whenAllComplete(timeseriesKeysFuture, attributesKeysFuture) + .call(() -> { + try { + return new AvailableEntityKeys(types, Futures.getDone(timeseriesKeysFuture), Futures.getDone(attributesKeysFuture)); + } catch (ExecutionException e) { + throw new ThingsboardException(e.getCause(), ThingsboardErrorCode.DATABASE); + } + }, dbCallbackExecutor); } } diff --git a/application/src/main/java/org/thingsboard/server/service/query/EntityQueryService.java b/application/src/main/java/org/thingsboard/server/service/query/EntityQueryService.java index 464fde4410..a90cf684a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/query/EntityQueryService.java +++ b/application/src/main/java/org/thingsboard/server/service/query/EntityQueryService.java @@ -15,13 +15,14 @@ */ package org.thingsboard.server.service.query; -import org.springframework.http.ResponseEntity; -import org.springframework.web.context.request.async.DeferredResult; +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; 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.common.data.query.AvailableEntityKeys; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -37,7 +38,7 @@ public interface EntityQueryService { long countAlarmsByQuery(SecurityUser securityUser, AlarmCountQuery query); - DeferredResult getKeysByQuery(SecurityUser securityUser, TenantId tenantId, EntityDataQuery query, - boolean isTimeseries, boolean isAttributes, String attributesScope); + ListenableFuture getKeysByQuery(SecurityUser securityUser, TenantId tenantId, EntityDataQuery query, + boolean isTimeseries, boolean isAttributes, AttributeScope scope); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java index 60e60b5444..d0f7bfa81c 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java @@ -22,6 +22,7 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.calculatedField.CalculatedFieldEntityActionEventMsg; import org.thingsboard.server.actors.calculatedField.CalculatedFieldLinkedTelemetryMsg; import org.thingsboard.server.actors.calculatedField.CalculatedFieldTelemetryMsg; import org.thingsboard.server.common.data.DataConstants; @@ -160,12 +161,7 @@ public class DefaultTbCalculatedFieldConsumerService extends AbstractPartitionBa try { ToCalculatedFieldMsg toCfMsg = msg.getValue(); pendingMsgHolder.setMsg(toCfMsg); - if (toCfMsg.hasTelemetryMsg()) { - log.trace("[{}] Forwarding regular telemetry message for processing {}", id, toCfMsg.getTelemetryMsg()); - forwardToActorSystem(toCfMsg.getTelemetryMsg(), callback); - } else if (toCfMsg.hasLinkedTelemetryMsg()) { - forwardToActorSystem(toCfMsg.getLinkedTelemetryMsg(), callback); - } + processMsg(toCfMsg, id, callback); } catch (Throwable e) { log.warn("[{}] Failed to process message: {}", id, msg, e); callback.onFailure(e); @@ -183,6 +179,17 @@ public class DefaultTbCalculatedFieldConsumerService extends AbstractPartitionBa consumer.commit(); } + private void processMsg(ToCalculatedFieldMsg toCfMsg, UUID id, TbCallback callback) { + if (toCfMsg.hasTelemetryMsg()) { + log.trace("[{}] Forwarding regular telemetry message for processing {}", id, toCfMsg.getTelemetryMsg()); + forwardToActorSystem(toCfMsg.getTelemetryMsg(), callback); + } else if (toCfMsg.hasLinkedTelemetryMsg()) { + forwardToActorSystem(toCfMsg.getLinkedTelemetryMsg(), callback); + } else if (toCfMsg.hasEventMsg()) { + actorContext.tell(CalculatedFieldEntityActionEventMsg.fromProto(toCfMsg.getEventMsg(), callback)); + } + } + @Override protected ServiceType getServiceType() { return ServiceType.TB_RULE_ENGINE; 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 718ec2adfa..bed3729328 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 @@ -26,6 +26,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageState; +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; @@ -55,6 +56,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; @@ -468,6 +470,17 @@ public class DefaultTbClusterService implements TbClusterService { broadcastEntityStateChangeEvent(resource.getTenantId(), resource.getId(), ComponentLifecycleEvent.DELETED); } + @Override + public void onCustomerUpdated(Customer customer, Customer oldCustomer) { + ComponentLifecycleMsg msg = ComponentLifecycleMsg.builder() + .tenantId(customer.getTenantId()) + .entityId(customer.getId()) + .event(oldCustomer == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED) + .ownerChanged(false) // for compatibility with PE + .build(); + broadcast(msg); + } + private void broadcastEntityChangeToTransport(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName(); log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName); @@ -597,7 +610,9 @@ public class DefaultTbClusterService implements TbClusterService { EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE, EntityType.JOB, - EntityType.TB_RESOURCE) + EntityType.TB_RESOURCE, + EntityType.CUSTOMER, + EntityType.USER) || (entityType == EntityType.ASSET && msg.getEvent() == ComponentLifecycleEvent.UPDATED) || (entityType == EntityType.DEVICE && msg.getEvent() == ComponentLifecycleEvent.UPDATED) ) { @@ -673,6 +688,7 @@ public class DefaultTbClusterService implements TbClusterService { } msg.event(ComponentLifecycleEvent.UPDATED) .oldProfileId(old.getDeviceProfileId()) + .ownerChanged(!entity.getOwnerId().equals(old.getOwnerId())) .oldName(old.getName()); } broadcast(msg.build()); @@ -693,6 +709,7 @@ public class DefaultTbClusterService implements TbClusterService { } else { msg.event(ComponentLifecycleEvent.UPDATED) .oldProfileId(old.getAssetProfileId()) + .ownerChanged(!entity.getOwnerId().equals(old.getOwnerId())) .oldName(old.getName()); } broadcast(msg.build()); @@ -708,6 +725,28 @@ public class DefaultTbClusterService implements TbClusterService { broadcastEntityStateChangeEvent(calculatedField.getTenantId(), calculatedField.getId(), ComponentLifecycleEvent.DELETED); } + @Override + public void onRelationUpdated(TenantId tenantId, EntityRelation entityRelation, TbQueueCallback callback) { + ComponentLifecycleMsg msg = ComponentLifecycleMsg.builder() + .tenantId(tenantId) + .entityId(entityRelation.getFrom()) + .event(ComponentLifecycleEvent.RELATION_UPDATED) + .info(JacksonUtil.valueToTree(entityRelation)) + .build(); + broadcast(msg); + } + + @Override + public void onRelationDeleted(TenantId tenantId, EntityRelation entityRelation, TbQueueCallback callback) { + ComponentLifecycleMsg msg = ComponentLifecycleMsg.builder() + .tenantId(tenantId) + .entityId(entityRelation.getFrom()) + .event(ComponentLifecycleEvent.RELATION_DELETED) + .info(JacksonUtil.valueToTree(entityRelation)) + .build(); + broadcast(msg); + } + @Override public void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action, EdgeId originatorEdgeId) { if (!edgesEnabled) { diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java index 732a330643..53da1bb8a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java @@ -87,8 +87,7 @@ public class DefaultTbEdgeConsumerService extends AbstractConsumerService> msgs, TbQueueConsumer> consumer, - Object consumerKey, + ConsumerKey consumerKey, Queue queue) throws Exception { TbRuleEngineSubmitStrategy submitStrategy = getSubmitStrategy(queue); TbRuleEngineProcessingStrategy ackStrategy = getProcessingStrategy(queue); 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 1fcf1fcc71..395dc7e955 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 @@ -75,7 +75,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements ActionType actionType = resource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = resource.getTenantId(); try { - if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { + if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType()) && resource.getId() == null) { toLwm2mResource(resource); } else if (resource.getResourceKey() == null) { resource.setResourceKey(resource.getFileName()); 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 afce336d0e..53801383a6 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 @@ -19,8 +19,8 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceDeleteResult; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.TbResourceInfo; +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.lwm2m.LwM2mObject; diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index 2c5e706838..19df974999 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -69,7 +69,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/AbstractAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/AbstractAuthenticationProvider.java new file mode 100644 index 0000000000..4da933e774 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/AbstractAuthenticationProvider.java @@ -0,0 +1,95 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserAuthDetails; +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.security.Authority; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.UserPrincipal; +import org.thingsboard.server.service.user.cache.UserAuthDetailsCache; + +import java.util.UUID; + +@Slf4j +@RequiredArgsConstructor +public abstract class AbstractAuthenticationProvider implements AuthenticationProvider { + + private final CustomerService customerService; + private final UserAuthDetailsCache userAuthDetailsCache; + + protected SecurityUser authenticateByPublicId(String publicId, String authContextName, UserPrincipal userPrincipal) { + TenantId systemId = TenantId.SYS_TENANT_ID; + CustomerId customerId; + try { + customerId = new CustomerId(UUID.fromString(publicId)); + } catch (Exception e) { + throw new BadCredentialsException(authContextName + " is not valid"); + } + Customer publicCustomer = customerService.findCustomerById(systemId, customerId); + if (publicCustomer == null) { + throw new UsernameNotFoundException("Public entity not found"); + } + + if (!publicCustomer.isPublic()) { + throw new BadCredentialsException(authContextName + " is not valid"); + } + + User user = new User(new UserId(EntityId.NULL_UUID)); + user.setTenantId(publicCustomer.getTenantId()); + user.setCustomerId(publicCustomer.getId()); + user.setEmail(publicId); + user.setAuthority(Authority.CUSTOMER_USER); + user.setFirstName("Public"); + user.setLastName("Public"); + + UserPrincipal principal = userPrincipal == null ? new UserPrincipal(UserPrincipal.Type.PUBLIC_ID, publicId) : userPrincipal; + + return new SecurityUser(user, true, principal); + } + + protected SecurityUser authenticateByUserId(TenantId tenantId, UserId userId) { + UserAuthDetails userAuthDetails = userAuthDetailsCache.getUserAuthDetails(tenantId, userId); + if (userAuthDetails == null) { + throw new UsernameNotFoundException("User with credentials not found"); + } + if (!userAuthDetails.credentialsEnabled()) { + throw new DisabledException("User is not active"); + } + + User user = userAuthDetails.user(); + if (user.getAuthority() == null) { + throw new InsufficientAuthenticationException("User has no authority assigned"); + } + + UserPrincipal userPrincipal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + return new SecurityUser(user, true, userPrincipal); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/AuthExceptionHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/AuthExceptionHandler.java index 6da2ca51b2..050a805971 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/AuthExceptionHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/AuthExceptionHandler.java @@ -18,8 +18,10 @@ package org.thingsboard.server.service.security.auth; import jakarta.servlet.FilterChain; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; @@ -32,6 +34,10 @@ public class AuthExceptionHandler extends OncePerRequestFilter { private final ThingsboardErrorResponseHandler errorResponseHandler; + @Value("${server.log_controller_error_stack_trace}") + @Getter + private boolean logControllerErrorStackTrace; + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { try { @@ -39,8 +45,15 @@ public class AuthExceptionHandler extends OncePerRequestFilter { } catch (AuthenticationException e) { throw e; } catch (Exception e) { + log(e); errorResponseHandler.handle(e, response); } } + private void log(Exception e) { + if (logControllerErrorStackTrace) { + log.error("Auth error", e); + } + } + } 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 5e183d9bc1..77ac52ff79 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 @@ -49,21 +49,22 @@ public class DefaultTokenOutdatingService implements TokenOutdatingService { @Override public boolean isOutdated(String token, UserId userId) { - Claims claims = tokenFactory.parseTokenClaims(token).getBody(); + Claims claims = tokenFactory.parseTokenClaims(token).getPayload(); long issueTime = claims.getIssuedAt().getTime(); String sessionId = claims.get("sessionId", String.class); - if (isTokenOutdated(issueTime, userId.toString())){ - return true; + if (isTokenOutdated(issueTime, userId.toString())) { + return true; } else { - return sessionId != null && isTokenOutdated(issueTime, sessionId); + return sessionId != null && isTokenOutdated(issueTime, sessionId); } } private Boolean isTokenOutdated(long issueTime, String sessionId) { - return Optional.ofNullable(cache.get(sessionId)).map(outdatageTime -> isTokenOutdated(issueTime, outdatageTime.get())).orElse(false); + return Optional.ofNullable(cache.get(sessionId)).map(outdatedTime -> isTokenOutdated(issueTime, outdatedTime.get())).orElse(false); } private boolean isTokenOutdated(long issueTime, Long outdatageTime) { return MILLISECONDS.toSeconds(issueTime) < MILLISECONDS.toSeconds(outdatageTime); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/MfaConfigurationToken.java b/application/src/main/java/org/thingsboard/server/service/security/auth/MfaConfigurationToken.java new file mode 100644 index 0000000000..048ba1ff26 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/MfaConfigurationToken.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth; + +import org.thingsboard.server.service.security.model.SecurityUser; + +public class MfaConfigurationToken extends AbstractJwtAuthenticationToken { + public MfaConfigurationToken(SecurityUser securityUser) { + super(securityUser); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/AbstractHeaderTokenExtractor.java similarity index 74% rename from application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java rename to application/src/main/java/org/thingsboard/server/service/security/auth/extractor/AbstractHeaderTokenExtractor.java index 3d9fbc4dd2..533e921995 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/AbstractHeaderTokenExtractor.java @@ -13,32 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.security.auth.jwt.extractor; +package org.thingsboard.server.service.security.auth.extractor; import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.config.ThingsboardSecurityConfiguration; -@Component(value="jwtHeaderTokenExtractor") -public class JwtHeaderTokenExtractor implements TokenExtractor { - public static final String HEADER_PREFIX = "Bearer "; +public abstract class AbstractHeaderTokenExtractor implements TokenExtractor { + + private final String headerPrefix; + + protected AbstractHeaderTokenExtractor(String headerPrefix) { + this.headerPrefix = headerPrefix; + } @Override public String extract(HttpServletRequest request) { - String header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM); + String header = request.getHeader(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER); if (StringUtils.isBlank(header)) { - header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM_V2); + header = request.getHeader(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER_V2); if (StringUtils.isBlank(header)) { throw new AuthenticationServiceException("Authorization header cannot be blank!"); } } - if (header.length() < HEADER_PREFIX.length()) { + if (header.length() < headerPrefix.length()) { throw new AuthenticationServiceException("Invalid authorization header size."); } - return header.substring(HEADER_PREFIX.length(), header.length()); + return header.substring(headerPrefix.length()); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/ApiKeyHeaderTokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/ApiKeyHeaderTokenExtractor.java new file mode 100644 index 0000000000..4165726bca --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/ApiKeyHeaderTokenExtractor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth.extractor; + +import org.springframework.stereotype.Component; + +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX; + +@Component(value = "apiKeyHeaderTokenExtractor") +public class ApiKeyHeaderTokenExtractor extends AbstractHeaderTokenExtractor { + + public ApiKeyHeaderTokenExtractor() { + super(API_KEY_HEADER_PREFIX); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/JwtHeaderTokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/JwtHeaderTokenExtractor.java new file mode 100644 index 0000000000..d48be8899c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/JwtHeaderTokenExtractor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth.extractor; + +import org.springframework.stereotype.Component; + +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.BEARER_HEADER_PREFIX; + +@Component(value = "jwtHeaderTokenExtractor") +public class JwtHeaderTokenExtractor extends AbstractHeaderTokenExtractor { + + public JwtHeaderTokenExtractor() { + super(BEARER_HEADER_PREFIX); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/JwtQueryTokenExtractor.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java rename to application/src/main/java/org/thingsboard/server/service/security/auth/extractor/JwtQueryTokenExtractor.java index 41e9594834..9124a5fa6d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/JwtQueryTokenExtractor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.security.auth.jwt.extractor; +package org.thingsboard.server.service.security.auth.extractor; import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.authentication.AuthenticationServiceException; @@ -21,7 +21,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.config.ThingsboardSecurityConfiguration; -@Component(value="jwtQueryTokenExtractor") +@Component(value = "jwtQueryTokenExtractor") public class JwtQueryTokenExtractor implements TokenExtractor { @Override @@ -39,4 +39,5 @@ public class JwtQueryTokenExtractor implements TokenExtractor { return token; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/TokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/TokenExtractor.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/TokenExtractor.java rename to application/src/main/java/org/thingsboard/server/service/security/auth/extractor/TokenExtractor.java index 46fb71c226..aee3689f27 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/TokenExtractor.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/extractor/TokenExtractor.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.security.auth.jwt.extractor; +package org.thingsboard.server.service.security.auth.extractor; import jakarta.servlet.http.HttpServletRequest; public interface TokenExtractor { + String extract(HttpServletRequest request); -} \ No newline at end of file + +} 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 914ac9691f..2bffb2b338 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 @@ -39,7 +39,7 @@ public class JwtAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); - SecurityUser securityUser = authenticate(rawAccessToken.getToken()); + SecurityUser securityUser = authenticate(rawAccessToken.token()); return new JwtAuthenticationToken(securityUser); } @@ -58,4 +58,5 @@ public class JwtAuthenticationProvider implements AuthenticationProvider { public boolean supports(Class authentication) { return (JwtAuthenticationToken.class.isAssignableFrom(authentication)); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java index 96aabcf937..76422dabbb 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java @@ -28,12 +28,17 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.util.matcher.RequestMatcher; import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; -import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; +import org.thingsboard.server.service.security.auth.extractor.TokenExtractor; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; import java.io.IOException; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER_V2; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.BEARER_HEADER_PREFIX; + public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { + private final AuthenticationFailureHandler failureHandler; private final TokenExtractor tokenExtractor; @@ -46,8 +51,7 @@ public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticati } @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException, IOException, ServletException { + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(request)); return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token)); } @@ -61,10 +65,27 @@ public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticati chain.doFilter(request, response); } + @Override + protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) { + if (!super.requiresAuthentication(request, response)) { + return false; + } + String header = request.getHeader(AUTHORIZATION_HEADER); + if (header == null) { + header = request.getHeader(AUTHORIZATION_HEADER_V2); + } + if (header == null) { + // If there is NO auth header at all, let the JWT filter try to attempt Authentication and failure in the process. + return true; + } + return header.startsWith(BEARER_HEADER_PREFIX); + } + @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { SecurityContextHolder.clearContext(); failureHandler.onAuthenticationFailure(request, response, failed); } + } 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 eac622e6fa..16cc507e0b 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 @@ -15,123 +15,63 @@ */ package org.thingsboard.server.service.security.auth.jwt; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.CredentialsExpiredException; -import org.springframework.security.authentication.DisabledException; -import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; import org.springframework.util.Assert; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.User; -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.security.Authority; -import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.service.security.auth.AbstractAuthenticationProvider; import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken; import org.thingsboard.server.service.security.auth.TokenOutdatingService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; - -import java.util.UUID; +import org.thingsboard.server.service.user.cache.UserAuthDetailsCache; @Component -@RequiredArgsConstructor -public class RefreshTokenAuthenticationProvider implements AuthenticationProvider { +public class RefreshTokenAuthenticationProvider extends AbstractAuthenticationProvider { + private final JwtTokenFactory tokenFactory; - private final UserService userService; - private final CustomerService customerService; private final TokenOutdatingService tokenOutdatingService; + public RefreshTokenAuthenticationProvider(JwtTokenFactory jwtTokenFactory, UserAuthDetailsCache userAuthDetailsCache, + CustomerService customerService, TokenOutdatingService tokenOutdatingService) { + super(customerService, userAuthDetailsCache); + this.tokenFactory = jwtTokenFactory; + this.tokenOutdatingService = tokenOutdatingService; + } + @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.notNull(authentication, "No authentication data provided"); RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); - SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken.getToken()); + SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken.token()); UserPrincipal principal = unsafeUser.getUserPrincipal(); SecurityUser securityUser; if (principal.getType() == UserPrincipal.Type.USER_NAME) { - securityUser = authenticateByUserId(unsafeUser.getId()); + securityUser = authenticateByUserId(TenantId.SYS_TENANT_ID, unsafeUser.getId()); } else { securityUser = authenticateByPublicId(principal.getValue()); } securityUser.setSessionId(unsafeUser.getSessionId()); - if (tokenOutdatingService.isOutdated(rawAccessToken.getToken(), securityUser.getId())) { + if (tokenOutdatingService.isOutdated(rawAccessToken.token(), securityUser.getId())) { throw new CredentialsExpiredException("Token is outdated"); } return new RefreshAuthenticationToken(securityUser); } - private SecurityUser authenticateByUserId(UserId userId) { - TenantId systemId = TenantId.SYS_TENANT_ID; - User user = userService.findUserById(systemId, userId); - if (user == null) { - throw new UsernameNotFoundException("User not found by refresh token"); - } - - UserCredentials userCredentials = userService.findUserCredentialsByUserId(systemId, user.getId()); - if (userCredentials == null) { - throw new UsernameNotFoundException("User credentials not found"); - } - - if (!userCredentials.isEnabled()) { - throw new DisabledException("User is not active"); - } - - if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned"); - - UserPrincipal userPrincipal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), userPrincipal); - - return securityUser; - } - private SecurityUser authenticateByPublicId(String publicId) { - TenantId systemId = TenantId.SYS_TENANT_ID; - CustomerId customerId; - try { - customerId = new CustomerId(UUID.fromString(publicId)); - } catch (Exception e) { - throw new BadCredentialsException("Refresh token is not valid"); - } - Customer publicCustomer = customerService.findCustomerById(systemId, customerId); - if (publicCustomer == null) { - throw new UsernameNotFoundException("Public entity not found by refresh token"); - } - - if (!publicCustomer.isPublic()) { - throw new BadCredentialsException("Refresh token is not valid"); - } - - User user = new User(new UserId(EntityId.NULL_UUID)); - user.setTenantId(publicCustomer.getTenantId()); - user.setCustomerId(publicCustomer.getId()); - user.setEmail(publicId); - user.setAuthority(Authority.CUSTOMER_USER); - user.setFirstName("Public"); - user.setLastName("Public"); - - UserPrincipal userPrincipal = new UserPrincipal(UserPrincipal.Type.PUBLIC_ID, publicId); - - SecurityUser securityUser = new SecurityUser(user, true, userPrincipal); - - return securityUser; + return super.authenticateByPublicId(publicId, "Refresh token", null); } @Override public boolean supports(Class authentication) { return (RefreshAuthenticationToken.class.isAssignableFrom(authentication)); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java index fedeccf7d6..84eb4295f2 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java @@ -51,11 +51,10 @@ public class RefreshTokenProcessingFilter extends AbstractAuthenticationProcessi } @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException, IOException, ServletException { + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!HttpMethod.POST.name().equals(request.getMethod())) { - if(log.isDebugEnabled()) { - log.debug("Authentication method not supported. Request method: " + request.getMethod()); + if (log.isDebugEnabled()) { + log.debug("Authentication method not supported. Request method: {}", request.getMethod()); } throw new AuthMethodNotSupportedException("Authentication method not supported"); } @@ -67,11 +66,11 @@ public class RefreshTokenProcessingFilter extends AbstractAuthenticationProcessi throw new AuthenticationServiceException("Invalid refresh token request payload"); } - if (StringUtils.isBlank(refreshTokenRequest.getRefreshToken())) { + if (refreshTokenRequest == null || StringUtils.isBlank(refreshTokenRequest.refreshToken())) { throw new AuthenticationServiceException("Refresh token is not provided"); } - RawAccessJwtToken token = new RawAccessJwtToken(refreshTokenRequest.getRefreshToken()); + RawAccessJwtToken token = new RawAccessJwtToken(refreshTokenRequest.refreshToken()); return this.getAuthenticationManager().authenticate(new RefreshAuthenticationToken(token)); } @@ -88,4 +87,5 @@ public class RefreshTokenProcessingFilter extends AbstractAuthenticationProcessi SecurityContextHolder.clearContext(); failureHandler.onAuthenticationFailure(request, response, failed); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java index ea5a4d8d0d..4435bc49a0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java @@ -18,15 +18,11 @@ package org.thingsboard.server.service.security.auth.jwt; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -public class RefreshTokenRequest { - private String refreshToken; +public record RefreshTokenRequest(String refreshToken) { @JsonCreator public RefreshTokenRequest(@JsonProperty("refreshToken") String refreshToken) { this.refreshToken = refreshToken; } - public String getRefreshToken() { - return refreshToken; - } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java index 2a77d46ddd..e6891e41e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java @@ -25,12 +25,13 @@ import java.util.List; import java.util.stream.Collectors; public class SkipPathRequestMatcher implements RequestMatcher { - private OrRequestMatcher matchers; - private RequestMatcher processingMatcher; + + private final OrRequestMatcher matchers; + private final RequestMatcher processingMatcher; public SkipPathRequestMatcher(List pathsToSkip, String processingPath) { Assert.notNull(pathsToSkip, "List of paths to skip is required."); - List m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); + List m = pathsToSkip.stream().map(AntPathRequestMatcher::new).collect(Collectors.toList()); matchers = new OrRequestMatcher(m); processingMatcher = new AntPathRequestMatcher(processingPath); } @@ -40,6 +41,7 @@ public class SkipPathRequestMatcher implements RequestMatcher { if (matchers.matches(request)) { return false; } - return processingMatcher.matches(request) ? true : false; + return processingMatcher.matches(request); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java index f0a8257a2c..80b201e656 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.Arrays; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.security.model.JwtSettings; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.util.Base64; import java.util.Optional; @@ -66,7 +66,7 @@ public class DefaultJwtSettingsValidator implements JwtSettingsValidator { throw new DataValidationException("JWT token signing key should be a Base64 encoded string representing at least 512 bits of data!"); } - System.arraycopy(decodedKey, 0, RandomUtils.nextBytes(decodedKey.length), 0, decodedKey.length); //secure memory + System.arraycopy(decodedKey, 0, RandomUtils.secure().randomBytes(decodedKey.length), 0, decodedKey.length); // secure memory } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/InstallJwtSettingsValidator.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/InstallJwtSettingsValidator.java index 0b6e246b59..e30f542f17 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/InstallJwtSettingsValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/InstallJwtSettingsValidator.java @@ -22,9 +22,8 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.security.model.JwtSettings; /** - * During Install or upgrade the validation is suppressed to keep existing data + * During Install or upgrade, the validation is suppressed to keep existing data * */ - @Primary @Profile("install") @Component diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/JwtSettingsValidator.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/JwtSettingsValidator.java index 05e72d8de0..39ccd65738 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/JwtSettingsValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/JwtSettingsValidator.java @@ -20,4 +20,5 @@ import org.thingsboard.server.common.data.security.model.JwtSettings; public interface JwtSettingsValidator { void validate(JwtSettings jwtSettings); + } 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 6c471aeee5..0f81e251cb 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 @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; 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.targets.platform.SystemLevelUsersFilter; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; @@ -61,12 +62,25 @@ public class DefaultTwoFactorAuthService implements TwoFactorAuthService { private static final ThingsboardException TOO_MANY_REQUESTS_ERROR = new ThingsboardException("Too many requests", ThingsboardErrorCode.TOO_MANY_REQUESTS); @Override - public boolean isTwoFaEnabled(TenantId tenantId, UserId userId) { - return configManager.getAccountTwoFaSettings(tenantId, userId) + public boolean isTwoFaEnabled(TenantId tenantId, User user) { + return configManager.getAccountTwoFaSettings(tenantId, user) .map(settings -> !settings.getConfigs().isEmpty()) .orElse(false); } + @Override + public boolean isEnforceTwoFaEnabled(TenantId tenantId, User user) { + SystemLevelUsersFilter enforcedUsersFilter = configManager.getPlatformTwoFaSettings(TenantId.SYS_TENANT_ID, true) + .filter(PlatformTwoFaSettings::isEnforceTwoFa) + .map(PlatformTwoFaSettings::getEnforcedUsersFilter) + .orElse(null); + if (enforcedUsersFilter == null) { + return false; + } + + return userService.matchesFilter(tenantId, enforcedUsersFilter, user); + } + @Override public void checkProvider(TenantId tenantId, TwoFaProviderType providerType) throws ThingsboardException { getTwoFaProvider(providerType).check(tenantId); @@ -75,7 +89,7 @@ public class DefaultTwoFactorAuthService implements TwoFactorAuthService { @Override public void prepareVerificationCode(SecurityUser user, TwoFaProviderType providerType, boolean checkLimits) throws Exception { - TwoFaAccountConfig accountConfig = configManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType) + TwoFaAccountConfig accountConfig = configManager.getTwoFaAccountConfig(user.getTenantId(), user, providerType) .orElseThrow(() -> ACCOUNT_NOT_CONFIGURED_ERROR); prepareVerificationCode(user, accountConfig, checkLimits); } @@ -104,7 +118,7 @@ public class DefaultTwoFactorAuthService implements TwoFactorAuthService { @Override public boolean checkVerificationCode(SecurityUser user, TwoFaProviderType providerType, String verificationCode, boolean checkLimits) throws ThingsboardException { - TwoFaAccountConfig accountConfig = configManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType) + TwoFaAccountConfig accountConfig = configManager.getTwoFaAccountConfig(user.getTenantId(), user, providerType) .orElseThrow(() -> ACCOUNT_NOT_CONFIGURED_ERROR); return checkVerificationCode(user, verificationCode, accountConfig, checkLimits); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java index dd4ed4d7b7..ee77d5d3a0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java @@ -18,17 +18,17 @@ package org.thingsboard.server.service.security.auth.mfa; 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.id.UserId; import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; import org.thingsboard.server.service.security.model.SecurityUser; public interface TwoFactorAuthService { - boolean isTwoFaEnabled(TenantId tenantId, UserId userId); + boolean isTwoFaEnabled(TenantId tenantId, User user); - void checkProvider(TenantId tenantId, TwoFaProviderType providerType) throws ThingsboardException; + boolean isEnforceTwoFaEnabled(TenantId tenantId, User user); + void checkProvider(TenantId tenantId, TwoFaProviderType providerType) throws ThingsboardException; void prepareVerificationCode(SecurityUser user, TwoFaProviderType providerType, boolean checkLimits) throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java index 5cabbc8e58..eb2342c23d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java @@ -16,14 +16,13 @@ package org.thingsboard.server.service.security.auth.mfa.config; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; +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.id.UserId; import org.thingsboard.server.common.data.security.UserAuthSettings; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings; @@ -34,6 +33,7 @@ import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.settings.AdminSettingsDao; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.user.UserAuthSettingsDao; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; import java.util.Comparator; @@ -48,16 +48,15 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { private final UserAuthSettingsDao userAuthSettingsDao; private final AdminSettingsService adminSettingsService; private final AdminSettingsDao adminSettingsDao; - @Autowired @Lazy - private TwoFactorAuthService twoFactorAuthService; + @Lazy + private final TwoFactorAuthService twoFactorAuthService; protected static final String TWO_FACTOR_AUTH_SETTINGS_KEY = "twoFaSettings"; - @Override - public Optional getAccountTwoFaSettings(TenantId tenantId, UserId userId) { + public Optional getAccountTwoFaSettings(TenantId tenantId, User user) { PlatformTwoFaSettings platformTwoFaSettings = getPlatformTwoFaSettings(tenantId, true).orElse(null); - return Optional.ofNullable(userAuthSettingsDao.findByUserId(userId)) + return Optional.ofNullable(userAuthSettingsDao.findByUserId(user.getId())) .map(userAuthSettings -> { AccountTwoFaSettings twoFaSettings = userAuthSettings.getTwoFaSettings(); if (twoFaSettings == null) return null; @@ -79,17 +78,17 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { } if (updateNeeded) { - twoFaSettings = saveAccountTwoFaSettings(tenantId, userId, twoFaSettings); + twoFaSettings = saveAccountTwoFaSettings(tenantId, user, twoFaSettings); } return twoFaSettings; }); } - protected AccountTwoFaSettings saveAccountTwoFaSettings(TenantId tenantId, UserId userId, AccountTwoFaSettings settings) { - UserAuthSettings userAuthSettings = Optional.ofNullable(userAuthSettingsDao.findByUserId(userId)) + protected AccountTwoFaSettings saveAccountTwoFaSettings(TenantId tenantId, User user, AccountTwoFaSettings settings) { + UserAuthSettings userAuthSettings = Optional.ofNullable(userAuthSettingsDao.findByUserId(user.getId())) .orElseGet(() -> { UserAuthSettings newUserAuthSettings = new UserAuthSettings(); - newUserAuthSettings.setUserId(userId); + newUserAuthSettings.setUserId(user.getId()); return newUserAuthSettings; }); userAuthSettings.setTwoFaSettings(settings); @@ -99,20 +98,19 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { return settings; } - @Override - public Optional getTwoFaAccountConfig(TenantId tenantId, UserId userId, TwoFaProviderType providerType) { - return getAccountTwoFaSettings(tenantId, userId) + public Optional getTwoFaAccountConfig(TenantId tenantId, User user, TwoFaProviderType providerType) { + return getAccountTwoFaSettings(tenantId, user) .map(AccountTwoFaSettings::getConfigs) .flatMap(configs -> Optional.ofNullable(configs.get(providerType))); } @Override - public AccountTwoFaSettings saveTwoFaAccountConfig(TenantId tenantId, UserId userId, TwoFaAccountConfig accountConfig) { + public AccountTwoFaSettings saveTwoFaAccountConfig(TenantId tenantId, User user, TwoFaAccountConfig accountConfig) { getTwoFaProviderConfig(tenantId, accountConfig.getProviderType()) .orElseThrow(() -> new IllegalArgumentException("2FA provider is not configured")); - AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, userId).orElseGet(() -> { + AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, user).orElseGet(() -> { AccountTwoFaSettings newSettings = new AccountTwoFaSettings(); newSettings.setConfigs(new LinkedHashMap<>()); return newSettings; @@ -128,12 +126,13 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { if (configs.values().stream().noneMatch(TwoFaAccountConfig::isUseByDefault)) { configs.values().stream().findFirst().ifPresent(config -> config.setUseByDefault(true)); } - return saveAccountTwoFaSettings(tenantId, userId, settings); + checkAccountTwoFaSettings(tenantId, user, settings); + return saveAccountTwoFaSettings(tenantId, user, settings); } @Override - public AccountTwoFaSettings deleteTwoFaAccountConfig(TenantId tenantId, UserId userId, TwoFaProviderType providerType) { - AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, userId) + public AccountTwoFaSettings deleteTwoFaAccountConfig(TenantId tenantId, User user, TwoFaProviderType providerType) { + AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, user) .orElseThrow(() -> new IllegalArgumentException("2FA not configured")); settings.getConfigs().remove(providerType); if (settings.getConfigs().size() == 1) { @@ -145,7 +144,8 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { .min(Comparator.comparing(TwoFaAccountConfig::getProviderType)) .ifPresent(config -> config.setUseByDefault(true)); } - return saveAccountTwoFaSettings(tenantId, userId, settings); + checkAccountTwoFaSettings(tenantId, user, settings); + return saveAccountTwoFaSettings(tenantId, user, settings); } @@ -166,6 +166,19 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { for (TwoFaProviderConfig providerConfig : twoFactorAuthSettings.getProviders()) { twoFactorAuthService.checkProvider(tenantId, providerConfig.getProviderType()); } + if (tenantId.isSysTenantId()) { + if (twoFactorAuthSettings.isEnforceTwoFa()) { + if (twoFactorAuthSettings.getProviders().isEmpty()) { + throw new DataValidationException("At least one 2FA provider is required if enforcing is enabled"); + } + if (twoFactorAuthSettings.getEnforcedUsersFilter() == null) { + throw new DataValidationException("Users filter to enforce 2FA for is required"); + } + } + } else { + twoFactorAuthSettings.setEnforceTwoFa(false); + twoFactorAuthSettings.setEnforcedUsersFilter(null); + } AdminSettings settings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY)) .orElseGet(() -> { @@ -184,4 +197,12 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager { .ifPresent(adminSettings -> adminSettingsDao.removeById(tenantId, adminSettings.getId().getId())); } + private void checkAccountTwoFaSettings(TenantId tenantId, User user, AccountTwoFaSettings settings) { + if (settings.getConfigs().isEmpty()) { + if (twoFactorAuthService.isEnforceTwoFaEnabled(tenantId, user)) { + throw new DataValidationException("At least one 2FA provider is required"); + } + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java index 6edc580c3c..9557ce9118 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/TwoFaConfigManager.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.service.security.auth.mfa.config; +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.id.UserId; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig; @@ -27,14 +27,14 @@ import java.util.Optional; public interface TwoFaConfigManager { - Optional getAccountTwoFaSettings(TenantId tenantId, UserId userId); + Optional getAccountTwoFaSettings(TenantId tenantId, User user); - Optional getTwoFaAccountConfig(TenantId tenantId, UserId userId, TwoFaProviderType providerType); + Optional getTwoFaAccountConfig(TenantId tenantId, User user, TwoFaProviderType providerType); - AccountTwoFaSettings saveTwoFaAccountConfig(TenantId tenantId, UserId userId, TwoFaAccountConfig accountConfig); + AccountTwoFaSettings saveTwoFaAccountConfig(TenantId tenantId, User user, TwoFaAccountConfig accountConfig); - AccountTwoFaSettings deleteTwoFaAccountConfig(TenantId tenantId, UserId userId, TwoFaProviderType providerType); + AccountTwoFaSettings deleteTwoFaAccountConfig(TenantId tenantId, User user, TwoFaProviderType providerType); Optional getPlatformTwoFaSettings(TenantId tenantId, boolean sysadminSettingsAsDefault); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java index 59b2ebb8d3..ccb82e5b70 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java @@ -58,7 +58,7 @@ public class BackupCodeTwoFaProvider implements TwoFaProvider authentication) { + return ApiKeyAuthenticationToken.class.isAssignableFrom(authentication); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationToken.java b/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationToken.java new file mode 100644 index 0000000000..0665f23298 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationToken.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth.pat; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.token.ApiKeyAuthRequest; + +import java.io.Serial; + +public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken { + + @Serial + private static final long serialVersionUID = 2978710889397403536L; + + private ApiKeyAuthRequest apiKeyAuthRequest; + private SecurityUser securityUser; + + public ApiKeyAuthenticationToken(ApiKeyAuthRequest apiKeyAuthRequest) { + super(null); + this.apiKeyAuthRequest = apiKeyAuthRequest; + setAuthenticated(false); + } + + public ApiKeyAuthenticationToken(SecurityUser securityUser) { + super(securityUser.getAuthorities()); + this.eraseCredentials(); + this.securityUser = securityUser; + super.setAuthenticated(true); + } + + @Override + public Object getCredentials() { + return apiKeyAuthRequest; + } + + @Override + public Object getPrincipal() { + return this.securityUser; + } + + @Override + public void eraseCredentials() { + super.eraseCredentials(); + this.apiKeyAuthRequest = null; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyTokenAuthenticationProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyTokenAuthenticationProcessingFilter.java new file mode 100644 index 0000000000..789efe6ad0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyTokenAuthenticationProcessingFilter.java @@ -0,0 +1,88 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth.pat; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.thingsboard.server.service.security.auth.extractor.TokenExtractor; +import org.thingsboard.server.service.security.model.token.ApiKeyAuthRequest; + +import java.io.IOException; + +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER_V2; + +public class ApiKeyTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { + + private final AuthenticationFailureHandler failureHandler; + private final TokenExtractor tokenExtractor; + + @Autowired + public ApiKeyTokenAuthenticationProcessingFilter(AuthenticationFailureHandler failureHandler, + @Qualifier("apiKeyHeaderTokenExtractor") TokenExtractor tokenExtractor, RequestMatcher matcher) { + super(matcher); + this.failureHandler = failureHandler; + this.tokenExtractor = tokenExtractor; + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + String apiKeyValue = tokenExtractor.extract(request); + ApiKeyAuthRequest apiKeyAuthRequest = new ApiKeyAuthRequest(apiKeyValue); + return getAuthenticationManager().authenticate(new ApiKeyAuthenticationToken(apiKeyAuthRequest)); + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, + Authentication authResult) throws IOException, ServletException { + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(authResult); + SecurityContextHolder.setContext(context); + chain.doFilter(request, response); + } + + @Override + protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) { + if (!super.requiresAuthentication(request, response)) { + return false; + } + String header = request.getHeader(AUTHORIZATION_HEADER); + if (header == null) { + header = request.getHeader(AUTHORIZATION_HEADER_V2); + } + return header != null && header.startsWith(API_KEY_HEADER_PREFIX); + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, + AuthenticationException failed) throws IOException, ServletException { + SecurityContextHolder.clearContext(); + failureHandler.onAuthenticationFailure(request, response, failed); + } + +} 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 e94cacd99c..7bf35e22fe 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 @@ -17,7 +17,6 @@ package org.thingsboard.server.service.security.auth.rest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.authentication.LockedException; @@ -27,41 +26,34 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; import org.springframework.util.Assert; -import org.thingsboard.server.common.data.Customer; 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.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.exception.DataValidationException; import org.thingsboard.server.dao.settings.SecuritySettingsService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.auth.AbstractAuthenticationProvider; import org.thingsboard.server.service.security.auth.MfaAuthenticationToken; +import org.thingsboard.server.service.security.auth.MfaConfigurationToken; 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; -import java.util.UUID; - - @Component @Slf4j @TbCoreComponent -public class RestAuthenticationProvider implements AuthenticationProvider { +public class RestAuthenticationProvider extends AbstractAuthenticationProvider { private final SystemSecurityService systemSecurityService; private final SecuritySettingsService securitySettingsService; private final UserService userService; - private final CustomerService customerService; private final TwoFactorAuthService twoFactorAuthService; @Autowired @@ -70,8 +62,8 @@ public class RestAuthenticationProvider implements AuthenticationProvider { final SystemSecurityService systemSecurityService, SecuritySettingsService securitySettingsService, TwoFactorAuthService twoFactorAuthService) { + super(customerService, null); this.userService = userService; - this.customerService = customerService; this.systemSecurityService = systemSecurityService; this.securitySettingsService = securitySettingsService; this.twoFactorAuthService = twoFactorAuthService; @@ -82,11 +74,10 @@ public class RestAuthenticationProvider implements AuthenticationProvider { Assert.notNull(authentication, "No authentication data provided"); Object principal = authentication.getPrincipal(); - if (!(principal instanceof UserPrincipal)) { + if (!(principal instanceof UserPrincipal userPrincipal)) { throw new BadCredentialsException("Authentication Failed. Bad user principal."); } - UserPrincipal userPrincipal = (UserPrincipal) principal; SecurityUser securityUser; if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) { String username = userPrincipal.getValue(); @@ -103,8 +94,10 @@ public class RestAuthenticationProvider implements AuthenticationProvider { } securityUser = authenticateByUsernameAndPassword(authentication, userPrincipal, username, password); - if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) { + if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser)) { return new MfaAuthenticationToken(securityUser); + } else if (twoFactorAuthService.isEnforceTwoFaEnabled(securityUser.getTenantId(), securityUser)) { + return new MfaConfigurationToken(securityUser); } else { systemSecurityService.logLoginAction(securityUser, authentication.getDetails(), ActionType.LOGIN, null); } @@ -123,7 +116,6 @@ public class RestAuthenticationProvider implements AuthenticationProvider { } try { - UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, user.getId()); if (userCredentials == null) { throw new UsernameNotFoundException("User credentials not found"); @@ -136,8 +128,9 @@ public class RestAuthenticationProvider implements AuthenticationProvider { throw e; } - if (user.getAuthority() == null) + if (user.getAuthority() == null) { throw new InsufficientAuthenticationException("User has no authority assigned"); + } return new SecurityUser(user, userCredentials.isEnabled(), userPrincipal); } catch (Exception e) { @@ -147,28 +140,7 @@ public class RestAuthenticationProvider implements AuthenticationProvider { } private SecurityUser authenticateByPublicId(UserPrincipal userPrincipal, String publicId) { - CustomerId customerId; - try { - customerId = new CustomerId(UUID.fromString(publicId)); - } catch (Exception e) { - throw new BadCredentialsException("Authentication Failed. Public Id is not valid."); - } - Customer publicCustomer = customerService.findCustomerById(TenantId.SYS_TENANT_ID, customerId); - if (publicCustomer == null) { - throw new UsernameNotFoundException("Public entity not found: " + publicId); - } - if (!publicCustomer.isPublic()) { - throw new BadCredentialsException("Authentication Failed. Public Id is not valid."); - } - User user = new User(new UserId(EntityId.NULL_UUID)); - user.setTenantId(publicCustomer.getTenantId()); - user.setCustomerId(publicCustomer.getId()); - user.setEmail(publicId); - user.setAuthority(Authority.CUSTOMER_USER); - user.setFirstName("Public"); - user.setLastName("Public"); - - return new SecurityUser(user, true, userPrincipal); + return super.authenticateByPublicId(publicId, "Public Id", userPrincipal); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java index 2b3a6cc5c9..f774b541ba 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.security.auth.rest; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; @@ -24,8 +23,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand import org.springframework.stereotype.Component; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; -import java.io.IOException; - @Component(value = "defaultAuthenticationFailureHandler") public class RestAwareAuthenticationFailureHandler implements AuthenticationFailureHandler { @@ -37,8 +34,8 @@ public class RestAwareAuthenticationFailureHandler implements AuthenticationFail } @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException e) throws IOException, ServletException { + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { errorResponseHandler.handle(e, response); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java index aa8149ea71..ff4435788e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.service.security.auth.rest; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; @@ -30,6 +30,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.service.security.auth.MfaAuthenticationToken; +import org.thingsboard.server.service.security.auth.MfaConfigurationToken; import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; @@ -38,7 +39,7 @@ import java.io.IOException; import java.util.Optional; import java.util.concurrent.TimeUnit; -@Component(value = "defaultAuthenticationSuccessHandler") +@Slf4j @Component(value = "defaultAuthenticationSuccessHandler") @RequiredArgsConstructor public class RestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private final JwtTokenFactory tokenFactory; @@ -46,18 +47,14 @@ public class RestAwareAuthenticationSuccessHandler implements AuthenticationSucc @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { + Authentication authentication) throws IOException { SecurityUser securityUser = (SecurityUser) authentication.getPrincipal(); - JwtPair tokenPair = new JwtPair(); + JwtPair tokenPair; if (authentication instanceof MfaAuthenticationToken) { - int preVerificationTokenLifetime = twoFaConfigManager.getPlatformTwoFaSettings(securityUser.getTenantId(), true) - .flatMap(settings -> Optional.ofNullable(settings.getTotalAllowedTimeForVerification()) - .filter(time -> time > 0)) - .orElse((int) TimeUnit.MINUTES.toSeconds(30)); - tokenPair.setToken(tokenFactory.createPreVerificationToken(securityUser, preVerificationTokenLifetime).getToken()); - tokenPair.setRefreshToken(null); - tokenPair.setScope(Authority.PRE_VERIFICATION_TOKEN); + tokenPair = createMfaTokenPair(securityUser, Authority.PRE_VERIFICATION_TOKEN); + } else if (authentication instanceof MfaConfigurationToken) { + tokenPair = createMfaTokenPair(securityUser, Authority.MFA_CONFIGURATION_TOKEN); } else { tokenPair = tokenFactory.createTokenPair(securityUser); } @@ -69,6 +66,19 @@ public class RestAwareAuthenticationSuccessHandler implements AuthenticationSucc clearAuthenticationAttributes(request); } + public JwtPair createMfaTokenPair(SecurityUser securityUser, Authority scope) { + log.debug("[{}][{}] Creating {} token", securityUser.getTenantId(), securityUser.getId(), scope); + JwtPair tokenPair = new JwtPair(); + int preVerificationTokenLifetime = twoFaConfigManager.getPlatformTwoFaSettings(securityUser.getTenantId(), true) + .flatMap(settings -> Optional.ofNullable(settings.getTotalAllowedTimeForVerification()) + .filter(time -> time > 0)) + .orElse((int) TimeUnit.MINUTES.toSeconds(30)); + tokenPair.setToken(tokenFactory.createMfaToken(securityUser, scope, preVerificationTokenLifetime).token()); + tokenPair.setRefreshToken(null); + tokenPair.setScope(scope); + return tokenPair; + } + /** * Removes temporary authentication-related data which may have been stored * in the session during the authentication process.. @@ -83,4 +93,5 @@ public class RestAwareAuthenticationSuccessHandler implements AuthenticationSucc session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); } + } 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 35bf3367c8..2e59976e86 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 @@ -17,7 +17,11 @@ package org.thingsboard.server.service.security.exception; import org.springframework.security.core.AuthenticationException; +import java.io.Serial; + public class JwtExpiredTokenException extends AuthenticationException { + + @Serial private static final long serialVersionUID = -5959543783324224864L; private String token; @@ -34,4 +38,5 @@ public class JwtExpiredTokenException extends AuthenticationException { public String token() { return this.token; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java index db0ce87052..a6504fdd73 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java @@ -17,15 +17,6 @@ package org.thingsboard.server.service.security.model.token; import org.thingsboard.server.common.data.security.model.JwtToken; -public final class AccessJwtToken implements JwtToken { - private final String rawToken; - - public AccessJwtToken(String rawToken) { - this.rawToken = rawToken; - } - - public String getToken() { - return this.rawToken; - } +public record AccessJwtToken(String token) implements JwtToken { } diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/ApiKeyAuthRequest.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/ApiKeyAuthRequest.java new file mode 100644 index 0000000000..fee06eea55 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/ApiKeyAuthRequest.java @@ -0,0 +1,18 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model.token; + +public record ApiKeyAuthRequest(String apiKey) {} 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 5b4f4dc94a..051e061857 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 @@ -115,13 +115,16 @@ public class JwtTokenFactory { throw new IllegalArgumentException("JWT Token doesn't have any scopes"); } + Authority authority = Authority.parse(scopes.get(0)); + SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); securityUser.setEmail(subject); - securityUser.setAuthority(Authority.parse(scopes.get(0))); + securityUser.setAuthority(authority); String tenantId = claims.get(TENANT_ID, String.class); + if (tenantId != null) { securityUser.setTenantId(TenantId.fromUUID(UUID.fromString(tenantId))); - } else if (securityUser.getAuthority() == Authority.SYS_ADMIN) { + } else if (authority == Authority.SYS_ADMIN) { securityUser.setTenantId(TenantId.SYS_TENANT_ID); } String customerId = claims.get(CUSTOMER_ID, String.class); @@ -132,18 +135,15 @@ public class JwtTokenFactory { securityUser.setSessionId(claims.get(SESSION_ID, String.class)); } - UserPrincipal principal; - if (securityUser.getAuthority() != Authority.PRE_VERIFICATION_TOKEN) { + boolean isPublic = false; + if (authority != Authority.PRE_VERIFICATION_TOKEN && authority != Authority.MFA_CONFIGURATION_TOKEN) { securityUser.setFirstName(claims.get(FIRST_NAME, String.class)); securityUser.setLastName(claims.get(LAST_NAME, String.class)); securityUser.setEnabled(claims.get(ENABLED, Boolean.class)); - boolean isPublic = claims.get(IS_PUBLIC, Boolean.class); - principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject); - } else { - principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, subject); + isPublic = claims.get(IS_PUBLIC, Boolean.class); } + UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject); securityUser.setUserPrincipal(principal); - return securityUser; } @@ -179,8 +179,8 @@ public class JwtTokenFactory { return securityUser; } - public JwtToken createPreVerificationToken(SecurityUser user, Integer expirationTime) { - JwtBuilder jwtBuilder = setUpToken(user, Collections.singletonList(Authority.PRE_VERIFICATION_TOKEN.name()), expirationTime) + public JwtToken createMfaToken(SecurityUser user, Authority scope, Integer expirationTime) { + JwtBuilder jwtBuilder = setUpToken(user, Collections.singletonList(scope.name()), expirationTime) .claim(TENANT_ID, user.getTenantId().toString()); if (user.getCustomerId() != null) { jwtBuilder.claim(CUSTOMER_ID, user.getCustomerId().toString()); @@ -235,7 +235,7 @@ public class JwtTokenFactory { securityUser.setSessionId(UUID.randomUUID().toString()); JwtToken accessToken = createAccessJwtToken(securityUser); JwtToken refreshToken = createRefreshToken(securityUser); - return new JwtPair(accessToken.getToken(), refreshToken.getToken()); + return new JwtPair(accessToken.token(), refreshToken.token()); } private SecretKey getSecretKey(boolean forceReload) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java index 98f9215ebf..c08497bdda 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java @@ -20,9 +20,9 @@ import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.UnsupportedJwtException; import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; @@ -43,8 +43,7 @@ public class OAuth2AppTokenFactory { Jws jwsClaims; try { jwsClaims = Jwts.parser().verifyWith(Keys.hmacShaKeyFor(Base64.getDecoder().decode(appSecret))).build().parseSignedClaims(appToken); - } - catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) { + } catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) { throw new IllegalArgumentException("Invalid Application token: ", ex); } catch (ExpiredJwtException expiredEx) { throw new IllegalArgumentException("Application token expired", expiredEx); diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java index 0caffe21c1..af767cb2ab 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java @@ -19,18 +19,6 @@ import org.thingsboard.server.common.data.security.model.JwtToken; import java.io.Serializable; -public class RawAccessJwtToken implements JwtToken, Serializable { +public record RawAccessJwtToken(String token) implements JwtToken, Serializable { - private static final long serialVersionUID = -797397445703066079L; - - private String token; - - public RawAccessJwtToken(String token) { - this.token = token; - } - - @Override - public String getToken() { - return token; - } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/AccessControlService.java b/application/src/main/java/org/thingsboard/server/service/security/permission/AccessControlService.java index 6056cb3ba7..46bd079b8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/AccessControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/AccessControlService.java @@ -24,6 +24,10 @@ public interface AccessControlService { void checkPermission(SecurityUser user, Resource resource, Operation operation) throws ThingsboardException; + boolean hasPermission(SecurityUser user, Resource resource, Operation operation) throws ThingsboardException; + void checkPermission(SecurityUser user, Resource resource, Operation operation, I entityId, T entity) throws ThingsboardException; + boolean hasPermission(SecurityUser user, Resource resource, Operation operation, I entityId, T entity) throws ThingsboardException; + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index 8d5269a332..a3e3ff458b 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -21,14 +21,16 @@ import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.service.security.model.SecurityUser; -@Component(value = "customerUserPermissions") +@Component public class CustomerUserPermissions extends AbstractPermissions { public CustomerUserPermissions() { @@ -48,6 +50,7 @@ public class CustomerUserPermissions extends AbstractPermissions { put(Resource.ASSET_PROFILE, profilePermissionChecker); put(Resource.TB_RESOURCE, customerResourcePermissionChecker); put(Resource.MOBILE_APP_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ)); + put(Resource.API_KEY, apiKeysPermissionChecker); } private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() { @@ -202,4 +205,19 @@ public class CustomerUserPermissions extends AbstractPermissions { return user.getTenantId().equals(entity.getTenantId()); } }; + + private static final PermissionChecker apiKeysPermissionChecker = new PermissionChecker() { + + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return true; + } + + @Override + @SuppressWarnings("unchecked") + public boolean hasPermission(SecurityUser user, Operation operation, ApiKeyId entityId, ApiKeyInfo entity) { + return user.getTenantId().equals(entity.getTenantId()); + } + }; + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java b/application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java index 946bbb8221..fb9768a920 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.security.permission; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -33,18 +32,18 @@ import java.util.Optional; @Slf4j public class DefaultAccessControlService implements AccessControlService { - private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; private static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; private final Map authorityPermissions = new HashMap<>(); - public DefaultAccessControlService( - @Qualifier("sysAdminPermissions") Permissions sysAdminPermissions, - @Qualifier("tenantAdminPermissions") Permissions tenantAdminPermissions, - @Qualifier("customerUserPermissions") Permissions customerUserPermissions) { + public DefaultAccessControlService(SysAdminPermissions sysAdminPermissions, + TenantAdminPermissions tenantAdminPermissions, + CustomerUserPermissions customerUserPermissions, + MfaConfigurationPermissions mfaConfigurationPermissions) { authorityPermissions.put(Authority.SYS_ADMIN, sysAdminPermissions); authorityPermissions.put(Authority.TENANT_ADMIN, tenantAdminPermissions); authorityPermissions.put(Authority.CUSTOMER_USER, customerUserPermissions); + authorityPermissions.put(Authority.MFA_CONFIGURATION_TOKEN, mfaConfigurationPermissions); } @Override @@ -55,23 +54,37 @@ public class DefaultAccessControlService implements AccessControlService { } } + @Override + @SuppressWarnings("unchecked") + public boolean hasPermission(SecurityUser user, Resource resource, Operation operation) throws ThingsboardException { + var permissionChecker = getPermissionChecker(user.getAuthority(), resource); + return permissionChecker.hasPermission(user, operation); + } + @Override @SuppressWarnings("unchecked") public void checkPermission(SecurityUser user, Resource resource, - Operation operation, I entityId, T entity) throws ThingsboardException { + Operation operation, I entityId, T entity) throws ThingsboardException { PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource); if (!permissionChecker.hasPermission(user, operation, entityId, entity)) { permissionDenied(); } } + @Override + @SuppressWarnings("unchecked") + public boolean hasPermission(SecurityUser user, Resource resource, Operation operation, I entityId, T entity) throws ThingsboardException { + var permissionChecker = getPermissionChecker(user.getAuthority(), resource); + return permissionChecker.hasPermission(user, operation, entityId, entity); + } + private PermissionChecker getPermissionChecker(Authority authority, Resource resource) throws ThingsboardException { Permissions permissions = authorityPermissions.get(authority); if (permissions == null) { permissionDenied(); } Optional permissionChecker = permissions.getPermissionChecker(resource); - if (!permissionChecker.isPresent()) { + if (permissionChecker.isEmpty()) { permissionDenied(); } return permissionChecker.get(); diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/MfaConfigurationPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/MfaConfigurationPermissions.java new file mode 100644 index 0000000000..4fe8b4d127 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/MfaConfigurationPermissions.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.permission; + +import org.springframework.stereotype.Component; + +@Component +public class MfaConfigurationPermissions extends AbstractPermissions { + + public MfaConfigurationPermissions() { + super(); + // for compatibility with PE + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 7b2959b0cc..ebeb65df40 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -53,7 +53,8 @@ public enum Resource { EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE), MOBILE_APP_SETTINGS, JOB(EntityType.JOB), - AI_MODEL(EntityType.AI_MODEL); + AI_MODEL(EntityType.AI_MODEL), + API_KEY(EntityType.API_KEY); private final Set entityTypes; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index 7dd220b7b5..801a7bc684 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -18,12 +18,14 @@ package org.thingsboard.server.service.security.permission; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.service.security.model.SecurityUser; -@Component(value = "sysAdminPermissions") +@Component public class SysAdminPermissions extends AbstractPermissions { public SysAdminPermissions() { @@ -45,6 +47,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.QUEUE, systemEntityPermissionChecker); put(Resource.NOTIFICATION, systemEntityPermissionChecker); put(Resource.MOBILE_APP_SETTINGS, PermissionChecker.allowAllPermissionChecker); + put(Resource.API_KEY, PermissionChecker.allowAllPermissionChecker); } private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { @@ -71,4 +74,13 @@ public class SysAdminPermissions extends AbstractPermissions { }; + private static final PermissionChecker apiKeysPermissionChecker = new PermissionChecker<>() { + + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return true; + } + + }; + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 89bee32c93..a576ea6f82 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -20,12 +20,14 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.ai.AiModel; import org.thingsboard.server.common.data.id.AiModelId; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.service.security.model.SecurityUser; -@Component(value = "tenantAdminPermissions") +@Component public class TenantAdminPermissions extends AbstractPermissions { public TenantAdminPermissions() { @@ -59,10 +61,16 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.MOBILE_APP_BUNDLE, tenantEntityPermissionChecker); put(Resource.JOB, tenantEntityPermissionChecker); put(Resource.AI_MODEL, aiModelPermissionChecker); + put(Resource.API_KEY, apiKeysPermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return true; + } + @Override public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { if (!user.getTenantId().equals(entity.getTenantId())) { @@ -73,7 +81,7 @@ public class TenantAdminPermissions extends AbstractPermissions { }; private static final PermissionChecker tenantPermissionChecker = - new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { + new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.DELETE) { @Override @SuppressWarnings("unchecked") @@ -163,4 +171,18 @@ public class TenantAdminPermissions extends AbstractPermissions { }; + private static final PermissionChecker apiKeysPermissionChecker = new PermissionChecker<>() { + + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return true; + } + + @Override + public boolean hasPermission(SecurityUser user, Operation operation, ApiKeyId entityId, ApiKeyInfo entity) { + return user.getTenantId().equals(entity.getTenantId()); + } + + }; + } 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 339767f32f..f5c4f40928 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 @@ -50,7 +50,7 @@ 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.audit.AuditLogService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.SecuritySettingsService; import org.thingsboard.server.dao.user.UserService; 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 2f68f71870..5ea3838a75 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 @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.UserCredentials; 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.exception.DataValidationException; import org.thingsboard.server.service.security.model.SecurityUser; public interface SystemSecurityService { 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 d64ce942d0..cf8d9b1139 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 @@ -31,7 +31,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.TbLogEntityActionService; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java index 8cf852ef2f..a488ad165e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.sync.ie.exporting.impl; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; @@ -24,6 +25,9 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasVersion; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -153,11 +157,28 @@ public class DefaultEntityExportService calculatedFields = calculatedFieldService.findCalculatedFieldsByEntityId(ctx.getTenantId(), entityId); calculatedFields.forEach(calculatedField -> { calculatedField.setEntityId(getExternalIdOrElseInternal(ctx, entityId)); - calculatedField.getConfiguration().getArguments().values().forEach(argument -> { - if (argument.getRefEntityId() != null) { - argument.setRefEntityId(getExternalIdOrElseInternal(ctx, argument.getRefEntityId())); + if (calculatedField.getConfiguration() instanceof ArgumentsBasedCalculatedFieldConfiguration argBasedConfig) { + if (argBasedConfig instanceof GeofencingCalculatedFieldConfiguration geofencingCfg) { + geofencingCfg.getZoneGroups().values().forEach(zoneGroupConfiguration -> { + if (zoneGroupConfiguration.getRefEntityId() != null) { + zoneGroupConfiguration.setRefEntityId(getExternalIdOrElseInternal(ctx, zoneGroupConfiguration.getRefEntityId())); + } + }); + } else { + argBasedConfig.getArguments().values().forEach(argument -> { + if (argument.getRefEntityId() != null) { + argument.setRefEntityId(getExternalIdOrElseInternal(ctx, argument.getRefEntityId())); + } + }); } - }); + } + if (calculatedField.getConfiguration() instanceof AlarmCalculatedFieldConfiguration alarmCfConfig) { + alarmCfConfig.getAllRules().map(Pair::getValue).forEach(rule -> { + if (rule.getDashboardId() != null) { + rule.setDashboardId(getExternalIdOrElseInternal(ctx, rule.getDashboardId())); + } + }); + } }); return calculatedFields; } 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 bec5eab531..c2669a6e30 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 @@ -17,6 +17,7 @@ package org.thingsboard.server.service.sync.ie.importing.csv; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.FutureCallback; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import jakarta.annotation.Nullable; @@ -183,7 +184,13 @@ public abstract class AbstractBulkImportService dataEntry.getKey().getType() == kvType && StringUtils.isNotEmpty(dataEntry.getKey().getKey())) - .forEach(dataEntry -> kvs.add(dataEntry.getKey().getKey(), dataEntry.getValue().toJsonPrimitive())); + .forEach(dataEntry -> { + ParsedValue value = dataEntry.getValue(); + JsonElement kvValue = (value.getDataType() == DataType.JSON) + ? (JsonElement) value.getValue() + : value.toJsonPrimitive(); + kvs.add(dataEntry.getKey().getKey(), kvValue); + }); return Map.entry(kvType, kvs); }) .filter(kvsEntry -> !kvsEntry.getValue().entrySet().isEmpty()) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index e770573cf7..4fc0945bb3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -22,6 +22,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -35,6 +36,9 @@ import org.thingsboard.server.common.data.HasVersion; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -321,11 +325,28 @@ public abstract class BaseEntityImportService { calculatedField.setTenantId(ctx.getTenantId()); calculatedField.setEntityId(savedEntity.getId()); - calculatedField.getConfiguration().getArguments().values().forEach(argument -> { - if (argument.getRefEntityId() != null) { - argument.setRefEntityId(idProvider.getInternalId(argument.getRefEntityId(), ctx.isFinalImportAttempt())); + if (calculatedField.getConfiguration() instanceof ArgumentsBasedCalculatedFieldConfiguration argBasedConfig) { + if (argBasedConfig instanceof GeofencingCalculatedFieldConfiguration geofencingCfg) { + geofencingCfg.getZoneGroups().values().forEach(zoneGroupConfiguration -> { + if (zoneGroupConfiguration.getRefEntityId() != null) { + zoneGroupConfiguration.setRefEntityId(idProvider.getInternalId(zoneGroupConfiguration.getRefEntityId(), ctx.isFinalImportAttempt())); + } + }); + } else { + argBasedConfig.getArguments().values().forEach(argument -> { + if (argument.getRefEntityId() != null) { + argument.setRefEntityId(idProvider.getInternalId(argument.getRefEntityId(), ctx.isFinalImportAttempt())); + } + }); } - }); + } + if (calculatedField.getConfiguration() instanceof AlarmCalculatedFieldConfiguration alarmCfConfig) { + alarmCfConfig.getAllRules().map(Pair::getValue).forEach(rule -> { + if (rule.getDashboardId() != null) { + rule.setDashboardId(idProvider.getInternalId(rule.getDashboardId(), ctx.isFinalImportAttempt())); + } + }); + } }).toList(); for (CalculatedField existingField : existing) { 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 c258a30281..79c7868f2b 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 @@ -61,6 +61,8 @@ import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.Collection; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.SEVERITY_CHANGED; + /** * Created by ashvayka on 27.03.18. */ @@ -102,7 +104,12 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService @Override public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details) { - return withWsCallback(alarmService.clearAlarm(tenantId, alarmId, clearTs, details)); + return clearAlarm(tenantId, alarmId, clearTs, details, true); + } + + @Override + public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details, boolean pushEvent) { + return withWsCallback(alarmService.clearAlarm(tenantId, alarmId, clearTs, details, pushEvent)); } @Override @@ -246,8 +253,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService AlarmComment.AlarmCommentBuilder alarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", - String.format("Alarm severity was updated from %s to %s", result.getOldSeverity(), alarm.getSeverity()))); + .comment(JacksonUtil.newObjectNode() + .put("text", String.format(SEVERITY_CHANGED.getText(), result.getOldSeverity(), alarm.getSeverity())) + .put("subtype", SEVERITY_CHANGED.name()) + .put("oldSeverity", result.getOldSeverity().name()) + .put("newSeverity", alarm.getSeverity().name())); if (request != null && request.getUserId() != null) { alarmComment.userId(request.getUserId()); } 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 7396860272..48bd497c6e 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 @@ -22,6 +22,7 @@ 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 org.thingsboard.server.exception.EntitiesLimitExceededException; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; @@ -76,7 +77,6 @@ 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; @@ -398,7 +398,7 @@ public class DefaultTransportApiService implements TransportApiService { } 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) { + } catch (EntitiesLimitExceededException e) { log.warn("[{}][{}] API limit exception: [{}]", e.getTenantId(), gatewayId, e.getMessage()); return TransportApiResponseMsg.newBuilder() .setGetOrCreateDeviceResponseMsg( diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/ApiKeysCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/ApiKeysCleanUpService.java new file mode 100644 index 0000000000..895fed555d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ttl/ApiKeysCleanUpService.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ttl; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.server.dao.pat.ApiKeyDao; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Slf4j +@Service +@TbCoreComponent +@ConditionalOnExpression("${sql.ttl.api_keys.enabled:true} && ${sql.ttl.api_keys.ttl:0} > 0") +public class ApiKeysCleanUpService extends AbstractCleanUpService { + + public static final String RANDOM_DELAY_INTERVAL_MS_EXPRESSION = + "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.api_keys.checking_interval_ms})}"; + + private final ApiKeyDao apiKeyDao; + + public ApiKeysCleanUpService(PartitionService partitionService, ApiKeyDao apiKeyDao) { + super(partitionService); + this.apiKeyDao = apiKeyDao; + } + + @Scheduled( + initialDelayString = RANDOM_DELAY_INTERVAL_MS_EXPRESSION, + fixedDelayString = "${sql.ttl.api_keys.checking_interval_ms:86400000}" + ) + public void cleanUp() { + long threshold = System.currentTimeMillis(); + if (isSystemTenantPartitionMine()) { + int deleted = apiKeyDao.deleteAllByExpirationTimeBefore(threshold); + if (deleted > 0) { + log.info("API key cleanup removed {} keys (thresholdTs={})", deleted, threshold); + } + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java b/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java new file mode 100644 index 0000000000..e4ed589e16 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.user.cache; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.UserAuthDetails; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.dao.user.UserService; + +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DefaultUserAuthDetailsCache implements UserAuthDetailsCache { + + private final UserService userService; + + @Value("${cache.userAuthDetails.maxSize:1000}") + private int cacheMaxSize; + @Value("${cache.userAuthDetails.timeToLiveInMinutes:30}") + private int cacheValueTtl; + private Cache cache; + + @PostConstruct + private void init() { + cache = Caffeine.newBuilder() + .maximumSize(cacheMaxSize) + .expireAfterAccess(cacheValueTtl, TimeUnit.MINUTES) + .build(); + } + + @EventListener(ComponentLifecycleMsg.class) + public void onComponentLifecycleEvent(ComponentLifecycleMsg event) { + if (event.getEntityId() != null) { + if (event.getEntityId().getEntityType() == EntityType.USER) { + evict(new UserId(event.getEntityId().getId())); + } + } + } + + @Override + public UserAuthDetails getUserAuthDetails(TenantId tenantId, UserId userId) { + log.trace("Retrieving user with enabled credentials status for id {} for tenant {} from cache", userId, tenantId); + return cache.get(userId, id -> userService.findUserAuthDetailsByUserId(tenantId, id)); + } + + public void evict(UserId userId) { + cache.invalidate(userId); + log.trace("Evicted record for user {} from cache", userId); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java b/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java new file mode 100644 index 0000000000..c7614bcc5e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.user.cache; + +import org.thingsboard.server.common.data.UserAuthDetails; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; + +public interface UserAuthDetailsCache { + + UserAuthDetails getUserAuthDetails(TenantId tenantId, UserId userId); + +} diff --git a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java index f07f155c4e..fcf8c0593b 100644 --- a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java @@ -19,6 +19,8 @@ import lombok.NonNull; import org.apache.commons.lang3.math.NumberUtils; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -33,6 +35,18 @@ import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.ScriptCalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.SimpleCalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntryStatus; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.alarm.AlarmCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry.DEFAULT_VERSION; @@ -42,12 +56,34 @@ public class CalculatedFieldArgumentUtils { return kvEntry.getValue() != null ? ArgumentEntry.createSingleValueArgument(kvEntry) : new SingleValueArgumentEntry(); } - public static TsKvEntry createDefaultTsKvEntry(Argument argument, long ts) { - return new BasicTsKvEntry(ts, createDefaultKvEntry(argument), DEFAULT_VERSION); + public static ArgumentEntry transformTsRollingArgument(List tsRolling, int limit, long argTimeWindow) { + return ArgumentEntry.createTsRollingArgument(tsRolling, limit, argTimeWindow); } - public static AttributeKvEntry createDefaultAttributeEntry(Argument argument, long ts) { - return new BaseAttributeKvEntry(createDefaultKvEntry(argument), ts, DEFAULT_VERSION); + public static ArgumentEntry transformAggMetricArgument(List timeSeries, String argKey, AggMetric aggMetric) { + if (timeSeries == null || timeSeries.isEmpty()) { + return createDefaultMetricArgumentEntry(argKey, aggMetric); + } + return ArgumentEntry.createSingleValueArgument(timeSeries.get(0)); + } + + public static ArgumentEntry createDefaultMetricArgumentEntry(String argKey, AggMetric metric) { + Double defaultValue = metric.getDefaultValue(); + if (defaultValue != null) { + return ArgumentEntry.createSingleValueArgument(new DoubleDataEntry(argKey, defaultValue)); + } + return new SingleValueArgumentEntry(); + } + + public static ArgumentEntry transformAggregationArgument(List timeSeries, long startIntervalTs, long endIntervalTs) { + Map aggIntervals = new HashMap<>(); + AggIntervalEntry aggIntervalEntry = new AggIntervalEntry(startIntervalTs, endIntervalTs); + if (timeSeries == null || timeSeries.isEmpty()) { + aggIntervals.put(aggIntervalEntry, new AggIntervalEntryStatus()); + } else { + aggIntervals.put(aggIntervalEntry, new AggIntervalEntryStatus(System.currentTimeMillis())); + } + return new EntityAggregationArgumentEntry(aggIntervals); } private static KvEntry createDefaultKvEntry(Argument argument) { @@ -65,10 +101,22 @@ public class CalculatedFieldArgumentUtils { return new StringDataEntry(key, defaultValue); } - public static CalculatedFieldState createStateByType(CalculatedFieldCtx ctx) { + public static TsKvEntry createDefaultTsKvEntry(Argument argument, long ts) { + return new BasicTsKvEntry(ts, createDefaultKvEntry(argument), DEFAULT_VERSION); + } + public static AttributeKvEntry createDefaultAttributeEntry(Argument argument, long ts) { + return new BaseAttributeKvEntry(createDefaultKvEntry(argument), ts, DEFAULT_VERSION); + } + + public static CalculatedFieldState createStateByType(CalculatedFieldCtx ctx, EntityId entityId) { return switch (ctx.getCfType()) { - case SIMPLE -> new SimpleCalculatedFieldState(ctx.getArgNames()); - case SCRIPT -> new ScriptCalculatedFieldState(ctx.getArgNames()); + case SIMPLE -> new SimpleCalculatedFieldState(entityId); + case SCRIPT -> new ScriptCalculatedFieldState(entityId); + case GEOFENCING -> new GeofencingCalculatedFieldState(entityId); + case ALARM -> new AlarmCalculatedFieldState(entityId); + case PROPAGATION -> new PropagationCalculatedFieldState(entityId); + case RELATED_ENTITIES_AGGREGATION -> new RelatedEntitiesAggregationCalculatedFieldState(entityId); + case ENTITY_AGGREGATION -> new EntityAggregationCalculatedFieldState(entityId); }; } diff --git a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldUtils.java b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldUtils.java index 66dee78d96..49d3c22c3c 100644 --- a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldUtils.java @@ -15,31 +15,62 @@ */ package org.thingsboard.server.utils; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus; import org.thingsboard.server.common.data.id.CalculatedFieldId; 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.kv.BasicKvEntry; import org.thingsboard.server.common.util.KvProtoUtil; +import org.thingsboard.server.common.util.ProtoUtils; +import org.thingsboard.server.gen.transport.TransportProtos.AlarmRuleStateProto; +import org.thingsboard.server.gen.transport.TransportProtos.AlarmStateProto; +import org.thingsboard.server.gen.transport.TransportProtos.ArgumentIntervalProto; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldEntityCtxIdProto; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldIdProto; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; +import org.thingsboard.server.gen.transport.TransportProtos.EntityIdProto; +import org.thingsboard.server.gen.transport.TransportProtos.GeofencingArgumentProto; +import org.thingsboard.server.gen.transport.TransportProtos.GeofencingZoneProto; import org.thingsboard.server.gen.transport.TransportProtos.SingleValueArgumentProto; import org.thingsboard.server.gen.transport.TransportProtos.TsDoubleValProto; import org.thingsboard.server.gen.transport.TransportProtos.TsRollingArgumentProto; import org.thingsboard.server.gen.transport.TransportProtos.TsValueProto; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.ScriptCalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.SimpleCalculatedFieldState; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.TsRollingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntryStatus; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.single.EntityAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.alarm.AlarmCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.alarm.AlarmRuleState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingZoneState; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; public class CalculatedFieldUtils { @@ -75,15 +106,65 @@ public class CalculatedFieldUtils { .setType(state.getType().name()); state.getArguments().forEach((argName, argEntry) -> { - if (argEntry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { - builder.addSingleValueArguments(toSingleValueArgumentProto(argName, singleValueArgumentEntry)); - } else if (argEntry instanceof TsRollingArgumentEntry rollingArgumentEntry) { - builder.addRollingValueArguments(toRollingArgumentProto(argName, rollingArgumentEntry)); + switch (argEntry.getType()) { + case SINGLE_VALUE -> builder.addSingleValueArguments(toSingleValueArgumentProto(argName, (SingleValueArgumentEntry) argEntry)); + case TS_ROLLING -> builder.addRollingValueArguments(toRollingArgumentProto(argName, (TsRollingArgumentEntry) argEntry)); + case GEOFENCING -> builder.addGeofencingArguments(toGeofencingArgumentProto(argName, (GeofencingArgumentEntry) argEntry)); + case PROPAGATION -> builder.addAllPropagationEntityIds(toPropagationEntityIdsProto((PropagationArgumentEntry) argEntry)); + case RELATED_ENTITIES -> { + RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry = (RelatedEntitiesArgumentEntry) argEntry; + Map entityInputs = relatedEntitiesArgumentEntry.getEntityInputs(); + if (entityInputs.isEmpty()) { + builder.addSingleValueArguments(SingleValueArgumentProto.newBuilder().setArgName(argName).build()); + } else { + entityInputs.forEach((entityId, entry) -> builder.addSingleValueArguments(toSingleValueArgumentProto(argName, (SingleValueArgumentEntry) entry))); + } + } + case ENTITY_AGGREGATION -> { + EntityAggregationArgumentEntry entityAggregationArgumentEntry = (EntityAggregationArgumentEntry) argEntry; + entityAggregationArgumentEntry.getAggIntervals().forEach((interval, argumentStatus) -> builder.addAggregationArguments(toArgumentIntervalProto(argName, interval, argumentStatus))); + } } }); + if (state instanceof AlarmCalculatedFieldState alarmState) { + AlarmStateProto.Builder alarmStateProto = AlarmStateProto.newBuilder(); + alarmState.getCreateRuleStates().forEach((severity, ruleState) -> { + alarmStateProto.addCreateRuleStates(toAlarmRuleStateProto(ruleState)); + }); + if (alarmState.getClearRuleState() != null) { + alarmStateProto.setClearRuleState(toAlarmRuleStateProto(alarmState.getClearRuleState())); + } + builder.setAlarmState(alarmStateProto); + } + if (state instanceof RelatedEntitiesAggregationCalculatedFieldState aggState) { + builder.setLastArgsUpdateTs(aggState.getLastArgsRefreshTs()); + builder.setLastMetricsEvalTs(aggState.getLastMetricsEvalTs()); + } return builder.build(); } + private static List toPropagationEntityIdsProto(PropagationArgumentEntry argEntry) { + return argEntry.getEntityIds().stream().map(ProtoUtils::toProto).collect(Collectors.toList()); + } + + private static AlarmRuleStateProto toAlarmRuleStateProto(AlarmRuleState ruleState) { + return AlarmRuleStateProto.newBuilder() + .setSeverity(Optional.ofNullable(ruleState.getSeverity()).map(Enum::name).orElse("")) + .setEventCount(ruleState.getEventCount()) + .setFirstEventTs(ruleState.getFirstEventTs()) + .setLastCheckTs(ruleState.getLastCheckTs()) + .build(); + } + + private static AlarmRuleState fromAlarmRuleStateProto(AlarmRuleStateProto proto, AlarmCalculatedFieldState state) { + AlarmSeverity severity = StringUtils.isNotEmpty(proto.getSeverity()) ? AlarmSeverity.valueOf(proto.getSeverity()) : null; + AlarmRuleState ruleState = new AlarmRuleState(severity, null, state); + ruleState.setEventCount(proto.getEventCount()); + ruleState.setFirstEventTs(proto.getFirstEventTs()); + ruleState.setLastCheckTs(proto.getLastCheckTs()); + return ruleState; + } + public static SingleValueArgumentProto toSingleValueArgumentProto(String argName, SingleValueArgumentEntry entry) { SingleValueArgumentProto.Builder builder = SingleValueArgumentProto.newBuilder() .setArgName(argName); @@ -94,9 +175,23 @@ public class CalculatedFieldUtils { Optional.ofNullable(entry.getVersion()).ifPresent(builder::setVersion); + if (entry.getEntityId() != null) { + builder.setEntityId(ProtoUtils.toProto(entry.getEntityId())); + } + return builder.build(); } + public static ArgumentIntervalProto toArgumentIntervalProto(String argName, AggIntervalEntry intervalEntry, AggIntervalEntryStatus argumentStatus) { + return ArgumentIntervalProto.newBuilder() + .setArgName(argName) + .setStartTs(intervalEntry.getStartTs()) + .setEndTs(intervalEntry.getEndTs()) + .setLastArgsRefreshTs(argumentStatus.getLastArgsRefreshTs()) + .setLastMetricsEvalTs(argumentStatus.getLastMetricsEvalTs()) + .build(); + } + public static TsRollingArgumentProto toRollingArgumentProto(String argName, TsRollingArgumentEntry entry) { TsRollingArgumentProto.Builder builder = TsRollingArgumentProto.newBuilder() .setKey(argName) @@ -108,7 +203,28 @@ public class CalculatedFieldUtils { return builder.build(); } - public static CalculatedFieldState fromProto(CalculatedFieldStateProto proto) { + private static GeofencingArgumentProto toGeofencingArgumentProto(String argName, GeofencingArgumentEntry geofencingArgumentEntry) { + Map zoneStates = geofencingArgumentEntry.getZoneStates(); + GeofencingArgumentProto.Builder builder = GeofencingArgumentProto.newBuilder() + .setArgName(argName); + zoneStates.forEach((entityId, zoneState) -> + builder.addZones(toGeofencingZoneProto(entityId, zoneState))); + return builder.build(); + } + + private static GeofencingZoneProto toGeofencingZoneProto(EntityId entityId, GeofencingZoneState zoneState) { + GeofencingZoneProto.Builder builder = GeofencingZoneProto.newBuilder() + .setZoneId(ProtoUtils.toProto(entityId)) + .setTs(zoneState.getTs()) + .setVersion(zoneState.getVersion()) + .setPerimeterDefinition(JacksonUtil.toString(zoneState.getPerimeterDefinition())); + if (zoneState.getLastPresence() != null) { + builder.setInside(zoneState.getLastPresence().equals(GeofencingPresenceStatus.INSIDE)); + } + return builder.build(); + } + + public static CalculatedFieldState fromProto(CalculatedFieldEntityCtxId id, CalculatedFieldStateProto proto) { if (StringUtils.isEmpty(proto.getType())) { return null; } @@ -116,16 +232,71 @@ public class CalculatedFieldUtils { CalculatedFieldType type = CalculatedFieldType.valueOf(proto.getType()); CalculatedFieldState state = switch (type) { - case SIMPLE -> new SimpleCalculatedFieldState(); - case SCRIPT -> new ScriptCalculatedFieldState(); + case SIMPLE -> new SimpleCalculatedFieldState(id.entityId()); + case SCRIPT -> new ScriptCalculatedFieldState(id.entityId()); + case GEOFENCING -> new GeofencingCalculatedFieldState(id.entityId()); + case ALARM -> new AlarmCalculatedFieldState(id.entityId()); + case PROPAGATION -> new PropagationCalculatedFieldState(id.entityId()); + case RELATED_ENTITIES_AGGREGATION -> new RelatedEntitiesAggregationCalculatedFieldState(id.entityId()); + case ENTITY_AGGREGATION -> new EntityAggregationCalculatedFieldState(id.entityId()); }; + if (state instanceof RelatedEntitiesAggregationCalculatedFieldState relatedEntitiesAggState) { + Map> arguments = new HashMap<>(); + proto.getSingleValueArgumentsList().forEach(argProto -> { + SingleValueArgumentEntry entry = fromSingleValueArgumentProto(argProto); + Map entityInputs = arguments.computeIfAbsent(argProto.getArgName(), name -> new HashMap<>()); + if (entry.getEntityId() != null) { + entityInputs.put(entry.getEntityId(), entry); + } + }); + arguments.forEach((argName, entityInputs) -> { + relatedEntitiesAggState.getArguments().put(argName, new RelatedEntitiesArgumentEntry(entityInputs, false)); + }); + relatedEntitiesAggState.setLastArgsRefreshTs(proto.getLastArgsUpdateTs()); + relatedEntitiesAggState.setLastMetricsEvalTs(proto.getLastMetricsEvalTs()); + + return relatedEntitiesAggState; + } + + if (state instanceof EntityAggregationCalculatedFieldState entityAggregationState) { + Map arguments = new HashMap<>(); + + proto.getAggregationArgumentsList().forEach(argProto -> { + AggIntervalEntry intervalEntry = new AggIntervalEntry(argProto.getStartTs(), argProto.getEndTs()); + AggIntervalEntryStatus intervalStatus = new AggIntervalEntryStatus(argProto.getLastArgsRefreshTs(), argProto.getLastMetricsEvalTs()); + EntityAggregationArgumentEntry argEntry = arguments.computeIfAbsent(argProto.getArgName(), name -> new EntityAggregationArgumentEntry(new HashMap<>())); + argEntry.getAggIntervals().put(intervalEntry, intervalStatus); + }); + + entityAggregationState.getArguments().putAll(arguments); + + return entityAggregationState; + } + proto.getSingleValueArgumentsList().forEach(argProto -> state.getArguments().put(argProto.getArgName(), fromSingleValueArgumentProto(argProto))); - if (CalculatedFieldType.SCRIPT.equals(type)) { - proto.getRollingValueArgumentsList().forEach(argProto -> + switch (type) { + case SCRIPT -> proto.getRollingValueArgumentsList().forEach(argProto -> state.getArguments().put(argProto.getKey(), fromRollingArgumentProto(argProto))); + case GEOFENCING -> proto.getGeofencingArgumentsList().forEach(argProto -> + state.getArguments().put(argProto.getArgName(), fromGeofencingArgumentProto(argProto))); + case PROPAGATION -> { + List propagationEntityIds = proto.getPropagationEntityIdsList().stream().map(ProtoUtils::fromProto).toList(); + state.getArguments().put(PROPAGATION_CONFIG_ARGUMENT, new PropagationArgumentEntry(propagationEntityIds)); + } + case ALARM -> { + AlarmCalculatedFieldState alarmState = (AlarmCalculatedFieldState) state; + AlarmStateProto alarmStateProto = proto.getAlarmState(); + for (AlarmRuleStateProto ruleStateProto : alarmStateProto.getCreateRuleStatesList()) { + AlarmRuleState ruleState = fromAlarmRuleStateProto(ruleStateProto, alarmState); + alarmState.getCreateRuleStates().put(ruleState.getSeverity(), ruleState); + } + if (alarmStateProto.hasClearRuleState()) { + alarmState.setClearRuleState(fromAlarmRuleStateProto(alarmStateProto.getClearRuleState(), alarmState)); + } + } } return state; @@ -136,11 +307,14 @@ public class CalculatedFieldUtils { return new SingleValueArgumentEntry(); } TsValueProto tsValueProto = proto.getValue(); - return new SingleValueArgumentEntry( - tsValueProto.getTs(), - (BasicKvEntry) KvProtoUtil.fromTsValueProto(proto.getArgName(), tsValueProto), - proto.getVersion() - ); + BasicKvEntry kvEntry = (BasicKvEntry) KvProtoUtil.fromTsValueProto(proto.getArgName(), tsValueProto); + long ts = tsValueProto.getTs(); + long version = proto.getVersion(); + if (proto.hasEntityId()) { + EntityId entityId = ProtoUtils.fromProto(proto.getEntityId()); + return new SingleValueArgumentEntry(entityId, ts, kvEntry, version); + } + return new SingleValueArgumentEntry(ts, kvEntry, version); } public static TsRollingArgumentEntry fromRollingArgumentProto(TsRollingArgumentProto proto) { @@ -149,4 +323,15 @@ public class CalculatedFieldUtils { return new TsRollingArgumentEntry(tsRecords, proto.getLimit(), proto.getTimeWindow()); } + + private static ArgumentEntry fromGeofencingArgumentProto(GeofencingArgumentProto proto) { + Map zoneStates = proto.getZonesList() + .stream() + .map(GeofencingZoneState::new) + .collect(Collectors.toMap(GeofencingZoneState::getZoneId, Function.identity())); + GeofencingArgumentEntry geofencingArgumentEntry = new GeofencingArgumentEntry(); + geofencingArgumentEntry.setZoneStates(zoneStates); + return geofencingArgumentEntry; + } + } diff --git a/application/src/main/java/org/thingsboard/server/utils/CsvUtils.java b/application/src/main/java/org/thingsboard/server/utils/CsvUtils.java index fdf9481191..4d63d01060 100644 --- a/application/src/main/java/org/thingsboard/server/utils/CsvUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/CsvUtils.java @@ -17,10 +17,15 @@ package org.thingsboard.server.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import lombok.SneakyThrows; import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.CSVRecord; import org.apache.commons.io.input.CharSequenceReader; +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -43,4 +48,18 @@ public class CsvUtils { .collect(Collectors.toList()); } + @SneakyThrows + public static byte[] generateCsv(List> rows) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); + CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT)) { + + for (List row : rows) { + csvPrinter.printRecord(row); + } + csvPrinter.flush(); + } + return out.toByteArray(); + } + } 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 450c3bb888..b93568a7e0 100644 --- a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.lwm2m.LwM2mInstance; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.lwm2m.LwM2mResourceObserve; import org.thingsboard.server.common.data.util.TbDDFFileParser; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -73,7 +73,7 @@ public class LwM2mObjectModelUtils { try { List objectModels = ddfFileParser.parse(new ByteArrayInputStream(resource.getData()), resource.getSearchText()); - if (objectModels.size() == 0) { + if (objectModels.isEmpty()) { return null; } else { ObjectModel obj = objectModels.get(0); @@ -95,7 +95,7 @@ public class LwM2mObjectModelUtils { resources.add(lwM2MResourceObserve); } }); - if (isSave || resources.size() > 0) { + if (isSave || !resources.isEmpty()) { instance.setResources(resources.toArray(LwM2mResourceObserve[]::new)); lwM2mObject.setInstances(new LwM2mInstance[]{instance}); return lwM2mObject; diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index 0efb669ec2..3904875b34 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -56,6 +56,9 @@ + + + diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bdef34df3b..072e18af69 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -100,6 +100,9 @@ server: 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}" + rule_engine: + # Default timeout for waiting response of REST API request to Rule Engine in milliseconds + response_timeout: "${DEFAULT_RULE_ENGINE_RESPONSE_TIMEOUT:10000}" # Application info parameters app: @@ -148,6 +151,12 @@ security: 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}" + # API key parameters + api_key: + # Prefix for the auto-generated API key. For example, tb_Ood4dQMxWvMH-76z3E_Cv0mZaBWT0Clk3hRSO0P_jNQ + value_prefix: "${SECURITY_API_KEY_VALUE_PREFIX:tb_}" + # Length of the auto-generated API key. Max is 255 + value_bytes_size: "${SECURITY_API_KEY_VALUE_PREFIX:64}" # Enable/disable case-sensitive username login user_login_case_sensitive: "${SECURITY_USER_LOGIN_CASE_SENSITIVE:true}" claim: @@ -208,7 +217,7 @@ ui: # Help parameters help: # Base URL for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.2.1}" + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.3}" # Database telemetry parameters database: @@ -427,6 +436,9 @@ sql: 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 + api_keys: + enabled: "${SQL_TTL_API_KEYS_ENABLED:true}" # Enable/disable TTL (Time To Live) for expired api keys records + checking_interval_ms: "${SQL_TTL_API_KEYS_CHECKING_INTERVAL_MS:86400000}" # Default value - 1 day relations: 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 @@ -504,14 +516,14 @@ actors: response_timeout_ms: "${ACTORS_RPC_RESPONSE_TIMEOUT_MS:30000}" # Close transport session if RPC delivery timed out. If enabled, RPC will be reverted to the queued state. # Note: - # - For MQTT transport: - # - QoS level 0: This feature does not apply, as no acknowledgment is expected, and therefore no timeout is triggered. - # - QoS level 1: This feature applies, as an acknowledgment is expected. - # - QoS level 2: Unsupported. - # - For CoAP transport: - # - Confirmable requests: This feature applies, as delivery confirmation is expected. - # - Non-confirmable requests: This feature does not apply, as no delivery acknowledgment is expected. - # - For HTTP and SNPM transports: RPC is considered delivered immediately, and there is no logic to await acknowledgment. + #
  • For MQTT transport: + #
    • QoS level 0: This feature does not apply, as no acknowledgment is expected, and therefore no timeout is triggered.
    • + #
    • QoS level 1: This feature applies, as an acknowledgment is expected.
    • + #
    • QoS level 2: Unsupported.
  • + #
  • For CoAP transport: + #
    • Confirmable requests: This feature applies, as delivery confirmation is expected.
    • + #
    • Non-confirmable requests: This feature does not apply, as no delivery acknowledgment is expected.
  • + #
  • For HTTP and SNPM transports: RPC is considered delivered immediately, and there is no logic to await acknowledgment.
close_session_on_rpc_delivery_timeout: "${ACTORS_RPC_CLOSE_SESSION_ON_RPC_DELIVERY_TIMEOUT:false}" statistics: # Enable/disable actor statistics @@ -662,6 +674,9 @@ cache: aiModel: timeToLiveInMinutes: "${CACHE_SPECS_AI_MODEL_TTL:1440}" # AI model cache TTL maxSize: "${CACHE_SPECS_AI_MODEL_MAX_SIZE:10000}" # 0 means the cache is disabled + apiKeys: + timeToLiveInMinutes: "${CACHE_SPECS_API_KEYS_TTL:1440}" # API keys cache TTL + maxSize: "${CACHE_SPECS_API_KEYS_MAX_SIZE:10000}" # 0 means the cache is disabled # Deliberately placed outside the 'specs' group above notificationRules: @@ -682,6 +697,9 @@ cache: tbResourceData: timeToLiveInMinutes: "${CACHE_SPECS_RESOURCE_DATA_TTL:10080}" # TB resource data cache TTL maxSize: "${CACHE_SPECS_RESOURCE_DATA_MAX_SIZE:100000}" # 0 means the cache is disabled + userAuthDetails: + timeToLiveInMinutes: "${CACHE_SPECS_USER_AUTH_DETAILS_TTL:120}" # User auth details cache TTL + maxSize: "${CACHE_SPECS_USER_AUTH_DETAILS_MAX_SIZE:200000}" # 0 means the cache is disabled # Spring data parameters spring.data.redis.repositories.enabled: false # Disable this because it is not required. @@ -715,13 +733,13 @@ 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 the master node + # Name of the master node master: "${REDIS_MASTER:}" - # comma-separated list of "host:port" pairs of sentinels + # Comma-separated list of "host:port" pairs of sentinels sentinels: "${REDIS_SENTINELS:}" - # password to authenticate with sentinel + # Password to authenticate with sentinel password: "${REDIS_SENTINEL_PASSWORD:}" - # if set false will be used pool config build from values of the pool config section + # If set false will be used pool config build from values of the pool config section useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}" # db index db: "${REDIS_DB:0}" @@ -1023,10 +1041,10 @@ transport: activity: # This property specifies the strategy for reporting activity events within each reporting period. # The accepted values are 'FIRST', 'LAST', 'FIRST_AND_LAST' and 'ALL'. - # - 'FIRST': Only the first activity event in each reporting period is reported. - # - 'LAST': Only the last activity event in the reporting period is reported. - # - 'FIRST_AND_LAST': Both the first and last activity events in the reporting period are reported. - # - 'ALL': All activity events in the reporting period are reported. + #
  • 'FIRST': Only the first activity event in each reporting period is reported.
  • + #
  • 'LAST': Only the last activity event in the reporting period is reported.
  • + #
  • 'FIRST_AND_LAST': Both the first and last activity events in the reporting period are reported.
  • + #
  • 'ALL': All activity events in the reporting period are reported.
reporting_strategy: "${TB_TRANSPORT_ACTIVITY_REPORTING_STRATEGY:LAST}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -1144,16 +1162,16 @@ transport: dtls: # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${LWM2M_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" - # CoAP DTLS connection ID length for LWM2M. RFC 9146, Connection Identifier for DTLS 1.2 - # Default: off + # LWM2M DTLS connection ID length for LWM2M. RFC 9146, Connection Identifier for DTLS 1.2 + # Default: off.
# Control usage of DTLS connection ID length (CID). - # - 'off' to deactivate it. - # - 'on' to activate Connection ID support (same as CID 0 or more 0). - # - A positive value defines generated CID size in bytes. - # - A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic). - # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used - # - A value that are > 4: MultiNodeConnectionIdGenerator is used - connection_id_length: "${LWM2M_DTLS_CONNECTION_ID_LENGTH:}" + #
  • 'off' to deactivate it.
  • + #
  • 'on' to activate Connection ID support (same as CID 0 or more 0).
  • + #
  • A positive value defines generated CID size in bytes.
  • + #
  • A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic).
  • + #
  • A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used
  • + #
  • A value that are > 4: MultiNodeConnectionIdGenerator is used
+ connection_id_length: "${LWM2M_DTLS_CONNECTION_ID_LENGTH:8}" server: # LwM2M Server ID id: "${LWM2M_SERVER_ID:123}" @@ -1198,7 +1216,7 @@ transport: # 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}" + id: "${LWM2M_SERVER_ID_BS:0}" # 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 @@ -1312,7 +1330,7 @@ transport: # URL of gateways dashboard repository repository_url: "${TB_GATEWAY_DASHBOARD_SYNC_REPOSITORY_URL:https://github.com/thingsboard/gateway-management-extensions-dist.git}" # Branch of gateways dashboard repository to work with - branch: "${TB_GATEWAY_DASHBOARD_SYNC_BRANCH:release/4.0.0}" + branch: "${TB_GATEWAY_DASHBOARD_SYNC_BRANCH:release/4.3.0}" # Fetch frequency in hours for gateways dashboard repository fetch_frequency: "${TB_GATEWAY_DASHBOARD_SYNC_FETCH_FREQUENCY:24}" @@ -1335,15 +1353,15 @@ coap: # CoAP DTLS bind port bind_port: "${COAP_DTLS_BIND_PORT:5684}" # CoAP DTLS connection ID length. RFC 9146, Connection Identifier for DTLS 1.2 - # Default: off + # Default: off.
# Control usage of DTLS connection ID length (CID). - # - 'off' to deactivate it. - # - 'on' to activate Connection ID support (same as CID 0 or more 0). - # - A positive value defines generated CID size in bytes. - # - A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic). - # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used - # - A value that are > 4: MultiNodeConnectionIdGenerator is used - connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + #
  • 'off' to deactivate it.
  • + #
  • 'on' to activate Connection ID support (same as CID 0 or more 0).
  • + #
  • A positive value defines generated CID size in bytes.
  • + #
  • A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic).
  • + #
  • A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used
  • + #
  • A value that are > 4: MultiNodeConnectionIdGenerator is used
+ connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:8}" # Specify the MTU (Maximum Transmission Unit). # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. # Default = 1024 @@ -1359,13 +1377,13 @@ coap: # In order to negotiate smaller maximum fragment lengths, # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. # The "extension_data" field of this extension SHALL contain: - # enum { + #
 enum {
     #   2^9(1) == 512,
     #   2^10(2) == 1024,
     #   2^11(3) == 2048,
     #   2^12(4) == 4096,
     #   (255)
-    # } MaxFragmentLength;
+    # } MaxFragmentLength; 
# TLS already requires clients and servers to support fragmentation of handshake messages. max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials @@ -1451,7 +1469,7 @@ device: pem_cert_file: "${DEVICE_CONNECTIVITY_COAPS_CA_ROOT_CERT:cafile.pem}" gateway: # The docker tag for thingsboard/tb-gateway image used in docker-compose file for gateway launch - image_version: "${DEVICE_CONNECTIVITY_GATEWAY_IMAGE_VERSION:3.7-stable}" + image_version: "${DEVICE_CONNECTIVITY_GATEWAY_IMAGE_VERSION:3.8-stable}" # Edges parameters edges: @@ -1490,6 +1508,11 @@ edges: no_read_records_sleep: "${EDGES_NO_READ_RECORDS_SLEEP:1000}" # Number of milliseconds to wait before resending failed batch of edge events to edge sleep_between_batches: "${EDGES_SLEEP_BETWEEN_BATCHES:60000}" + # Time (in milliseconds) to subtract from the start timestamp when fetching edge events. + # This compensates for possible misordering between `created_time` (used for partitioning) + # and `seqId` (used for sorting). Without this, events with smaller seqId but larger created_time + # might be skipped, especially across partition boundaries. + misordering_compensation_millis: "${EDGES_MISORDERING_COMPENSATION_MILLIS:60000}" # Max number of high priority edge events per edge session. No persistence - stored in memory max_high_priority_queue_size_per_session: "${EDGES_MAX_HIGH_PRIORITY_QUEUE_SIZE_PER_SESSION:10000}" # Number of threads that are used to check DB for edge events @@ -1544,6 +1567,8 @@ swagger: version: "${SWAGGER_VERSION:}" # The group name (definition) on the API doc UI page. group_name: "${SWAGGER_GROUP_NAME:thingsboard}" + # Control the initial display state of API operations and tags (none or list) + doc_expansion: "${SWAGGER_DOC_EXPANSION:list}" # Queue configuration parameters queue: diff --git a/application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java b/application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java new file mode 100644 index 0000000000..cf94940e07 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java @@ -0,0 +1,1016 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cf; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.action.TbAlarmResult; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.Device; +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.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; +import org.thingsboard.server.common.data.alarm.rule.condition.DurationAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.RepeatingAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.SimpleAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionFilter; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.SimpleAlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NoDataFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate.NumericOperation; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.StringFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.StringFilterPredicate.StringOperation; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSchedule; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.SpecificTimeSchedule; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.CurrentOwnerDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.debug.DebugSettings; +import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent; +import org.thingsboard.server.common.data.event.EventType; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EventId; +import org.thingsboard.server.common.data.query.EntityKeyValueType; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.event.EventDao; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; + +@Slf4j +@DaoSqlTest +public class AlarmRulesTest extends AbstractControllerTest { + + @MockitoSpyBean + private ActorSystemContext actorSystemContext; + + @Autowired + private EventDao eventDao; + + private Device device; + private DeviceId deviceId; + private EntityId originatorId; + private EventId latestEventId; + + @Before + public void beforeEach() throws Exception { + loginSysAdmin(); + + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setCfReevaluationCheckInterval(1); + tenantProfileConfig.setAlarmsReevaluationInterval(1); + }); + + loginTenantAdmin(); + device = createDevice("Device A", "aaa"); + deviceId = device.getId(); + originatorId = deviceId; + } + + @Test + public void testCreateAlarm_severityUpdate_clear() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + Map createRules = Map.of( + AlarmSeverity.MAJOR, new Condition("return temperature >= 50;", null, null), + AlarmSeverity.CRITICAL, new Condition("return temperature >= 100;", null, null) + ); + + Condition clearRule = new Condition("return temperature <= 25;", null, null); + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, clearRule); + + postTelemetry(deviceId, "{\"temperature\":50}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.MAJOR); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + + postTelemetry(deviceId, "{\"temperature\":100}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isSeverityUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + + postTelemetry(deviceId, "{\"temperature\":101}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + + postTelemetry(deviceId, "{\"temperature\":20}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCleared()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.CLEARED_UNACK); + }); + } + + @Test + public void testCreateAlarm_simpleConditionExpression() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + SimpleAlarmConditionExpression simpleExpression = new SimpleAlarmConditionExpression(); + AlarmConditionFilter filter = new AlarmConditionFilter(); + filter.setArgument("temperature"); + filter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate predicate = new NumericFilterPredicate(); + predicate.setOperation(NumericOperation.GREATER_OR_EQUAL); + AlarmConditionValue thresholdValue = new AlarmConditionValue<>(); + thresholdValue.setStaticValue(100.0); + predicate.setValue(thresholdValue); + filter.setPredicates(List.of(predicate)); + simpleExpression.setFilters(List.of(filter)); + simpleExpression.setOperation(ComplexOperation.AND); + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition(simpleExpression, null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"temperature\":100}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testCreateAlarm_eventBeforeDefaultTs() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"values\": {\"temperature\": 50}, \"ts\": " + (System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30) + "}")); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testCreateAlarm_repeatingCondition() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + int eventsCountMajor = 5; + int eventsCountCritical = 10; + Map createRules = Map.of( + AlarmSeverity.MAJOR, new Condition("return temperature >= 50;", eventsCountMajor, null), + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", eventsCountCritical, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + for (int i = 0; i < 4; i++) { + postTelemetry(deviceId, "{\"temperature\":50}"); + Thread.sleep(10); + } + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + postTelemetry(deviceId, "{\"temperature\":50}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.MAJOR); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionRepeats()).isEqualTo(5); + }); + + for (int i = 0; i < 4; i++) { + postTelemetry(deviceId, "{\"temperature\":50}"); + Thread.sleep(10); + } + checkAlarmResult(calculatedField, alarmResult -> alarmResult.getConditionRepeats() == 9, alarmResult -> { + assertThat(alarmResult.isUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.MAJOR); + }); + postTelemetry(deviceId, "{\"temperature\":50}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isSeverityUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionRepeats()).isEqualTo(10); + }); + } + + @Test + public void testCreateAlarm_dynamicRepeatingCondition() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + + Argument eventsCountArgument = new Argument(); + eventsCountArgument.setRefEntityKey(new ReferencedEntityKey("eventsCount", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + eventsCountArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument, + "eventsCount", eventsCountArgument + ); + + int eventsCount = 5; + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", null, + new AlarmConditionValue<>(null, "eventsCount"), null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"eventsCount\":" + eventsCount + "}"); + for (int i = 0; i < eventsCount; i++) { + postTelemetry(deviceId, "{\"temperature\":50}"); + Thread.sleep(10); + } + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionRepeats()).isEqualTo(eventsCount); + }); + } + + @Test + public void testCreateAlarm_durationCondition() throws Exception { + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("powerConsumption", ArgumentType.TS_LATEST, null)); + argument.setDefaultValue("0"); + Map arguments = Map.of( + "powerConsumption", argument + ); + + long createDurationMs = 5000L; + long clearDurationMs = 3000L; + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return powerConsumption >= 3000;", null, createDurationMs) + ); + Condition clearRule = new Condition("return powerConsumption < 3000;", null, clearDurationMs); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High power consumption during 5 seconds", + arguments, createRules, clearRule); + postTelemetry(deviceId, "{\"powerConsumption\":3500}"); + Thread.sleep(createDurationMs - 2000); + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionDuration()).isBetween(createDurationMs, createDurationMs + 2000); + }); + + postTelemetry(deviceId, "{\"powerConsumption\":2000}"); + Thread.sleep(clearDurationMs - 2000); + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCleared()).isTrue(); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.CLEARED_UNACK); + assertThat(alarmResult.getConditionDuration()).isBetween(clearDurationMs, clearDurationMs + 2000); + }); + } + + @Test + public void testCreateAlarm_dynamicDurationCondition() throws Exception { + Argument powerConsumptionArgument = new Argument(); + powerConsumptionArgument.setRefEntityKey(new ReferencedEntityKey("powerConsumption", ArgumentType.TS_LATEST, null)); + powerConsumptionArgument.setDefaultValue("0"); + + Argument durationArgument = new Argument(); + durationArgument.setRefEntityKey(new ReferencedEntityKey("duration", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + durationArgument.setDefaultValue("-1"); + Map arguments = Map.of( + "powerConsumption", powerConsumptionArgument, + "duration", durationArgument + ); + + long createDurationMs = 2000L; + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return powerConsumption >= 3000;", null, null, + new AlarmConditionValue(null, "duration"), null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High power consumption during 2 seconds", + arguments, createRules, null); + postTelemetry(deviceId, "{\"powerConsumption\":3500}"); + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"duration\":" + createDurationMs + "}"); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionDuration()).isBetween(createDurationMs, createDurationMs + 2000); + }); + } + + @Test + public void testCreateAlarm_durationCondition_defaultValue() { + Argument powerConsumptionArgument = new Argument(); + powerConsumptionArgument.setRefEntityKey(new ReferencedEntityKey("powerConsumption", ArgumentType.TS_LATEST, null)); + powerConsumptionArgument.setDefaultValue("3500"); + Map arguments = Map.of( + "powerConsumption", powerConsumptionArgument + ); + + long createDurationMs = 2000L; + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return powerConsumption >= 3000;", null, null, + new AlarmConditionValue(2000L, null), null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High power consumption during 2 seconds", + arguments, createRules, null); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionDuration()).isBetween(createDurationMs, createDurationMs + 2000); + }); + } + + @Test + public void testCreateAlarm_currentOwnerArgument() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + + Argument temperatureThresholdArgument = new Argument(); + temperatureThresholdArgument.setRefEntityKey(new ReferencedEntityKey("temperatureThreshold", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + temperatureThresholdArgument.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + temperatureThresholdArgument.setDefaultValue("1000"); + + Map arguments = Map.of( + "temperature", temperatureArgument, + "temperatureThreshold", temperatureThresholdArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= temperatureThreshold;", null, null) + ); + + device.setCustomerId(customerId); + device = doPost("/api/device", device, Device.class); + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"temperatureThreshold\":50}"); + + postTelemetry(deviceId, "{\"temperature\":51}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testCreateAndClearAlarm_customerAlarmRule_simpleExpression() throws Exception { + Argument locationArgument = new Argument(); + locationArgument.setRefEntityKey(new ReferencedEntityKey("location", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + locationArgument.setDefaultValue("unknown"); + originatorId = customerId; + + Argument locationFilterArgument = new Argument(); + locationFilterArgument.setRefEntityKey(new ReferencedEntityKey("locationFilter", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + locationFilterArgument.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + locationFilterArgument.setDefaultValue("None"); + + Map arguments = Map.of( + "location", locationArgument, + "locationFilter", locationFilterArgument + ); + + Map createRules = Map.of( + AlarmSeverity.INDETERMINATE, new Condition(createSimpleExpression( + "location", StringOperation.CONTAINS, new AlarmConditionValue<>(null, "locationFilter") + ), null, null) + ); + Condition clearRule = new Condition(createSimpleExpression( + "location", StringOperation.NOT_CONTAINS, new AlarmConditionValue<>(null, "locationFilter") + ), null, null); + + CalculatedField calculatedField = createAlarmCf(customerId, "New resident", + arguments, createRules, clearRule); + + loginSysAdmin(); + postAttributes(tenantId, AttributeScope.SERVER_SCOPE, "{\"locationFilter\":\"Kyiv\"}"); + loginTenantAdmin(); + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"location\":\"Ukraine, Kyiv\"}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.INDETERMINATE); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"location\":\"Ukraine, Lviv\"}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCleared()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.INDETERMINATE); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.CLEARED_UNACK); + }); + } + + @Test + public void testCreateAlarm_dynamicSchedule() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Argument scheduleArgument = new Argument(); + scheduleArgument.setRefEntityKey(new ReferencedEntityKey("schedule", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + scheduleArgument.setDefaultValue("None"); + Map arguments = Map.of( + "temperature", temperatureArgument, + "schedule", scheduleArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", null, null, null, + new AlarmConditionValue<>(null, "schedule")) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + String schedule = """ + {"timezone":"Europe/Kiev","items":[{"enabled":false,"dayOfWeek":1,"startsOn":0,"endsOn":0},{"enabled":false,"dayOfWeek":2,"startsOn":0,"endsOn":0},{"enabled":false,"dayOfWeek":3,"startsOn":0,"endsOn":0},{"enabled":false,"dayOfWeek":4,"startsOn":0,"endsOn":0},{"enabled":false,"dayOfWeek":5,"startsOn":0,"endsOn":0},{"enabled":false,"dayOfWeek":6,"startsOn":0,"endsOn":0},{"enabled":false,"dayOfWeek":7,"startsOn":0,"endsOn":0}]} + """; + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"schedule\":" + schedule + "}"); + postTelemetry(deviceId, "{\"temperature\":50}"); + + Thread.sleep(1000); + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + + schedule = schedule.replace("\"enabled\":false", "\"enabled\":true"); + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"schedule\":" + schedule + "}"); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + // checking multiple debug events due to scheduled reevaluation (which also produces debug events) + CalculatedFieldDebugEvent debugEvent = getDebugEvents(calculatedField.getId(), 5).stream() + .filter(event -> event.getResult() != null) + .findFirst().orElse(null); + assertThat(debugEvent).isNotNull(); + TbAlarmResult alarmResult = JacksonUtil.fromString(debugEvent.getResult(), TbAlarmResult.class); + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testCreateAlarm_noDataPredicate() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + long majorNoDataDuration = 3L; + long criticalNoDataDuration = 10L; + + SimpleAlarmConditionExpression majorExpression = new SimpleAlarmConditionExpression(); + AlarmConditionFilter majorFilter = new AlarmConditionFilter(); + majorFilter.setArgument("temperature"); + majorFilter.setValueType(EntityKeyValueType.NUMERIC); + majorFilter.setPredicates(List.of( + new NumericFilterPredicate(NumericOperation.GREATER, new AlarmConditionValue<>(25.0, null)), + new NoDataFilterPredicate(TimeUnit.SECONDS, new AlarmConditionValue(majorNoDataDuration, null)) + )); + majorExpression.setFilters(List.of(majorFilter)); + + SimpleAlarmConditionExpression criticalExpression = new SimpleAlarmConditionExpression(); + AlarmConditionFilter criticalFilter = new AlarmConditionFilter(); + criticalFilter.setArgument("temperature"); + criticalFilter.setValueType(EntityKeyValueType.NUMERIC); + criticalFilter.setPredicates(List.of( + new NumericFilterPredicate(NumericOperation.GREATER, new AlarmConditionValue<>(25.0, null)), + new NoDataFilterPredicate(TimeUnit.SECONDS, new AlarmConditionValue(criticalNoDataDuration, null)) + )); + criticalExpression.setFilters(List.of(criticalFilter)); + + Map createRules = Map.of( + AlarmSeverity.MAJOR, new Condition(majorExpression, null, null), + AlarmSeverity.CRITICAL, new Condition(criticalExpression, null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "No Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"temperature\":50}"); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.MAJOR); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isSeverityUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testChangeAlarmType() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"temperature\":50}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + + calculatedField.setName("New alarm type"); + calculatedField = saveCalculatedField(calculatedField); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testChangeRuleExpression() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 100;", null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"temperature\":50}"); + Thread.sleep(1000); + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + + AlarmCalculatedFieldConfiguration configuration = (AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration(); + ((TbelAlarmConditionExpression) configuration.getCreateRules().get(AlarmSeverity.CRITICAL).getCondition().getExpression()) + .setExpression("return temperature >= 50;"); + calculatedField = saveCalculatedField(calculatedField); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testChangeRequiredEventsCountForRepeatingCondition() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + int eventsCountMajor = 5; + int eventsCountCritical = 10; + Map createRules = Map.of( + AlarmSeverity.MAJOR, new Condition("return temperature >= 50;", eventsCountMajor, null), + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", eventsCountCritical, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + for (int i = 0; i < eventsCountMajor; i++) { + postTelemetry(deviceId, "{\"temperature\":50}"); + Thread.sleep(10); + } + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.MAJOR); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionRepeats()).isEqualTo(5); + }); + + postTelemetry(deviceId, "{\"temperature\":50}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.MAJOR); + assertThat(alarmResult.getConditionRepeats()).isEqualTo(6); + }); + + // decreasing required events count for critical rule + AlarmCalculatedFieldConfiguration configuration = (AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration(); + ((RepeatingAlarmCondition) configuration.getCreateRules().get(AlarmSeverity.CRITICAL).getCondition()) + .setCount(new AlarmConditionValue<>(6, null)); + calculatedField = saveCalculatedField(calculatedField); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isSeverityUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + assertThat(alarmResult.getConditionRepeats()).isEqualTo(6); + }); + } + + @Test + public void testChangeConditionArgumentSource() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + + Argument temperatureThresholdArgument = new Argument(); + temperatureThresholdArgument.setRefEntityKey(new ReferencedEntityKey("temperatureThreshold", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + temperatureThresholdArgument.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + temperatureThresholdArgument.setDefaultValue("100"); + loginSysAdmin(); + postAttributes(tenantId, AttributeScope.SERVER_SCOPE, "{\"temperatureThreshold\":100}"); + loginTenantAdmin(); + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"temperatureThreshold\":50}"); + + Map arguments = Map.of( + "temperature", temperatureArgument, + "temperatureThreshold", temperatureThresholdArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= temperatureThreshold;", null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"temperature\":50}"); + Thread.sleep(1000); + // not created because tenant's threshold 100 is used + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + + ((AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration()).getArguments().get("temperatureThreshold") + .setRefDynamicSourceConfiguration(null); + // using threshold 50 on device level + calculatedField = saveCalculatedField(calculatedField); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testAlarmDetails() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Argument humidityArgument = new Argument(); + humidityArgument.setRefEntityKey(new ReferencedEntityKey("humidity", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + humidityArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument, + "humidity", humidityArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50 && humidity >= 50;", null, null) + ); + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature and Humidity Alarm", + arguments, createRules, null, configuration -> { + configuration.getCreateRules().get(AlarmSeverity.CRITICAL).setAlarmDetails( + "temperature is ${temperature}, humidity is ${humidity}" + ); + }); + + postTelemetry(deviceId, "{\"temperature\":50}"); + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"humidity\":50}"); + + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getDetails().get("data").asText()) + .isEqualTo("temperature is 50, humidity is 50"); + }); + + ((AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration()).getCreateRules().get(AlarmSeverity.CRITICAL).setAlarmDetails( + "UPDATED temperature is ${temperature}, humidity is ${humidity}" + ); + calculatedField = saveCalculatedField(calculatedField); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isFalse(); + assertThat(alarmResult.isUpdated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getDetails().get("data").asText()) + .isEqualTo("UPDATED temperature is 50, humidity is 50"); + }); + } + + @Test + public void testCreateAlarm_scheduleStarted() throws Exception { + Argument parkingSpotOccupiedArgument = new Argument(); + parkingSpotOccupiedArgument.setRefEntityKey(new ReferencedEntityKey("parkingSpotOccupied", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + parkingSpotOccupiedArgument.setDefaultValue("false"); + Map arguments = Map.of( + "parkingSpotOccupied", parkingSpotOccupiedArgument + ); + + SpecificTimeSchedule schedule = new SpecificTimeSchedule(); + schedule.setTimezone(ZoneId.systemDefault().getId()); + schedule.setDaysOfWeek(Set.of(1, 2, 3, 4, 5, 6, 7)); + long startsOn = Duration.between(LocalDate.now().atStartOfDay(), LocalDateTime.now()) + .plus(15, ChronoUnit.SECONDS).toMillis(); + schedule.setStartsOn(startsOn); + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return parkingSpotOccupied == true;", null, null, null, + new AlarmConditionValue<>(schedule, null)) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "Illegal parking alarm", + arguments, createRules, null); + + postAttributes(deviceId, AttributeScope.SERVER_SCOPE, "{\"parkingSpotOccupied\":true}"); + + Thread.sleep(10000); + assertThat(getLatestAlarmResult(calculatedField.getId())).isNull(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + CalculatedFieldDebugEvent debugEvent = getDebugEvents(calculatedField.getId(), 5).stream() + .filter(event -> event.getResult() != null) + .findFirst().orElse(null); + assertThat(debugEvent).isNotNull(); + TbAlarmResult alarmResult = JacksonUtil.fromString(debugEvent.getResult(), TbAlarmResult.class); + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + @Test + public void testManualClearAlarm() throws Exception { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + Map createRules = Map.of( + AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", null, null) + ); + + CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm", + arguments, createRules, null); + + postTelemetry(deviceId, "{\"temperature\":50}"); + Alarm alarm = checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }).getAlarm(); + + doPost("/api/alarm/" + alarm.getId() + "/clear", AlarmInfo.class); + Thread.sleep(1000); + postTelemetry(deviceId, "{\"temperature\":50}"); + checkAlarmResult(calculatedField, alarmResult -> { + assertThat(alarmResult.getAlarm().getId()).isNotEqualTo(alarm.getId()); + assertThat(alarmResult.isCreated()).isTrue(); + assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); + }); + } + + // TODO: MSA tests + + private TbAlarmResult checkAlarmResult(CalculatedField calculatedField, Consumer assertion) { + return checkAlarmResult(calculatedField, null, assertion); + } + + private TbAlarmResult checkAlarmResult(CalculatedField calculatedField, + Predicate waitFor, + Consumer assertion) { + TbAlarmResult alarmResult = await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> getLatestAlarmResult(calculatedField.getId()), result -> + result != null && (waitFor == null || waitFor.test(result))); + assertion.accept(alarmResult); + + Alarm alarm = alarmResult.getAlarm(); + assertThat(alarm.getOriginator()).isEqualTo(originatorId); + assertThat(alarm.getType()).isEqualTo(calculatedField.getName()); + return alarmResult; + } + + private TbAlarmResult getLatestAlarmResult(CalculatedFieldId calculatedFieldId) { + List debugEvents = getDebugEvents(calculatedFieldId, 1); + if (debugEvents.isEmpty()) { + return null; + } + CalculatedFieldDebugEvent debugEvent = debugEvents.get(0); + if (debugEvent.getError() != null) { + throw new RuntimeException(debugEvent.getError()); + } + if (debugEvent.getId().equals(latestEventId)) { + return null; + } + latestEventId = debugEvent.getId(); + return JacksonUtil.fromString(debugEvent.getResult(), TbAlarmResult.class); + } + + private CalculatedField createAlarmCf(EntityId entityId, + String alarmType, + Map arguments, + Map createConditions, + Condition clearCondition, + Consumer... modifier) { + Map createRules = new HashMap<>(); + createConditions.forEach((severity, condition) -> { + createRules.put(severity, toAlarmRule(condition)); + }); + AlarmRule clearRule = clearCondition != null ? toAlarmRule(clearCondition) : null; + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(entityId); + calculatedField.setName(alarmType); + calculatedField.setType(CalculatedFieldType.ALARM); + AlarmCalculatedFieldConfiguration configuration = new AlarmCalculatedFieldConfiguration(); + configuration.setArguments(arguments); + configuration.setCreateRules(createRules); + configuration.setClearRule(clearRule); + calculatedField.setConfiguration(configuration); + calculatedField.setDebugSettings(DebugSettings.all()); + if (modifier.length > 0) { + modifier[0].accept(configuration); + } + CalculatedField savedCalculatedField = saveCalculatedField(calculatedField); + + CalculatedFieldDebugEvent debugEvent = await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> getDebugEvents(savedCalculatedField.getId(), 1), + events -> !events.isEmpty()).get(0); + latestEventId = debugEvent.getId(); + return savedCalculatedField; + } + + private AlarmRule toAlarmRule(Condition condition) { + AlarmRule rule = new AlarmRule(); + AlarmConditionExpression expression; + if (condition.getTbelExpression() != null) { + TbelAlarmConditionExpression tbelExpression = new TbelAlarmConditionExpression(); + tbelExpression.setExpression(condition.getTbelExpression()); + expression = tbelExpression; + } else { + expression = condition.getSimpleExpression(); + } + if (condition.getEventsCount() != null) { + RepeatingAlarmCondition alarmCondition = new RepeatingAlarmCondition(); + alarmCondition.setExpression(expression); + alarmCondition.setCount(condition.getEventsCount()); + rule.setCondition(alarmCondition); + } else if (condition.getDuration() != null) { + DurationAlarmCondition alarmCondition = new DurationAlarmCondition(); + alarmCondition.setExpression(expression); + alarmCondition.setUnit(TimeUnit.MILLISECONDS); + alarmCondition.setValue(condition.getDuration()); + rule.setCondition(alarmCondition); + } else { + SimpleAlarmCondition alarmCondition = new SimpleAlarmCondition(); + alarmCondition.setExpression(expression); + rule.setCondition(alarmCondition); + } + if (condition.getSchedule() != null) { + rule.getCondition().setSchedule(condition.getSchedule()); + } + return rule; + } + + private SimpleAlarmConditionExpression createSimpleExpression(String argument, StringOperation stringOperation, AlarmConditionValue conditionValue) { + SimpleAlarmConditionExpression simpleExpression = new SimpleAlarmConditionExpression(); + AlarmConditionFilter filter = new AlarmConditionFilter(); + filter.setArgument(argument); + filter.setValueType(EntityKeyValueType.STRING); + StringFilterPredicate predicate = new StringFilterPredicate(); + predicate.setOperation(stringOperation); + predicate.setValue(conditionValue); + filter.setPredicates(List.of(predicate)); + simpleExpression.setFilters(List.of(filter)); + return simpleExpression; + } + + private List getDebugEvents(CalculatedFieldId calculatedFieldId, int limit) { + return eventDao.findLatestEvents(tenantId.getId(), calculatedFieldId.getId(), EventType.DEBUG_CALCULATED_FIELD, limit).stream() + .map(e -> (CalculatedFieldDebugEvent) e).toList(); + } + + @Getter + @AllArgsConstructor + private static final class Condition { + + private final String tbelExpression; + private final SimpleAlarmConditionExpression simpleExpression; + private AlarmConditionValue eventsCount; + private AlarmConditionValue duration; + private AlarmConditionValue schedule; + + private Condition(String tbelExpression, Integer eventsCount, Long durationMs) { + this.tbelExpression = tbelExpression; + this.simpleExpression = null; + if (eventsCount != null) { + this.eventsCount = new AlarmConditionValue<>(eventsCount, null); + } + if (durationMs != null) { + this.duration = new AlarmConditionValue<>(durationMs, null); + } + } + + private Condition(SimpleAlarmConditionExpression simpleExpression, Integer eventsCount, Long durationMs) { + this.tbelExpression = null; + this.simpleExpression = simpleExpression; + if (eventsCount != null) { + this.eventsCount = new AlarmConditionValue<>(eventsCount, null); + } + if (durationMs != null) { + this.duration = new AlarmConditionValue<>(durationMs, null); + } + } + + } + +} diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldCurrentOwnerTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldCurrentOwnerTest.java new file mode 100644 index 0000000000..52180a888d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldCurrentOwnerTest.java @@ -0,0 +1,198 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cf; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.Test; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.CurrentOwnerDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +public class CalculatedFieldCurrentOwnerTest extends AbstractControllerTest { + + public static final int TIMEOUT = 60; + public static final int POLL_INTERVAL = 1; + + @Test + public void testCreateCFWithCurrentOwner() throws Exception { + loginTenantAdmin(); + + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"attrKey\":5}"); + + Device testDevice = createDevice("Test device", "1234567890"); + + doPost("/api/customer/" + customerId.getId() + "/device/" + testDevice.getId().getId()).andExpect(status().isOk()); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", buildCalculatedField(testDevice.getId()), CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "result"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("result").get(0).get("value").asText()).isEqualTo("105"); + }); + + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"attrKey\":10}"); + + await().alias("update telemetry -> perform calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "result"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("result").get(0).get("value").asText()).isEqualTo("110"); + }); + } + + @Test + public void testChangeOwner() throws Exception { + loginSysAdmin(); + + postAttributes(tenantId, AttributeScope.SERVER_SCOPE, "{\"attrKey\":50}"); + + loginTenantAdmin(); + + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"attrKey\":5}"); + Device testDevice = createDevice("Test device", "1234567890"); + doPost("/api/customer/" + customerId.getId() + "/device/" + testDevice.getId().getId()).andExpect(status().isOk()); + + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", buildCalculatedField(testDevice.getId()), CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "result"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("result").get(0).get("value").asText()).isEqualTo("105"); + }); + + doDelete("/api/customer/device/" + testDevice.getId().getId()).andExpect(status().isOk()); + + await().alias("change owner -> perform calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "result"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("result").get(0).get("value").asText()).isEqualTo("150"); + }); + } + + @Test + public void testCreateCFWithCurrentOwnerWhenEntityIsProfile() throws Exception { + loginSysAdmin(); + + postAttributes(tenantId, AttributeScope.SERVER_SCOPE, "{\"attrKey\":50}"); + + loginTenantAdmin(); + + postAttributes(customerId, AttributeScope.SERVER_SCOPE, "{\"attrKey\":5}"); + + AssetProfile assetProfile = doPost("/api/assetProfile", createAssetProfile("Test Asset Profile"), AssetProfile.class); + + Asset asset1 = createAsset("Test asset 1", assetProfile.getId()); + doPost("/api/customer/" + customerId.getId() + "/asset/" + asset1.getId().getId()).andExpect(status().isOk()); + + Asset asset2 = createAsset("Test asset 2", assetProfile.getId()); // owner - TENANT + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", buildCalculatedField(assetProfile.getId()), CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + // result of asset 1 + ObjectNode result1 = getLatestTelemetry(asset1.getId(), "result"); + assertThat(result1).isNotNull(); + assertThat(result1.get("result").get(0).get("value").asText()).isEqualTo("105"); + + // result of asset 2 + ObjectNode result2 = getLatestTelemetry(asset2.getId(), "result"); + assertThat(result2).isNotNull(); + assertThat(result2.get("result").get(0).get("value").asText()).isEqualTo("150"); + }); + + doPost("/api/customer/" + customerId.getId() + "/asset/" + asset2.getId().getId()).andExpect(status().isOk()); + + await().alias("change asset2 owner -> recalculate state for asset 2").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + // result of asset 2 + ObjectNode result2 = getLatestTelemetry(asset2.getId(), "result"); + assertThat(result2).isNotNull(); + assertThat(result2.get("result").get(0).get("value").asText()).isEqualTo("105"); + }); + } + + private CalculatedField buildCalculatedField(EntityId entityId) { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(entityId); + calculatedField.setType(CalculatedFieldType.SIMPLE); + calculatedField.setName("Test Calculated Field"); + + SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); + + Argument argument = new Argument(); + ReferencedEntityKey refEntityKey = new ReferencedEntityKey("attrKey", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE); + argument.setRefEntityKey(refEntityKey); + argument.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + + config.setArguments(Map.of("a", argument)); + + config.setExpression("a + 100"); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setName("result"); + output.setDecimalsByDefault(0); + + config.setOutput(output); + + calculatedField.setConfiguration(config); + return calculatedField; + } + + private ObjectNode getLatestTelemetry(EntityId entityId, String... keys) throws Exception { + return doGetAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + "/values/timeseries?keys=" + String.join(",", keys), ObjectNode.class); + } + + private Asset createAsset(String name, AssetProfileId assetProfileId) { + Asset asset = new Asset(); + asset.setName(name); + asset.setAssetProfileId(assetProfileId); + return doPost("/api/asset", asset, Asset.class); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index 7f905ec0ae..ba7d11fee9 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -15,40 +15,58 @@ */ package org.thingsboard.server.cf; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Test; -import org.junit.jupiter.api.BeforeEach; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EventInfo; +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.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; +import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesImmediateOutputStrategy; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import org.thingsboard.server.controller.CalculatedFieldControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; @DaoSqlTest public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTest { @@ -56,16 +74,11 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes public static final int TIMEOUT = 60; public static final int POLL_INTERVAL = 1; - @BeforeEach - void setUp() throws Exception { - loginTenantAdmin(); - } - @Test public void testSimpleCalculatedFieldWhenAllTelemetryPresent() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":25}")); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"deviceTemperature\":40}")); + postTelemetry(testDevice.getId(), "{\"temperature\":25}"); + postAttributes(testDevice.getId(), AttributeScope.SERVER_SCOPE, "{\"deviceTemperature\":40}"); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -83,13 +96,11 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("T", argument)); config.setExpression("(T * 9/5) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); calculatedField.setConfiguration(config); - calculatedField.setVersion(1L); CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); @@ -101,7 +112,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("77.0"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); + postTelemetry(testDevice.getId(), "{\"temperature\":30}"); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -111,10 +122,12 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("86.0"); }); - Output savedOutput = savedCalculatedField.getConfiguration().getOutput(); - savedOutput.setType(OutputType.ATTRIBUTES); - savedOutput.setScope(AttributeScope.SERVER_SCOPE); - savedOutput.setName("temperatureF"); + AttributesOutput newOutput = new AttributesOutput(); + newOutput.setScope(AttributeScope.SERVER_SCOPE); + newOutput.setName("temperatureF"); + config.setOutput(newOutput); + savedCalculatedField.setConfiguration(config); + savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); await().alias("update CF output -> perform calculation with updated output").atMost(TIMEOUT, TimeUnit.SECONDS) @@ -122,10 +135,11 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes .untilAsserted(() -> { ArrayNode temperatureF = getServerAttributes(testDevice.getId(), "temperatureF"); assertThat(temperatureF).isNotNull(); + assertThat(temperatureF.get(0)).isNotNull(); assertThat(temperatureF.get(0).get("value").asText()).isEqualTo("86.0"); }); - Argument savedArgument = savedCalculatedField.getConfiguration().getArguments().get("T"); + Argument savedArgument = ((SimpleCalculatedFieldConfiguration) savedCalculatedField.getConfiguration()).getArguments().get("T"); savedArgument.setRefEntityKey(new ReferencedEntityKey("deviceTemperature", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); @@ -137,7 +151,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(temperatureF.get(0).get("value").asText()).isEqualTo("104.0"); }); - savedCalculatedField.getConfiguration().setExpression("1.8 * T + 32"); + ((SimpleCalculatedFieldConfiguration) savedCalculatedField.getConfiguration()).setExpression("1.8 * T + 32"); savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); await().alias("update CF expression -> perform calculation with new expression").atMost(TIMEOUT, TimeUnit.SECONDS) @@ -168,9 +182,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("T", argument)); config.setExpression("(T * 9/5) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); calculatedField.setConfiguration(config); @@ -186,7 +199,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").isNull()).isTrue(); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); + postTelemetry(testDevice.getId(), "{\"temperature\":30}"); await().alias("update telemetry -> perform calculation").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -217,9 +230,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("T", argument)); config.setExpression("(T * 9/5) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); calculatedField.setConfiguration(config); @@ -235,7 +247,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("53.6"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); + postTelemetry(testDevice.getId(), "{\"temperature\":30}"); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -249,15 +261,15 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes @Test public void testSimpleCalculatedFieldWhenEntityIdIsProfile() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"x\":40}")); + postAttributes(testDevice.getId(), AttributeScope.SERVER_SCOPE, "{\"x\":40}"); AssetProfile assetProfile = doPost("/api/assetProfile", createAssetProfile("Test Asset Profile"), AssetProfile.class); Asset asset1 = createAsset("Test asset 1", assetProfile.getId()); - doPost("/api/plugins/telemetry/ASSET/" + asset1.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":11}")); + postAttributes(asset1.getId(), AttributeScope.SERVER_SCOPE, "{\"y\":11}"); Asset asset2 = createAsset("Test asset 2", assetProfile.getId()); - doPost("/api/plugins/telemetry/ASSET/" + asset2.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":12}")); + postAttributes(asset2.getId(), AttributeScope.SERVER_SCOPE, "{\"y\":12}"); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(assetProfile.getId()); @@ -281,9 +293,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setExpression("x + y"); - Output output = new Output(); + AttributesOutput output = new AttributesOutput(); output.setName("z"); - output.setType(OutputType.ATTRIBUTES); output.setScope(AttributeScope.SERVER_SCOPE); config.setOutput(output); @@ -307,7 +318,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(z2.get(0).get("value").asText()).isEqualTo("52.0"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"x\":25}")); + postAttributes(testDevice.getId(), AttributeScope.SERVER_SCOPE, "{\"x\":25}"); await().alias("update device telemetry -> recalculate state for all assets").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -323,7 +334,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(z2.get(0).get("value").asText()).isEqualTo("37.0"); }); - doPost("/api/plugins/telemetry/ASSET/" + asset1.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":15}")); + postAttributes(asset1.getId(), AttributeScope.SERVER_SCOPE, "{\"y\":15}"); await().alias("update asset 1 telemetry -> recalculate state only for asset 1").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -339,7 +350,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(z2.get(0).get("value").asText()).isEqualTo("37.0"); }); - doPost("/api/plugins/telemetry/ASSET/" + asset2.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":5}")); + postAttributes(asset2.getId(), AttributeScope.SERVER_SCOPE, "{\"y\":5}"); await().alias("update asset 2 telemetry -> recalculate state only for asset 2").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -356,7 +367,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); Asset asset3 = createAsset("Test asset 3", assetProfile.getId()); - doPost("/api/plugins/telemetry/ASSET/" + asset3.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":13}")); + postAttributes(asset3.getId(), AttributeScope.SERVER_SCOPE, "{\"y\":13}"); Asset finalAsset3 = asset3; await().alias("add new entity to profile -> calculate state for new entity").atMost(TIMEOUT, TimeUnit.SECONDS) @@ -368,7 +379,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(z3.get(0).get("value").asText()).isEqualTo("38.0"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"x\":20}")); + postAttributes(testDevice.getId(), AttributeScope.SERVER_SCOPE, "{\"x\":20}"); await().alias("update device telemetry -> recalculate state for all assets").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -394,7 +405,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes asset3.setAssetProfileId(newAssetProfile.getId()); asset3 = doPost("/api/asset", asset3, Asset.class); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"x\":15}")); + postAttributes(testDevice.getId(), AttributeScope.SERVER_SCOPE, "{\"x\":15}"); Asset updatedAsset3 = asset3; await().alias("update device telemetry -> recalculate state for asset 1 and asset 2").atMost(TIMEOUT, TimeUnit.SECONDS) @@ -420,7 +431,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes @Test public void testSimpleCalculatedFieldWhenExpressionIsInvalid() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":25}")); + postTelemetry(testDevice.getId(), "{\"temperature\":25}"); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -438,9 +449,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("T", argument)); config.setExpression("(T * 9/0) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); calculatedField.setConfiguration(config); @@ -456,7 +466,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").isNull()).isTrue(); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); + postTelemetry(testDevice.getId(), "{\"temperature\":30}"); await().alias("update telemetry -> ctx is not initialized -> no calculation perform").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -471,7 +481,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes public void testSimpleCalculatedFieldWhenUseLatestTsIsTrue() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); long ts = System.currentTimeMillis() - 300000L; - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts)); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -488,9 +498,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("T", argument)); config.setExpression("(T * 9/5) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); config.setUseLatestTs(true); @@ -515,10 +524,10 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes long ts = System.currentTimeMillis(); long tsA = ts - 300000L; - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":1}}", tsA))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"a\":1}}", tsA)); long tsB = ts - 300L; - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"b\":5}}", tsB))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"b\":5}}", tsB)); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -538,9 +547,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("a", argument1, "b", argument2)); config.setExpression("a + b"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("c"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); config.setUseLatestTs(true); @@ -559,7 +567,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); long tsABeforeTsB = tsB - 300L; - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":10}}", tsABeforeTsB))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"a\":10}}", tsABeforeTsB)); await().alias("update telemetry with ts less than latest -> save result with latest ts").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -576,7 +584,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes Device testDevice = createDevice("Test device", "1234567890"); long ts = System.currentTimeMillis() - 300000L; - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts)); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -593,9 +601,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("T", argument)); config.setExpression("return {\"ts\": ctx.latestTs, \"values\": {\"fahrenheitTemp\": (T * 1.8) + 32}};"); - Output output = new Output(); - output.setType(OutputType.TIME_SERIES); - config.setOutput(output); + config.setOutput(new TimeSeriesOutput()); calculatedField.setConfiguration(config); @@ -639,9 +645,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("a", argument1, "b", argument2, "c", argument3)); config.setExpression("a + b + c"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("d"); - output.setType(OutputType.TIME_SERIES); output.setDecimalsByDefault(0); config.setOutput(output); @@ -659,7 +664,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(d.get("d").get(0).get("value").asText()).isEqualTo("600"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"a\":10}")); + postTelemetry(testDevice.getId(), "{\"a\":10}"); await().alias("update telemetry -> save result with ts of 'a' argument").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -671,8 +676,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(keys.get("d").get(0).get("value").asText()).isEqualTo("510"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"b\":20}")); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"c\":30}")); + postTelemetry(testDevice.getId(), "{\"b\":20}"); + postTelemetry(testDevice.getId(), "{\"c\":30}"); await().alias("update telemetry -> save result with latest ts of updated arguments").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -714,9 +719,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("m", argument)); config.setExpression("m + 1"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("m1"); - output.setType(OutputType.TIME_SERIES); output.setDecimalsByDefault(0); config.setOutput(output); @@ -724,7 +728,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes calculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"m\":1}")); + postTelemetry(testDevice.getId(), "{\"m\":1}"); await().alias("create CF -> ctx is initialized -> perform calculation").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -738,7 +742,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes calculatedField.setConfiguration(config); calculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"m\":2}")); + postTelemetry(testDevice.getId(), "{\"m\":2}"); await().alias("update CF -> ctx is not initialized -> no calculation performed").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -749,10 +753,483 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); } + @Test + public void testGeofencingCalculatedField_withZonesCreatedOnDevice() throws Exception { + // --- Arrange entities --- + Device device = createDevice("GF Test Device", "sn-geo-2"); + + // Allowed zone polygon (square) + String allowedPolygon = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"; + // Restricted zone polygon (square) + String restrictedPolygon = "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"; + + postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, "{\"allowedZone\":" + allowedPolygon + "}"); + postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, "{\"restrictedZone\":" + restrictedPolygon + "}"); + + // Initial device coordinates (inside Allowed, outside Restricted) + postTelemetry(device.getId(), "{\"latitude\":50.4730,\"longitude\":30.5050}"); + + // --- Build CF: GEOFENCING --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getDeviceProfileId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("Geofencing CF"); + cf.setDebugSettings(DebugSettings.off()); + + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + + // Coordinates: TS_LATEST on the device + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + cfg.setEntityCoordinates(entityCoordinates); + + // Zone groups: ATTRIBUTE on the device + ZoneGroupConfiguration allowedZonesGroup = new ZoneGroupConfiguration("allowedZone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + ZoneGroupConfiguration restrictedZonesGroup = new ZoneGroupConfiguration("restrictedZone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + + cfg.setZoneGroups(Map.of("allowedZones", allowedZonesGroup, "restrictedZones", restrictedZonesGroup)); + + // Output to server attributes + AttributesOutput out = new AttributesOutput(); + out.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(out); + + cf.setConfiguration(cfg); + + doPost("/api/calculatedField", cf, CalculatedField.class); + + // --- Assert initial evaluation --- + await().alias("initial geofencing evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), + "allowedZonesEvent", "allowedZonesStatus", "restrictedZonesStatus", "restrictedZonesEvent"); + // --- no transition events as no transitions happened yet + assertThat(attrs).isNotNull().isNotEmpty().hasSize(2); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesStatus", "INSIDE") + .containsEntry("restrictedZonesStatus", "OUTSIDE"); + }); + + // --- delete attributes reported in previous evaluation + doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/SERVER_SCOPE?keys=allowedZonesStatus,restrictedZonesStatus", String.class); + + // --- Update restrictedZone by 'restrictedZone' attribute update + postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, "{\"restrictedZone\":" + restrictedPolygon + "}"); + + // --- Assert no transition --- + // --- Assert attributes updated with the same values for restrictedZones --- + // --- Assert attributes updated with the new values for allowedZones --- + await().alias("evaluation after version bump of geo argument") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), + "allowedZonesEvent", "allowedZonesStatus", + "restrictedZonesEvent", "restrictedZonesStatus"); + assertThat(attrs).isNotNull().isNotEmpty().hasSize(2); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesStatus", "INSIDE") + .containsEntry("restrictedZonesStatus", "OUTSIDE"); + }); + } + + @Test + public void testGeofencingCalculatedField_withoutRelationsCreationAndDynamicRefresh() throws Exception { + // --- Arrange entities --- + Device device = createDevice("GF Device", "sn-geo-1"); + + // Allowed zone polygon (square) + String allowedPolygon = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"; + // Restricted zone polygon (square) + String restrictedPolygon = "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"; + + Asset allowedZoneAsset = createAsset("Allowed Zone", null); + postAttributes(allowedZoneAsset.getId(), AttributeScope.SERVER_SCOPE, "{\"zone\":" + allowedPolygon + "}"); + + Asset restrictedZoneAsset = createAsset("Restricted Zone", null); + postAttributes(restrictedZoneAsset.getId(), AttributeScope.SERVER_SCOPE, "{\"zone\":" + restrictedPolygon + "}"); + + // Relations from device to zones + createEntityRelation(device.getId(), allowedZoneAsset.getId(), "AllowedZone"); + createEntityRelation(device.getId(), restrictedZoneAsset.getId(), "RestrictedZone"); + + // Initial device coordinates (inside Allowed, outside Restricted) + postTelemetry(device.getId(), "{\"latitude\":50.4730,\"longitude\":30.5050}"); + + // --- Build CF: GEOFENCING --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("Geofencing CF"); + cf.setDebugSettings(DebugSettings.off()); + + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + + // Coordinates: TS_LATEST on the device + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + cfg.setEntityCoordinates(entityCoordinates); + + // Zone groups: ATTRIBUTE on specific assets (one zone per group) + ZoneGroupConfiguration allowedZonesGroup = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var allowedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "AllowedZone"))); + allowedZonesGroup.setRefDynamicSourceConfiguration(allowedZoneDynamicSourceConfiguration); + + ZoneGroupConfiguration restrictedZonesGroup = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var restrictedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + restrictedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "RestrictedZone"))); + restrictedZonesGroup.setRefDynamicSourceConfiguration(restrictedZoneDynamicSourceConfiguration); + + cfg.setZoneGroups(Map.of("allowedZones", allowedZonesGroup, "restrictedZones", restrictedZonesGroup)); + + // Output to server attributes + AttributesOutput out = new AttributesOutput(); + out.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(out); + + cf.setConfiguration(cfg); + + doPost("/api/calculatedField", cf, CalculatedField.class); + + // --- Assert initial evaluation --- + await().alias("initial geofencing evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), + "allowedZonesEvent", "allowedZonesStatus", "restrictedZonesStatus", "restrictedZonesEvent"); + assertThat(attrs).isNotNull().isNotEmpty().hasSize(2); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesStatus", "INSIDE") + .containsEntry("restrictedZonesStatus", "OUTSIDE"); + }); + + // --- Move the device into Restricted zone (and outside Allowed) --- + postTelemetry(device.getId(), "{\"latitude\":50.4760,\"longitude\":30.5110}"); + + // --- Assert transition (LEFT / ENTERED) --- + await().alias("transition evaluation after movement") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), + "allowedZonesEvent", "allowedZonesStatus", + "restrictedZonesEvent", "restrictedZonesStatus"); + assertThat(attrs).isNotNull().isNotEmpty().hasSize(4); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesEvent", "LEFT") + .containsEntry("restrictedZonesEvent", "ENTERED") + .containsEntry("allowedZonesStatus", "OUTSIDE") + .containsEntry("restrictedZonesStatus", "INSIDE"); + }); + } + + @Test + public void testGeofencingCalculatedField_DynamicRefresh_RebindsZoneArguments() throws Exception { + // --- Update min allowed scheduled update intervals for CFs --- + loginSysAdmin(); + EntityInfo tenantProfileEntityInfo = doGet("/api/tenantProfileInfo/default", EntityInfo.class); + assertThat(tenantProfileEntityInfo).isNotNull(); + TenantProfile foundTenantProfile = doGet("/api/tenantProfile/" + tenantProfileEntityInfo.getId().getId().toString(), TenantProfile.class); + assertThat(foundTenantProfile).isNotNull(); + assertThat(foundTenantProfile.getDefaultProfileConfiguration()).isNotNull(); + int minAllowedScheduledUpdateIntervalInSecForCF = TIMEOUT / 10; + foundTenantProfile.getDefaultProfileConfiguration().setMinAllowedScheduledUpdateIntervalInSecForCF(minAllowedScheduledUpdateIntervalInSecForCF); + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", foundTenantProfile, TenantProfile.class); + assertThat(savedTenantProfile).isNotNull(); + assertThat(savedTenantProfile.getDefaultProfileConfiguration().getMinAllowedScheduledUpdateIntervalInSecForCF()).isEqualTo(minAllowedScheduledUpdateIntervalInSecForCF); + loginTenantAdmin(); + + // --- Arrange entities --- + Device device = createDevice("GF Device dyn", "sn-geo-dyn-1"); + + // Allowed Zone A: covers initial point (ENTERED) + String allowedPolygonA = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"; + + Asset allowedZoneA = createAsset("Allowed Zone A", null); + postAttributes(allowedZoneA.getId(), AttributeScope.SERVER_SCOPE, "{\"zone\":" + allowedPolygonA + "}"); + + // Relation from device to Allowed Zone A + createEntityRelation(device.getId(), allowedZoneA.getId(), "AllowedZone"); + + // Initial device coordinates: INSIDE Zone A + postTelemetry(device.getId(), "{\"latitude\":50.4730,\"longitude\":30.5050}"); + + // --- Build CF: GEOFENCING with dynamic 'allowedZones' and short scheduled refresh --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("Geofencing CF (dynamic refresh)"); + cf.setDebugSettings(DebugSettings.off()); + + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + cfg.setEntityCoordinates(new EntityCoordinates(ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY)); + + var allowedZonesGroup = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var allowedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "AllowedZone"))); + allowedZonesGroup.setRefDynamicSourceConfiguration(allowedZoneDynamicSourceConfiguration); + cfg.setZoneGroups(Map.of("allowedZones", allowedZonesGroup)); + + // Server attributes output + AttributesOutput out = new AttributesOutput(); + out.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(out); + + // Enable scheduled refresh with a 6-second interval + cfg.setScheduledUpdateInterval(minAllowedScheduledUpdateIntervalInSecForCF); + cfg.setScheduledUpdateEnabled(true); + + cf.setConfiguration(cfg); + CalculatedField savedCalculatedField = doPost("/api/calculatedField", cf, CalculatedField.class); + assertThat(savedCalculatedField).isNotNull(); + CalculatedFieldConfiguration configuration = savedCalculatedField.getConfiguration(); + assertThat(configuration).isInstanceOf(GeofencingCalculatedFieldConfiguration.class); + var geofencingConfiguration = (GeofencingCalculatedFieldConfiguration) configuration; + assertThat(geofencingConfiguration.isScheduledUpdateEnabled()).isTrue(); + + // --- Assert initial evaluation --- + await().alias("initial geofencing evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), "allowedZonesEvent", "allowedZonesStatus"); + assertThat(attrs).isNotNull().isNotEmpty().hasSize(1); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesStatus", "INSIDE"); + }); + + // --- Move device OUTSIDE Zone A (expect LEFT) --- + postTelemetry(device.getId(), "{\"latitude\":50.4760,\"longitude\":30.5110}"); + + await().alias("outside zone A (LEFT)") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), "allowedZonesEvent", "allowedZonesStatus"); + assertThat(attrs).isNotNull().isNotEmpty().hasSize(2); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesEvent", "LEFT") + .containsEntry("allowedZonesStatus", "OUTSIDE"); + }); + + // --- Create Allowed Zone B covering the CURRENT location --- + String allowedPolygonB = "[[50.475500, 30.510500], [50.475500, 30.511500], [50.476500, 30.511500], [50.476500, 30.510500]]"; + + Asset allowedZoneB = createAsset("Allowed Zone B", null); + postAttributes(allowedZoneB.getId(), AttributeScope.SERVER_SCOPE, "{\"zone\":" + allowedPolygonB + "}"); + + // Add a new relation + createEntityRelation(device.getId(), allowedZoneB.getId(), "AllowedZone"); + + awaitForCalculatedFieldEntityMessageProcessorToRegisterCfStateAsReadyToRefreshDynamicArguments(device.getId(), savedCalculatedField.getId(), minAllowedScheduledUpdateIntervalInSecForCF); + + // --- Same coordinates as before, but now we expect INSIDE group status since a new zone is registered --- + postTelemetry(device.getId(), "{\"latitude\":50.4760,\"longitude\":30.5110}"); + + // --- Assert dynamic refresh picks up a new relation and flips status back to INSIDE on the next telemetry update --- + await().alias("dynamic refresh rebinds allowedZones") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = getServerAttributes(device.getId(), "allowedZonesEvent", "allowedZonesStatus"); + assertThat(attrs).isNotNull().isNotEmpty().hasSize(2); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesEvent", "LEFT") // attribute from previous eval with outdated ts. + .containsEntry("allowedZonesStatus", "INSIDE"); + }); + } + + @Test + public void testPropagationCalculatedField_withExpression() throws Exception { + // --- Arrange entities --- + Device device = createDevice("Propagation Device With Expression", "sn-prop-1"); + Asset asset1 = createAsset("Propagated Asset 1", null); + Asset asset2 = createAsset("Propagated Asset 2", null); + + // Create relations FROM assets TO device + createEntityRelation(asset1.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + createEntityRelation(asset2.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + + // Telemetry on device + postTelemetry(device.getId(), "{\"temperature\":12.5, \"humidity\":85}"); + + // --- Build CF: PROPAGATION with expression --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.PROPAGATION); + cf.setName("Propagation CF (expr)"); + cf.setConfigurationVersion(1); + + PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + cfg.setApplyExpressionToResolvedArguments(true); + + Argument arg1 = new Argument(); + arg1.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + + Argument arg2 = new Argument(); + arg2.setRefEntityKey(new ReferencedEntityKey("humidity", ArgumentType.TS_LATEST, null)); + + cfg.setArguments(Map.of("t", arg1, "h", arg2)); + cfg.setExpression("return { testResult: (t + h) / 2};"); + + AttributesOutput output = new AttributesOutput(); + output.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(output); + + cf.setConfiguration(cfg); + + doPost("/api/calculatedField", cf, CalculatedField.class); + + // --- Assert propagated calculation (expression applied) --- + await().alias("propagation expr mode evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs1 = getServerAttributes(asset1.getId(), "testResult"); + ArrayNode attrs2 = getServerAttributes(asset2.getId(), "testResult"); + assertThat(attrs1).isNotNull(); + assertThat(attrs2).isNotNull(); + assertThat(attrs1.get(0).get("value").asDouble()).isEqualTo(48.75); + assertThat(attrs2.get(0).get("value").asDouble()).isEqualTo(48.75); + }); + + String deleteUrl = String.format("/api/v2/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", + asset1.getId().getId(), EntityType.ASSET, + EntityRelation.CONTAINS_TYPE, device.getId().getId(), EntityType.DEVICE + ); + doDelete(deleteUrl).andExpect(status().isOk()); + doDelete("/api/plugins/telemetry/ASSET/" + asset1.getId() + "/SERVER_SCOPE?keys=testResult").andExpect(status().isOk()); + + postTelemetry(device.getId(), "{\"temperature\":25}"); + + // --- Assert propagated calculation (expression applied with new temperature argument and one relation removed) --- + await().alias("propagation expr mode evaluation after temperature update") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs1 = getServerAttributes(asset1.getId(), "testResult"); + ArrayNode attrs2 = getServerAttributes(asset2.getId(), "testResult"); + assertThat(attrs1).isNullOrEmpty(); + assertThat(attrs2).isNotNull(); + assertThat(attrs2.get(0).get("value").asDouble()).isEqualTo(55); + }); + } + + @Test + public void testPropagationCalculatedField_withoutExpression() throws Exception { + // --- Arrange entities --- + Device device = createDevice("Propagation Device Without Expression", "sn-prop-2"); + Asset asset1 = createAsset("Propagated Asset 1", null); + Asset asset2 = createAsset("Propagated Asset 2", null); + + // Create relations FROM assets TO device + createEntityRelation(asset1.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + createEntityRelation(asset2.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + + // Telemetry on device + long ts = System.currentTimeMillis() - 300000L; + postTelemetry(device.getId(), String.format("{\"ts\": %s, \"values\": {\"temperature\":12.5, \"humidity\":85}}", ts)); + + // --- Build CF: PROPAGATION without expression --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.PROPAGATION); + cf.setName("Propagation CF (args-only)"); + cf.setConfigurationVersion(1); + + PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + cfg.setApplyExpressionToResolvedArguments(false); // arguments-only mode + + Argument arg1 = new Argument(); + arg1.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + Argument arg2 = new Argument(); + arg2.setRefEntityKey(new ReferencedEntityKey("humidity", ArgumentType.TS_LATEST, null)); + + cfg.setArguments(Map.of("temperatureComputed", arg1, "humidityComputed", arg2)); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setStrategy(new TimeSeriesImmediateOutputStrategy(0, true, true, true, true)); + cfg.setOutput(output); + + cf.setConfiguration(cfg); + + doPost("/api/calculatedField", cf, CalculatedField.class); + + // --- Assert propagated calculation (arguments-only mode) --- + await().alias("propagation args-only evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode telemetry1 = getLatestTelemetry(asset1.getId(), "temperatureComputed,humidityComputed"); + ObjectNode telemetry2 = getLatestTelemetry(asset2.getId(), "temperatureComputed,humidityComputed"); + assertThat(telemetry1).isNotNull(); + assertThat(telemetry2).isNotNull(); + assertThat(telemetry1.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(telemetry1.get("temperatureComputed").get(0).get("value").asDouble()).isEqualTo(12.5); + assertThat(telemetry1.get("humidityComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(telemetry1.get("humidityComputed").get(0).get("value").asDouble()).isEqualTo(85); + assertThat(telemetry2.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(telemetry2.get("temperatureComputed").get(0).get("value").asDouble()).isEqualTo(12.5); + assertThat(telemetry2.get("humidityComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(telemetry2.get("humidityComputed").get(0).get("value").asDouble()).isEqualTo(85); + }); + + String deleteUrl = String.format("/api/v2/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", + asset1.getId().getId(), EntityType.ASSET, + EntityRelation.CONTAINS_TYPE, device.getId().getId(), EntityType.DEVICE + ); + doDelete(deleteUrl).andExpect(status().isOk()); + doDelete("/api/plugins/telemetry/ASSET/" + asset1.getId() + "/timeseries/delete?keys=temperatureComputed,humidityComputed&deleteAllDataForKeys=true").andExpect(status().isOk()); + + // Update telemetry on the device + long newTs = ts + 300000L; + postTelemetry(device.getId(), String.format("{\"ts\": %s, \"values\": {\"temperature\":25}}", newTs)); + + // --- Assert propagated calculation (arguments-only mode after update) --- + await().alias("propagation args-only evaluation after temperature update") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode telemetry1 = getLatestTelemetry(asset1.getId(), "temperatureComputed,humidityComputed"); + ObjectNode telemetry2 = getLatestTelemetry(asset2.getId(), "temperatureComputed,humidityComputed"); + assertThat(telemetry1).isNotNull(); + assertThat(telemetry2).isNotNull(); + assertThat(telemetry1.get("temperatureComputed").get(0).get("value")).isEqualTo(NullNode.instance); + assertThat(telemetry1.get("humidityComputed").get(0).get("value")).isEqualTo(NullNode.instance); + + assertThat(telemetry2.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(newTs)); + assertThat(telemetry2.get("temperatureComputed").get(0).get("value").asDouble()).isEqualTo(25); + // TS for humidity is not updated -> expected + assertThat(telemetry2.get("humidityComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(telemetry2.get("humidityComputed").get(0).get("value").asDouble()).isEqualTo(85); + }); + + Asset asset3 = createAsset("Propagated Asset 3", null); + createEntityRelation(asset3.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + + // --- Assert propagated calculation (arguments-only mode after update) --- + await().alias("propagation args-only to new entity after relation creation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode telemetry = getLatestTelemetry(asset3.getId(), "temperatureComputed,humidityComputed"); + assertThat(telemetry).isNotNull(); + assertThat(telemetry.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(newTs)); + assertThat(telemetry.get("temperatureComputed").get(0).get("value").asDouble()).isEqualTo(25); + assertThat(telemetry.get("humidityComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(newTs)); + assertThat(telemetry.get("humidityComputed").get(0).get("value").asDouble()).isEqualTo(85); + }); + + } + @Test public void testCalculatedFieldWhenTheSameTelemetryKeysUsed() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"a\":5}")); + postTelemetry(testDevice.getId(), "{\"a\":5}"); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -770,9 +1247,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes config.setArguments(Map.of("a", argumentA, "b", argumentB)); config.setExpression("a + b"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("c"); - output.setType(OutputType.TIME_SERIES); output.setDecimalsByDefault(0); config.setOutput(output); @@ -788,7 +1264,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(c.get("c").get(0).get("value").asText()).isEqualTo("10"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"a\":10}")); + postTelemetry(testDevice.getId(), "{\"a\":10}"); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -803,8 +1279,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes public void testCalculatedFieldWhenBatchOfTelemetrySent() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); long now = System.currentTimeMillis(); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":5, \"b\":10}}", now - TimeUnit.MINUTES.toMillis(3)))); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"b\":20}}", now - TimeUnit.MINUTES.toMillis(1)))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"a\":5, \"b\":10}}", now - TimeUnit.MINUTES.toMillis(3))); + postTelemetry(testDevice.getId(), String.format("{\"ts\": %s, \"values\": {\"b\":20}}", now - TimeUnit.MINUTES.toMillis(1))); CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(testDevice.getId()); @@ -830,9 +1306,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }; """); - Output output = new Output(); - output.setType(OutputType.TIME_SERIES); - config.setOutput(output); + config.setOutput(new TimeSeriesOutput()); calculatedField.setConfiguration(config); @@ -847,7 +1321,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(result.get("avgB").get(0).get("value").asText()).isEqualTo("15.0"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format(""" + postTelemetry(testDevice.getId(), String.format(""" [{ "ts": %s, "values": { @@ -866,7 +1340,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes "a": 8, "b": 300 } - }]""", now - TimeUnit.MINUTES.toMillis(2), now, now - TimeUnit.MINUTES.toMillis(5)))); + }]""", now - TimeUnit.MINUTES.toMillis(2), now, now - TimeUnit.MINUTES.toMillis(5))); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -878,11 +1352,53 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); } + @Test + public void testSimpleCalculatedFieldWhenSkipRuleEngineOutputProcessing() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + + postTelemetry(testDevice.getId(), "{\"temperature\":24.5}"); + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(testDevice.getId()); + calculatedField.setType(CalculatedFieldType.SIMPLE); + calculatedField.setName("C to F"); + calculatedField.setDebugSettings(DebugSettings.all()); + + SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); + + Argument argument = new Argument(); + ReferencedEntityKey refEntityKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null); + argument.setRefEntityKey(refEntityKey); + config.setArguments(Map.of("T", argument)); + config.setExpression("(T * 9/5) + 32"); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setName("fahrenheitTemp"); + output.setDecimalsByDefault(1); + output.setStrategy(new TimeSeriesImmediateOutputStrategy(1000L, true, true, true, true)); + + config.setOutput(output); + + config.setUseLatestTs(true); + + calculatedField.setConfiguration(config); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("76.1"); + }); + } + @Test public void testCalculatedFieldsWhenOneIsInvalid() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); long now = System.currentTimeMillis(); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":5}}", now - TimeUnit.MINUTES.toMillis(3)))); + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + AttributeScope.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":5}}", now - TimeUnit.MINUTES.toMillis(3)))); // Script CF - invalid CalculatedField invalidCF = new CalculatedField(); @@ -903,9 +1419,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }; """); - Output scriptOutput = new Output(); - scriptOutput.setType(OutputType.TIME_SERIES); - scriptConfig.setOutput(scriptOutput); + scriptConfig.setOutput(new TimeSeriesOutput()); invalidCF.setConfiguration(scriptConfig); @@ -933,9 +1447,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes simpleConfig.setArguments(Map.of("a", argumentA)); simpleConfig.setExpression("a+1"); - Output simpleOutput = new Output(); + TimeSeriesOutput simpleOutput = new TimeSeriesOutput(); simpleOutput.setName("a+1"); - simpleOutput.setType(OutputType.TIME_SERIES); simpleOutput.setDecimalsByDefault(0); simpleConfig.setOutput(simpleOutput); @@ -957,7 +1470,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes assertThat(result.get("a+1").get(0).get("value").asText()).isEqualTo("6"); }); - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"a\":6}")); + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + AttributeScope.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"a\":6}")); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -983,4 +1496,12 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes return doPost("/api/asset", asset, Asset.class); } + private static Map kv(ArrayNode attrs) { + Map m = new HashMap<>(); + for (JsonNode n : attrs) { + m.put(n.get("key").asText(), n.get("value").asText()); + } + return m; + } + } diff --git a/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java b/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java new file mode 100644 index 0000000000..0872e21dae --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java @@ -0,0 +1,372 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cf; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.annotation.DirtiesContext; +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.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.CustomInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; +import org.thingsboard.server.common.data.debug.DebugSettings; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.thingsboard.server.cf.CalculatedFieldIntegrationTest.POLL_INTERVAL; + +@DaoSqlTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest { + + private final String TZ = "Europe/Kyiv"; + + private Tenant savedTenant; + + @Before + public void beforeEach() throws Exception { + loginSysAdmin(); + + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMinAllowedDeduplicationIntervalInSecForCF(1); + tenantProfileConfig.setMinAllowedAggregationIntervalInSecForCF(1); + tenantProfileConfig.setCfReevaluationCheckInterval(1); + }); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = saveTenant(tenant); + assertThat(savedTenant).isNotNull(); + + User tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant@thingsboard.org"); + tenantAdmin.setFirstName("John"); + tenantAdmin.setLastName("Doe"); + + createUserAndLogin(tenantAdmin, "testPassword"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + + deleteTenant(savedTenant.getId()); + } + + @Test + public void testCreateCfAndNoTelemetryDuringInterval_checkAggregation() throws Exception { + Device device = createDevice("Device", "1234567890111"); + + CustomInterval customInterval = new CustomInterval(TZ, 0L, 5L); + createConsumptionCF(device.getId(), customInterval, null); + + long interval = customInterval.getCurrentIntervalDurationMillis(); + + await().alias("create CF and no telemetry during interval -> save metric with default value") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("9999"); + assertThat(result.get("avgConsumption").get(0).get("value").isNull()).isTrue(); + }); + } + + @Test + public void testCreateCfWithoutWatermark_checkAggregation() throws Exception { + Device device = createDevice("Device", "1234567890111"); + + CustomInterval customInterval = new CustomInterval(TZ, 0L, 5L); + createConsumptionCF(device.getId(), customInterval, null); + + long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); + + long tsBeforeInterval = currentIntervalStartTs - 1000; + long tsInInterval_1 = currentIntervalStartTs + 1000; + long tsInInterval_2 = currentIntervalStartTs + 500; + long tsInInterval_3 = currentIntervalStartTs + 200; + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsBeforeInterval)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":100}}", tsInInterval_1)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":180}}", tsInInterval_2)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); + + long interval = customInterval.getCurrentIntervalDurationMillis(); + + await().alias("create CF -> perform aggregation after interval end") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("400"); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("133"); + }); + + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":500}}", tsInInterval_1)); + + await().alias("update telemetry that belongs to previous interval -> no aggregation since watermark is not set ") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("400"); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("133"); + }); + } + + @Test + public void testCreateCfWithWatermark_checkAggregationDuringWatermark() throws Exception { + Device device = createDevice("Device", "1234567890111"); + + CustomInterval customInterval = new CustomInterval(TZ, 0L, 5L); + Watermark watermark = new Watermark(10); + createConsumptionCF(device.getId(), customInterval, watermark); + + long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); + + long tsBeforeInterval = currentIntervalStartTs - 1000L; + long tsInInterval_1 = currentIntervalStartTs + 1000L; + long tsInInterval_2 = currentIntervalStartTs + 500L; + long tsInInterval_3 = currentIntervalStartTs + 200L; + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsBeforeInterval)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":100}}", tsInInterval_1)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":180}}", tsInInterval_2)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); + + long interval = customInterval.getCurrentIntervalDurationMillis(); + + await().alias("create CF -> perform aggregation after interval end") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("400"); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("133"); + }); + + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":300}}", tsInInterval_1)); + + await().alias("update telemetry during watermark -> perform aggregation") + .atMost(2 * 10, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("600"); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("200"); + }); + } + + @Test + public void testSendFutureTelemetry_checkAggregation() throws Exception { + Device device = createDevice("Device", "1234567890111"); + + CustomInterval customInterval = new CustomInterval(TZ, 0L, 2L); + createConsumptionCF(device.getId(), customInterval, null); + + long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); + + long tsBeforeInterval = currentIntervalStartTs - 1000; + long tsInInterval_1 = currentIntervalStartTs + 1000; + long tsInInterval_2 = currentIntervalStartTs + 500; + long tsInInterval_3 = currentIntervalStartTs + 200; + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsBeforeInterval)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":100}}", tsInInterval_1)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":180}}", tsInInterval_2)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); + + long interval = customInterval.getCurrentIntervalDurationMillis(); + + await().alias("create CF -> perform aggregation after interval end") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("400"); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("133"); + }); + + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":500}}", currentIntervalStartTs + 4500L)); + + await().alias("update telemetry that belongs to future interval -> check aggregation ") + .atMost(3 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("500"); + assertThat(result.get("consumption").get(0).get("ts").asLong()).isEqualTo(currentIntervalStartTs + 4000L); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("500"); + assertThat(result.get("avgConsumption").get(0).get("ts").asLong()).isEqualTo(currentIntervalStartTs + 4000L); + }); + } + + private CalculatedField createConsumptionCF(EntityId entityId, AggInterval aggInterval, Watermark watermark) { + Map arguments = new HashMap<>(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("energy", ArgumentType.TS_LATEST, null)); + arguments.put("en", argument); + + Map aggMetrics = new HashMap<>(); + + AggMetric consumption = new AggMetric(); + consumption.setFunction(AggFunction.SUM); + consumption.setInput(new AggKeyInput("en")); + consumption.setDefaultValue(9999.0); + aggMetrics.put("consumption", consumption); + + AggMetric avgEnergyConsumption = new AggMetric(); + avgEnergyConsumption.setFunction(AggFunction.AVG); + avgEnergyConsumption.setInput(new AggKeyInput("en")); + aggMetrics.put("avgConsumption", avgEnergyConsumption); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + + return createAggCf("Consumption", entityId, + aggInterval, + watermark, + arguments, + aggMetrics, + output); + } + + @Test + public void testCreateCfWith2Arguments_checkAggregation() throws Exception { + Device device = createDevice("Device", "1234567890111"); + + CustomInterval customInterval = new CustomInterval("Europe/Kyiv", 0L, 5L); + createCFWith2Args(device.getId(), customInterval, null); + + long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); + + long tsBeforeInterval = currentIntervalStartTs - 1000; + long tsInInterval_1 = currentIntervalStartTs + 1000; + long tsInInterval_2 = currentIntervalStartTs + 500; + long tsInInterval_3 = currentIntervalStartTs + 200; + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120, \"temperature\":43}}", tsBeforeInterval)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":100, \"temperature\":39}}", tsInInterval_1)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":180, \"temperature\":27}}", tsInInterval_2)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120, \"temperature\":50}}", tsInInterval_3)); + + long interval = customInterval.getCurrentIntervalDurationMillis(); + + await().alias("create CF -> perform aggregation after interval end") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgTemperature"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("400"); + assertThat(result.get("avgTemperature").get(0).get("value").asText()).isEqualTo("39"); + }); + } + + private CalculatedField createCFWith2Args(EntityId entityId, AggInterval aggInterval, Watermark watermark) { + Map arguments = new HashMap<>(); + Argument energy = new Argument(); + energy.setRefEntityKey(new ReferencedEntityKey("energy", ArgumentType.TS_LATEST, null)); + arguments.put("en", energy); + + Argument temperature = new Argument(); + temperature.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + arguments.put("temp", temperature); + + Map aggMetrics = new HashMap<>(); + + AggMetric consumption = new AggMetric(); + consumption.setFunction(AggFunction.SUM); + consumption.setInput(new AggKeyInput("en")); + consumption.setDefaultValue(9999.0); + aggMetrics.put("consumption", consumption); + + AggMetric avgTemperature = new AggMetric(); + avgTemperature.setFunction(AggFunction.AVG); + avgTemperature.setInput(new AggKeyInput("temp")); + aggMetrics.put("avgTemperature", avgTemperature); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + + return createAggCf("CF with 2 args", entityId, + aggInterval, + watermark, + arguments, + aggMetrics, + output); + } + + private CalculatedField createAggCf(String name, + EntityId entityId, + AggInterval aggInterval, + Watermark watermark, + Map inputs, + Map metrics, + Output output) { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setName(name); + calculatedField.setEntityId(entityId); + calculatedField.setType(CalculatedFieldType.ENTITY_AGGREGATION); + + EntityAggregationCalculatedFieldConfiguration configuration = new EntityAggregationCalculatedFieldConfiguration(); + + configuration.setArguments(inputs); + configuration.setMetrics(metrics); + configuration.setInterval(aggInterval); + if (watermark != null) { + configuration.setWatermark(watermark); + } + configuration.setOutput(output); + + calculatedField.setConfiguration(configuration); + calculatedField.setDebugSettings(DebugSettings.all()); + return saveCalculatedField(calculatedField); + } + + private ObjectNode getLatestTelemetry(EntityId entityId, String... keys) throws Exception { + return doGetAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + "/values/timeseries?keys=" + String.join(",", keys), ObjectNode.class); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/cf/RelatedEntitiesAggregationCalculatedFieldTest.java b/application/src/test/java/org/thingsboard/server/cf/RelatedEntitiesAggregationCalculatedFieldTest.java new file mode 100644 index 0000000000..4c708bce56 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/cf/RelatedEntitiesAggregationCalculatedFieldTest.java @@ -0,0 +1,978 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cf; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.annotation.DirtiesContext; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +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.asset.AssetProfile; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunctionInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.debug.DebugSettings; +import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; +import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; +import org.thingsboard.server.common.data.device.data.DeviceData; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.cf.CalculatedFieldIntegrationTest.POLL_INTERVAL; + +@DaoSqlTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +public class RelatedEntitiesAggregationCalculatedFieldTest extends AbstractControllerTest { + + private Tenant savedTenant; + + private DeviceProfile deviceProfile; + private Device device1; + private String accessToken1 = "1234567890111"; + private Device device2; + private String accessToken2 = "1234567890222"; + + private AssetProfile assetProfile; + private Asset asset; + + private final long deduplicationInterval = 5; + + @Before + public void beforeEach() throws Exception { + loginSysAdmin(); + + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMinAllowedDeduplicationIntervalInSecForCF(1); + tenantProfileConfig.setMinAllowedScheduledUpdateIntervalInSecForCF(1); + }); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = saveTenant(tenant); + assertThat(savedTenant).isNotNull(); + + User tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant@thingsboard.org"); + tenantAdmin.setFirstName("John"); + tenantAdmin.setLastName("Doe"); + + createUserAndLogin(tenantAdmin, "testPassword"); + + deviceProfile = doPost("/api/deviceProfile", createDeviceProfile("Device Profile"), DeviceProfile.class); + device1 = createDevice("Device 1", deviceProfile.getId(), accessToken1); + device2 = createDevice("Device 2", deviceProfile.getId(), accessToken2); + + postTelemetry(device1.getId(), "{\"occupied\":true}"); + postTelemetry(device2.getId(), "{\"occupied\":false}"); + + assetProfile = doPost("/api/assetProfile", createAssetProfile("Asset Profile"), AssetProfile.class); + asset = createAsset("Asset", assetProfile.getId()); + + createEntityRelation(asset.getId(), device1.getId(), "Contains"); + createEntityRelation(asset.getId(), device2.getId(), "Contains"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + + deleteTenant(savedTenant.getId()); + } + + @Test + public void testCreateCfOnProfile_checkInitialAggregation() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + createOccupancyCF(assetProfile.getId()); + + await().alias("create CF and perform initial aggregation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + + postTelemetry(device3.getId(), "{\"occupied\":true}"); + + await().alias("update telemetry and perform aggregation") + .atLeast(deduplicationInterval / 2, TimeUnit.SECONDS) + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testAddEntityToProfile_checkAggregation() throws Exception { + createOccupancyCF(assetProfile.getId()); + + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + postTelemetry(device3.getId(), "{\"occupied\":true}"); + postTelemetry(device4.getId(), "{\"occupied\":true}"); + + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + + await().alias("add entity to profile with no related entities and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode occupancy = getLatestTelemetry(asset2.getId(), "freeSpaces", "occupiedSpaces", "totalSpaces"); + assertThat(occupancy).isNotNull(); + assertThat(occupancy.get("freeSpaces").get(0).get("value").isNull()).isTrue(); + assertThat(occupancy.get("occupiedSpaces").get(0).get("value").isNull()).isTrue(); + assertThat(occupancy.get("totalSpaces").get(0).get("value").isNull()).isTrue(); + }); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + await().alias("create relations and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "0", + "occupiedSpaces", "2", + "totalSpaces", "2" + )); + }); + + postTelemetry(device3.getId(), "{\"occupied\":false}"); + + await().alias("update telemetry and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testChangeEntityProfile_checkAggregation() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + createOccupancyCF(assetProfile.getId()); + + await().alias("create CF and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + + AssetProfile newAssetProfile = createAssetProfile("New Asset Profile"); + asset2.setAssetProfileId(newAssetProfile.getId()); + doPost("/api/asset", asset2, Asset.class); + + postTelemetry(device3.getId(), "{\"occupied\":true}"); + + await().alias("change profile and no aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testCreateCfOnAssetAndNoTelemetryOnDevices_checkDefaultValueUsed() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + createOccupancyCF(asset2.getId()); + + await().alias("create CF and perform aggregation with default values").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testCreateCfAndUpdateTelemetry_checkAggregation() throws Exception { + createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + postTelemetry(device1.getId(), "{\"occupied\":false}"); + + await().alias("update telemetry and perform aggregation") + .atLeast(deduplicationInterval / 2, TimeUnit.SECONDS) + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testCreateCfAndRelationToRuleChain_checkAggregation() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + postTelemetry(device3.getId(), "{\"occupied\":true}"); + + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("RuleChain"); + ruleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); + postTelemetry(ruleChain.getId(), "{\"occupied\":true}"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), ruleChain.getId(), "Contains"); + + createOccupancyCF(asset2.getId()); + + await().alias("create CF and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "0", + "occupiedSpaces", "1", + "totalSpaces", "1" + )); + }); + + postTelemetry(ruleChain.getId(), "{\"occupied\":true}"); + + await().alias("update telemetry on rule chain and no aggregation performed").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "0", + "occupiedSpaces", "1", + "totalSpaces", "1" + )); + }); + } + + @Test + public void testDeleteCf_checkNoAggregation() throws Exception { + CalculatedField cf = createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + doDelete("/api/calculatedField/" + cf.getId().getId().toString()) + .andExpect(status().isOk()); + + postTelemetry(device1.getId(), "{\"occupied\":false}"); + + await().alias("delete cf and update telemetry and no aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testUpdateTelemetry_checkAggregationNotExecutedUntilDeduplicationInterval() throws Exception { + createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + postTelemetry(device1.getId(), "{\"occupied\":false}"); + + await().alias("update telemetry -> no changes").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(this::checkInitialCalculationValues); + + postTelemetry(device2.getId(), "{\"occupied\":false}"); + + await().alias("create CF and perform initial calculation") + .atLeast(deduplicationInterval / 2, TimeUnit.SECONDS) + .atMost(TIMEOUT, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testDeleteTelemetry_checkAggregationWithPreviousValuesOrDefault() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + long currentTime = System.currentTimeMillis(); + long firstTs = currentTime - 10; + long secondTs = currentTime - 10; + long thirdTs = currentTime - 5; + postTelemetry(device3.getId(), "{\"ts\": " + firstTs + ", \"values\": {\"occupied\":true}}"); + postTelemetry(device4.getId(), "{\"ts\": " + secondTs + ", \"values\": {\"occupied\":true}}"); + postTelemetry(device3.getId(), "{\"ts\": " + thirdTs + ", \"values\": {\"occupied\":true}}"); + + createOccupancyCF(asset2.getId()); + + await().alias("create CF and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "0", + "occupiedSpaces", "2", + "totalSpaces", "2" + )); + }); + + doDelete("/api/plugins/telemetry/DEVICE/" + device3.getId() + "/timeseries/delete?keys=occupied&deleteAllDataForKeys=false&rewriteLatestIfDeleted=true&deleteLatest=true&startTs=" + thirdTs + "&endTs=" + thirdTs + 1, String.class); + doDelete("/api/plugins/telemetry/DEVICE/" + device4.getId() + "/timeseries/delete?keys=occupied&deleteAllDataForKeys=false&rewriteLatestIfDeleted=true&deleteLatest=true&startTs=" + secondTs + "&endTs=" + secondTs + 1, String.class); + + await().alias("delete latest telemetry and perform aggregation with previous or default values").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testDeleteAttr_checkAggregationWithDefault() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + postAttributes(device3.getId(), AttributeScope.SERVER_SCOPE, "{\"occupied\":true}"); + postAttributes(device4.getId(), AttributeScope.SERVER_SCOPE, "{\"occupied\":true}"); + + createOccupancyCFWithAttr(asset2.getId()); + + await().alias("create CF and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "0", + "occupiedSpaces", "2", + "totalSpaces", "2" + )); + }); + + doDelete("/api/plugins/telemetry/DEVICE/" + device3.getUuidId() + "/SERVER_SCOPE?keys=occupied", String.class); + doDelete("/api/plugins/telemetry/DEVICE/" + device4.getUuidId() + "/SERVER_SCOPE?keys=occupied", String.class); + + await().alias("delete attribute and perform aggregation with default values").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testCreateRelation_checkAggregation() throws Exception { + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + Device device4 = createDevice("Device 4", "1234567890444"); + + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + createEntityRelation(asset2.getId(), device4.getId(), "Contains"); + + createOccupancyCF(assetProfile.getId()); + + await().alias("create CF and perform initial aggregation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + + Device device5 = createDevice("Device 5", "1234567890555"); + createEntityRelation(asset2.getId(), device5.getId(), "Contains"); + + await().alias("create relation and perform aggregation on asset 2") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "1", + "totalSpaces", "2" + )); + + verifyTelemetry(asset2.getId(), Map.of( + "freeSpaces", "3", + "occupiedSpaces", "0", + "totalSpaces", "3" + )); + }); + } + + @Test + public void testDeleteRelation_checkAggregation() throws Exception { + createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + deleteEntityRelation(new EntityRelation(asset.getId(), device1.getId(), "Contains", RelationTypeGroup.COMMON)); + + await().alias("create relation and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "0", + "totalSpaces", "1" + )); + }); + } + + @Test + public void testDeleteEntityByRelation_checkAggregation() throws Exception { + createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + doDelete("/api/device/" + device1.getId()).andExpect(status().isOk()); + + await().alias("create relation and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "1", + "occupiedSpaces", "0", + "totalSpaces", "1" + )); + }); + } + + @Test + public void testUpdateRelationPath_checkAggregation() throws Exception { + CalculatedField cf = createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + Device device3 = createDevice("Device 3", "1234567890333"); + createEntityRelation(asset.getId(), device3.getId(), "Has"); + postTelemetry(device3.getId(), "{\"occupied\":true}"); + + var configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) cf.getConfiguration(); + configuration.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, "Has")); + saveCalculatedField(cf); + + await().alias("update relation path and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "0", + "occupiedSpaces", "1", + "totalSpaces", "1" + )); + }); + } + + @Test + public void testUpdateArguments_checkAggregation() throws Exception { + CalculatedField cf = createOccupancyCF(asset.getId()); + checkInitialCalculation(); + + postTelemetry(device1.getId(), "{\"occupiedStatus\":false}"); + postTelemetry(device2.getId(), "{\"occupiedStatus\":false}"); + + var configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) cf.getConfiguration(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("oc", ArgumentType.TS_LATEST, null)); + argument.setDefaultValue("false"); + configuration.setArguments(Map.of("oc", argument)); + saveCalculatedField(cf); + + await().alias("update arguments and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of( + "freeSpaces", "2", + "occupiedSpaces", "0", + "totalSpaces", "2" + )); + }); + } + + @Test + public void testUpdateMetrics_checkAggregation() throws Exception { + postTelemetry(device1.getId(), "{\"temperature\":24.2}"); + postTelemetry(device2.getId(), "{\"temperature\":19.6}"); + CalculatedField cf = createAvgTemperatureCF(asset.getId()); + + await().alias("create avg temp cf and perform initial aggregation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("avgTemperature", "24")); + }); + + var configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) cf.getConfiguration(); + AggMetric aggMetric = new AggMetric(); + aggMetric.setInput(new AggKeyInput("temp")); + aggMetric.setFilter("return temp < 100;"); + aggMetric.setFunction(AggFunction.MAX); + configuration.setMetrics(Map.of("maxTemperature", aggMetric)); + saveCalculatedField(cf); + + await().alias("update metrics and perform aggregation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("maxTemperature", "24")); + }); + + postTelemetry(device1.getId(), "{\"temperature\":101.3}"); + postTelemetry(device2.getId(), "{\"temperature\":25.8}"); + + await().alias("update telemetry and perform aggregation") + .atLeast(deduplicationInterval / 2, TimeUnit.SECONDS) + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("maxTemperature", "26")); + }); + } + + @Test + public void testUpdateOutput_checkAggregation() throws Exception { + postTelemetry(device1.getId(), "{\"temperature\":24.2}"); + postTelemetry(device2.getId(), "{\"temperature\":19.6}"); + CalculatedField cf = createAvgTemperatureCF(asset.getId()); + + await().alias("create avg temp cf and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("avgTemperature", "24")); + }); + + var configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) cf.getConfiguration(); + AttributesOutput output = new AttributesOutput(); + output.setScope(AttributeScope.SERVER_SCOPE); + configuration.setOutput(output); + saveCalculatedField(cf); + + await().alias("update output and perform aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode avgTemperature = getServerAttributes(asset.getId(), "avgTemperature"); + assertThat(avgTemperature).isNotNull(); + assertThat(avgTemperature.get(0)).isNotNull(); + assertThat(avgTemperature.get(0).get("value").asText()).isEqualTo("24.2"); + }); + } + + @Test + public void testUpdateDeduplicationInterval_checkAggregationNotExecutedUntilDeduplicationInterval() throws Exception { + postTelemetry(device1.getId(), "{\"temperature\":24.2}"); + postTelemetry(device2.getId(), "{\"temperature\":19.6}"); + CalculatedField cf = createAvgTemperatureCF(asset.getId()); + + await().alias("create avg temp cf and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("avgTemperature", "24")); + }); + + var configuration = (RelatedEntitiesAggregationCalculatedFieldConfiguration) cf.getConfiguration(); + configuration.setDeduplicationIntervalInSec(2 * deduplicationInterval); + saveCalculatedField(cf); + + await().alias("update deduplication interval and perform aggregation").atMost(deduplicationInterval / 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("avgTemperature", "24")); + }); + + postTelemetry(device2.getId(), "{\"temperature\":32.1}"); + + await().alias("update telemetry and perform aggregation").atMost(2 * deduplicationInterval + 10, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + verifyTelemetry(asset.getId(), Map.of("avgTemperature", "28")); + }); + } + + + @Test + public void testTheSameRelationTypeAndKeyButDifferentCfs_checkAggregation() throws Exception { + postTelemetry(device1.getId(), "{\"temperature\":24.2}"); + postTelemetry(device2.getId(), "{\"temperature\":19.6}"); + CalculatedField cf = createAvgTemperatureCF(asset.getId()); + + Asset asset2 = createAsset("Asset 2", assetProfile.getId()); + Device device3 = createDevice("Device 3", "1234567890333"); + createEntityRelation(asset2.getId(), device3.getId(), "Contains"); + postTelemetry(device3.getId(), "{\"temperature\":10.2}"); + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setName("Average temperature"); + calculatedField.setEntityId(asset2.getId()); + calculatedField.setType(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION); + + var config = (RelatedEntitiesAggregationCalculatedFieldConfiguration) cf.getConfiguration(); + AggMetric avgMetric = new AggMetric(); + avgMetric.setFunction(AggFunction.AVG); + avgMetric.setInput(new AggKeyInput("temp")); + config.setMetrics(Map.of("avgTemperature_2", avgMetric)); + + calculatedField.setConfiguration(config); + calculatedField.setDebugSettings(DebugSettings.all()); + saveCalculatedField(calculatedField); + + await().alias("create avg temp cf and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode avgTemperature = getLatestTelemetry(asset.getId(), "avgTemperature", "avgTemperature_2"); + assertThat(avgTemperature).isNotNull(); + assertThat(avgTemperature.get("avgTemperature").get(0).get("value").asText()).isEqualTo("24"); + assertThat(avgTemperature.get("avgTemperature_2").get(0).get("value").isNull()).isTrue(); + }); + + postTelemetry(device1.getId(), "{\"temperature\":26.2}"); + + await().alias("create avg temp cf and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode avgTemperature = getLatestTelemetry(asset.getId(), "avgTemperature", "avgTemperature_2"); + assertThat(avgTemperature).isNotNull(); + assertThat(avgTemperature.get("avgTemperature").get(0).get("value").asText()).isEqualTo("26"); + assertThat(avgTemperature.get("avgTemperature_2").get(0).get("value").isNull()).isTrue(); + }); + } + + @Test + public void testUpdateMaxRelatedEntitiesPerArgument_checkAggregation() throws Exception { + loginSysAdmin(); + + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMaxRelatedEntitiesToReturnPerCfArgument(1); + }); + + login("tenant@thingsboard.org", "testPassword"); + + createCountCF(asset.getId()); + + await().alias("create CF and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode numberOfDevices = getLatestTelemetry(asset.getId(), "numberOfDevices"); + assertThat(numberOfDevices).isNotNull(); + assertThat(numberOfDevices.get("numberOfDevices").get(0).get("value").asText()).isEqualTo("1"); + }); + + loginSysAdmin(); + + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMaxRelatedEntitiesToReturnPerCfArgument(10); + }); + + login("tenant@thingsboard.org", "testPassword"); + + await().alias("update max related entities per argument and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode numberOfDevices = getLatestTelemetry(asset.getId(), "numberOfDevices"); + assertThat(numberOfDevices).isNotNull(); + assertThat(numberOfDevices.get("numberOfDevices").get(0).get("value").asText()).isEqualTo("2"); + }); + } + + private void checkInitialCalculation() { + await().alias("create CF and perform initial aggregation").atMost(deduplicationInterval * 2, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(this::checkInitialCalculationValues); + } + + private void checkInitialCalculationValues() throws Exception { + ObjectNode occupancy = getLatestTelemetry(asset.getId(), "freeSpaces", "occupiedSpaces", "totalSpaces"); + assertThat(occupancy).isNotNull(); + assertThat(occupancy.get("freeSpaces").get(0).get("value").asText()).isEqualTo("1"); + assertThat(occupancy.get("occupiedSpaces").get(0).get("value").asText()).isEqualTo("1"); + assertThat(occupancy.get("totalSpaces").get(0).get("value").asText()).isEqualTo("2"); + } + + private CalculatedField createAvgTemperatureCF(EntityId entityId) { + Map arguments = new HashMap<>(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + argument.setDefaultValue("20"); + arguments.put("temp", argument); + + Map aggMetrics = new HashMap<>(); + + AggMetric avgMetric = new AggMetric(); + avgMetric.setFunction(AggFunction.AVG); + avgMetric.setFilter("return temp >= 20;"); + avgMetric.setInput(new AggKeyInput("temp")); + aggMetrics.put("avgTemperature", avgMetric); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + + return createAggCf("Average temperature", entityId, + new RelationPathLevel(EntitySearchDirection.FROM, "Contains"), + arguments, + aggMetrics, + output); + } + + private CalculatedField createOccupancyCF(EntityId entityId) { + Map arguments = new HashMap<>(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("occupied", ArgumentType.TS_LATEST, null)); + argument.setDefaultValue("false"); + arguments.put("oc", argument); + + Map aggMetrics = new HashMap<>(); + + AggMetric freeSpaces = new AggMetric(); + freeSpaces.setFunction(AggFunction.COUNT); + freeSpaces.setFilter("return oc == false;"); + freeSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("freeSpaces", freeSpaces); + + AggMetric occupiedSpaces = new AggMetric(); + occupiedSpaces.setFunction(AggFunction.COUNT); + occupiedSpaces.setFilter("return oc == true;"); + occupiedSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("occupiedSpaces", occupiedSpaces); + + AggMetric totalSpaces = new AggMetric(); + totalSpaces.setFunction(AggFunction.COUNT); + totalSpaces.setInput(new AggFunctionInput("return 1;")); + aggMetrics.put("totalSpaces", totalSpaces); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + + return createAggCf("Occupied spaces", entityId, + new RelationPathLevel(EntitySearchDirection.FROM, "Contains"), + arguments, + aggMetrics, + output); + } + + private CalculatedField createOccupancyCFWithAttr(EntityId entityId) { + Map arguments = new HashMap<>(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("occupied", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + argument.setDefaultValue("false"); + arguments.put("oc", argument); + + Map aggMetrics = new HashMap<>(); + + AggMetric freeSpaces = new AggMetric(); + freeSpaces.setFunction(AggFunction.COUNT); + freeSpaces.setFilter("return oc == false;"); + freeSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("freeSpaces", freeSpaces); + + AggMetric occupiedSpaces = new AggMetric(); + occupiedSpaces.setFunction(AggFunction.COUNT); + occupiedSpaces.setFilter("return oc == true;"); + occupiedSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("occupiedSpaces", occupiedSpaces); + + AggMetric totalSpaces = new AggMetric(); + totalSpaces.setFunction(AggFunction.COUNT); + totalSpaces.setInput(new AggFunctionInput("return 1;")); + aggMetrics.put("totalSpaces", totalSpaces); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + + return createAggCf("Occupied spaces", entityId, + new RelationPathLevel(EntitySearchDirection.FROM, "Contains"), + arguments, + aggMetrics, + output); + } + + private CalculatedField createCountCF(EntityId entityId) { + Map arguments = new HashMap<>(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("active", ArgumentType.TS_LATEST, null)); + argument.setDefaultValue("true"); + arguments.put("active", argument); + + Map aggMetrics = new HashMap<>(); + + AggMetric avgMetric = new AggMetric(); + avgMetric.setFunction(AggFunction.COUNT); + avgMetric.setInput(new AggKeyInput("active")); + aggMetrics.put("numberOfDevices", avgMetric); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + + return createAggCf("Number of devices", entityId, + new RelationPathLevel(EntitySearchDirection.FROM, "Contains"), + arguments, + aggMetrics, + output); + } + + private CalculatedField createAggCf(String name, + EntityId entityId, + RelationPathLevel relation, + Map inputs, + Map metrics, + Output output) { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setName(name); + calculatedField.setEntityId(entityId); + calculatedField.setType(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION); + + RelatedEntitiesAggregationCalculatedFieldConfiguration configuration = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + configuration.setRelation(relation); + configuration.setArguments(inputs); + configuration.setDeduplicationIntervalInSec(deduplicationInterval); + configuration.setScheduledUpdateInterval(10); + configuration.setMetrics(metrics); + configuration.setOutput(output); + + calculatedField.setConfiguration(configuration); + calculatedField.setDebugSettings(DebugSettings.all()); + return saveCalculatedField(calculatedField); + } + + private Device createDevice(String name, DeviceProfileId deviceProfileId, String accessToken) { + Device device = new Device(); + device.setName(name); + device.setDeviceProfileId(deviceProfileId); + DeviceData deviceData = new DeviceData(); + deviceData.setTransportConfiguration(new DefaultDeviceTransportConfiguration()); + deviceData.setConfiguration(new DefaultDeviceConfiguration()); + device.setDeviceData(deviceData); + return doPost("/api/device?accessToken=" + accessToken, device, Device.class); + } + + private Asset createAsset(String name, AssetProfileId assetProfileId) { + Asset asset = new Asset(); + asset.setName(name); + asset.setAssetProfileId(assetProfileId); + return doPost("/api/asset", asset, Asset.class); + } + + private void verifyTelemetry(EntityId entityId, Map expectedResults) throws Exception { + ObjectNode result = getLatestTelemetry(entityId, expectedResults.keySet().toArray(new String[0])); + assertThat(result).isNotNull(); + expectedResults.forEach((key, value) -> assertThat(result.get(key).get(0).get("value").asText()).isEqualTo(value)); + } + + private ObjectNode getLatestTelemetry(EntityId entityId, String... keys) throws Exception { + return doGetAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + "/values/timeseries?keys=" + String.join(",", keys), ObjectNode.class); + } + + private ArrayNode getServerAttributes(EntityId entityId, String... keys) throws Exception { + return doGetAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + "/values/attributes/SERVER_SCOPE?keys=" + String.join(",", keys), ArrayNode.class); + } + +} 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 58bf4b9798..fcbd60eb4d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -212,8 +212,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - ArgumentMatcher matcherCustomerId = customerId == null ? - argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherCustomerId = customerId == null ? argument -> true : actualCustomerId -> actualCustomerId.equals(customerId); ArgumentMatcher matcherUserId = userId == null ? argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, @@ -638,7 +637,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { return fieldName + " length must be equal or less than 255"; } - protected String msgErrorNoFound(String entityClassName, String entityIdStr) { + protected static String msgErrorNoFound(String entityClassName, String entityIdStr) { return entityClassName + " with id [" + entityIdStr + "] is not found"; } 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 f104c51ca4..0cf114fa35 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java @@ -15,19 +15,13 @@ */ package org.thingsboard.server.controller; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EventInfo; -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; @@ -61,18 +55,6 @@ public abstract class AbstractRuleEngineControllerTest extends AbstractControlle return doGet("/api/ruleChain/metadata/" + ruleChainId.getId().toString(), RuleChainMetaData.class); } - protected PageData getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception { - return getEvents(tenantId, entityId, EventType.DEBUG_RULE_NODE.getOldName(), limit); - } - - protected PageData getEvents(TenantId tenantId, EntityId entityId, String eventType, int limit) throws Exception { - TimePageLink pageLink = new TimePageLink(limit); - return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&", - new TypeReference>() { - }, pageLink, entityId.getEntityType(), entityId.getId(), eventType, tenantId.getId()); - } - - protected JsonNode getMetadata(EventInfo outEvent) { String metaDataStr = outEvent.getBody().get("metadata").asText(); try { 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 3b7286cf01..6fbe9d9864 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -69,17 +69,23 @@ import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.actors.DefaultTbActorSystem; import org.thingsboard.server.actors.TbActorId; import org.thingsboard.server.actors.TbActorMailbox; +import org.thingsboard.server.actors.TbCalculatedFieldEntityActorId; import org.thingsboard.server.actors.TbEntityActorId; +import org.thingsboard.server.actors.calculatedField.CalculatedFieldEntityActor; +import org.thingsboard.server.actors.calculatedField.CalculatedFieldEntityMessageProcessor; 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.AttributeScope; 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.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.StringUtils; @@ -88,6 +94,9 @@ 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.AssetProfile; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldInfo; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; @@ -102,6 +111,7 @@ import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeCon import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; @@ -141,6 +151,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; 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.common.data.security.model.JwtPair; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.msg.session.FeatureType; @@ -153,6 +164,8 @@ import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.memory.InMemoryStorage; import org.thingsboard.server.service.cf.CfRocksDb; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; import org.thingsboard.server.service.entitiy.tenant.profile.TbTenantProfileService; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest; import org.thingsboard.server.service.security.auth.rest.LoginRequest; @@ -191,6 +204,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; import static org.thingsboard.server.common.data.CacheConstants.CLAIM_DEVICES_CACHE; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX; +import static org.thingsboard.server.config.ThingsboardSecurityConfiguration.BEARER_HEADER_PREFIX; @Slf4j public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { @@ -201,13 +216,13 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected static final String TEST_DIFFERENT_TENANT_NAME = "TEST DIFFERENT TENANT"; protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org"; - private static final String SYS_ADMIN_PASSWORD = "sysadmin"; + protected static final String SYS_ADMIN_PASSWORD = "sysadmin"; protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org"; protected static final String TENANT_ADMIN_PASSWORD = "tenant"; protected static final String DIFFERENT_TENANT_ADMIN_EMAIL = "testdifftenant@thingsboard.org"; - private static final String DIFFERENT_TENANT_ADMIN_PASSWORD = "difftenant"; + protected static final String DIFFERENT_TENANT_ADMIN_PASSWORD = "difftenant"; protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org"; private static final String CUSTOMER_USER_PASSWORD = "customer"; @@ -236,6 +251,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected String mobileToken; protected String username; + protected String apiKey; + protected TenantId tenantId; protected TenantProfileId tenantProfileId; protected UserId tenantAdminUserId; @@ -610,8 +627,13 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { Assert.assertNotNull(tokenInfo); Assert.assertTrue(tokenInfo.has("token")); Assert.assertTrue(tokenInfo.has("refreshToken")); - String token = tokenInfo.get("token").asText(); - String refreshToken = tokenInfo.get("refreshToken").asText(); + validateAndSetJwtToken(JacksonUtil.treeToValue(tokenInfo, JwtPair.class), username); + } + + protected void validateAndSetJwtToken(JwtPair jwtPair, String username) { + Assert.assertNotNull(jwtPair); + String token = jwtPair.getToken(); + String refreshToken = jwtPair.getRefreshToken(); validateJwtToken(token, username); validateJwtToken(refreshToken, username); this.token = token; @@ -640,13 +662,27 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected void setJwtToken(MockHttpServletRequestBuilder request) { if (this.token != null) { - request.header(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM, "Bearer " + this.token); + request.header(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER, BEARER_HEADER_PREFIX + this.token); } if (this.mobileToken != null) { request.header(UserController.MOBILE_TOKEN_HEADER, this.mobileToken); } } + protected void resetApiKey() { + this.apiKey = null; + } + + protected void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + protected void setApiKey(MockHttpServletRequestBuilder request) { + if (this.apiKey != null) { + request.header(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER, API_KEY_HEADER_PREFIX + this.apiKey); + } + } + protected DeviceProfile createDeviceProfile(String name) { return createDeviceProfile(name, null); } @@ -682,9 +718,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } protected Device createDevice(String name, String accessToken) throws Exception { + return createDevice(name, "default", null, accessToken); + } + + protected Device createDevice(String name, String type, String label, String accessToken) throws Exception { Device device = new Device(); device.setName(name); - device.setType("default"); + device.setType(type); + device.setLabel(label); DeviceData deviceData = new DeviceData(); deviceData.setTransportConfiguration(new DefaultDeviceTransportConfiguration()); deviceData.setConfiguration(new DefaultDeviceConfiguration()); @@ -740,6 +781,12 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return doPost("/api/device-with-credentials", request, Device.class); } + protected ResultActions doGetAsync(String urlTemplate, MultiValueMap params) throws Exception { + MockHttpServletRequestBuilder getRequest = get(urlTemplate).params(params); + setJwtToken(getRequest); + return mockMvc.perform(asyncDispatch(mockMvc.perform(getRequest).andExpect(request().asyncStarted()).andReturn())); + } + protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception { MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables); setJwtToken(getRequest); @@ -753,6 +800,12 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return mockMvc.perform(getRequest); } + protected ResultActions doGetWithApiKey(String urlTemplate, Object... urlVariables) throws Exception { + MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables); + setApiKey(getRequest); + return mockMvc.perform(getRequest); + } + protected T doGet(String urlTemplate, Class responseClass, Object... urlVariables) throws Exception { return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); } @@ -776,6 +829,10 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return mockMvc.perform(asyncDispatch(mockMvc.perform(getRequest).andExpect(request().asyncStarted()).andReturn())); } + protected T doGetWithApiKey(String urlTemplate, Class responseClass, Object... urlVariables) throws Exception { + return readResponse(doGetWithApiKey(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); + } + protected T doGetTyped(String urlTemplate, TypeReference responseType, Object... urlVariables) throws Exception { return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseType); } @@ -855,6 +912,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } } + protected R doPostWithApiKey(String urlTemplate, T content, Class responseClass, String... params) { + try { + return readResponse(doPostWithApiKey(urlTemplate, content, params).andExpect(status().isOk()), responseClass); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + protected R doPostWithResponse(String urlTemplate, T content, Class responseClass, String... params) throws Exception { return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass); } @@ -922,6 +987,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return mockMvc.perform(postRequest); } + protected ResultActions doPostWithApiKey(String urlTemplate, T content, String... params) throws Exception { + MockHttpServletRequestBuilder postRequest = post(urlTemplate, params); + setApiKey(postRequest); + String json = json(content); + postRequest.contentType(contentType).content(json); + return mockMvc.perform(postRequest); + } + protected ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception { MockHttpServletRequestBuilder postRequest = post(urlTemplate, params); setJwtToken(postRequest); @@ -939,6 +1012,22 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return mockMvc.perform(deleteRequest); } + protected ResultActions doDeleteAsync(String urlTemplate, MultiValueMap params) throws Exception { + MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate) + .params(params); + setJwtToken(deleteRequest); + MvcResult result = mockMvc.perform(deleteRequest).andReturn(); + result.getAsyncResult(DEFAULT_TIMEOUT); + return mockMvc.perform(asyncDispatch(result)); + } + + protected ResultActions doDeleteWithApiKey(String urlTemplate, String... params) throws Exception { + MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate); + setApiKey(deleteRequest); + populateParams(deleteRequest, params); + return mockMvc.perform(deleteRequest); + } + protected ResultActions doDeleteAsync(String urlTemplate, Long timeout, String... params) throws Exception { MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate, params); setJwtToken(deleteRequest); @@ -1062,7 +1151,17 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected void createEntityRelation(EntityId entityIdFrom, EntityId entityIdTo, String typeRelation) throws Exception { EntityRelation relation = new EntityRelation(entityIdFrom, entityIdTo, typeRelation); - doPost("/api/relation", relation); + doPost("/api/relation", relation).andExpect(status().isOk()); + } + + protected void deleteEntityRelation(EntityRelation entityRelation) throws Exception { + String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", + entityRelation.getFrom().getId(), + entityRelation.getFrom().getEntityType(), + entityRelation.getType(), + entityRelation.getTo().getId(), + entityRelation.getTo().getEntityType()); + doDelete(url); } protected List findRelationsByTo(EntityId entityId) throws Exception { @@ -1113,6 +1212,18 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { }); } + protected void awaitForCalculatedFieldEntityMessageProcessorToRegisterCfStateAsReadyToRefreshDynamicArguments(EntityId entityId, CalculatedFieldId cfId, int scheduledUpdateInterval) { + CalculatedFieldEntityMessageProcessor processor = getCalculatedFieldEntityMessageProcessor(entityId); + Map statesMap = (Map) ReflectionTestUtils.getField(processor, "states"); + Awaitility.await("CF state for entity actor ready to refresh dynamic arguments").atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> { + CalculatedFieldState calculatedFieldState = statesMap.get(cfId); + boolean isReady = calculatedFieldState != null && ((GeofencingCalculatedFieldState) calculatedFieldState).getLastScheduledRefreshTs() < + System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(scheduledUpdateInterval); + log.warn("entityId {}, cfId {}, state ready to refresh == {}", entityId, cfId, isReady); + return isReady; + }); + } + protected static String getMapName(FeatureType featureType) { switch (featureType) { case ATTRIBUTES: @@ -1134,6 +1245,16 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return (DeviceActorMessageProcessor) ReflectionTestUtils.getField(actor, "processor"); } + protected CalculatedFieldEntityMessageProcessor getCalculatedFieldEntityMessageProcessor(EntityId entityId) { + DefaultTbActorSystem actorSystem = (DefaultTbActorSystem) ReflectionTestUtils.getField(actorService, "system"); + ConcurrentMap actors = (ConcurrentMap) ReflectionTestUtils.getField(actorSystem, "actors"); + Awaitility.await("CF entity actor was created").atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> actors.containsKey(new TbCalculatedFieldEntityActorId(entityId))); + TbActorMailbox actorMailbox = actors.get(new TbCalculatedFieldEntityActorId(entityId)); + CalculatedFieldEntityActor actor = (CalculatedFieldEntityActor) ReflectionTestUtils.getField(actorMailbox, "actor"); + return (CalculatedFieldEntityMessageProcessor) ReflectionTestUtils.getField(actor, "processor"); + } + protected void updateDefaultTenantProfileConfig(Consumer updater) throws ThingsboardException { updateDefaultTenantProfile(tenantProfile -> { TenantProfileData profileData = tenantProfile.getProfileData(); @@ -1288,7 +1409,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected List findJobs(List types, List entities) throws Exception { return doGetTypedWithPageLink("/api/jobs?types=" + types.stream().map(Enum::name).collect(Collectors.joining(",")) + - "&entities=" + entities.stream().map(UUID::toString).collect(Collectors.joining(",")) + "&", + "&entities=" + entities.stream().map(UUID::toString).collect(Collectors.joining(",")) + "&", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); } @@ -1300,6 +1421,43 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { doPost("/api/job/" + jobId + "/reprocess").andExpect(status().isOk()); } + protected void postTelemetry(EntityId entityId, String payload) throws Exception { + doPostAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(payload), 30_000L).andExpect(status().isOk()); + } + + protected void postAttributes(EntityId entityId, AttributeScope scope, String payload) throws Exception { + doPostAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + + "/attributes/" + scope, JacksonUtil.toJsonNode(payload), 30_000L).andExpect(status().isOk()); + } + + protected CalculatedField saveCalculatedField(CalculatedField calculatedField) { + return doPost("/api/calculatedField", calculatedField, CalculatedField.class); + } + + protected PageData getEntityCalculatedFields(EntityId entityId, CalculatedFieldType type, PageLink pageLink) throws Exception { + return doGetTypedWithPageLink("/api/" + entityId.getEntityType() + "/" + entityId.getId() + "/calculatedFields" + + (type != null ? "?type=" + type.name() + "&" : "?"), new TypeReference<>() {}, pageLink); + } + + protected PageData getCalculatedFieldNames(CalculatedFieldType type, PageLink pageLink) throws Exception { + return doGetTypedWithPageLink("/api/calculatedFields/names?type=" + type + "&", + new TypeReference>() {}, pageLink); + } + + protected List getCalculatedFields(CalculatedFieldType type, + EntityType entityType, + List entities, + List names) throws Exception { + return doGetTypedWithPageLink("/api/calculatedFields?" + + (type != null ? "types=" + type + "&" : "") + + (entityType != null ? "entityType=" + entityType + "&" : "") + + (entities != null ? "entities=" + String.join(",", + entities.stream().map(UUID::toString).toList()) + "&" : "") + + (names != null ? names.stream().map(name -> "name=" + name + "&").collect(Collectors.joining("")) : ""), + new TypeReference>() {}, new PageLink(10)).getData(); + } + protected PageData getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception { return getEvents(tenantId, entityId, EventType.DEBUG_RULE_NODE, limit); } 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 b87473f6e3..17a30cac35 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java @@ -48,6 +48,7 @@ import java.util.List; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.COMMENT_DELETED; @Slf4j @ContextConfiguration(classes = {AlarmCommentControllerTest.Config.class}) @@ -207,8 +208,10 @@ public class AlarmCommentControllerTest extends AbstractControllerTest { AlarmComment expectedAlarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment", - CUSTOMER_USER_EMAIL))) + .comment(JacksonUtil.newObjectNode() + .put("text", String.format(COMMENT_DELETED.getText(), CUSTOMER_USER_EMAIL)) + .put("subtype", COMMENT_DELETED.name()) + .put("userName", CUSTOMER_USER_EMAIL)) .build(); testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment); } @@ -226,8 +229,10 @@ public class AlarmCommentControllerTest extends AbstractControllerTest { AlarmComment expectedAlarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment", - TENANT_ADMIN_EMAIL))) + .comment(JacksonUtil.newObjectNode() + .put("text", String.format(COMMENT_DELETED.getText(), TENANT_ADMIN_EMAIL)) + .put("subtype", COMMENT_DELETED.name()) + .put("userName", TENANT_ADMIN_EMAIL)) .build(); testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment); } diff --git a/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java new file mode 100644 index 0000000000..836d1cbfad --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java @@ -0,0 +1,151 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.UUID; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +public class ApiKeyControllerTest extends AbstractControllerTest { + + @Before + public void setUp() throws Exception { + loginTenantAdmin(); + } + + @Test + public void testSaveApiKey() throws Exception { + ApiKeyInfo apiKeyInfo = constructApiKeyInfo("New API key description", true); + + doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + + PageData pageData = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData.getData().size()); + + ApiKeyInfo savedApiKey = pageData.getData().get(0); + Assert.assertNotNull(savedApiKey); + Assert.assertEquals(apiKeyInfo.getDescription(), savedApiKey.getDescription()); + Assert.assertEquals(apiKeyInfo.isEnabled(), savedApiKey.isEnabled()); + Assert.assertEquals(tenantId, savedApiKey.getTenantId()); + Assert.assertEquals(tenantAdminUser.getId(), savedApiKey.getUserId()); + + String newDescription = "Updated API Key Description"; + savedApiKey.setDescription(newDescription); + ApiKey updatedApiKey = doPost("/api/apiKey", savedApiKey, ApiKey.class); + Assert.assertNotNull(updatedApiKey); + Assert.assertEquals(newDescription, updatedApiKey.getDescription()); + Assert.assertNull("Verify we do not expose API key value on update", updatedApiKey.getValue()); + + doDelete("/api/apiKey/" + updatedApiKey.getId()).andExpect(status().isOk()); + } + + @Test + public void tesFindUserApiKeys() throws Exception { + PageData pageData = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertTrue(pageData.getData().isEmpty()); + + ApiKeyInfo apiKeyInfo = constructApiKeyInfo("Test API key description", true); + int expectedSize = 10; + for (int i = 0; i < expectedSize; i++) { + doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + } + + PageData pageData2 = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(expectedSize, pageData2.getData().size()); + + pageData2.getData().forEach(apiKey -> { + try { + doDelete("/api/apiKey/" + apiKey.getId()).andExpect(status().isOk()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @Test + public void testUpdateApiKeyDescription() throws Exception { + ApiKeyInfo apiKeyInfo = constructApiKeyInfo("Test API key description", true); + doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + + PageData pageData = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData.getData().size()); + + ApiKeyInfo savedApiKey = pageData.getData().get(0); + + String newDescription = "Updated API Key Description"; + + ApiKeyInfo updatedApiKeyInfo = doPut("/api/apiKey/" + savedApiKey.getId().getId() + "/description", newDescription, ApiKeyInfo.class); + Assert.assertNotNull(updatedApiKeyInfo); + Assert.assertEquals(newDescription, updatedApiKeyInfo.getDescription()); + + doDelete("/api/apiKey/" + savedApiKey.getId()).andExpect(status().isOk()); + } + + @Test + public void testEnableApiKey() throws Exception { + ApiKeyInfo apiKeyInfo = constructApiKeyInfo("Test API key description", true); + doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + + PageData pageData = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData.getData().size()); + + ApiKeyInfo savedApiKey = pageData.getData().get(0); + + ApiKeyInfo disabledApiKeyInfo = doPut("/api/apiKey/" + savedApiKey.getId().getId() + "/enabled/false", Boolean.FALSE, ApiKeyInfo.class); + Assert.assertNotNull(disabledApiKeyInfo); + Assert.assertFalse(disabledApiKeyInfo.isEnabled()); + + ApiKeyInfo enabledApiKeyInfo = doPut("/api/apiKey/" + savedApiKey.getId().getId() + "/enabled/true", Boolean.TRUE, ApiKeyInfo.class); + Assert.assertNotNull(enabledApiKeyInfo); + Assert.assertTrue(enabledApiKeyInfo.isEnabled()); + + doDelete("/api/apiKey/" + savedApiKey.getId()).andExpect(status().isOk()); + } + + @Test + public void testDeleteApiKey() throws Exception { + doDelete("/api/apiKey/" + UUID.randomUUID()).andExpect(status().isNotFound()); + + ApiKeyInfo apiKeyInfo = constructApiKeyInfo("Test API key description", false); + doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + + PageData pageData = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData.getData().size()); + ApiKeyInfo savedApiKey = pageData.getData().get(0); + + doDelete("/api/apiKey/" + savedApiKey.getId().getId()).andExpect(status().isOk()); + } + + private ApiKeyInfo constructApiKeyInfo(String description, boolean enabled) { + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); + apiKeyInfo.setDescription(description); + apiKeyInfo.setEnabled(enabled); + apiKeyInfo.setUserId(tenantAdminUserId); + return apiKeyInfo; + } + +} 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 501b639388..98b06ebbd1 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java @@ -50,7 +50,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.asset.AssetDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -1080,6 +1080,30 @@ public class AssetControllerTest extends AbstractControllerTest { testEntityDaoWithRelationsTransactionalException(assetDao, savedTenant.getId(), assetId, "/api/asset/" + assetId); } + @Test + public void testSaveAssetWithUniquifyStrategy() throws Exception { + Asset asset = new Asset(); + asset.setName("My unique asset"); + asset.setType("default"); + doPost("/api/asset", asset, Asset.class); + + doPost("/api/asset", asset).andExpect(status().isBadRequest()); + + doPost("/api/asset?nameConflictPolicy=FAIL", asset).andExpect(status().isBadRequest()); + + Asset secondAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY", asset, Asset.class); + assertThat(secondAsset.getName()).startsWith("My unique asset_"); + + Asset thirdAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", asset, Asset.class); + assertThat(thirdAsset.getName()).startsWith("My unique asset-"); + + Asset fourthAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", asset, Asset.class); + assertThat(fourthAsset.getName()).isEqualTo("My unique asset_1"); + + Asset fifthAsset = doPost("/api/asset?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", asset, Asset.class); + assertThat(fifthAsset.getName()).isEqualTo("My unique asset_2"); + } + private Asset createAsset(String name) { Asset asset = new Asset(); asset.setName(name); 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 ac2a14fbff..490973f66c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AssetProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AssetProfileControllerTest.java @@ -42,13 +42,17 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.asset.AssetProfileDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; @@ -195,6 +199,38 @@ public class AssetProfileControllerTest extends AbstractControllerTest { Assert.assertEquals(savedAssetProfile.getName(), foundAssetProfileInfo.getName()); } + @Test + public void testFindAssetProfileInfoByIds() throws Exception { + List assetProfiles = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + AssetProfile assetProfile = this.createAssetProfile("Asset Profile" + i); + assetProfile.setTenantId(savedTenant.getId()); + assetProfiles.add(doPost("/api/assetProfile", assetProfile, AssetProfileInfo.class)); + } + + List expected = assetProfiles.subList(5, 15); + + String idsParam = expected.stream() + .map(ap -> ap.getId().getId().toString()) + .collect(Collectors.joining(",")); + AssetProfileInfo[] foundAssetProfileInfos = doGet("/api/assetProfileInfos?assetProfileIds=" + idsParam, AssetProfileInfo[].class); + + Assert.assertNotNull(foundAssetProfileInfos); + Assert.assertEquals(expected.size(), foundAssetProfileInfos.length); + + Map infoById = Arrays.stream(foundAssetProfileInfos) + .collect(Collectors.toMap(info -> info.getId().getId(), Function.identity())); + + for (AssetProfileInfo assetProfileInfo : expected) { + UUID id = assetProfileInfo.getId().getId(); + AssetProfileInfo info = infoById.get(id); + Assert.assertNotNull("AssetProfileInfo not found for id " + id, info); + + Assert.assertEquals(assetProfileInfo.getId(), info.getId()); + Assert.assertEquals(assetProfileInfo.getName(), info.getName()); + } + } + @Test public void whenGetAssetProfileInfoById_thenPermissionsAreChecked() throws Exception { AssetProfile assetProfile = createAssetProfile("Asset profile 1"); 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 46a1dba3a7..0d79882a94 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java @@ -24,8 +24,8 @@ 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.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.DataConstants; @@ -96,15 +96,15 @@ public class BaseQueueControllerTest extends AbstractControllerTest { private RuleEngineStatisticsService ruleEngineStatisticsService; @Autowired private StatsFactory statsFactory; - @SpyBean - private TimeseriesDao timeseriesDao; @Autowired private QueueStatsService queueStatsService; - @SpyBean + @MockitoSpyBean + private TimeseriesDao timeseriesDao; + @MockitoSpyBean private PartitionService partitionService; - @SpyBean + @MockitoSpyBean private TimeseriesService timeseriesService; - @SpyBean + @MockitoSpyBean private ActorSystemContext actorSystemContext; @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java index 1802bd29fa..d9ec187e39 100644 --- a/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java @@ -19,25 +19,48 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; 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.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldInfo; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; -import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.HourInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; +import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration; +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.common.data.page.SortOrder; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.Comparator; +import java.util.List; import java.util.Map; +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.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; @DaoSqlTest public class CalculatedFieldControllerTest extends AbstractControllerTest { @@ -73,7 +96,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { @Test public void testSaveCalculatedField() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - CalculatedField calculatedField = getCalculatedField(testDevice.getId()); + CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId()); CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); @@ -84,7 +107,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { assertThat(savedCalculatedField.getEntityId()).isEqualTo(calculatedField.getEntityId()); assertThat(savedCalculatedField.getType()).isEqualTo(calculatedField.getType()); assertThat(savedCalculatedField.getName()).isEqualTo(calculatedField.getName()); - assertThat(savedCalculatedField.getConfiguration()).isEqualTo(getCalculatedFieldConfig()); + assertThat(savedCalculatedField.getConfiguration()).isEqualTo(getSimpleCalculatedFieldConfig()); assertThat(savedCalculatedField.getVersion()).isEqualTo(1L); savedCalculatedField.setName("Test CF"); @@ -98,10 +121,104 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testSaveGeofencingCalculatedField() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.GEOFENCING); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + assertThat(savedCalculatedField).isNotNull(); + assertThat(savedCalculatedField.getId()).isNotNull(); + assertThat(savedCalculatedField.getCreatedTime()).isGreaterThan(0); + assertThat(savedCalculatedField.getTenantId()).isEqualTo(savedTenant.getId()); + assertThat(savedCalculatedField.getEntityId()).isEqualTo(calculatedField.getEntityId()); + assertThat(savedCalculatedField.getType()).isEqualTo(calculatedField.getType()); + assertThat(savedCalculatedField.getName()).isEqualTo(calculatedField.getName()); + assertThat(savedCalculatedField.getConfiguration()).isEqualTo(getGeofencingCalculatedFieldConfig()); + assertThat(savedCalculatedField.getVersion()).isEqualTo(1L); + + savedCalculatedField.setName("Test CF"); + + CalculatedField updatedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); + + assertThat(updatedCalculatedField.getName()).isEqualTo(savedCalculatedField.getName()); + assertThat(updatedCalculatedField.getVersion()).isEqualTo(savedCalculatedField.getVersion() + 1); + + doDelete("/api/calculatedField/" + savedCalculatedField.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testSavePropagationCalculatedField() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.PROPAGATION); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + assertThat(savedCalculatedField).isNotNull(); + assertThat(savedCalculatedField.getId()).isNotNull(); + assertThat(savedCalculatedField.getCreatedTime()).isGreaterThan(0); + assertThat(savedCalculatedField.getTenantId()).isEqualTo(savedTenant.getId()); + assertThat(savedCalculatedField.getEntityId()).isEqualTo(calculatedField.getEntityId()); + assertThat(savedCalculatedField.getType()).isEqualTo(calculatedField.getType()); + assertThat(savedCalculatedField.getName()).isEqualTo(calculatedField.getName()); + assertThat(savedCalculatedField.getConfiguration()).isEqualTo(getPropagationCalculatedFieldConfig()); + assertThat(savedCalculatedField.getVersion()).isEqualTo(1L); + + savedCalculatedField.setName("Test CF"); + + CalculatedField updatedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); + + assertThat(updatedCalculatedField.getName()).isEqualTo(savedCalculatedField.getName()); + assertThat(updatedCalculatedField.getVersion()).isEqualTo(savedCalculatedField.getVersion() + 1); + + doDelete("/api/calculatedField/" + savedCalculatedField.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testSaveEntityAggregationCalculatedField() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.ENTITY_AGGREGATION); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + assertThat(savedCalculatedField).isNotNull(); + assertThat(savedCalculatedField.getId()).isNotNull(); + assertThat(savedCalculatedField.getCreatedTime()).isGreaterThan(0); + assertThat(savedCalculatedField.getTenantId()).isEqualTo(savedTenant.getId()); + assertThat(savedCalculatedField.getEntityId()).isEqualTo(calculatedField.getEntityId()); + assertThat(savedCalculatedField.getType()).isEqualTo(calculatedField.getType()); + assertThat(savedCalculatedField.getName()).isEqualTo(calculatedField.getName()); + assertThat(savedCalculatedField.getConfiguration()).isEqualTo(getEntityAggregationCalculatedFieldConfig()); + assertThat(savedCalculatedField.getVersion()).isEqualTo(1L); + + savedCalculatedField.setName("Test CF"); + + CalculatedField updatedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); + + assertThat(updatedCalculatedField.getName()).isEqualTo(savedCalculatedField.getName()); + assertThat(updatedCalculatedField.getVersion()).isEqualTo(savedCalculatedField.getVersion() + 1); + + doDelete("/api/calculatedField/" + savedCalculatedField.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testSavePropagationCalculatedFieldWithNullArguments() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.PROPAGATION, getPropagationCalculatedFieldConfig(null)); + + doPost("/api/calculatedField", calculatedField) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("arguments must not be empty"))); + } + @Test public void testGetCalculatedFieldById() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - CalculatedField calculatedField = getCalculatedField(testDevice.getId()); + CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId()); CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); CalculatedField fetchedCalculatedField = doGet("/api/calculatedField/" + savedCalculatedField.getId().getId(), CalculatedField.class); @@ -113,10 +230,74 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testGetEntityCalculatedFields() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId()); + calculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + assertThat(getEntityCalculatedFields(testDevice.getId(), null, new PageLink(10)).getData()) + .singleElement().isEqualTo(calculatedField); + assertThat(getEntityCalculatedFields(testDevice.getId(), CalculatedFieldType.SIMPLE, new PageLink(10)).getData()) + .singleElement().isEqualTo(calculatedField); + } + + @Test + public void testGetCalculatedFieldsByFilter() throws Exception { + Device device = createDevice("Device A", "1234567890"); + CalculatedFieldInfo deviceCalculatedField = new CalculatedFieldInfo( + doPost("/api/calculatedField", getSimpleCalculatedField(device.getId()), CalculatedField.class), + "Device A" + ); + + DeviceProfile deviceProfile = doPost("/api/deviceProfile", createDeviceProfile("Profile A"), DeviceProfile.class); + CalculatedFieldInfo profileCalculatedField = new CalculatedFieldInfo( + doPost("/api/calculatedField", getSimpleCalculatedField(deviceProfile.getId()), CalculatedField.class), + "Profile A" + ); + + List allCalculatedFields = getCalculatedFields(CalculatedFieldType.SIMPLE, + null, null, null); + assertThat(allCalculatedFields).contains(deviceCalculatedField, profileCalculatedField); + allCalculatedFields = getCalculatedFields(null, null, null, null); + assertThat(allCalculatedFields).contains(deviceCalculatedField, profileCalculatedField); + + List profileLevelCalculatedFields = getCalculatedFields(CalculatedFieldType.SIMPLE, + EntityType.DEVICE_PROFILE, null, null); + assertThat(profileLevelCalculatedFields).containsOnly(profileCalculatedField); + + List specificDeviceCalculatedFields = getCalculatedFields(CalculatedFieldType.SIMPLE, + EntityType.DEVICE, List.of(device.getUuidId()), null); + assertThat(specificDeviceCalculatedFields).containsOnly(deviceCalculatedField); + + List byNameCalculatedFields = getCalculatedFields(CalculatedFieldType.SIMPLE, + null, null, List.of(deviceCalculatedField.getName())); + assertThat(byNameCalculatedFields).containsOnly(deviceCalculatedField); + + byNameCalculatedFields = getCalculatedFields(CalculatedFieldType.SIMPLE, null, null, + List.of(deviceCalculatedField.getName(), profileCalculatedField.getName())); + assertThat(byNameCalculatedFields).contains(deviceCalculatedField, profileCalculatedField); + + PageData names = getCalculatedFieldNames(CalculatedFieldType.SIMPLE, new PageLink(10, 0, + null, new SortOrder("", SortOrder.Direction.ASC))); + assertThat(names.getTotalElements()).isEqualTo(2); + assertThat(names.getData()).isSortedAccordingTo(Comparator.naturalOrder()); + assertThat(names.getData()).contains(deviceCalculatedField.getName(), profileCalculatedField.getName()); + + names = getCalculatedFieldNames(CalculatedFieldType.SIMPLE, new PageLink(10, 0, + null, new SortOrder("", SortOrder.Direction.DESC))); + assertThat(names.getData()).isSortedAccordingTo(Comparator.reverseOrder()); + + names = getCalculatedFieldNames(CalculatedFieldType.SIMPLE, new PageLink(10, 0, + device.getId().toString(), new SortOrder("", SortOrder.Direction.DESC))); + assertThat(names.getTotalElements()).isEqualTo(1); + assertThat(names.getData()).containsOnly(deviceCalculatedField.getName()); + } + @Test public void testDeleteCalculatedField() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); - CalculatedField calculatedField = getCalculatedField(testDevice.getId()); + CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId()); CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); @@ -127,18 +308,92 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { doGet("/api/calculatedField/" + savedCalculatedField.getId().getId()).andExpect(status().isNotFound()); } - private CalculatedField getCalculatedField(DeviceId deviceId) { + private CalculatedField getSimpleCalculatedField(EntityId entityId) { + return getCalculatedField(entityId, CalculatedFieldType.SIMPLE); + } + + private CalculatedField getCalculatedField(EntityId entityId, CalculatedFieldType cfType) { + return getCalculatedField(entityId, cfType, null); + } + + private CalculatedField getCalculatedField(EntityId entityId, CalculatedFieldType cfType, CalculatedFieldConfiguration customConfiguration) { CalculatedField calculatedField = new CalculatedField(); - calculatedField.setEntityId(deviceId); - calculatedField.setType(CalculatedFieldType.SIMPLE); - calculatedField.setName("Test Calculated Field"); + calculatedField.setEntityId(entityId); + calculatedField.setType(cfType); + calculatedField.setName("Test Calculated Field for " + entityId); calculatedField.setConfigurationVersion(1); - calculatedField.setConfiguration(getCalculatedFieldConfig()); + if (customConfiguration != null) { + calculatedField.setConfiguration(customConfiguration); + } else switch (cfType) { + case SIMPLE -> calculatedField.setConfiguration(getSimpleCalculatedFieldConfig()); + case GEOFENCING -> calculatedField.setConfiguration(getGeofencingCalculatedFieldConfig()); + case PROPAGATION -> calculatedField.setConfiguration(getPropagationCalculatedFieldConfig()); + case ENTITY_AGGREGATION -> calculatedField.setConfiguration(getEntityAggregationCalculatedFieldConfig()); + } calculatedField.setVersion(1L); return calculatedField; } - private CalculatedFieldConfiguration getCalculatedFieldConfig() { + private CalculatedFieldConfiguration getGeofencingCalculatedFieldConfig() { + var config = new GeofencingCalculatedFieldConfiguration(); + + var refDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + refDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.TO, "FromSafeArea"))); + + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration); + + config.setEntityCoordinates(new EntityCoordinates("latitide", "longitude")); + config.setZoneGroups(Map.of("safeArea", zoneGroupConfiguration)); + config.setScheduledUpdateEnabled(false); + config.setOutput(new TimeSeriesOutput()); + + return config; + } + + private CalculatedFieldConfiguration getPropagationCalculatedFieldConfig() { + Argument arg = new Argument(); + arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + return getPropagationCalculatedFieldConfig(Map.of("t", arg)); + } + + private CalculatedFieldConfiguration getPropagationCalculatedFieldConfig(Map arguments) { + var config = new PropagationCalculatedFieldConfiguration(); + + config.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + + config.setApplyExpressionToResolvedArguments(false); + config.setExpression(null); + config.setOutput(new TimeSeriesOutput()); + + Argument arg = new Argument(); + arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + config.setArguments(arguments); + + return config; + } + + private CalculatedFieldConfiguration getEntityAggregationCalculatedFieldConfig() { + var config = new EntityAggregationCalculatedFieldConfiguration(); + + Argument energyArgument = new Argument(); + energyArgument.setRefEntityKey(new ReferencedEntityKey("energy", ArgumentType.TS_LATEST, null)); + config.setArguments(Map.of("en", energyArgument)); + + AggMetric metric = new AggMetric(); + metric.setInput(new AggKeyInput("en")); + metric.setDefaultValue(9999.0); + config.setMetrics(Map.of("consumption", metric)); + + config.setWatermark(new Watermark(TimeUnit.DAYS.toSeconds(1))); + config.setInterval(new HourInterval("Europe/Kiev", TimeUnit.MINUTES.toSeconds(15))); + + config.setOutput(new TimeSeriesOutput()); + + return config; + } + + private CalculatedFieldConfiguration getSimpleCalculatedFieldConfig() { SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); Argument argument = new Argument(); @@ -150,9 +405,8 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); 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 74bad63d07..dd3ab07ba0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/CustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/CustomerControllerTest.java @@ -33,6 +33,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; 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.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; @@ -43,12 +44,17 @@ 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.dao.customer.CustomerDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; @@ -260,6 +266,40 @@ public class CustomerControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testFindCustomersByIds() throws Exception { + List savedCustomers = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + Customer customer = new Customer(); + customer.setTitle("My customer " + i); + savedCustomers.add(doPost("/api/customer", customer, Customer.class)); + } + + String idsParam = savedCustomers.stream() + .map(c -> c.getId().getId().toString()) + .collect(Collectors.joining(",")); + + Customer[] foundCustomers = doGet("/api/customers?customerIds=" + idsParam, Customer[].class); + + Assert.assertNotNull(foundCustomers); + Assert.assertEquals(savedCustomers.size(), foundCustomers.length); + + Map foundById = Arrays.stream(foundCustomers) + .collect(Collectors.toMap(c -> c.getId().getId(), Function.identity())); + + for (Customer savedCustomer : savedCustomers) { + UUID id = savedCustomer.getId().getId(); + Customer foundCustomer = foundById.get(id); + Assert.assertNotNull("Customer not found for id " + id, foundCustomer); + Assert.assertEquals(savedCustomer, foundCustomer); + } + + for (Customer savedCustomer : savedCustomers) { + doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) + .andExpect(status().isOk()); + } + } + @Test public void testDeleteCustomer() throws Exception { Customer customer = new Customer(); @@ -462,6 +502,27 @@ public class CustomerControllerTest extends AbstractControllerTest { testEntityDaoWithRelationsTransactionalException(customerDao, savedTenant.getId(), customerId, "/api/customer/" + customerId); } + @Test + public void testSaveCustomerWithUniquifyStrategy() throws Exception { + Customer customer = new Customer(); + customer.setTitle("My unique customer"); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + doPost("/api/customer?nameConflictPolicy=FAIL", customer).andExpect(status().isBadRequest()); + + Customer secondCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY", customer, Customer.class); + assertThat(secondCustomer.getName()).startsWith("My unique customer_"); + + Customer thirdCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", customer, Customer.class); + assertThat(thirdCustomer.getName()).startsWith("My unique customer-"); + + Customer fourthCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", customer, Customer.class); + assertThat(fourthCustomer.getName()).isEqualTo("My unique customer_1"); + + Customer fifthCustomer = doPost("/api/customer?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", customer, Customer.class); + assertThat(fifthCustomer.getName()).isEqualTo("My unique customer_2"); + } + private Customer createCustomer(String title) { Customer customer = new Customer(); customer.setTitle(title); 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 ff904bb948..27fcabbd2c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java @@ -51,13 +51,16 @@ 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.dao.dashboard.DashboardDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -155,6 +158,42 @@ public class DashboardControllerTest extends AbstractControllerTest { Assert.assertEquals(savedDashboard, foundDashboard); } + @Test + public void testFindDashboardInfosByIds() throws Exception { + List dashboards = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + Dashboard dashboard = new Dashboard(); + dashboard.setTitle("My dashboard " + i); + dashboards.add(doPost("/api/dashboard", dashboard, Dashboard.class)); + } + + List expected = dashboards.subList(5, 15); + + String idsParam = expected.stream() + .map(d -> d.getId().getId().toString()) + .collect(Collectors.joining(",")); + + DashboardInfo[] result = doGet( + "/api/dashboards?dashboardIds=" + idsParam, + DashboardInfo[].class + ); + + Assert.assertNotNull(result); + Assert.assertEquals(expected.size(), result.length); + + Map infoById = Arrays.stream(result) + .collect(Collectors.toMap(info -> info.getId().getId(), Function.identity())); + + for (Dashboard dashboard : expected) { + UUID id = dashboard.getId().getId(); + DashboardInfo info = infoById.get(id); + Assert.assertNotNull("DashboardInfo not found for id " + id, info); + + Assert.assertEquals(dashboard.getId(), info.getId()); + Assert.assertEquals(dashboard.getTitle(), info.getTitle()); + } + } + @Test public void testDeleteDashboard() throws Exception { Dashboard dashboard = new Dashboard(); diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java index 7d78877b9e..9a80ad2e25 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -95,7 +95,7 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { private DeviceProfileId mqttDeviceProfileId; private DeviceProfileId coapDeviceProfileId; - @Value("${device.connectivity.gateway.image_version:3.7-stable}") + @Value("${device.connectivity.gateway.image_version:3.8-stable}") private String gatewayImageVersion; @Before @@ -321,9 +321,10 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { "\n" + " # Environment variables\n" + " environment:\n" + - " - host=host.docker.internal\n" + - " - port=1883\n" + - " - accessToken=" + credentials.getCredentialsId() + "\n" + + " - TB_GW_HOST=host.docker.internal\n" + + " - TB_GW_PORT=1883\n" + + " - TB_GW_SECURITY_TYPE=accessToken\n" + + " - TB_GW_ACCESS_TOKEN=" + credentials.getCredentialsId() + "\n" + "\n" + " # Volumes bind\n" + " volumes:\n" + 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 0e404b2857..7332ee26c6 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java @@ -38,6 +38,7 @@ 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.AttributeScope; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; @@ -59,6 +60,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceCredentialsId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -70,15 +72,19 @@ 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.dao.device.DeviceDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.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 org.thingsboard.server.utils.CsvUtils; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -1629,6 +1635,57 @@ public class DeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(newAttributeValue, actualAttribute.get("value")); } + @Test + public void testBulkImportDeviceWithJsonAttr() throws Exception { + String deviceName = "some_device"; + String deviceType = "some_type"; + String deviceAttr = "{\"threshold\":45}"; + + List> content = new LinkedList<>(); + content.add(Arrays.asList("NAME", "TYPE", "ATTR")); + content.add(Arrays.asList(deviceName, deviceType, deviceAttr)); + + byte[] bytes = CsvUtils.generateCsv(content); + BulkImportRequest request = new BulkImportRequest(); + request.setFile(new String(bytes, StandardCharsets.UTF_8)); + BulkImportRequest.Mapping mapping = new BulkImportRequest.Mapping(); + BulkImportRequest.ColumnMapping name = new BulkImportRequest.ColumnMapping(); + name.setType(BulkImportColumnType.NAME); + BulkImportRequest.ColumnMapping type = new BulkImportRequest.ColumnMapping(); + type.setType(BulkImportColumnType.TYPE); + BulkImportRequest.ColumnMapping attr = new BulkImportRequest.ColumnMapping(); + attr.setType(BulkImportColumnType.SERVER_ATTRIBUTE); + attr.setKey("attr"); + List columns = new ArrayList<>(); + columns.add(name); + columns.add(type); + columns.add(attr); + + mapping.setColumns(columns); + mapping.setDelimiter(','); + mapping.setUpdate(true); + mapping.setHeader(true); + request.setMapping(mapping); + + BulkImportResult deviceBulkImportResult = doPostWithTypedResponse("/api/device/bulk_import", request, new TypeReference<>() {}); + + Assert.assertEquals(1, deviceBulkImportResult.getCreated().get()); + Assert.assertEquals(0, deviceBulkImportResult.getErrors().get()); + Assert.assertEquals(0, deviceBulkImportResult.getUpdated().get()); + Assert.assertTrue(deviceBulkImportResult.getErrorsList().isEmpty()); + + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); + + Assert.assertNotNull(savedDevice); + Assert.assertEquals(deviceName, savedDevice.getName()); + Assert.assertEquals(deviceType, savedDevice.getType()); + + Optional retrieved = await().atMost(5, TimeUnit.SECONDS) + .until(() -> attributesService.find(tenantId, savedDevice.getId(), AttributeScope.SERVER_SCOPE, "attr").get(), Optional::isPresent); + assertThat(retrieved.get().getJsonValue().get()).isEqualTo(deviceAttr); + assertThat(retrieved.get().getStrValue()).isNotPresent(); + } + @Test public void testSaveDeviceWithOutdatedVersion() throws Exception { Device device = createDevice("Device v1.0"); @@ -1651,6 +1708,30 @@ public class DeviceControllerTest extends AbstractControllerTest { assertThat(device.getVersion()).isEqualTo(3); } + @Test + public void testSaveDeviceWithUniquifyStrategy() throws Exception { + Device device = new Device(); + device.setName("My unique device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + + doPost("/api/device", device).andExpect(status().isBadRequest()); + + doPost("/api/device?nameConflictPolicy=FAIL", device).andExpect(status().isBadRequest()); + + Device secondDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY", device, Device.class); + assertThat(secondDevice.getName()).startsWith("My unique device_"); + + Device thirdDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", device, Device.class); + assertThat(thirdDevice.getName()).startsWith("My unique device-"); + + Device fourthDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", device, Device.class); + assertThat(fourthDevice.getName()).isEqualTo("My unique device_1"); + + Device fifthDevice = doPost("/api/device?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", device, Device.class); + assertThat(fifthDevice.getName()).isEqualTo("My unique device_2"); + } + private Device createDevice(String name) { Device device = new Device(); device.setName(name); 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 225757e451..3966bbb97a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java @@ -52,13 +52,17 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.device.DeviceProfileDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -214,6 +218,42 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType()); } + @Test + public void testFindDeviceProfileInfosByIds() throws Exception { + List deviceProfiles = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile " + i); + deviceProfiles.add(saveDeviceProfile(deviceProfile)); + } + + List expected = deviceProfiles.subList(5, 15); + + String idsParam = expected.stream() + .map(dp -> dp.getId().getId().toString()) + .collect(Collectors.joining(",")); + + DeviceProfileInfo[] result = doGet( + "/api/deviceProfileInfos?deviceProfileIds=" + idsParam, + DeviceProfileInfo[].class + ); + + Assert.assertNotNull(result); + Assert.assertEquals(expected.size(), result.length); + + Map infoById = Arrays.stream(result) + .collect(Collectors.toMap(info -> info.getId().getId(), Function.identity())); + + for (DeviceProfile dp : expected) { + UUID id = dp.getId().getId(); + DeviceProfileInfo info = infoById.get(id); + Assert.assertNotNull("DeviceProfileInfo not found for id " + id, info); + + Assert.assertEquals(dp.getId(), info.getId()); + Assert.assertEquals(dp.getName(), info.getName()); + Assert.assertEquals(dp.getType(), info.getType()); + } + } + @Test public void whenGetDeviceProfileInfoById_thenPermissionsAreChecked() throws Exception { DeviceProfile deviceProfile = createDeviceProfile("Device profile 1", null); 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 fc65d67949..9d50e3a62c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -75,7 +75,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.UserCredentials; 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.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.edge.imitator.EdgeImitator; diff --git a/application/src/test/java/org/thingsboard/server/controller/EdqsControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdqsControllerTest.java index 61d821ba83..fbd7d73421 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdqsControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdqsControllerTest.java @@ -22,6 +22,7 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; @@ -41,6 +42,7 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.edqs.util.EdqsRocksDb; import java.util.ArrayList; import java.util.Collections; @@ -62,8 +64,11 @@ public class EdqsControllerTest extends AbstractControllerTest { @Autowired private JdbcTemplate jdbcTemplate; + @MockitoBean + private EdqsRocksDb edqsRocksDb; + @Before - public void beforeEdqsControllerTest() throws Exception { + public void before() throws Exception { loginTenantAdmin(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java index 932980a3ca..f15ab09ab2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java @@ -19,7 +19,6 @@ import org.assertj.core.api.ThrowingConsumer; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.server.common.data.edqs.EdqsState; import org.thingsboard.server.common.data.edqs.EdqsState.EdqsApiMode; @@ -33,7 +32,6 @@ import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.msg.edqs.EdqsService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.edqs.util.EdqsRocksDb; import org.thingsboard.server.queue.discovery.DiscoveryService; import java.util.concurrent.TimeUnit; @@ -59,9 +57,6 @@ public class EdqsEntityQueryControllerTest extends EntityQueryControllerTest { @Autowired private DiscoveryService discoveryService; - @MockBean // so that we don't do backup for tests - private EdqsRocksDb edqsRocksDb; - @Before public void before() { await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> edqsService.getState().isApiEnabled()); diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index e557626ee7..e1173ef9a7 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; @@ -77,12 +78,14 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.edqs.util.EdqsRocksDb; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -107,6 +110,9 @@ public class EntityQueryControllerTest extends AbstractControllerTest { @Autowired private QueueStatsService queueStatsService; + @MockitoBean + private EdqsRocksDb edqsRocksDb; + @Before public void beforeTest() throws Exception { loginSysAdmin(); @@ -495,19 +501,22 @@ public class EntityQueryControllerTest extends AbstractControllerTest { List alarmFields = new ArrayList<>(); alarmFields.add(new EntityKey(EntityKeyType.ALARM_FIELD, "type")); + alarmFields.add(new EntityKey(EntityKeyType.ALARM_FIELD, "originatorDisplayName")); EntityTypeFilter assetTypeFilter = new EntityTypeFilter(); assetTypeFilter.setEntityType(EntityType.ASSET); - AlarmDataQuery assetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, null, alarmFields); + AlarmDataQuery assetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, null, alarmFields); PageData alarmPageData = findAlarmsByQueryAndCheck(assetAlarmQuery, 10); - List retrievedAlarmTypes = alarmPageData.getData().stream().map(Alarm::getType).toList(); + List retrievedAlarmTypes = alarmPageData.getData().stream().map(AlarmData::getType).toList(); assertThat(retrievedAlarmTypes).containsExactlyInAnyOrderElementsOf(assetAlarmTypes); + List retrievedAlarmDisplayName = alarmPageData.getData().stream().map(AlarmData::getOriginatorDisplayName).toList(); + assertThat(retrievedAlarmDisplayName).containsExactlyInAnyOrderElementsOf(assets.stream().map(Asset::getLabel).toList()); KeyFilter nameFilter = buildStringKeyFilter(EntityKeyType.ENTITY_FIELD, "name", StringFilterPredicate.StringOperation.STARTS_WITH, "Asset1"); List keyFilters = Collections.singletonList(nameFilter); - AlarmDataQuery filteredAssetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, keyFilters, alarmFields); - PageData filteredAssetAlamData = doPostWithTypedResponse("/api/alarmsQuery/find", filteredAssetAlarmQuery, new TypeReference<>() { + AlarmDataQuery filteredAssetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, keyFilters, alarmFields); + PageData filteredAssetAlamData = doPostWithTypedResponse("/api/alarmsQuery/find", filteredAssetAlarmQuery, new TypeReference<>() { }); Assert.assertEquals(1, filteredAssetAlamData.getTotalElements()); } @@ -569,16 +578,16 @@ public class EntityQueryControllerTest extends AbstractControllerTest { EntityTypeFilter assetTypeFilter = new EntityTypeFilter(); assetTypeFilter.setEntityType(EntityType.ASSET); - AlarmDataQuery assetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, null, Collections.emptyList()); + AlarmDataQuery assetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, null, Collections.emptyList()); - PageData alarmPageData = findAlarmsByQueryAndCheck(assetAlarmQuery, 10); + PageData alarmPageData = findAlarmsByQueryAndCheck(assetAlarmQuery, 10); List retrievedAlarmTypes = alarmPageData.getData().stream().map(Alarm::getType).toList(); assertThat(retrievedAlarmTypes).containsExactlyInAnyOrderElementsOf(assetAlarmTypes); KeyFilter nameFilter = buildStringKeyFilter(EntityKeyType.ENTITY_FIELD, "name", StringFilterPredicate.StringOperation.STARTS_WITH, "Asset1"); List keyFilters = Collections.singletonList(nameFilter); - AlarmDataQuery filteredAssetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, keyFilters, Collections.emptyList()); - PageData filteredAssetAlamData = doPostWithTypedResponse("/api/alarmsQuery/find", filteredAssetAlarmQuery, new TypeReference<>() { + AlarmDataQuery filteredAssetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, keyFilters, Collections.emptyList()); + PageData filteredAssetAlamData = doPostWithTypedResponse("/api/alarmsQuery/find", filteredAssetAlarmQuery, new TypeReference<>() { }); Assert.assertEquals(1, filteredAssetAlamData.getTotalElements()); } @@ -634,7 +643,7 @@ public class EntityQueryControllerTest extends AbstractControllerTest { EntityTypeFilter deviceTypeFilter = new EntityTypeFilter(); deviceTypeFilter.setEntityType(EntityType.DEVICE); - AlarmDataQuery deviceAlarmQuery = new AlarmDataQuery(deviceTypeFilter, pageLink, entityFields, latestValues, null, alarmFields); + AlarmDataQuery deviceAlarmQuery = new AlarmDataQuery(deviceTypeFilter, pageLink, entityFields, latestValues, null, alarmFields); PageData alarmPageData = findAlarmsByQueryAndCheck(deviceAlarmQuery, 10); List retrievedAlarmTemps = alarmPageData.getData().stream().map(alarmData -> alarmData.getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getValue()).toList(); @@ -1132,6 +1141,119 @@ public class EntityQueryControllerTest extends AbstractControllerTest { countByQueryAndCheck(customerEntitiesQuery, 0); } + @Test + public void testFindDevicesByDisplayName() throws Exception { + loginTenantAdmin(); + int numOfDevices = 3; + + for (int i = 0; i < numOfDevices; i++) { + Device device = new Device(); + String name = "Device" + i; + device.setName(name); + device.setLabel("Device Label " + i); + device.setType("testFindDevicesByDisplayName"); + + Device savedDevice = doPost("/api/device?accessToken=" + name, device, Device.class); + } + + DeviceTypeFilter filter = new DeviceTypeFilter(); + filter.setDeviceTypes(List.of("testFindDevicesByDisplayName")); + filter.setDeviceNameFilter(""); + + KeyFilter displayNameFilter = getEntityFieldEqualFilter("displayName", "Device Label " + 0); + + EntityDataSortOrder sortOrder = new EntityDataSortOrder( + new EntityKey(EntityKeyType.ENTITY_FIELD, "displayName"), EntityDataSortOrder.Direction.ASC + ); + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); + List entityFields = List.of(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "displayName")); + + // all devices with ownerName = TEST TENANT + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, Collections.emptyList(), Collections.emptyList()); + checkEntitiesByQuery(query, numOfDevices, (i, entity) -> { + String name = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("name", new TsValue(0, "Invalid")).getValue(); + String displayName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("displayName", new TsValue(0, "Invalid")).getValue(); + Assert.assertEquals("Device" + i, name); + Assert.assertEquals("Device Label " + i, displayName); + }); + + // all devices with ownerName = TEST TENANT + EntityDataQuery displayNameFilterQuery = new EntityDataQuery(filter, pageLink, entityFields, Collections.emptyList(), List.of(displayNameFilter)); + checkEntitiesByQuery(displayNameFilterQuery, 1, (i, entity) -> { + String name = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("name", new TsValue(0, "Invalid")).getValue(); + String displayName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("displayName", new TsValue(0, "Invalid")).getValue(); + Assert.assertEquals("Device" + i, name); + Assert.assertEquals("Device Label " + i, displayName); + }); + } + + @Test + public void testFindUsersByDisplayName() throws Exception { + loginTenantAdmin(); + + User userA = new User(); + userA.setAuthority(Authority.TENANT_ADMIN); + userA.setFirstName("John"); + userA.setLastName("Doe"); + userA.setEmail("john.doe@tb.org"); + userA = doPost("/api/user", userA, User.class); + var aId = userA.getId(); + + User userB = new User(); + userB.setAuthority(Authority.TENANT_ADMIN); + userB.setFirstName("John"); + userB.setEmail("john@tb.org"); + userB = doPost("/api/user", userB, User.class); + var bId = userB.getId(); + + User userC = new User(); + userC.setAuthority(Authority.TENANT_ADMIN); + userC.setLastName("Doe"); + userC.setEmail("doe@tb.org"); + userC = doPost("/api/user", userC, User.class); + var cId = userC.getId(); + + User userD = new User(); + userD.setAuthority(Authority.TENANT_ADMIN); + userD.setEmail("noname@tb.org"); + userD = doPost("/api/user", userD, User.class); + var dId = userD.getId(); + + EntityTypeFilter filter = new EntityTypeFilter(); + filter.setEntityType(EntityType.USER); + + EntityDataSortOrder sortOrder = new EntityDataSortOrder( + new EntityKey(EntityKeyType.ENTITY_FIELD, "displayName"), EntityDataSortOrder.Direction.ASC + ); + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); + List entityFields = List.of(new EntityKey(EntityKeyType.ENTITY_FIELD, "displayName")); + + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, Collections.emptyList(), List.of(getEntityFieldEqualFilter("displayName", "John Doe"))); + checkEntitiesByQuery(query, 1, (i, entity) -> { + Assert.assertEquals(aId, entity.getEntityId()); + String displayName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("displayName", new TsValue(0, "Invalid")).getValue(); + Assert.assertEquals("John Doe", displayName); + }); + query = new EntityDataQuery(filter, pageLink, entityFields, Collections.emptyList(), List.of(getEntityFieldEqualFilter("displayName", "John"))); + checkEntitiesByQuery(query, 1, (i, entity) -> { + Assert.assertEquals(bId, entity.getEntityId()); + String displayName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("displayName", new TsValue(0, "Invalid")).getValue(); + Assert.assertEquals("John", displayName); + }); + query = new EntityDataQuery(filter, pageLink, entityFields, Collections.emptyList(), List.of(getEntityFieldEqualFilter("displayName", "Doe"))); + checkEntitiesByQuery(query, 1, (i, entity) -> { + Assert.assertEquals(cId, entity.getEntityId()); + String displayName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("displayName", new TsValue(0, "Invalid")).getValue(); + Assert.assertEquals("Doe", displayName); + }); + query = new EntityDataQuery(filter, pageLink, entityFields, Collections.emptyList(), List.of(getEntityFieldEqualFilter("displayName", "noname@tb.org"))); + checkEntitiesByQuery(query, 1, (i, entity) -> { + Assert.assertEquals(dId, entity.getEntityId()); + String displayName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("displayName", new TsValue(0, "Invalid")).getValue(); + Assert.assertEquals("noname@tb.org", displayName); + }); + } + @Test public void testFindDevicesByOwnerNameAndOwnerType() throws Exception { loginTenantAdmin(); @@ -1169,19 +1291,30 @@ public class EntityQueryControllerTest extends AbstractControllerTest { // all devices with ownerName = TEST TENANT EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, tenantOwnerNameFilter)); - checkEntitiesByQuery(query, numOfDevices, TEST_TENANT_NAME, "TENANT"); + BiConsumer checkFunction = (i, entity) -> { + String name = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("name", new TsValue(0, "Invalid")).getValue(); + String ownerName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("ownerName", new TsValue(0, "Invalid")).getValue(); + String ownerType = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("ownerType", new TsValue(0, "Invalid")).getValue(); + String alarmActiveTime = entity.getLatest().get(EntityKeyType.ATTRIBUTE).getOrDefault("alarmActiveTime", new TsValue(0, "-1")).getValue(); + + Assert.assertEquals("Device" + i, name); + Assert.assertEquals(TEST_TENANT_NAME, ownerName); + Assert.assertEquals("TENANT", ownerType); + Assert.assertEquals("1" + i, alarmActiveTime); + }; + checkEntitiesByQuery(query, numOfDevices, checkFunction); // all devices with wrong ownerName EntityDataQuery wrongTenantNameQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, wrongOwnerNameFilter)); - checkEntitiesByQuery(wrongTenantNameQuery, 0, null, null); + checkEntitiesByQuery(wrongTenantNameQuery, 0, null); // all devices with owner type = TENANT EntityDataQuery tenantEntitiesQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, tenantOwnerTypeFilter)); - checkEntitiesByQuery(tenantEntitiesQuery, numOfDevices, TEST_TENANT_NAME, "TENANT"); + checkEntitiesByQuery(tenantEntitiesQuery, numOfDevices, checkFunction); // all devices with owner type = CUSTOMER EntityDataQuery customerEntitiesQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, customerOwnerTypeFilter)); - checkEntitiesByQuery(customerEntitiesQuery, 0, null, null); + checkEntitiesByQuery(customerEntitiesQuery, 0, null); } @Test @@ -1227,6 +1360,28 @@ public class EntityQueryControllerTest extends AbstractControllerTest { findByQueryAndCheck(query, 0); } + private void checkEntitiesByQuery(EntityDataQuery query, int expectedNumOfDevices, BiConsumer checkFunction) throws Exception { + await() + .alias("data by query") + .atMost(30, TimeUnit.SECONDS) + .until(() -> { + var data = findByQuery(query); + var loadedEntities = new ArrayList<>(data.getData()); + return loadedEntities.size() == expectedNumOfDevices; + }); + if (expectedNumOfDevices == 0) { + return; + } + var data = findByQuery(query); + var loadedEntities = new ArrayList<>(data.getData()); + + Assert.assertEquals(expectedNumOfDevices, loadedEntities.size()); + + for (int i = 0; i < expectedNumOfDevices; i++) { + checkFunction.accept(i, loadedEntities.get(i)); + } + } + private void checkEntitiesByQuery(EntityDataQuery query, int expectedNumOfDevices, String expectedOwnerName, String expectedOwnerType) throws Exception { await() .alias("data by query") diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityRelationControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityRelationControllerTest.java index 62fc1e4b62..b0c76a16a8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityRelationControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityRelationControllerTest.java @@ -17,19 +17,15 @@ 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.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; 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.EntityView; -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.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -39,8 +35,7 @@ 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.common.data.security.Authority; -import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.Collections; @@ -48,57 +43,32 @@ import java.util.List; import java.util.UUID; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Slf4j @DaoSqlTest public class EntityRelationControllerTest extends AbstractControllerTest { public static final String BASE_DEVICE_NAME = "Test dummy device"; @Autowired - RelationService relationService; + private RelationDao relationDao; - private IdComparator idComparator; - private Tenant savedTenant; - private User tenantAdmin; private Device mainDevice; @Before public void beforeTest() throws Exception { - loginSysAdmin(); - idComparator = new IdComparator<>(); + loginTenantAdmin(); - Tenant tenant = new Tenant(); - tenant.setTitle("Test tenant"); - - savedTenant = saveTenant(tenant); - 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"); - - Device device = new Device(); + var device = new Device(); device.setName("Main test device"); device.setType("default"); mainDevice = doPost("/api/device", device, Device.class); } - @After - public void afterTest() throws Exception { - loginSysAdmin(); - - deleteTenant(savedTenant.getId()); - } - @Test public void testSaveAndFindRelation() throws Exception { - Device device = buildSimpleDevice("Test device 1"); + Device device = createDevice("Test device 1"); EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); Mockito.reset(tbClusterService, auditLogService); @@ -116,57 +86,117 @@ public class EntityRelationControllerTest extends AbstractControllerTest { Assert.assertEquals("Found relation is not equals origin!", relation, foundRelation); testNotifyEntityAllOneTimeRelation(foundRelation, - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.RELATION_ADD_OR_UPDATE, foundRelation); } @Test - public void testSaveWithDeviceFromNotCreated() throws Exception { - Device device = new Device(); - device.setName("Test device 2"); - device.setType("default"); - EntityRelation relation = createFromRelation(device, mainDevice, "CONTAINS"); - - Mockito.reset(tbClusterService, auditLogService); + public void testSaveRelationFromValidation() throws Exception { + // GIVEN + var relation = new EntityRelation(); + relation.setFrom(null); + relation.setTo(mainDevice.getId()); + relation.setType("Contains"); + + // WHEN-THEN + for (String endpoint : List.of("/api/relation", "/api/v2/relation")) { + doPost(endpoint, relation) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: from must not be null"))); + } + } - doPost("/api/relation", relation) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Parameter entityId can't be empty!"))); + @Test + public void testSaveRelationToValidation() throws Exception { + // GIVEN + var relation = new EntityRelation(); + relation.setFrom(mainDevice.getId()); + relation.setTo(null); + relation.setType("Contains"); + + // WHEN-THEN + for (String endpoint : List.of("/api/relation", "/api/v2/relation")) { + doPost(endpoint, relation) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: to must not be null"))); + } + } - testNotifyEntityNever(mainDevice.getId(), null); + @Test + public void testSaveRelationRelationTypeValidation() throws Exception { + // GIVEN + var device = createDevice("Test device"); + + EntityRelation relationTypeNull = createFromRelation(mainDevice, device, null); + EntityRelation relationTypeEmpty = createFromRelation(mainDevice, device, ""); + EntityRelation relationTypeBlank = createFromRelation(mainDevice, device, " "); + EntityRelation relationTypeContainsNullChar = createFromRelation(mainDevice, device, "null char \u0000"); + EntityRelation relationTypeTooLong = createFromRelation(mainDevice, device, "a".repeat(256)); + + // WHEN-THEN + for (String endpoint : List.of("/api/relation", "/api/v2/relation")) { + doPost(endpoint, relationTypeNull) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: type must not be blank"))); + + doPost(endpoint, relationTypeEmpty) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: type must not be blank"))); + + doPost(endpoint, relationTypeBlank) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: type must not be blank"))); + + doPost(endpoint, relationTypeContainsNullChar) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: type should not contain 0x00 symbol"))); + + doPost(endpoint, relationTypeTooLong) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(is("Validation error: type length must be equal or less than 255"))); + } } @Test - public void testSaveWithDeviceToNotCreated() throws Exception { - Device device = new Device(); - device.setName("Test device 2"); - device.setType("default"); - EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); + public void testSaveRelationFromNonexistentEntity() throws Exception { + // GIVEN + var nonexistentDevice = new Device(); + nonexistentDevice.setId(new DeviceId(UUID.randomUUID())); + nonexistentDevice.setName("Nonexistent device"); + nonexistentDevice.setType("default"); + EntityRelation relation = createFromRelation(nonexistentDevice, mainDevice, "CONTAINS"); Mockito.reset(tbClusterService, auditLogService); - doPost("/api/relation", relation) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Parameter entityId can't be empty!"))); + // WHEN-THEN + for (String endpoint : List.of("/api/relation", "/api/v2/relation")) { + doPost(endpoint, relation) + .andExpect(status().isNotFound()) + .andExpect(statusReason(is(msgErrorNoFound("Device", nonexistentDevice.getId().toString())))); - testNotifyEntityNever(mainDevice.getId(), null); + testNotifyEntityNever(mainDevice.getId(), null); + } } @Test - public void testSaveWithDeviceToMissing() throws Exception { - Device device = new Device(); - device.setName("Test device 2"); - device.setType("default"); - device.setId(new DeviceId(UUID.randomUUID())); - EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); + public void testSaveRelationToNonexistentEntity() throws Exception { + // GIVEN + var nonexistentDevice = new Device(); + nonexistentDevice.setId(new DeviceId(UUID.randomUUID())); + nonexistentDevice.setName("Nonexistent device"); + nonexistentDevice.setType("default"); + EntityRelation relation = createFromRelation(mainDevice, nonexistentDevice, "CONTAINS"); Mockito.reset(tbClusterService, auditLogService); - doPost("/api/relation", relation) - .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgErrorNoFound("Device", device.getId().getId().toString())))); + // WHEN-THEN + for (String endpoint : List.of("/api/relation", "/api/v2/relation")) { + doPost(endpoint, relation) + .andExpect(status().isNotFound()) + .andExpect(statusReason(is(msgErrorNoFound("Device", nonexistentDevice.getId().toString())))); - testNotifyEntityNever(mainDevice.getId(), null); + testNotifyEntityNever(mainDevice.getId(), null); + } } @Test @@ -178,7 +208,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME); EntityRelation relationTest = createFromRelation(mainDevice, mainDevice, "TEST_NOTIFY_ENTITY"); - testNotifyEntityAllManyRelation(relationTest, savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + testNotifyEntityAllManyRelation(relationTest, tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.RELATION_ADD_OR_UPDATE, numOfDevices); String url = String.format("/api/relations?fromId=%s&fromType=%s", @@ -204,7 +234,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { final int numOfDevices = 30; createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME); - Device device = buildSimpleDevice("Unique dummy test device "); + Device device = createDevice("Unique dummy test device "); String relationType = "TEST"; EntityRelation relation = createFromRelation(mainDevice, device, relationType); @@ -221,7 +251,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { final int numOfDevices = 30; createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME); - Device device = buildSimpleDevice("Unique dummy test device "); + Device device = createDevice("Unique dummy test device "); String relationType = "TEST"; EntityRelation relation = createFromRelation(mainDevice, device, relationType); @@ -240,7 +270,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { final int numOfDevices = 30; createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME); - Device device = buildSimpleDevice("Unique dummy test device "); + Device device = createDevice("Unique dummy test device "); String relationType = "TEST"; EntityRelation relation = createFromRelation(device, mainDevice, relationType); @@ -252,13 +282,12 @@ public class EntityRelationControllerTest extends AbstractControllerTest { assertFoundList(url, 1); } - @Test public void testSaveAndFindRelationsByToWithRelationTypeOther() throws Exception { final int numOfDevices = 30; createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME); - Device device = buildSimpleDevice("Unique dummy test device "); + Device device = createDevice("Unique dummy test device "); String relationType = "TEST"; EntityRelation relation = createFromRelation(device, mainDevice, relationType); @@ -280,9 +309,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { mainDevice.getUuidId(), EntityType.DEVICE ); - List relationsInfos = - JacksonUtil.convertValue(doGet(url, JsonNode.class), new TypeReference<>() { - }); + List relationsInfos = JacksonUtil.convertValue(doGet(url, JsonNode.class), new TypeReference<>() {}); Assert.assertNotNull("Relations is not found!", relationsInfos); Assert.assertEquals("List of found relationsInfos is not equal to number of created relations!", @@ -299,57 +326,88 @@ public class EntityRelationControllerTest extends AbstractControllerTest { mainDevice.getUuidId(), EntityType.DEVICE ); - List relationsInfos = - JacksonUtil.convertValue(doGet(url, JsonNode.class), new TypeReference<>() { - }); + List relationsInfos = JacksonUtil.convertValue(doGet(url, JsonNode.class), new TypeReference<>() {}); Assert.assertNotNull("Relations is not found!", relationsInfos); - Assert.assertEquals("List of found relationsInfos is not equal to number of created relations!", - numOfDevices, relationsInfos.size()); + Assert.assertEquals("List of found relationsInfos is not equal to number of created relations!", numOfDevices, relationsInfos.size()); assertRelationsInfosByTo(relationsInfos); } @Test public void testDeleteRelation() throws Exception { - Device device = buildSimpleDevice("Test device 1"); + // GIVEN + Device device = createDevice("Test device 1"); EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); relation = doPost("/api/v2/relation", relation, EntityRelation.class); - String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", - mainDevice.getUuidId(), EntityType.DEVICE, - "CONTAINS", device.getUuidId(), EntityType.DEVICE - ); - - EntityRelation foundRelation = doGet(url, EntityRelation.class); - - Assert.assertNotNull("Relation is not found!", foundRelation); - Assert.assertEquals("Found relation is not equals origin!", relation, foundRelation); - Mockito.reset(tbClusterService, auditLogService); + // WHEN String deleteUrl = String.format("/api/v2/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", mainDevice.getUuidId(), EntityType.DEVICE, "CONTAINS", device.getUuidId(), EntityType.DEVICE ); var deletedRelation = doDelete(deleteUrl, EntityRelation.class); + // THEN testNotifyEntityAllOneTimeRelation(deletedRelation, - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.RELATION_DELETED, deletedRelation); - doGet(url).andExpect(status().is4xxClientError()); + getRelation(relation) + .andExpect(status().isNotFound()) + .andExpect(statusReason(is(msgErrorNotFound))); + } + + @Test + public void testDeleteRelationWithTypeEmptyOrBlank() throws Exception { + // GIVEN + Device device = createDevice("Test device"); + + // WHEN-THEN + for (String endpoint : List.of("/api/relation", "/api/v2/relation")) { + // saving relation with empty type + EntityRelation emptyRelation = createFromRelation(mainDevice, device, ""); + relationDao.saveRelation(tenantId, emptyRelation); + + // saving relation with blank type + EntityRelation blankRelation = createFromRelation(mainDevice, device, " "); + relationDao.saveRelation(tenantId, blankRelation); + + // deleting relation with empty type + String deleteEmptyUrl = String.format(endpoint + "?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", + mainDevice.getUuidId(), EntityType.DEVICE, + emptyRelation.getType(), device.getUuidId(), EntityType.DEVICE + ); + doDelete(deleteEmptyUrl).andExpect(status().isOk()); + + getRelation(emptyRelation) + .andExpect(status().isNotFound()) + .andExpect(statusReason(is(msgErrorNotFound))); + + // deleting relation with blank type + String deleteBlankUrl = String.format(endpoint + "?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", + mainDevice.getUuidId(), EntityType.DEVICE, + blankRelation.getType(), device.getUuidId(), EntityType.DEVICE + ); + doDelete(deleteBlankUrl).andExpect(status().isOk()); + + getRelation(blankRelation) + .andExpect(status().isNotFound()) + .andExpect(statusReason(is(msgErrorNotFound))); + } } @Test public void testDeleteRelationWithOtherFromDeviceError() throws Exception { - Device device = buildSimpleDevice("Test device 1"); + Device device = createDevice("Test device 1"); EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); doPost("/api/relation", relation).andExpect(status().isOk()); - Device device2 = buildSimpleDevice("Test device 2"); + Device device2 = createDevice("Test device 2"); String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", device2.getUuidId(), EntityType.DEVICE, "CONTAINS", device.getUuidId(), EntityType.DEVICE @@ -366,12 +424,12 @@ public class EntityRelationControllerTest extends AbstractControllerTest { @Test public void testDeleteRelationWithOtherToDeviceError() throws Exception { - Device device = buildSimpleDevice("Test device 1"); + Device device = createDevice("Test device 1"); EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); doPost("/api/relation", relation).andExpect(status().isOk()); - Device device2 = buildSimpleDevice("Test device 2"); + Device device2 = createDevice("Test device 2"); String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", mainDevice.getUuidId(), EntityType.DEVICE, "CONTAINS", device2.getUuidId(), EntityType.DEVICE @@ -411,7 +469,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { doDelete(url).andExpect(status().isOk()); testNotifyEntityOneTimeMsgToEdgeServiceNever(null, mainDevice.getId(), mainDevice.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.RELATIONS_DELETED); Assert.assertTrue( @@ -442,8 +500,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { List relations = readResponse( doPost("/api/relations", query).andExpect(status().isOk()), - new TypeReference>() { - } + new TypeReference<>() {} ); assertFoundRelations(relations, numOfDevices); @@ -467,8 +524,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { List relations = readResponse( doPost("/api/relations", query).andExpect(status().isOk()), - new TypeReference<>() { - } + new TypeReference<>() {} ); assertFoundRelations(relations, numOfDevices); @@ -526,11 +582,11 @@ public class EntityRelationControllerTest extends AbstractControllerTest { @Test public void testCreateRelationFromTenantToDevice() throws Exception { - EntityRelation relation = new EntityRelation(tenantAdmin.getTenantId(), mainDevice.getId(), "CONTAINS"); + EntityRelation relation = new EntityRelation(tenantId, mainDevice.getId(), "CONTAINS"); relation = doPost("/api/v2/relation", relation, EntityRelation.class); String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", - tenantAdmin.getTenantId(), EntityType.TENANT, + tenantId, EntityType.TENANT, "CONTAINS", mainDevice.getUuidId(), EntityType.DEVICE ); @@ -542,12 +598,12 @@ public class EntityRelationControllerTest extends AbstractControllerTest { @Test public void testCreateRelationFromDeviceToTenant() throws Exception { - EntityRelation relation = new EntityRelation(mainDevice.getId(), tenantAdmin.getTenantId(), "CONTAINS"); + EntityRelation relation = new EntityRelation(mainDevice.getId(), tenantId, "CONTAINS"); relation = doPost("/api/v2/relation", relation, EntityRelation.class); String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s", mainDevice.getUuidId(), EntityType.DEVICE, - "CONTAINS", tenantAdmin.getTenantId(), EntityType.TENANT + "CONTAINS", tenantId, EntityType.TENANT ); EntityRelation foundRelation = doGet(url, EntityRelation.class); @@ -558,7 +614,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { @Test public void testSaveAndFindRelationDifferentTenant() throws Exception { - Device device = buildSimpleDevice("Test device 1"); + Device device = createDevice("Test device 1"); EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); doPost("/api/relation", relation).andExpect(status().isOk()); @@ -577,12 +633,20 @@ public class EntityRelationControllerTest extends AbstractControllerTest { deleteDifferentTenant(); } - private Device buildSimpleDevice(String name) throws Exception { - Device device = new Device(); + private Device createDevice(String name) { + var device = new Device(); device.setName(name); device.setType("default"); - device = doPost("/api/device", device, Device.class); - return device; + return doPost("/api/device", device, Device.class); + } + + private ResultActions getRelation(EntityRelation relation) throws Exception { + return doGet("/api/relation?" + + "fromId=" + relation.getFrom().getId() + + "&fromType=" + relation.getFrom().getEntityType() + + "&relationType=" + relation.getType() + + "&toId=" + relation.getTo().getId() + + "&toType=" + relation.getTo().getEntityType()); } private EntityRelation createFromRelation(Device mainDevice, Device device, String relationType) { @@ -591,7 +655,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { private void createDevicesByFrom(int numOfDevices, String baseName) throws Exception { for (int i = 0; i < numOfDevices; i++) { - Device device = buildSimpleDevice(baseName + i); + Device device = createDevice(baseName + i); EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS"); doPost("/api/relation", relation).andExpect(status().isOk()); @@ -600,7 +664,7 @@ public class EntityRelationControllerTest extends AbstractControllerTest { private void createDevicesByTo(int numOfDevices, String baseName) throws Exception { for (int i = 0; i < numOfDevices; i++) { - Device device = buildSimpleDevice(baseName + i); + Device device = createDevice(baseName + i); EntityRelation relation = createFromRelation(device, mainDevice, "CONTAINS"); doPost("/api/relation", relation).andExpect(status().isOk()); } @@ -633,4 +697,5 @@ public class EntityRelationControllerTest extends AbstractControllerTest { Assert.assertEquals("Wrong relationType!", "CONTAINS", info.getType()); } } + } 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 421f3785ca..a4d6b62a0c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java @@ -65,16 +65,19 @@ import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.entityview.EntityViewDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.stream.Collectors; import static java.util.concurrent.TimeUnit.HOURS; @@ -158,6 +161,36 @@ public class EntityViewControllerTest extends AbstractControllerTest { assertEquals(savedView, foundView); } + @Test + public void testFindEntityViewByIds() throws Exception { + List assetProfiles = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + assetProfiles.add(getNewSavedEntityView("Test entity view " + i)); + } + + List expected = assetProfiles.subList(5, 15); + + String idsParam = expected.stream() + .map(ap -> ap.getId().getId().toString()) + .collect(Collectors.joining(",")); + EntityView[] foundEntityViews = doGet("/api/entityViews?entityViewIds=" + idsParam, EntityView[].class); + + Assert.assertNotNull(foundEntityViews); + Assert.assertEquals(expected.size(), foundEntityViews.length); + + Map infoById = Arrays.stream(foundEntityViews) + .collect(Collectors.toMap(info -> info.getId().getId(), Function.identity())); + + for (EntityView entityView : expected) { + UUID id = entityView.getId().getId(); + EntityView view = infoById.get(id); + Assert.assertNotNull("Entity view not found for id " + id, view); + + Assert.assertEquals(entityView.getId(), view.getId()); + Assert.assertEquals(entityView.getName(), view.getName()); + } + } + @Test public void testSaveEntityView() throws Exception { String name = "Test entity view"; @@ -863,4 +896,29 @@ public class EntityViewControllerTest extends AbstractControllerTest { EntityViewId entityViewId = getNewSavedEntityView("EntityView for Test WithRelations Transactional Exception").getId(); testEntityDaoWithRelationsTransactionalException(entityViewDao, tenantId, entityViewId, "/api/entityView/" + entityViewId); } + + @Test + public void testSaveEntityViewWithUniquifyStrategy() throws Exception { + EntityView view = new EntityView(); + view.setEntityId(testDevice.getId()); + view.setTenantId(tenantId); + view.setType("default"); + view.setName("My unique view"); + + EntityView savedView = doPost("/api/entityView", view, EntityView.class); + + doPost("/api/entityView?nameConflictPolicy=FAIL", view).andExpect(status().isBadRequest()); + + EntityView secondView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY", view, EntityView.class); + assertThat(secondView.getName()).startsWith("My unique view_"); + + EntityView thirdView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY&uniquifySeparator=-", view, EntityView.class); + assertThat(thirdView.getName()).startsWith("My unique view-"); + + EntityView fourthView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", view, EntityView.class); + assertThat(fourthView.getName()).isEqualTo("My unique view_1"); + + EntityView fifthEntityView = doPost("/api/entityView?nameConflictPolicy=UNIQUIFY&uniquifyStrategy=INCREMENTAL", view, EntityView.class); + assertThat(fifthEntityView.getName()).isEqualTo("My unique view_2"); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java index 3fbe4bd845..0b8767e65d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java @@ -189,7 +189,7 @@ public class ImageControllerTest extends AbstractControllerTest { assertThat(newImageInfo.getTitle()).isEqualTo(newTitle); assertThat(newImageInfo.getDescriptor(ImageDescriptor.class)).isEqualTo(imageDescriptor); assertThat(newImageInfo.getResourceKey()).isEqualTo(imageInfo.getResourceKey()); - assertThat(newImageInfo.getPublicResourceKey()).isEqualTo(newImageInfo.getPublicResourceKey()); + assertThat(newImageInfo.getPublicResourceKey()).isEqualTo(imageInfo.getPublicResourceKey()); } @Test 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 7e99f6b110..5425c34740 100644 --- a/application/src/test/java/org/thingsboard/server/controller/OtaPackageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/OtaPackageControllerTest.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; 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.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.nio.ByteBuffer; 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 f8cd8e0f33..d53e69d721 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java @@ -47,12 +47,17 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.rule.RuleChainDao; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; @@ -231,6 +236,44 @@ public class RuleChainControllerTest extends AbstractControllerTest { Assert.assertEquals(savedRuleChain, foundRuleChain); } + @Test + public void testFindRuleChainsByIds() throws Exception { + List ruleChains = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("RuleChain " + i); + ruleChains.add(doPost("/api/ruleChain", ruleChain, RuleChain.class)); + } + + List expected = ruleChains.subList(5, 15); + + String idsParam = expected.stream() + .map(rc -> rc.getId().getId().toString()) + .collect(Collectors.joining(",")); + + RuleChain[] result = doGet( + "/api/ruleChains?ruleChainIds=" + idsParam, + RuleChain[].class + ); + + Assert.assertNotNull(result); + Assert.assertEquals(expected.size(), result.length); + + Map rcById = Arrays.stream(result) + .collect(Collectors.toMap(rc -> rc.getId().getId(), Function.identity())); + + for (RuleChain rc : expected) { + UUID id = rc.getId().getId(); + RuleChain found = rcById.get(id); + Assert.assertNotNull("RuleChain not found for id " + id, found); + + Assert.assertEquals(rc.getId(), found.getId()); + Assert.assertEquals(rc.getName(), found.getName()); + Assert.assertEquals(rc.getTenantId(), found.getTenantId()); + } + } + + @Test public void testDeleteRuleChain() throws Exception { RuleChain ruleChain = new RuleChain(); 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 22fa9c22d4..7fb316dd7e 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -25,11 +25,12 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockPart; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; @@ -47,15 +48,14 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; 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.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; import java.util.Base64; -import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; @@ -64,7 +64,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @DaoSqlTest public class TbResourceControllerTest extends AbstractControllerTest { - private IdComparator idComparator = new IdComparator<>(); + private final IdComparator idComparator = new IdComparator<>(); private static final String DEFAULT_FILE_NAME = "test.jks"; private static final String DEFAULT_FILE_NAME_2 = "test2.jks"; @@ -126,13 +126,10 @@ public class TbResourceControllerTest extends AbstractControllerTest { Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getResourceKey()); Assert.assertArrayEquals(resource.getData(), download(savedResource.getId())); - TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); - foundResource.setTitle("My new resource"); - foundResource.setData(null); - - savedResource = save(foundResource); - - Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); + String resourceTitle = "My new resource"; + savedResource.setTitle(resourceTitle); + savedResource = doPut("/api/resource/" + savedResource.getUuidId() + "/info", savedResource, TbResourceInfo.class); + assertThat(savedResource.getTitle()).isEqualTo(resourceTitle); testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(savedResource, savedResource.getId(), savedResource.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), @@ -501,8 +498,8 @@ public class TbResourceControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, cntEntity, cntEntity, cntEntity); - Collections.sort(resources, idComparator); - Collections.sort(loadedResources, idComparator); + resources.sort(idComparator); + loadedResources.sort(idComparator); Assert.assertEquals(resources, loadedResources); } @@ -549,8 +546,8 @@ public class TbResourceControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, jksCntEntity + lwm2mCntEntity, jksCntEntity + lwm2mCntEntity, jksCntEntity + lwm2mCntEntity); - Collections.sort(resources, idComparator); - Collections.sort(loadedResources, idComparator); + resources.sort(idComparator); + loadedResources.sort(idComparator); Assert.assertEquals(resources, loadedResources); } @@ -581,8 +578,8 @@ public class TbResourceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(resources, idComparator); - Collections.sort(loadedResources, idComparator); + resources.sort(idComparator); + loadedResources.sort(idComparator); Assert.assertEquals(resources, loadedResources); @@ -654,8 +651,8 @@ public class TbResourceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(jksResources, idComparator); - Collections.sort(loadedResources, idComparator); + jksResources.sort(idComparator); + loadedResources.sort(idComparator); Assert.assertEquals(jksResources, loadedResources); @@ -736,8 +733,8 @@ public class TbResourceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(expectedResources, idComparator); - Collections.sort(loadedResources, idComparator); + expectedResources.sort(idComparator); + loadedResources.sort(idComparator); Assert.assertEquals(expectedResources, loadedResources); @@ -770,7 +767,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { MockHttpServletResponse response = resultActions.andReturn().getResponse(); String eTag = response.getHeader("ETag"); Assert.assertNotNull(eTag); - Assert.assertEquals(Base64.getEncoder().encodeToString(response.getContentAsByteArray()), TEST_DATA); + Assert.assertEquals(TEST_DATA, Base64.getEncoder().encodeToString(response.getContentAsByteArray())); //download with if-none-match header HttpHeaders headers = new HttpHeaders(); @@ -814,7 +811,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { MockHttpServletResponse response = resultActions.andReturn().getResponse(); String eTag = response.getHeader("ETag"); Assert.assertNotNull(eTag); - Assert.assertEquals(Base64.getEncoder().encodeToString(response.getContentAsByteArray()), TEST_DATA); + Assert.assertEquals(TEST_DATA, Base64.getEncoder().encodeToString(response.getContentAsByteArray())); //download with if-none-match header HttpHeaders headers = new HttpHeaders(); @@ -859,10 +856,10 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("can't be updated"))); - foundResource.setData(null); - foundResource.setTitle("Updated resource"); - savedResource = doPost("/api/resource", foundResource, TbResource.class); - assertThat(savedResource.getTitle()).isEqualTo("Updated resource"); + String resourceTitle = "Updated resource"; + savedResource.setTitle(resourceTitle); + savedResource = doPut("/api/resource/" + savedResource.getUuidId() + "/info", savedResource, TbResourceInfo.class); + assertThat(savedResource.getTitle()).isEqualTo(resourceTitle); assertThat(savedResource.getFileName()).isEqualTo(resource.getFileName()); assertThat(savedResource.getEtag()).isEqualTo(resource.getEtag()); assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(TEST_DATA); @@ -923,8 +920,20 @@ public class TbResourceControllerTest extends AbstractControllerTest { } private TbResourceInfo save(TbResource tbResource) throws Exception { - return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>() { - }); + byte[] data = tbResource.getData() != null ? tbResource.getData() : tbResource.getEncodedData() != null ? Base64.getDecoder().decode(tbResource.getEncodedData()) : null; + List parts = new ArrayList<>(); + parts.add(new MockPart("resourceType", tbResource.getResourceType().name().getBytes())); + if (tbResource.getTitle() != null) { + parts.add(new MockPart("title", tbResource.getTitle().getBytes())); + } + if (tbResource.getDescriptor() != null) { + parts.add(new MockPart("descriptor", tbResource.getDescriptor().toString().getBytes())); + } + if (tbResource.getResourceSubType() != null) { + parts.add(new MockPart("resourceSubType", tbResource.getResourceSubType().name().getBytes())); + } + + return uploadResource(HttpMethod.POST, "/api/resource/upload", tbResource.getFileName(), tbResource.getResourceType().getMediaType(), data, parts); } private TbResourceInfo findResourceInfo(TbResourceId id) throws Exception { @@ -949,7 +958,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { for (String model : models) { String fileName = model + ".xml"; - byte[] bytes = IOUtils.toByteArray(getClass().getClassLoader().getResourceAsStream("lwm2m/" + fileName)); + byte[] bytes = IOUtils.toByteArray(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("lwm2m/" + fileName))); TbResource resource = new TbResource(); resource.setResourceType(ResourceType.LWM2M_MODEL); 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 4f89d4c838..dd51cdd440 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java @@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; import org.springframework.test.context.TestPropertySource; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -110,6 +113,13 @@ public class TelemetryControllerTest extends AbstractControllerTest { var monthResult = result.get("t").get(0); Assert.assertEquals(22L, monthResult.get("value").asLong()); Assert.assertEquals(middleOfTheInterval, monthResult.get("ts").asLong()); + + // get all latest (without keys parameter) + ObjectNode allLatest = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + + "/values/timeseries?startTs={startTs}&endTs={endTs}&agg={agg}&intervalType={intervalType}&timeZone={timeZone}", + ObjectNode.class, startTs, endTs, "SUM", "WEEK_ISO", "Europe/Kyiv"); + Assert.assertNotNull(allLatest); + Assert.assertNotNull(allLatest.get("t")); } @Test @@ -232,6 +242,63 @@ public class TelemetryControllerTest extends AbstractControllerTest { doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody2, String.class, status().isBadRequest()); } + @Test + public void testDeleteTelemetryByKeyWithComma() throws Exception { + loginTenantAdmin(); + Device device = createDevice(); + + String tsKey = "key1,key2"; + String testBody = JacksonUtil.newObjectNode() + .put(tsKey, "value") + .toString(); + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody, String.class, status().isOk()); + + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("key", tsKey); + params.add("deleteAllDataForKeys", "true"); + + ObjectNode tsData = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class); + assertThat(tsData.get("key1,key2").get(0).get("value").asText()).isEqualTo("value"); + + doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete", params); + + ObjectNode tsDataAfterDeletion = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class); + Assert.assertTrue(tsDataAfterDeletion.get("key1,key2").get(0).get("value").isNull()); + } + + @Test + public void testDeleteTelemetryByKeysWithComma() throws Exception { + loginTenantAdmin(); + Device device = createDevice(); + + String keyWithComma = "key1,key2"; + String testBody = JacksonUtil.newObjectNode() + .put(keyWithComma, "value") + .toString(); + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody, String.class, status().isOk()); + + String key = "key3"; + String testBody2 = JacksonUtil.newObjectNode() + .put(key, "value") + .toString(); + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("key", keyWithComma); + params.add("key", key); + params.add("deleteAllDataForKeys", "true"); + + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody2, String.class, status().isOk()); + + ObjectNode tsData = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class); + assertThat(tsData.get("key1,key2").get(0).get("value").asText()).isEqualTo("value"); + assertThat(tsData.get("key3").get(0).get("value").asText()).isEqualTo("value"); + + doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete", params); + + ObjectNode tsDataAfterDeletion = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class); + Assert.assertTrue(tsDataAfterDeletion.get("key1,key2").get(0).get("value").isNull()); + Assert.assertTrue(tsDataAfterDeletion.get("key3").get(0).get("value").isNull()); + } + 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 0f87dc1fd3..fb86ea7b43 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -27,8 +27,8 @@ 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.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.actors.ActorSystemContext; @@ -68,6 +68,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.QueueKey; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -77,6 +78,7 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -109,11 +111,11 @@ public class TenantControllerTest extends AbstractControllerTest { ListeningExecutorService executor; - @SpyBean + @MockitoSpyBean private PartitionService partitionService; - @SpyBean + @MockitoSpyBean private ActorSystemContext actorContext; - @SpyBean + @MockitoSpyBean private TbQueueAdmin queueAdmin; @Before @@ -187,6 +189,51 @@ public class TenantControllerTest extends AbstractControllerTest { deleteTenant(savedTenant.getId()); } + @Test + public void testFindTenantsByIds() throws Exception { + loginSysAdmin(); + + List savedTenants = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant " + i); + savedTenants.add(saveTenant(tenant)); + } + + String idsParam = savedTenants.stream() + .map(t -> t.getId().getId().toString()) + .collect(Collectors.joining(",")); + + Tenant[] foundTenants = doGet("/api/tenants?tenantIds=" + idsParam, Tenant[].class); + + Assert.assertNotNull(foundTenants); + Assert.assertEquals(savedTenants.size(), foundTenants.length); + + Map foundById = Arrays.stream(foundTenants) + .collect(Collectors.toMap(t -> t.getId().getId(), Function.identity())); + + for (Tenant savedTenant : savedTenants) { + UUID id = savedTenant.getId().getId(); + Tenant foundTenant = foundById.get(id); + Assert.assertNotNull("Tenant not found for id " + id, foundTenant); + Assert.assertEquals(savedTenant, foundTenant); + } + + loginTenantAdmin(); + Tenant[] foundTenantsByTenant = doGet("/api/tenants?tenantIds=" + idsParam, Tenant[].class); + assertThat(foundTenantsByTenant).isEmpty(); + + Tenant[] foundCurrentTenant = doGet("/api/tenants?tenantIds=" + tenantId, Tenant[].class); + assertThat(foundCurrentTenant).hasSize(1); + assertThat(foundCurrentTenant[0].getTenantId()).isEqualTo(tenantId); + + loginSysAdmin(); + for (Tenant savedTenant : savedTenants) { + deleteTenant(savedTenant.getId()); + } + } + + @Test public void testFindTenantInfoById() throws Exception { loginSysAdmin(); @@ -243,15 +290,37 @@ public class TenantControllerTest extends AbstractControllerTest { .andExpect(statusReason(containsString(msgErrorNoFound("Tenant", tenantIdStr)))); } + @Test + public void testDeleteTenantByTenantAdmin() throws Exception { + loginSysAdmin(); + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = saveTenant(tenant); + + //login as tenant admin + User tenantAdminUser = new User(); + tenantAdminUser.setAuthority(Authority.TENANT_ADMIN); + tenantAdminUser.setTenantId(savedTenant.getId()); + tenantAdminUser.setEmail("tenantToDelete@thingsboard.io"); + + createUserAndLogin(tenantAdminUser, TENANT_ADMIN_PASSWORD); + + String tenantIdStr = savedTenant.getId().getId().toString(); + deleteTenant(savedTenant.getId()); + loginSysAdmin(); + doGet("/api/tenant/" + tenantIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Tenant", tenantIdStr)))); + } + @Test public void testFindTenants() throws Exception { loginSysAdmin(); - List tenants = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); - tenants.addAll(pageData.getData()); + List tenants = new ArrayList<>(pageData.getData()); Mockito.reset(tbClusterService); @@ -377,12 +446,11 @@ public class TenantControllerTest extends AbstractControllerTest { @Test public void testFindTenantInfos() throws Exception { loginSysAdmin(); - List tenants = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); - tenants.addAll(pageData.getData()); + List tenants = new ArrayList<>(pageData.getData()); List> createFutures = new ArrayList<>(56); for (int i = 0; i < 56; i++) { 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 476428e016..42a27eb6d8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java @@ -42,8 +42,12 @@ import org.thingsboard.server.queue.TbQueueCallback; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; @@ -120,6 +124,43 @@ public class TenantProfileControllerTest extends AbstractControllerTest { Assert.assertEquals(savedTenantProfile.getName(), foundTenantProfileInfo.getName()); } + @Test + public void testFindTenantProfilesByIds() throws Exception { + loginSysAdmin(); + List tenantProfiles = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile " + i); + tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class)); + } + + List expected = tenantProfiles.subList(2, 7); + + String idsParam = expected.stream() + .map(tp -> tp.getId().getId().toString()) + .collect(Collectors.joining(",")); + + TenantProfile[] result = doGet( + "/api/tenantProfiles?ids=" + idsParam, + TenantProfile[].class + ); + + Assert.assertNotNull(result); + Assert.assertEquals(expected.size(), result.length); + + Map tpById = Arrays.stream(result) + .collect(Collectors.toMap(tp -> tp.getId().getId(), Function.identity())); + + for (TenantProfile tp : expected) { + UUID id = tp.getId().getId(); + TenantProfile found = tpById.get(id); + Assert.assertNotNull("TenantProfile not found for id " + id, found); + + Assert.assertEquals(tp.getId(), found.getId()); + Assert.assertEquals(tp.getName(), found.getName()); + } + } + + @Test public void testFindDefaultTenantProfileInfo() throws Exception { loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java index c3eb91db85..c8b5bf06c9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java @@ -30,6 +30,8 @@ import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.SmsTwoFaAccountConfig; @@ -48,6 +50,7 @@ import org.thingsboard.server.service.security.auth.mfa.provider.impl.TotpTwoFaP import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -85,7 +88,6 @@ public class TwoFactorAuthConfigTest extends AbstractControllerTest { twoFaConfigManager.deletePlatformTwoFaSettings(tenantId); } - @Test public void testSavePlatformTwoFaSettings() throws Exception { loginSysAdmin(); @@ -102,15 +104,32 @@ public class TwoFactorAuthConfigTest extends AbstractControllerTest { twoFaSettings.setVerificationCodeCheckRateLimit("3:900"); twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(10); twoFaSettings.setTotalAllowedTimeForVerification(3600); + twoFaSettings.setEnforceTwoFa(true); + twoFaSettings.setEnforcedUsersFilter(new AllUsersFilter()); - doPost("/api/2fa/settings", twoFaSettings).andExpect(status().isOk()); + saveTwoFaSettings(twoFaSettings); - PlatformTwoFaSettings savedTwoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class); + PlatformTwoFaSettings savedTwoFaSettings = findTwoFaSettings(); assertThat(savedTwoFaSettings.getProviders()).hasSize(2); assertThat(savedTwoFaSettings.getProviders()).contains(totpTwoFaProviderConfig, smsTwoFaProviderConfig); } + @Test + public void testSavePlatformTwoFaSettingsWithEnforceTwoFaWithoutProviders() throws Exception { + loginSysAdmin(); + + PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); + twoFaSettings.setProviders(List.of()); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setVerificationCodeCheckRateLimit("3:900"); + twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(10); + twoFaSettings.setTotalAllowedTimeForVerification(3600); + twoFaSettings.setEnforceTwoFa(true); + + doPost("/api/2fa/settings", twoFaSettings).andExpect(status().isBadRequest()); + } + @Test public void testSavePlatformTwoFaSettings_validationError() throws Exception { loginSysAdmin(); @@ -157,17 +176,6 @@ public class TwoFactorAuthConfigTest extends AbstractControllerTest { assertThat(errorResponse).containsIgnoringCase("verificationCodeLifetime is required"); } - private String savePlatformTwoFaSettingsAndGetError(TwoFaProviderConfig invalidTwoFaProviderConfig) throws Exception { - PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); - twoFaSettings.setProviders(Collections.singletonList(invalidTwoFaProviderConfig)); - twoFaSettings.setMinVerificationCodeSendPeriod(5); - twoFaSettings.setTotalAllowedTimeForVerification(100); - - return getErrorMessage(doPost("/api/2fa/settings", twoFaSettings) - .andExpect(status().isBadRequest())); - } - - @Test public void testSaveTwoFaAccountConfig_providerNotConfigured() throws Exception { configureSmsTwoFaProvider("${code}"); @@ -268,24 +276,6 @@ public class TwoFactorAuthConfigTest extends AbstractControllerTest { assertThat(errorMessage).containsIgnoringCase("verification code is incorrect"); } - private TotpTwoFaAccountConfig generateTotpTwoFaAccountConfig(TotpTwoFaProviderConfig totpTwoFaProviderConfig) throws Exception { - TwoFaAccountConfig generatedTwoFaAccountConfig = readResponse(doPost("/api/2fa/account/config/generate?providerType=TOTP") - .andExpect(status().isOk()), TwoFaAccountConfig.class); - assertThat(generatedTwoFaAccountConfig).isInstanceOf(TotpTwoFaAccountConfig.class); - - assertThat(((TotpTwoFaAccountConfig) generatedTwoFaAccountConfig)).satisfies(accountConfig -> { - UriComponents otpAuthUrl = UriComponentsBuilder.fromUriString(accountConfig.getAuthUrl()).build(); - assertThat(otpAuthUrl.getScheme()).isEqualTo("otpauth"); - assertThat(otpAuthUrl.getHost()).isEqualTo("totp"); - assertThat(otpAuthUrl.getQueryParams().getFirst("issuer")).isEqualTo(totpTwoFaProviderConfig.getIssuerName()); - assertThat(otpAuthUrl.getPath()).isEqualTo("/%s:%s", totpTwoFaProviderConfig.getIssuerName(), TENANT_ADMIN_EMAIL); - assertThat(otpAuthUrl.getQueryParams().getFirst("secret")).satisfies(secretKey -> { - assertDoesNotThrow(() -> Base32.decode(secretKey)); - }); - }); - return (TotpTwoFaAccountConfig) generatedTwoFaAccountConfig; - } - @Test public void testGetTwoFaAccountConfig_whenProviderNotConfigured() throws Exception { testVerifyAndSaveTotpTwoFaAccountConfig(); @@ -419,6 +409,56 @@ public class TwoFactorAuthConfigTest extends AbstractControllerTest { assertThat(accountConfig).isEqualTo(initialSmsTwoFaAccountConfig); } + @Test + public void testIsTwoFaEnabled() throws Exception { + configureSmsTwoFaProvider("${code}"); + SmsTwoFaAccountConfig accountConfig = new SmsTwoFaAccountConfig(); + accountConfig.setPhoneNumber("+38050505050"); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, tenantAdminUser, accountConfig); + + assertThat(twoFactorAuthService.isTwoFaEnabled(tenantId, tenantAdminUser)).isTrue(); + } + + @Test + public void testDeleteTwoFaAccountConfig() throws Exception { + configureSmsTwoFaProvider("${code}"); + loginTenantAdmin(); + SmsTwoFaAccountConfig accountConfig = new SmsTwoFaAccountConfig(); + accountConfig.setPhoneNumber("+38050505050"); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, tenantAdminUser, accountConfig); + + AccountTwoFaSettings accountTwoFaSettings = readResponse(doGet("/api/2fa/account/settings").andExpect(status().isOk()), AccountTwoFaSettings.class); + TwoFaAccountConfig savedAccountConfig = accountTwoFaSettings.getConfigs().get(TwoFaProviderType.SMS); + assertThat(savedAccountConfig).isEqualTo(accountConfig); + + PlatformTwoFaSettings twoFaSettings = twoFaConfigManager.getPlatformTwoFaSettings(TenantId.SYS_TENANT_ID, true).get(); + twoFaSettings.setEnforceTwoFa(true); + TenantAdministratorsFilter enforcedUsersFilter = new TenantAdministratorsFilter(); + enforcedUsersFilter.setTenantsIds(Set.of(tenantId.getId())); + twoFaSettings.setEnforcedUsersFilter(enforcedUsersFilter); + twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); + + String errorMessage = getErrorMessage(doDelete("/api/2fa/account/config?providerType=SMS") + .andExpect(status().isBadRequest())); + assertThat(errorMessage).isEqualTo("At least one 2FA provider is required"); + + twoFaSettings.setEnforceTwoFa(false); + twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); + + doDelete("/api/2fa/account/config?providerType=SMS").andExpect(status().isOk()); + + assertThat(readResponse(doGet("/api/2fa/account/settings").andExpect(status().isOk()), AccountTwoFaSettings.class).getConfigs()) + .doesNotContainKey(TwoFaProviderType.SMS); + } + + private PlatformTwoFaSettings findTwoFaSettings() throws Exception { + return doGet("/api/2fa/settings", PlatformTwoFaSettings.class); + } + + private void saveTwoFaSettings(PlatformTwoFaSettings twoFaSettings) throws Exception { + doPost("/api/2fa/settings", twoFaSettings).andExpect(status().isOk()); + } + private TotpTwoFaProviderConfig configureTotpTwoFaProvider() throws Exception { TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig(); totpTwoFaProviderConfig.setIssuerName("tb"); @@ -441,37 +481,35 @@ public class TwoFactorAuthConfigTest extends AbstractControllerTest { twoFaSettings.setProviders(Arrays.stream(providerConfigs).collect(Collectors.toList())); twoFaSettings.setMinVerificationCodeSendPeriod(5); twoFaSettings.setTotalAllowedTimeForVerification(100); - doPost("/api/2fa/settings", twoFaSettings).andExpect(status().isOk()); + saveTwoFaSettings(twoFaSettings); } - @Test - public void testIsTwoFaEnabled() throws Exception { - configureSmsTwoFaProvider("${code}"); - SmsTwoFaAccountConfig accountConfig = new SmsTwoFaAccountConfig(); - accountConfig.setPhoneNumber("+38050505050"); - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, tenantAdminUserId, accountConfig); + private TotpTwoFaAccountConfig generateTotpTwoFaAccountConfig(TotpTwoFaProviderConfig totpTwoFaProviderConfig) throws Exception { + TwoFaAccountConfig generatedTwoFaAccountConfig = readResponse(doPost("/api/2fa/account/config/generate?providerType=TOTP") + .andExpect(status().isOk()), TwoFaAccountConfig.class); + assertThat(generatedTwoFaAccountConfig).isInstanceOf(TotpTwoFaAccountConfig.class); - assertThat(twoFactorAuthService.isTwoFaEnabled(tenantId, tenantAdminUserId)).isTrue(); + assertThat(((TotpTwoFaAccountConfig) generatedTwoFaAccountConfig)).satisfies(accountConfig -> { + UriComponents otpAuthUrl = UriComponentsBuilder.fromUriString(accountConfig.getAuthUrl()).build(); + assertThat(otpAuthUrl.getScheme()).isEqualTo("otpauth"); + assertThat(otpAuthUrl.getHost()).isEqualTo("totp"); + assertThat(otpAuthUrl.getQueryParams().getFirst("issuer")).isEqualTo(totpTwoFaProviderConfig.getIssuerName()); + assertThat(otpAuthUrl.getPath()).isEqualTo("/%s:%s", totpTwoFaProviderConfig.getIssuerName(), TENANT_ADMIN_EMAIL); + assertThat(otpAuthUrl.getQueryParams().getFirst("secret")).satisfies(secretKey -> { + assertDoesNotThrow(() -> Base32.decode(secretKey)); + }); + }); + return (TotpTwoFaAccountConfig) generatedTwoFaAccountConfig; } - @Test - public void testDeleteTwoFaAccountConfig() throws Exception { - configureSmsTwoFaProvider("${code}"); - SmsTwoFaAccountConfig accountConfig = new SmsTwoFaAccountConfig(); - accountConfig.setPhoneNumber("+38050505050"); - - loginTenantAdmin(); - - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, tenantAdminUserId, accountConfig); - - AccountTwoFaSettings accountTwoFaSettings = readResponse(doGet("/api/2fa/account/settings").andExpect(status().isOk()), AccountTwoFaSettings.class); - TwoFaAccountConfig savedAccountConfig = accountTwoFaSettings.getConfigs().get(TwoFaProviderType.SMS); - assertThat(savedAccountConfig).isEqualTo(accountConfig); - - doDelete("/api/2fa/account/config?providerType=SMS").andExpect(status().isOk()); + private String savePlatformTwoFaSettingsAndGetError(TwoFaProviderConfig invalidTwoFaProviderConfig) throws Exception { + PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); + twoFaSettings.setProviders(Collections.singletonList(invalidTwoFaProviderConfig)); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); - assertThat(readResponse(doGet("/api/2fa/account/settings").andExpect(status().isOk()), AccountTwoFaSettings.class).getConfigs()) - .doesNotContainKey(TwoFaProviderType.SMS); + return getErrorMessage(doPost("/api/2fa/settings", twoFaSettings) + .andExpect(status().isBadRequest())); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java index b20dd570fb..ca81b97a48 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java @@ -25,6 +25,7 @@ import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; @@ -33,6 +34,8 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -58,6 +61,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -116,7 +120,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { public void testTwoFa_totp() throws Exception { TotpTwoFaAccountConfig totpTwoFaAccountConfig = configureTotpTwoFa(); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); doPost("/api/auth/2fa/verification/send?providerType=TOTP") .andExpect(status().isOk()); @@ -136,7 +140,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { public void testTwoFa_sms() throws Exception { configureSmsTwoFa(); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); doPost("/api/auth/2fa/verification/send?providerType=SMS") .andExpect(status().isOk()); @@ -160,7 +164,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { twoFaSettings.setTotalAllowedTimeForVerification(65); }); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); await("expiration of the pre-verification token") .atLeast(Duration.ofSeconds(30).plusMillis(500)) @@ -177,7 +181,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(10); }); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); Stream.generate(() -> StringUtils.randomNumeric(6)) .limit(9) @@ -206,7 +210,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { twoFaSettings.setMinVerificationCodeSendPeriod(10); }); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); doPost("/api/auth/2fa/verification/send?providerType=TOTP") .andExpect(status().isOk()); @@ -230,7 +234,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { twoFaSettings.setVerificationCodeCheckRateLimit("3:10"); }); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); for (int i = 0; i < 3; i++) { String incorrectVerificationCodeError = getErrorMessage(doPost("/api/auth/2fa/verification/check?providerType=TOTP&verificationCode=incorrect") @@ -258,7 +262,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { @Test public void testCheckVerificationCode_invalidVerificationCode() throws Exception { configureTotpTwoFa(); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); for (String invalidVerificationCode : new String[]{"1234567", "ab1212", "12311 ", "oewkriwejqf"}) { String errorMessage = getErrorMessage(doPost("/api/auth/2fa/verification/check?providerType=TOTP&verificationCode=" + invalidVerificationCode) @@ -273,7 +277,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { smsTwoFaProviderConfig.setVerificationCodeLifetime(10); }); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); ArgumentCaptor verificationCodeCaptor = ArgumentCaptor.forClass(String.class); doPost("/api/auth/2fa/verification/send?providerType=SMS").andExpect(status().isOk()); @@ -296,7 +300,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { public void testTwoFa_logLoginAction() throws Exception { TotpTwoFaAccountConfig totpTwoFaAccountConfig = configureTotpTwoFa(); - logInWithPreVerificationToken(username, password); + logInWithMfaToken(username, password, Authority.PRE_VERIFICATION_TOKEN); await("async audit log saving").during(1, TimeUnit.SECONDS); doPost("/api/auth/2fa/verification/check?providerType=TOTP&verificationCode=incorrect") @@ -334,7 +338,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { @Test public void testAuthWithoutTwoFaAccountConfig() throws ThingsboardException { configureTotpTwoFa(); - twoFaConfigManager.deleteTwoFaAccountConfig(tenantId, user.getId(), TwoFaProviderType.TOTP); + twoFaConfigManager.deleteTwoFaAccountConfig(tenantId, user, TwoFaProviderType.TOTP); assertDoesNotThrow(() -> { login(username, password); @@ -368,17 +372,17 @@ public class TwoFactorAuthTest extends AbstractControllerTest { TotpTwoFaAccountConfig totpTwoFaAccountConfig = (TotpTwoFaAccountConfig) twoFactorAuthService.generateNewAccountConfig(twoFaUser, TwoFaProviderType.TOTP); totpTwoFaAccountConfig.setUseByDefault(true); - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, twoFaUser.getId(), totpTwoFaAccountConfig); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, twoFaUser, totpTwoFaAccountConfig); SmsTwoFaAccountConfig smsTwoFaAccountConfig = new SmsTwoFaAccountConfig(); smsTwoFaAccountConfig.setPhoneNumber("+38012312322"); - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, twoFaUser.getId(), smsTwoFaAccountConfig); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, twoFaUser, smsTwoFaAccountConfig); EmailTwoFaAccountConfig emailTwoFaAccountConfig = new EmailTwoFaAccountConfig(); emailTwoFaAccountConfig.setEmail(twoFaUser.getEmail()); - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, twoFaUser.getId(), emailTwoFaAccountConfig); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, twoFaUser, emailTwoFaAccountConfig); - logInWithPreVerificationToken(twoFaUser.getEmail(), "12345678"); + logInWithMfaToken(twoFaUser.getEmail(), "12345678", Authority.PRE_VERIFICATION_TOKEN); Map providersInfos = readResponse(doGet("/api/auth/2fa/providers").andExpect(status().isOk()), new TypeReference>() {}).stream() .collect(Collectors.toMap(TwoFactorAuthController.TwoFaProviderInfo::getType, v -> v)); @@ -395,13 +399,79 @@ public class TwoFactorAuthTest extends AbstractControllerTest { assertThat(providersInfos.get(TwoFaProviderType.EMAIL).isDefault()).isFalse(); } - private void logInWithPreVerificationToken(String username, String password) throws Exception { + @Test + public void testEnforceTwoFa() throws Exception { + TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig(); + totpTwoFaProviderConfig.setIssuerName("tb"); + + PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); + twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{totpTwoFaProviderConfig}).collect(Collectors.toList())); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); + twoFaSettings.setEnforceTwoFa(true); + TenantAdministratorsFilter enforcedUsersFilter = new TenantAdministratorsFilter(); + enforcedUsersFilter.setTenantsIds(Set.of(tenantId.getId())); + twoFaSettings.setEnforcedUsersFilter(enforcedUsersFilter); + twoFaSettings = twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); + + logInWithMfaToken(username, password, Authority.MFA_CONFIGURATION_TOKEN); + + TotpTwoFaAccountConfig totpTwoFaAccountConfig = doPost("/api/2fa/account/config/generate?providerType=" + totpTwoFaProviderConfig.getProviderType(), TotpTwoFaAccountConfig.class); + + String secret = UriComponentsBuilder.fromUriString(totpTwoFaAccountConfig.getAuthUrl()).build() + .getQueryParams().getFirst("secret"); + String verificationCode = new Totp(secret).now(); + readResponse(doPost("/api/2fa/account/config?verificationCode=" + verificationCode, totpTwoFaAccountConfig).andExpect(status().isOk()), JsonNode.class); + + JwtPair tokenPair = readResponse(doPost("/api/auth/2fa/login").andExpect(status().isOk()), JwtPair.class); + assertThat(tokenPair.getToken()).isNotEmpty(); + assertThat(tokenPair.getRefreshToken()).isNotEmpty(); + validateAndSetJwtToken(tokenPair, username); + + doGet("/api/user/" + user.getId()).andExpect(status().isOk()); + + // verifying enforced users filter + createDifferentTenant(); + doGet("/api/user/" + savedDifferentTenantUser.getId()).andExpect(status().isOk()); + } + + @Test + public void testEnforceTwoFa_sysadmin() throws Exception { + TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig(); + totpTwoFaProviderConfig.setIssuerName("tb"); + + PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings(); + twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{totpTwoFaProviderConfig}).collect(Collectors.toList())); + twoFaSettings.setMinVerificationCodeSendPeriod(5); + twoFaSettings.setTotalAllowedTimeForVerification(100); + twoFaSettings.setEnforceTwoFa(true); + AllUsersFilter enforcedUsersFilter = new AllUsersFilter(); + twoFaSettings.setEnforcedUsersFilter(enforcedUsersFilter); + twoFaSettings = twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); + + logInWithMfaToken(SYS_ADMIN_EMAIL, SYS_ADMIN_PASSWORD, Authority.MFA_CONFIGURATION_TOKEN); + + TotpTwoFaAccountConfig totpTwoFaAccountConfig = doPost("/api/2fa/account/config/generate?providerType=" + totpTwoFaProviderConfig.getProviderType(), TotpTwoFaAccountConfig.class); + String secret = UriComponentsBuilder.fromUriString(totpTwoFaAccountConfig.getAuthUrl()).build() + .getQueryParams().getFirst("secret"); + String verificationCode = new Totp(secret).now(); + readResponse(doPost("/api/2fa/account/config?verificationCode=" + verificationCode, totpTwoFaAccountConfig).andExpect(status().isOk()), JsonNode.class); + + JwtPair tokenPair = readResponse(doPost("/api/auth/2fa/login").andExpect(status().isOk()), JwtPair.class); + assertThat(tokenPair.getToken()).isNotEmpty(); + assertThat(tokenPair.getRefreshToken()).isNotEmpty(); + validateAndSetJwtToken(tokenPair, SYS_ADMIN_EMAIL); + + doGet("/api/user/" + user.getId()).andExpect(status().isOk()); + } + + private void logInWithMfaToken(String username, String password, Authority expectedScope) throws Exception { LoginRequest loginRequest = new LoginRequest(username, password); JwtPair response = readResponse(doPost("/api/auth/login", loginRequest).andExpect(status().isOk()), JwtPair.class); assertThat(response.getToken()).isNotNull(); assertThat(response.getRefreshToken()).isNull(); - assertThat(response.getScope()).isEqualTo(Authority.PRE_VERIFICATION_TOKEN); + assertThat(response.getScope()).isEqualTo(expectedScope); this.token = response.getToken(); } @@ -418,7 +488,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings); TotpTwoFaAccountConfig totpTwoFaAccountConfig = (TotpTwoFaAccountConfig) twoFactorAuthService.generateNewAccountConfig(user, TwoFaProviderType.TOTP); - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, user.getId(), totpTwoFaAccountConfig); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, user, totpTwoFaAccountConfig); return totpTwoFaAccountConfig; } @@ -436,7 +506,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest { SmsTwoFaAccountConfig smsTwoFaAccountConfig = new SmsTwoFaAccountConfig(); smsTwoFaAccountConfig.setPhoneNumber("+38050505050"); - twoFaConfigManager.saveTwoFaAccountConfig(tenantId, user.getId(), smsTwoFaAccountConfig); + twoFaConfigManager.saveTwoFaAccountConfig(tenantId, user, smsTwoFaAccountConfig); return smsTwoFaAccountConfig; } 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 7d7131df96..957b3721e0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java @@ -50,13 +50,17 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.user.UserDao; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -263,6 +267,37 @@ public class UserControllerTest extends AbstractControllerTest { Assert.assertEquals(savedUser, foundUser); } + @Test + public void testFindUsersByIds() throws Exception { + loginTenantAdmin(); + List savedUsers = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + User user = createTenantAdminUser(); + savedUsers.add(doPost("/api/user", user, User.class)); + } + + String idsParam = savedUsers.stream() + .map(u -> u.getId().getId().toString()) + .collect(Collectors.joining(",")); + + User[] foundUsers = doGet("/api/users?userIds=" + idsParam, User[].class); + + Assert.assertNotNull(foundUsers); + Assert.assertEquals(savedUsers.size(), foundUsers.length); + + Map foundById = Arrays.stream(foundUsers) + .collect(Collectors.toMap(u -> u.getId().getId(), Function.identity())); + + for (User savedUser : savedUsers) { + User foundUser = foundById.get(savedUser.getId().getId()); + Assert.assertNotNull("User not found for id " + savedUser.getId().getId(), foundUser); + + foundUser.setAdditionalInfo(savedUser.getAdditionalInfo()); + Assert.assertEquals(savedUser, foundUser); + } + } + + @Test public void testSaveUserWithSameEmail() throws Exception { loginSysAdmin(); 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 0bc7cb4f59..917470eea1 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java @@ -29,12 +29,17 @@ 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -150,6 +155,37 @@ public class WidgetsBundleControllerTest extends AbstractControllerTest { Assert.assertEquals(savedWidgetsBundle, foundWidgetsBundle); } + @Test + public void testFindWidgetsBundlesByIds() throws Exception { + List savedWidgetsBundles = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTitle("My widgets bundle " + i); + savedWidgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class)); + } + + String idsParam = savedWidgetsBundles.stream() + .map(wb -> wb.getId().getId().toString()) + .collect(Collectors.joining(",")); + + WidgetsBundle[] foundWidgetsBundles = + doGet("/api/widgetsBundles?widgetsBundleIds=" + idsParam, WidgetsBundle[].class); + + Assert.assertNotNull(foundWidgetsBundles); + Assert.assertEquals(savedWidgetsBundles.size(), foundWidgetsBundles.length); + + Map foundById = Arrays.stream(foundWidgetsBundles) + .collect(Collectors.toMap(wb -> wb.getId().getId(), Function.identity())); + + for (WidgetsBundle savedWidgetsBundle : savedWidgetsBundles) { + UUID id = savedWidgetsBundle.getId().getId(); + WidgetsBundle foundWidgetsBundle = foundById.get(id); + Assert.assertNotNull("WidgetsBundle not found for id " + id, foundWidgetsBundle); + Assert.assertEquals(savedWidgetsBundle, foundWidgetsBundle); + } + } + + @Test public void testDeleteWidgetsBundle() throws Exception { WidgetsBundle widgetsBundle = new WidgetsBundle(); 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 b46811daf5..260476fe4d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -22,6 +22,7 @@ import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageLite; import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -105,6 +106,7 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; @@ -750,4 +752,27 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { return rpc; } + protected void verifyEdgeDisconnected() { + verifyEdgeActiveFlag(false); + } + + protected void verifyEdgeConnected() { + verifyEdgeActiveFlag(true); + } + + private void verifyEdgeActiveFlag(boolean value) { + Awaitility.await() + .atMost(TIMEOUT, 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 Boolean.toString(value).equals(activeAttr.get("value").toString()); + }); + } + } diff --git a/application/src/test/java/org/thingsboard/server/edge/AiModelEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AiModelEdgeTest.java new file mode 100644 index 0000000000..4b23ec3cf9 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/AiModelEdgeTest.java @@ -0,0 +1,216 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.google.protobuf.InvalidProtocolBufferException; +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.ai.AiModel; +import org.thingsboard.server.common.data.ai.model.chat.OpenAiChatModelConfig; +import org.thingsboard.server.common.data.ai.provider.OpenAiProviderConfig; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; +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 java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; + +@DaoSqlTest +public class AiModelEdgeTest extends AbstractEdgeTest { + + private static final String DEFAULT_AI_MODEL_NAME = "Edge Test AiModel"; + private static final String UPDATED_AI_MODEL_NAME = "Updated Edge Test AiModel"; + + @Test + public void testAiModel_create_update_delete_fromCloud() throws Exception { + // create AiModel + AiModel aiModel = createSimpleAiModel(DEFAULT_AI_MODEL_NAME); + + edgeImitator.expectMessageAmount(1); + AiModel savedAiModel = doPost("/api/ai/model", aiModel, AiModel.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AiModelUpdateMsg); + AiModelUpdateMsg aiModelUpdateMsg = (AiModelUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, aiModelUpdateMsg.getMsgType()); + Assert.assertEquals(savedAiModel.getUuidId().getMostSignificantBits(), aiModelUpdateMsg.getIdMSB()); + Assert.assertEquals(savedAiModel.getUuidId().getLeastSignificantBits(), aiModelUpdateMsg.getIdLSB()); + AiModel aiModelFromMsg = JacksonUtil.fromString(aiModelUpdateMsg.getEntity(), AiModel.class, true); + Assert.assertNotNull(aiModelFromMsg); + + Assert.assertEquals(DEFAULT_AI_MODEL_NAME, aiModelFromMsg.getName()); + Assert.assertEquals(savedAiModel.getTenantId(), aiModelFromMsg.getTenantId()); + + // update AiModel + edgeImitator.expectMessageAmount(1); + savedAiModel.setName(UPDATED_AI_MODEL_NAME); + savedAiModel = doPost("/api/ai/model", savedAiModel, AiModel.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AiModelUpdateMsg); + aiModelUpdateMsg = (AiModelUpdateMsg) latestMessage; + aiModelFromMsg = JacksonUtil.fromString(aiModelUpdateMsg.getEntity(), AiModel.class, true); + Assert.assertNotNull(aiModelFromMsg); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, aiModelUpdateMsg.getMsgType()); + Assert.assertEquals(UPDATED_AI_MODEL_NAME, aiModelFromMsg.getName()); + + // delete AiModel + edgeImitator.expectMessageAmount(1); + doDelete("/api/ai/model/" + savedAiModel.getUuidId()) + .andExpect(status().isOk()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AiModelUpdateMsg); + aiModelUpdateMsg = (AiModelUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, aiModelUpdateMsg.getMsgType()); + Assert.assertEquals(savedAiModel.getUuidId().getMostSignificantBits(), aiModelUpdateMsg.getIdMSB()); + Assert.assertEquals(savedAiModel.getUuidId().getLeastSignificantBits(), aiModelUpdateMsg.getIdLSB()); + } + + @Test + public void testAiModel_create_update_delete_toCloud() throws Exception { + // create + AiModel aiModel = createSimpleAiModel(DEFAULT_AI_MODEL_NAME); + UUID uuid = Uuids.timeBased(); + UplinkMsg uplinkMsg = getUplinkMsg(uuid, aiModel, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + + checkAiModelOnCloud(uplinkMsg, uuid, aiModel.getName()); + + // update + aiModel.setName(UPDATED_AI_MODEL_NAME); + UplinkMsg updatedUplinkMsg = getUplinkMsg(uuid, aiModel, UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE); + + checkAiModelOnCloud(updatedUplinkMsg, uuid, aiModel.getName()); + + // delete + UplinkMsg deleteUplinkMsg = getDeleteUplinkMsg(uuid); + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(deleteUplinkMsg); + Assert.assertTrue(edgeImitator.waitForResponses()); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> + doGet("/api/ai/model/" + uuid, AiModel.class, status().isNotFound()) + ); + } + + @Test + public void testAiModelToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + AiModel aiModel = createSimpleAiModel(DEFAULT_AI_MODEL_NAME); + + edgeImitator.expectMessageAmount(1); + AiModel savedAiModel = doPost("/api/ai/model", aiModel, AiModel.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + UUID uuid = Uuids.timeBased(); + + UplinkMsg uplinkMsg = getUplinkMsg(uuid, aiModel, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsg); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional aiModelUpdateMsgOpt = edgeImitator.findMessageByType(AiModelUpdateMsg.class); + Assert.assertTrue(aiModelUpdateMsgOpt.isPresent()); + AiModelUpdateMsg latestAiModelUpdateMsg = aiModelUpdateMsgOpt.get(); + AiModel aiModelFromMsg = JacksonUtil.fromString(latestAiModelUpdateMsg.getEntity(), AiModel.class, true); + Assert.assertNotNull(aiModelFromMsg); + Assert.assertNotEquals(DEFAULT_AI_MODEL_NAME, aiModelFromMsg.getName()); + + Assert.assertNotEquals(savedAiModel.getUuidId(), uuid); + + AiModel aiModelFromCloud = doGet("/api/ai/model/" + uuid, AiModel.class); + Assert.assertNotNull(aiModelFromCloud); + Assert.assertNotEquals(DEFAULT_AI_MODEL_NAME, aiModelFromCloud.getName()); + } + + private AiModel createSimpleAiModel(String name) { + AiModel aiModel = new AiModel(); + aiModel.setTenantId(tenantId); + aiModel.setName(name); + aiModel.setConfiguration(OpenAiChatModelConfig.builder() + .providerConfig(new OpenAiProviderConfig(null, "test-api-key")) + .modelId("gpt-4o") + .temperature(0.5) + .topP(0.3) + .frequencyPenalty(0.1) + .presencePenalty(0.2) + .maxOutputTokens(1000) + .timeoutSeconds(60) + .maxRetries(2) + .build()); + return aiModel; + } + + private UplinkMsg getDeleteUplinkMsg(UUID uuid) throws InvalidProtocolBufferException { + UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); + AiModelUpdateMsg.Builder aiModelDeleteMsgBuilder = AiModelUpdateMsg.newBuilder(); + aiModelDeleteMsgBuilder.setMsgType(ENTITY_DELETED_RPC_MESSAGE); + aiModelDeleteMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + aiModelDeleteMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + testAutoGeneratedCodeByProtobuf(aiModelDeleteMsgBuilder); + + upLinkMsgBuilder.addAiModelUpdateMsg(aiModelDeleteMsgBuilder.build()); + testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder); + + return upLinkMsgBuilder.build(); + } + + private UplinkMsg getUplinkMsg(UUID uuid, AiModel aiModel, UpdateMsgType updateMsgType) throws InvalidProtocolBufferException { + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + AiModelUpdateMsg.Builder aiModelUpdateMsgBuilder = AiModelUpdateMsg.newBuilder(); + aiModelUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + aiModelUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + aiModelUpdateMsgBuilder.setEntity(JacksonUtil.toString(aiModel)); + aiModelUpdateMsgBuilder.setMsgType(updateMsgType); + testAutoGeneratedCodeByProtobuf(aiModelUpdateMsgBuilder); + uplinkMsgBuilder.addAiModelUpdateMsg(aiModelUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + return uplinkMsgBuilder.build(); + } + + private void checkAiModelOnCloud(UplinkMsg uplinkMsg, UUID uuid, String resourceTitle) throws Exception { + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsg); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + + AiModel aiModel = doGet("/api/ai/model/" + uuid, AiModel.class); + Assert.assertNotNull(aiModel); + Assert.assertEquals(resourceTitle, aiModel.getName()); + } + +} 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 931a02de58..96c6dd42b7 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java @@ -15,7 +15,6 @@ */ 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; @@ -28,8 +27,6 @@ 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; @@ -37,10 +34,11 @@ 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 java.util.concurrent.TimeUnit; +import static org.awaitility.Awaitility.await; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @@ -277,12 +275,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { 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)); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> + doGet("/api/asset/info/" + savedAsset.getUuidId(), AssetInfo.class, status().isNotFound()) + ); } private Asset saveAssetOnCloudAndVerifyDeliveryToEdge() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/edge/CalculatedFieldEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/CalculatedFieldEdgeTest.java index 06a4aefd63..6199657936 100644 --- a/application/src/test/java/org/thingsboard/server/edge/CalculatedFieldEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/CalculatedFieldEdgeTest.java @@ -26,10 +26,9 @@ import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -224,9 +223,8 @@ public class CalculatedFieldEdgeTest extends AbstractEdgeTest { config.setExpression("(T * 9/5) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); output.setDecimalsByDefault(2); config.setOutput(output); 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 cf0cf9cd51..9c70db267e 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java @@ -40,10 +40,11 @@ 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 java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import static org.awaitility.Awaitility.await; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @@ -264,7 +265,7 @@ public class DashboardEdgeTest extends AbstractEdgeTest { } @Test - public void testSendDeleteEntityViewOnEdgeToCloud() throws Exception { + public void testSendDeleteDashboardOnEdgeToCloud() throws Exception { Dashboard savedDashboard = saveDashboardOnCloudAndVerifyDeliveryToEdge(); UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); @@ -281,12 +282,10 @@ public class DashboardEdgeTest extends AbstractEdgeTest { 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)); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> + doGet("/api/dashboard/info/" + savedDashboard.getUuidId(), DashboardInfo.class, status().isNotFound()) + ); } private Dashboard saveDashboardOnCloudAndVerifyDeliveryToEdge() throws Exception { @@ -300,10 +299,10 @@ public class DashboardEdgeTest extends AbstractEdgeTest { 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()); + DashboardUpdateMsg dashboardUpdateMsg = dashboardUpdateMsgOpt.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); + Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); + Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); return savedDashboard; } 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 a8f82f8b7d..9349f79c02 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -52,8 +52,6 @@ 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.ota.OtaPackageType; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; 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; @@ -82,6 +80,7 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; +import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -423,12 +422,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest { edgeImitator.expectResponsesAmount(1); edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build()); Assert.assertTrue(edgeImitator.waitForResponses()); - DeviceInfo deviceInfo = doGet("/api/device/info/" + savedDevice.getUuidId(), DeviceInfo.class); - Assert.assertNotNull(deviceInfo); - List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", - new TypeReference>() { - }, new PageLink(100)).getData(); - Assert.assertFalse(edgeDevices.contains(deviceInfo)); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> + doGet("/api/device/info/" + savedDevice.getUuidId(), DeviceInfo.class, status().isNotFound()) + ); } @Test @@ -682,8 +679,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { .atMost(10, TimeUnit.SECONDS) .until(() -> { String urlTemplate = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/keys/attributes/" + scope; - List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() { - }); + List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {}); return actualKeys != null && !actualKeys.isEmpty() && actualKeys.contains(expectedKey); }); @@ -904,18 +900,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { ObjectNode attributes = JacksonUtil.newObjectNode(); attributes.put("active", true); doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); - Awaitility.await() - .atMost(TIMEOUT, 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()); - }); + verifyEdgeConnected(); } } 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 810a03b415..f44b4e295e 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java @@ -151,14 +151,20 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { // delete profile when edge is offline edgeImitator.disconnect(); + verifyEdgeDisconnected(); + doDelete("/api/deviceProfile/" + deviceProfile.getUuidId()) .andExpect(status().isOk()); - edgeImitator.connect(); // 25 sync message - // + 2 RuleChain and RuleChainMetadata - // + 1 delete DeviceProfile + // + 1 RuleChain Added + // + 1 RuleChainMetadata Added + // + 1 DeviceProfile Delete edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT + 3); + + edgeImitator.connect(); + verifyEdgeConnected(); + Assert.assertTrue(edgeImitator.waitForMessages()); latestMessage = edgeImitator.getLatestMessage(); diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeStatsIntegrationTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeStatsIntegrationTest.java new file mode 100644 index 0000000000..e6cad9da6a --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeStatsIntegrationTest.java @@ -0,0 +1,146 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +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.asset.Asset; +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.kv.TsKvEntry; +import org.thingsboard.server.dao.edge.stats.EdgeStatsCounterService; +import org.thingsboard.server.dao.edge.stats.EdgeStatsKey; +import org.thingsboard.server.dao.edge.stats.MsgCounters; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.edge.stats.EdgeStatsService; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_ADDED; +import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_PERMANENTLY_FAILED; +import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_PUSHED; +import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_TMP_FAILED; + +@DaoSqlTest +@Slf4j +public class EdgeStatsIntegrationTest extends AbstractEdgeTest { + + private static final String STATISTICS_DEVICE_PROFILE = "STATISTICS"; + + private static final long EXPECTED_MSGS_ADDED = 6L; + private static final long EXPECTED_MSGS_PUSHED = 6L; + private static final long EXPECTED_MSGS_PERMANENTLY_FAILED = 0L; + private static final long EXPECTED_MSGS_TMP_FAILED = 0L; + + @Autowired + private EdgeStatsService edgeStatsService; + @Autowired + private EdgeStatsCounterService statsCounterService; + + @Test + public void testReportStats() throws Exception { + // GIVEN + simulateEdgeEventsAddedDownlinkPushed(); + + // Await Edge Counters Updated + await().atMost(10, TimeUnit.SECONDS).pollInterval(Duration.ofMillis(200)).untilAsserted(() -> { + MsgCounters counters = statsCounterService.getMsgCountersByEdge().get(edge.getId()); + assertEquals(EXPECTED_MSGS_ADDED, counters.getMsgsAdded().get()); + assertEquals(EXPECTED_MSGS_PUSHED, counters.getMsgsPushed().get()); + assertEquals(EXPECTED_MSGS_PERMANENTLY_FAILED, counters.getMsgsPermanentlyFailed().get()); + assertEquals(EXPECTED_MSGS_TMP_FAILED, counters.getMsgsTmpFailed().get()); + }); + + // WHEN + edgeStatsService.reportStats(); + + // THEN + await().atMost(10, TimeUnit.SECONDS).pollInterval(Duration.ofMillis(200)).untilAsserted(() -> { + List actualStats = fetchLatestStats(); + assertEquals(EXPECTED_MSGS_ADDED, getStatsLongValue(actualStats, DOWNLINK_MSGS_ADDED)); + assertEquals(EXPECTED_MSGS_PUSHED, getStatsLongValue(actualStats, DOWNLINK_MSGS_PUSHED)); + assertEquals(EXPECTED_MSGS_PERMANENTLY_FAILED, getStatsLongValue(actualStats, DOWNLINK_MSGS_PERMANENTLY_FAILED)); + assertEquals(EXPECTED_MSGS_TMP_FAILED, getStatsLongValue(actualStats, DOWNLINK_MSGS_TMP_FAILED)); + }); + } + + private long getStatsLongValue(List stats, EdgeStatsKey key) { + return stats.stream().filter(e -> e.getKey().equals(key.getKey())).findFirst().get().getLongValue().orElse(0L); + } + + private List fetchLatestStats() throws ExecutionException, InterruptedException { + return tsService.findLatest( + tenantId, + edge.getId(), + Arrays.stream(EdgeStatsKey.values()).map(EdgeStatsKey::getKey).toList()).get(); + } + + private void simulateEdgeEventsAddedDownlinkPushed() throws InterruptedException, ExecutionException { + statsCounterService.clear(edge.getId()); + + // Save device and assign to edge + // 2 DOWNLINK_MSGS_ADDED, EdgeEvents: [{DEVICE_PROFILE: ADDED}, {DEVICE: ASSIGNED_TO_EDGE}] + // 2 DOWNLINK_MSGS_PUSHED, Downlinks: [{deviceProfileUpdateMsg}, {deviceUpdateMsg, deviceProfileUpdateMsg, deviceCredentialsUpdateMsg}] + edgeImitator.expectMessageAmount(4); + Device savedDevice = saveDevice("StatisticDevice", STATISTICS_DEVICE_PROFILE); + doPost("/api/edge/" + edge.getUuidId() + "/device/" + savedDevice.getUuidId(), Device.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + // Save asset and assign to edge + // 1 DOWNLINK_MSGS_ADDED, EdgeEvents: [{ASSET: ASSIGNED_TO_EDGE}] + // 1 DOWNLINK_MSGS_PUSHED, Downlinks: [{assetUpdateMsg, assetProfileUpdateMsg}] + edgeImitator.expectMessageAmount(2); + Asset savedAsset = saveAsset("Edge Asset"); + doPost("/api/edge/" + edge.getUuidId() + + "/asset/" + savedAsset.getUuidId(), Asset.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + // Create customer and assign edge to the customer + // 2 DOWNLINK_MSGS_ADDED, EdgeEvents: [{CUSTOMER: ADDED}, {EDGE: ASSIGNED_TO_CUSTOMER}] + // 2 DOWNLINK_MSGS_PUSHED, Downlinks: [{customerUpdateMsg}, {edgeConfiguration}] + edgeImitator.expectMessageAmount(2); + Customer customer = new Customer(); + customer.setTitle("Edge Customer"); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + doPost("/api/customer/" + savedCustomer.getUuidId() + + "/edge/" + edge.getUuidId(), Edge.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + // Send device telemetry downlink for the device + // 1 DOWNLINK_MSGS_ADDED, EdgeEvents: [{DEVICE: TIMESERIES_UPDATED}] + // 1 DOWNLINK_MSGS_PUSHED, Downlinks: [{entityData}] + edgeImitator.expectMessageAmount(1); + String timeseriesData = "{\"data\":{\"temperature\":25},\"ts\":" + System.currentTimeMillis() + "}"; + JsonNode timeseriesEntityData = JacksonUtil.toJsonNode(timeseriesData); + EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED, savedDevice.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData); + edgeEventService.saveAsync(edgeEvent).get(); + Assert.assertTrue(edgeImitator.waitForMessages()); + } +} 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 f404508e36..ce7b915457 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java @@ -15,7 +15,6 @@ */ 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; @@ -31,8 +30,6 @@ 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; @@ -40,10 +37,11 @@ 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 java.util.concurrent.TimeUnit; +import static org.awaitility.Awaitility.await; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @@ -256,12 +254,9 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { 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)); + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> + doGet("/api/entityView/info/" + savedEntityView.getUuidId(), EntityViewInfo.class, status().isNotFound()) + ); } private void verifyEntityViewUpdateMsg(EntityView entityView, Device device) throws InvalidProtocolBufferException { 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 17bc177d10..5818179f45 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java @@ -223,8 +223,10 @@ public class TelemetryEdgeTest extends AbstractEdgeTest { device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData); edgeEventService.saveAsync(failedEdgeEvent).get(); + // add unique body to avoid merge and filter by device id in edge service (mergeAndFilterDownlinkDuplicates) + JsonNode uniqueBody = JacksonUtil.toJsonNode("{\"idx\":" + idx + "}"); EdgeEvent successEdgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.UPDATED, - device.getId().getId(), EdgeEventType.DEVICE, null); + device.getId().getId(), EdgeEventType.DEVICE, uniqueBody); edgeEventService.saveAsync(successEdgeEvent).get(); } 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 6b3bffc883..f344335748 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -15,15 +15,22 @@ */ package org.thingsboard.server.edge; -import com.google.protobuf.AbstractMessage; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.test.web.servlet.ResultMatcher; +import org.testcontainers.shaded.org.awaitility.Awaitility; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.UserCredentialsId; +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.dao.service.DaoSqlTest; @@ -32,11 +39,15 @@ import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils; import org.thingsboard.server.service.security.model.ChangePasswordRequest; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.user.UserServiceImpl.DEFAULT_TOKEN_LENGTH; @DaoSqlTest public class UserEdgeTest extends AbstractEdgeTest { @@ -44,216 +55,304 @@ public class UserEdgeTest extends AbstractEdgeTest { @Autowired private BCryptPasswordEncoder passwordEncoder; + private static final String DEFAULT_FIRST_NAME = "Boris"; + private static final String DEFAULT_LAST_NAME = "Johnson"; + private static final String UPDATED_LAST_NAME = "Borisov"; + private static final String DEFAULT_TENANT_ADMIN_EMAIL = "tenantAdmin@thingsboard.org"; + private static final String DEFAULT_CUSTOMER_USER_EMAIL = "customerUser@thingsboard.org"; + @Test public void testCreateUpdateDeleteTenantUser() throws Exception { // create user - edgeImitator.expectMessageAmount(3); - User newTenantAdmin = new User(); - newTenantAdmin.setAuthority(Authority.TENANT_ADMIN); - newTenantAdmin.setTenantId(tenantId); - newTenantAdmin.setEmail("tenantAdmin@thingsboard.org"); - newTenantAdmin.setFirstName("Boris"); - newTenantAdmin.setLastName("Johnson"); - User savedTenantAdmin = createUser(newTenantAdmin, "tenant"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user) - Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); - Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); - 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.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()); + User newTenantAdmin = buildUser(Authority.TENANT_ADMIN, null); + User savedTenantAdmin = createAndVerifyUserOnEdge(newTenantAdmin); // update user - edgeImitator.expectMessageAmount(2); - savedTenantAdmin.setLastName("Borisov"); - savedTenantAdmin = doPost("/api/user", savedTenantAdmin, User.class); - Assert.assertTrue(edgeImitator.waitForMessages()); - userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); - Assert.assertTrue(userUpdateMsgOpt.isPresent()); - userUpdateMsg = userUpdateMsgOpt.get(); - userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); - Assert.assertNotNull(userMsg); - Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName()); + updateAndVerifyUserLastName(savedTenantAdmin); // update user credentials login(savedTenantAdmin.getEmail(), "tenant"); + updateAndVerifyUserCredentials(savedTenantAdmin); + loginTenantAdmin(); - edgeImitator.expectMessageAmount(1); - ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(); - changePasswordRequest.setCurrentPassword("tenant"); - changePasswordRequest.setNewPassword("newTenant"); - doPost("/api/auth/changePassword", changePasswordRequest); - Assert.assertTrue(edgeImitator.waitForMessages()); - AbstractMessage latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); - UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - 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())); + // delete user + deleteAndVerifyUser(savedTenantAdmin); + } + + @Test + public void testCreateUpdateDeleteCustomerUser() throws Exception { + // create customer + Customer savedCustomer = createAndAssignCustomerToEdge(); + // create user + User customerUser = buildUser(Authority.CUSTOMER_USER, savedCustomer.getId()); + User savedCustomerUser = createAndVerifyUserOnEdge(customerUser); + + // update user + updateAndVerifyUserLastName(savedCustomerUser); + + // update user credentials + login(savedCustomerUser.getEmail(), "customer"); + updateAndVerifyUserCredentials(savedCustomerUser); loginTenantAdmin(); // delete user + deleteAndVerifyUser(savedCustomerUser); + } + + @Test + public void testSendUserToCloudFromEdge() throws Exception { + // create customer + Customer savedCustomer = createAndAssignCustomerToEdge(); + + // create uplinkMsg with user and userCredentials + UserId userId = new UserId(UUID.randomUUID()); + UserCredentialsId userCredentialsId = new UserCredentialsId(UUID.randomUUID()); + UplinkMsg uplinkMsg = buildUserUplinkMsg(userId, savedCustomer.getId(), userCredentialsId); + + User userFromCloud = verifyMsgOnCloud(uplinkMsg, userId, false); + assertUserCredentialsFlags(userFromCloud, false, false); + + // create uplinkMsg with enabled userCredentials + UplinkMsg uplinkMsgWithEnabledCredentials = constructUserCredentialsUplinkMsg(userCredentialsId, userId); + + User cloudUserWithCredentials = verifyMsgOnCloud(uplinkMsgWithEnabledCredentials, userId, false); + assertUserCredentialsFlags(cloudUserWithCredentials, true, true); + + // create uplinkMsg with user the same email + UserId secondUserId = new UserId(UUID.randomUUID()); + UserCredentialsId secondCredentialsId = new UserCredentialsId(UUID.randomUUID()); + UplinkMsg uplinkMsgForUserExistingEmail = buildUserUplinkMsg(secondUserId, savedCustomer.getId(), secondCredentialsId); + + verifyMsgOnCloud(uplinkMsgForUserExistingEmail, secondUserId, true); + } + + @Test + public void testSendUserCredentialsRequestToCloud() throws Exception { + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder(); + userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits()); + userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits()); + testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder); + uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); edgeImitator.expectMessageAmount(1); - doDelete("/api/user/" + savedTenantAdmin.getUuidId()) - .andExpect(status().isOk()); + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + Assert.assertTrue(edgeImitator.waitForResponses()); Assert.assertTrue(edgeImitator.waitForMessages()); - latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof UserUpdateMsg); - userUpdateMsg = (UserUpdateMsg) latestMessage; - Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); + + UserCredentialsUpdateMsg userCredentialsUpdateMsg = getLatestUserCredentialsUpdateMsg(); + UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); + + testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg); } @Test - public void testCreateUpdateDeleteCustomerUser() throws Exception { + public void testSendUserDeleteFromEdgeToCloud() throws Exception { // create customer + Customer savedCustomer = createAndAssignCustomerToEdge(); + + // create user + User customerUser = buildUser(Authority.CUSTOMER_USER, savedCustomer.getId()); + User savedCustomerUser = createAndVerifyUserOnEdge(customerUser); + + // simulate user removal event from edge to cloud + UserUpdateMsg.Builder userUpdateMsg = UserUpdateMsg.newBuilder().setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(savedCustomerUser.getUuidId().getMostSignificantBits()) + .setIdLSB(savedCustomerUser.getUuidId().getLeastSignificantBits()); + + UplinkMsg uplink = UplinkMsg.newBuilder() + .setUplinkMsgId(EdgeUtils.nextPositiveInt()) + .addUserUpdateMsg(userUpdateMsg).build(); + + testAutoGeneratedCodeByProtobuf(userUpdateMsg); + + // expect edge message sent & cloud message response + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplink); + Assert.assertTrue(edgeImitator.waitForResponses()); + + loginTenantAdmin(); + Awaitility.await().atMost(10, TimeUnit.SECONDS) + .until(() -> { + try { + doGet("/api/user/" + savedCustomerUser.getId(), User.class, status().isNotFound()); + return true; + } catch (Throwable ex) { + return false; + } + }); + } + + private Customer createAndAssignCustomerToEdge() throws Exception { edgeImitator.expectMessageAmount(1); Customer customer = new Customer(); customer.setTitle("Edge Customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); Assert.assertFalse(edgeImitator.waitForMessages(5)); - // assign edge to customer edgeImitator.expectMessageAmount(2); - doPost("/api/customer/" + savedCustomer.getUuidId() - + "/edge/" + edge.getUuidId(), Edge.class); + doPost("/api/customer/" + savedCustomer.getUuidId() + "/edge/" + edge.getUuidId(), Edge.class); Assert.assertTrue(edgeImitator.waitForMessages()); - // create user + return savedCustomer; + } + + private User buildUser(Authority authority, CustomerId customerId) { + User user = new User(); + + user.setAuthority(authority); + user.setTenantId(tenantId); + user.setCustomerId(customerId); + user.setEmail(authority == Authority.TENANT_ADMIN ? DEFAULT_TENANT_ADMIN_EMAIL : DEFAULT_CUSTOMER_USER_EMAIL); + user.setFirstName(DEFAULT_FIRST_NAME); + user.setLastName(DEFAULT_LAST_NAME); + + return user; + } + + private User createAndVerifyUserOnEdge(User user) throws Exception { + String password = user.getAuthority() == Authority.TENANT_ADMIN ? "tenant" : "customer"; + + // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user) edgeImitator.expectMessageAmount(3); - User customerUser = new User(); - customerUser.setAuthority(Authority.CUSTOMER_USER); - 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 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user) + User savedUser = createUser(user, password); + Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); - Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); - Assert.assertTrue(userUpdateMsgOpt.isPresent()); - UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); + + UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg(); User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - 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()); + Assert.assertEquals(savedUser.getId(), userMsg.getId()); + Assert.assertEquals(savedUser.getCustomerId(), userMsg.getCustomerId()); + Assert.assertEquals(savedUser.getAuthority(), userMsg.getAuthority()); + Assert.assertEquals(savedUser.getEmail(), userMsg.getEmail()); + Assert.assertEquals(savedUser.getFirstName(), userMsg.getFirstName()); + Assert.assertEquals(savedUser.getLastName(), userMsg.getLastName()); + + return savedUser; + } + + private void updateAndVerifyUserLastName(User user) throws Exception { + user.setLastName(UPDATED_LAST_NAME); - // update user edgeImitator.expectMessageAmount(2); - savedCustomerUser.setLastName("Addams"); - savedCustomerUser = doPost("/api/user", savedCustomerUser, User.class); + doPost("/api/user", user, User.class); Assert.assertTrue(edgeImitator.waitForMessages()); - userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); - Assert.assertTrue(userUpdateMsgOpt.isPresent()); - userUpdateMsg = userUpdateMsgOpt.get(); - userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); - Assert.assertNotNull(userMsg); + + UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg(); + User userFromMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + Assert.assertNotNull(userFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName()); + Assert.assertEquals(UPDATED_LAST_NAME, userFromMsg.getLastName()); + } - // update user credentials - login(savedCustomerUser.getEmail(), "customer"); + private void updateAndVerifyUserCredentials(User user) throws Exception { + String password = user.getAuthority() == Authority.TENANT_ADMIN ? "tenant" : "customer"; + String newPassword = "new" + password; edgeImitator.expectMessageAmount(1); ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(); - changePasswordRequest.setCurrentPassword("customer"); - changePasswordRequest.setNewPassword("newCustomer"); + changePasswordRequest.setCurrentPassword(password); + changePasswordRequest.setNewPassword(newPassword); doPost("/api/auth/changePassword", changePasswordRequest); Assert.assertTrue(edgeImitator.waitForMessages()); - AbstractMessage latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); - UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - 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(); + UserCredentialsUpdateMsg msg = getLatestUserCredentialsUpdateMsg(); + UserCredentials creds = JacksonUtil.fromString(msg.getEntity(), UserCredentials.class, true); + Assert.assertNotNull(creds); + Assert.assertEquals(user.getId(), creds.getUserId()); + Assert.assertTrue(passwordEncoder.matches(newPassword, creds.getPassword())); + } - // delete user + private void deleteAndVerifyUser(User savedTenantAdmin) throws Exception { edgeImitator.expectMessageAmount(1); - doDelete("/api/user/" + savedCustomerUser.getUuidId()) + doDelete("/api/user/" + savedTenantAdmin.getUuidId()) .andExpect(status().isOk()); Assert.assertTrue(edgeImitator.waitForMessages()); - latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof UserUpdateMsg); - userUpdateMsg = (UserUpdateMsg) latestMessage; + + UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg(); Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomerUser.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomerUser.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); + Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); + Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); + } + + private UplinkMsg buildUserUplinkMsg(UserId userId, CustomerId customerId, UserCredentialsId userCredentialsUuid) { + User customerUser = buildUser(Authority.CUSTOMER_USER, customerId); + customerUser.setId(userId); + UserCredentials userCredentials = buildCredentials(userCredentialsUuid, userId, false); + userCredentials.setActivateToken(StringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH)); + UserUpdateMsg userUpdateMsg = EdgeMsgConstructorUtils.constructUserUpdatedMsg(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUser); + UserCredentialsUpdateMsg userCredentialsMsg = EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentials); + + return UplinkMsg.newBuilder() + .addUserUpdateMsg(userUpdateMsg) + .addUserCredentialsUpdateMsg(userCredentialsMsg) + .build(); } - @Test - public void testSendUserCredentialsRequestToCloud() throws Exception { - UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); - UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder(); - userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits()); - userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits()); - testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder); - uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build()); + private UserCredentials buildCredentials(UserCredentialsId userCredentialsUuid, UserId userId, boolean enabled) { + UserCredentials userCredentials = new UserCredentials(); - testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + userCredentials.setId(userCredentialsUuid); + userCredentials.setUserId(userId); + userCredentials.setEnabled(enabled); + userCredentials.setAdditionalInfo(JacksonUtil.newObjectNode()); + + return userCredentials; + } + private User verifyMsgOnCloud(UplinkMsg uplinkMsg, UserId userId, boolean emailExist) throws Exception { edgeImitator.expectResponsesAmount(1); - edgeImitator.expectMessageAmount(1); - edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + edgeImitator.sendUplinkMsg(uplinkMsg); Assert.assertTrue(edgeImitator.waitForResponses()); - Assert.assertTrue(edgeImitator.waitForMessages()); - AbstractMessage latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); - UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); - Assert.assertNotNull(userCredentialsMsg); - Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); + User userFromCloud = doGet("/api/user/" + userId, User.class); + Assert.assertNotNull(userFromCloud); + + if (emailExist) { + Assert.assertNotEquals(DEFAULT_CUSTOMER_USER_EMAIL, userFromCloud.getEmail()); + } else { + Assert.assertEquals(DEFAULT_CUSTOMER_USER_EMAIL, userFromCloud.getEmail()); + } + return userFromCloud; } - @Test - public void sendUserCredentialsRequest() throws Exception { - UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); - UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder(); - userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits()); - userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits()); - testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder); - uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build()); + private UplinkMsg constructUserCredentialsUplinkMsg(UserCredentialsId userCredentialsUuid, UserId userId) { + UserCredentials userCredentials = buildCredentials(userCredentialsUuid, userId, true); + userCredentials.setPassword("password"); + UserCredentialsUpdateMsg credsMsg = EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentials); - testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + return UplinkMsg.newBuilder() + .addUserCredentialsUpdateMsg(credsMsg) + .build(); + } - edgeImitator.expectResponsesAmount(1); - edgeImitator.expectMessageAmount(1); - edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); - Assert.assertTrue(edgeImitator.waitForResponses()); - Assert.assertTrue(edgeImitator.waitForMessages()); + private void assertUserCredentialsFlags(User user, boolean enabled, boolean activated) { + JsonNode info = user.getAdditionalInfo(); + Assert.assertNotNull(info); + Assert.assertEquals(enabled, info.get("userCredentialsEnabled").asBoolean()); + Assert.assertEquals(activated, info.get("userActivated").asBoolean()); + } - AbstractMessage latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); - UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); - Assert.assertNotNull(userCredentialsMsg); - Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); + private UserUpdateMsg getLatestUserUpdateMsg() { + Optional opt = edgeImitator.findMessageByType(UserUpdateMsg.class); + Assert.assertTrue(opt.isPresent()); + return opt.get(); + } - testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg); + private UserCredentialsUpdateMsg getLatestUserCredentialsUpdateMsg() { + Optional opt = edgeImitator.findMessageByType(UserCredentialsUpdateMsg.class); + Assert.assertTrue(opt.isPresent()); + return opt.get(); } } 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 266a4dbb62..8cf2b309f6 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 @@ -29,6 +29,7 @@ import org.thingsboard.edge.rpc.EdgeGrpcClient; import org.thingsboard.edge.rpc.EdgeRpcClient; import org.thingsboard.server.controller.AbstractWebTest; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AiModelUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; @@ -369,6 +370,11 @@ public class EdgeImitator { result.add(saveDownlinkMsg(calculatedFieldUpdateMsg)); } } + if (downlinkMsg.getAiModelUpdateMsgCount() > 0) { + for (AiModelUpdateMsg aiModelUpdateMsg : downlinkMsg.getAiModelUpdateMsgList()) { + result.add(saveDownlinkMsg(aiModelUpdateMsg)); + } + } if (downlinkMsg.hasEdgeConfiguration()) { result.add(saveDownlinkMsg(downlinkMsg.getEdgeConfiguration())); } @@ -443,7 +449,7 @@ public class EdgeImitator { Optional result; lock.lock(); try { - result = (Optional) downlinkMsgs.stream().filter(downlinkMsg -> downlinkMsg.getClass().isAssignableFrom(tClass)).findAny(); + result = (Optional) downlinkMsgs.stream().filter(downlinkMsg -> tClass.isAssignableFrom(downlinkMsg.getClass())).findAny(); } finally { lock.unlock(); } @@ -455,7 +461,7 @@ public class EdgeImitator { List result; lock.lock(); try { - result = (List) downlinkMsgs.stream().filter(downlinkMsg -> downlinkMsg.getClass().isAssignableFrom(tClass)).collect(Collectors.toList()); + result = (List) downlinkMsgs.stream().filter(downlinkMsg -> tClass.isAssignableFrom(downlinkMsg.getClass())).collect(Collectors.toList()); } finally { lock.unlock(); } 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 bcd7c25085..f4a913f41d 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 @@ -118,7 +118,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac .pollInterval(10, MILLISECONDS) .atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> { - List debugEvents = getEvents(tenantId, ruleChainFinal.getFirstRuleNodeId(), EventType.LC_EVENT.getOldName(), 1000) + List debugEvents = getEvents(tenantId, ruleChainFinal.getFirstRuleNodeId(), EventType.LC_EVENT, 1000) .getData().stream().filter(e -> { var body = e.getBody(); return body.has("event") && body.get("event").asText().equals("STARTED") diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java new file mode 100644 index 0000000000..a20c7b0253 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java @@ -0,0 +1,511 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import com.google.common.util.concurrent.Futures; +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.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy; +import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration; +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.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +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.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; + +@ExtendWith(MockitoExtension.class) +public class GeofencingCalculatedFieldStateTest { + + private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("8f83eeca-b5cd-4955-9241-09d1393768c6")); + private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("688b529d-cfbe-4430-91c5-60b4f4e5d3cf")); + private final AssetId ZONE_1_ID = new AssetId(UUID.fromString("c0e3031c-7df1-45e4-9590-cfd621a4d714")); + private final AssetId ZONE_2_ID = new AssetId(UUID.fromString("e7da6200-2096-4038-a343-ade9ea4fa3e4")); + + private final SingleValueArgumentEntry latitudeArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 10, new DoubleDataEntry("latitude", 50.4730), 145L); + private final SingleValueArgumentEntry longitudeArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 6, new DoubleDataEntry("longitude", 30.5050), 165L); + + private final JsonDataEntry allowedZoneDataEntry = new JsonDataEntry("zone", "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"); + private final BaseAttributeKvEntry allowedZoneAttributeKvEntry = new BaseAttributeKvEntry(allowedZoneDataEntry, System.currentTimeMillis(), 0L); + private final GeofencingArgumentEntry geofencingAllowedZoneArgEntry = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry)); + + private final JsonDataEntry restrictedZoneDataEntry = new JsonDataEntry("zone", "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"); + private final BaseAttributeKvEntry restrictedZoneAttributeKvEntry = new BaseAttributeKvEntry(restrictedZoneDataEntry, System.currentTimeMillis(), 0L); + private final GeofencingArgumentEntry geofencingRestrictedZoneArgEntry = new GeofencingArgumentEntry(Map.of(ZONE_2_ID, restrictedZoneAttributeKvEntry)); + + + private GeofencingCalculatedFieldState state; + private CalculatedFieldCtx ctx; + + @Mock + private TenantProfile tenantProfile; + @Mock + private TbTenantProfileCache tenantProfileCache; + @Mock + private RelationService relationService; + @InjectMocks + private ActorSystemContext systemContext; + + @BeforeEach + void setUp() { + when(tenantProfileCache.get(any(TenantId.class))).thenReturn(tenantProfile); + when(tenantProfile.getProfileConfiguration()).thenReturn(Optional.of(new DefaultTenantProfileConfiguration())); + ctx = new CalculatedFieldCtx(getCalculatedField(), systemContext); + ctx.init(); + state = new GeofencingCalculatedFieldState(ctx.getEntityId()); + state.setCtx(ctx, null); + state.init(false); + } + + @Test + void testType() { + assertThat(state.getType()).isEqualTo(CalculatedFieldType.GEOFENCING); + } + + @Test + void testUpdateState() { + state.arguments = new HashMap<>(Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry + )); + + Map newArgs = Map.of("allowedZones", geofencingAllowedZoneArgEntry); + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); + + assertThat(stateUpdated).isTrue(); + assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( + Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry, + "allowedZones", geofencingAllowedZoneArgEntry + ) + ); + } + + @Test + void testUpdateStateWithInvalidArgumentTypeForLatitudeArgument() { + assertThatThrownBy(() -> state.update(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, geofencingAllowedZoneArgEntry), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for latitude argument: GEOFENCING. Only SINGLE_VALUE type is allowed."); + } + + @Test + void testUpdateStateWithInvalidArgumentTypeForLongitudeArgument() { + assertThatThrownBy(() -> state.update(Map.of(ENTITY_ID_LONGITUDE_ARGUMENT_KEY, geofencingAllowedZoneArgEntry), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for longitude argument: GEOFENCING. Only SINGLE_VALUE type is allowed."); + } + + @Test + void testUpdateStateWithInvalidArgumentTypeForGeofencingArgument() { + assertThatThrownBy(() -> state.update(Map.of("someArgumentName", latitudeArgEntry), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for someArgumentName argument: SINGLE_VALUE. Only GEOFENCING type is allowed."); + } + + @Test + void testUpdateStateWhenUpdateExistingSingleValueArgumentEntry() { + state.arguments = new HashMap<>(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry)); + + SingleValueArgumentEntry newArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 50.4760), 190L); + Map newArgs = Map.of("latitude", newArgEntry); + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); + + assertThat(stateUpdated).isTrue(); + assertThat(state.getArguments()).isEqualTo(newArgs); + } + + @Test + void testUpdateStateWhenUpdateExistingGeofencingValueArgumentEntryWithTheSameValue() { + state.arguments = new HashMap<>(Map.of("allowedZones", geofencingAllowedZoneArgEntry)); + + Map newArgs = Map.of("allowedZones", geofencingAllowedZoneArgEntry); + + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); + + assertThat(stateUpdated).isFalse(); + assertThat(state.getArguments()).isEqualTo(newArgs); + } + + @Test + void testUpdateStateWhenUpdateExistingSingleValueArgumentEntryWithValueOfAnotherType() { + state.arguments = new HashMap<>(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry)); + + assertThatThrownBy(() -> state.update(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, geofencingAllowedZoneArgEntry), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for single value argument entry: GEOFENCING"); + } + + + @Test + void testUpdateStateWhenUpdateExistingGeofencingValueArgumentEntryWithValueOfAnotherType() { + state.arguments = new HashMap<>(Map.of("allowedZones", geofencingAllowedZoneArgEntry)); + + assertThatThrownBy(() -> state.update(Map.of("allowedZones", latitudeArgEntry), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for geofencing argument entry: SINGLE_VALUE"); + } + + @Test + void testIsReadyWhenNotAllArgPresent() { + assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).contains(state.getRequiredArguments()); + } + + @Test + void testIsReadyWhenAllArgPresent() { + state.update(Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry, + "allowedZones", geofencingAllowedZoneArgEntry, + "restrictedZones", geofencingRestrictedZoneArgEntry + ), ctx); + assertThat(state.isReady()).isTrue(); + assertThat(state.getReadinessStatus().errorMsg()).isNull(); + } + + @Test + void testIsReadyWhenEmptyEntryPresents() { + state.update(Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry, + "allowedZones", geofencingAllowedZoneArgEntry, + "restrictedZones", new GeofencingArgumentEntry(Collections.emptyMap()) + ), ctx); + assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).contains("restrictedZones"); + } + + @Test + void testPerformCalculation() throws ExecutionException, InterruptedException { + state.arguments = new HashMap<>(Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry, + "allowedZones", geofencingAllowedZoneArgEntry, + "restrictedZones", geofencingRestrictedZoneArgEntry + )); + + Output output = ctx.getOutput(); + var configuration = (GeofencingCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + + when(relationService.saveRelationAsync(any(), any())).thenReturn(Futures.immediateFuture(true)); + when(relationService.deleteRelationAsync(any(), any())).thenReturn(Futures.immediateFuture(true)); + + TelemetryCalculatedFieldResult result = performCalculation(); + + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(output.getType()); + assertThat(result.getScope()).isEqualTo(output.getScope()); + assertThat(result.getResult().get("values")).isEqualTo( + JacksonUtil.newObjectNode() + .put("allowedZonesStatus", "INSIDE") + .put("restrictedZonesStatus", "OUTSIDE") + ); + + SingleValueArgumentEntry newLatitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 50.4760), 146L); + SingleValueArgumentEntry newLongitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", 30.5110), 166L); + + // move the device to new coordinates → leaves allowed, enters restricted + state.update(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, newLatitude, ENTITY_ID_LONGITUDE_ARGUMENT_KEY, newLongitude), ctx); + + TelemetryCalculatedFieldResult result2 = performCalculation(); + + assertThat(result2).isNotNull(); + assertThat(result2.getType()).isEqualTo(output.getType()); + assertThat(result2.getScope()).isEqualTo(output.getScope()); + assertThat(result2.getResult().get("values")).isEqualTo( + JacksonUtil.newObjectNode() + .put("allowedZonesEvent", "LEFT") + .put("allowedZonesStatus", "OUTSIDE") + .put("restrictedZonesEvent", "ENTERED") + .put("restrictedZonesStatus", "INSIDE") + ); + + // Check relations are created and deleted correctly for both iterations. + ArgumentCaptor saveCaptor = ArgumentCaptor.forClass(EntityRelation.class); + verify(relationService, times(2)).saveRelationAsync(eq(ctx.getTenantId()), saveCaptor.capture()); + List saveValues = saveCaptor.getAllValues(); + assertThat(saveValues).hasSize(2); + + EntityRelation relationFromFirstIteration = saveValues.get(0); + assertThat(relationFromFirstIteration.getTo()).isEqualTo(ctx.getEntityId()); + assertThat(relationFromFirstIteration.getFrom()).isEqualTo(ZONE_1_ID); + assertThat(relationFromFirstIteration.getType()).isEqualTo("CurrentZone"); + + EntityRelation relationFromSecondIteration = saveValues.get(1); + assertThat(relationFromSecondIteration.getTo()).isEqualTo(ctx.getEntityId()); + assertThat(relationFromSecondIteration.getFrom()).isEqualTo(ZONE_2_ID); + assertThat(relationFromSecondIteration.getType()).isEqualTo("CurrentZone"); + + ArgumentCaptor deleteCaptor = ArgumentCaptor.forClass(EntityRelation.class); + verify(relationService, times(2)).deleteRelationAsync(eq(ctx.getTenantId()), deleteCaptor.capture()); + List deleteValues = deleteCaptor.getAllValues(); + assertThat(deleteValues).hasSize(2); + + EntityRelation deleteRelationFromFirstIteration = deleteValues.get(0); + assertThat(deleteRelationFromFirstIteration.getFrom()).isEqualTo(ZONE_2_ID); + assertThat(deleteRelationFromFirstIteration.getTo()).isEqualTo(ctx.getEntityId()); + + EntityRelation deleteRelationFromSecondIteration = deleteValues.get(1); + assertThat(deleteRelationFromSecondIteration.getFrom()).isEqualTo(ZONE_1_ID); + assertThat(deleteRelationFromSecondIteration.getTo()).isEqualTo(ctx.getEntityId()); + } + + @Test + void testPerformCalculationWithOnlyTransitionEventsReportingStrategy() throws ExecutionException, InterruptedException { + state.arguments = new HashMap<>(Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry, + "allowedZones", geofencingAllowedZoneArgEntry, + "restrictedZones", geofencingRestrictedZoneArgEntry + )); + + Output output = ctx.getOutput(); + + var calculatedFieldConfig = getCalculatedFieldConfig(GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_ONLY); + + ctx.setCalculatedField(getCalculatedField(calculatedFieldConfig)); + ctx.init(); + + var configuration = (GeofencingCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + + when(relationService.saveRelationAsync(any(), any())).thenReturn(Futures.immediateFuture(true)); + when(relationService.deleteRelationAsync(any(), any())).thenReturn(Futures.immediateFuture(true)); + + TelemetryCalculatedFieldResult result = performCalculation(); + + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(output.getType()); + assertThat(result.getScope()).isEqualTo(output.getScope()); + assertThat(result.getResult().get("values")).isEmpty(); + + SingleValueArgumentEntry newLatitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 50.4760), 146L); + SingleValueArgumentEntry newLongitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", 30.5110), 166L); + + // move the device to new coordinates → leaves allowed, enters restricted + state.update(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, newLatitude, ENTITY_ID_LONGITUDE_ARGUMENT_KEY, newLongitude), ctx); + + TelemetryCalculatedFieldResult result2 = performCalculation(); + + assertThat(result2).isNotNull(); + assertThat(result2.getType()).isEqualTo(output.getType()); + assertThat(result2.getScope()).isEqualTo(output.getScope()); + assertThat(result2.getResult().get("values")).isEqualTo( + JacksonUtil.newObjectNode() + .put("allowedZonesEvent", "LEFT") + .put("restrictedZonesEvent", "ENTERED") + ); + + // Check relations are created and deleted correctly for both iterations. + ArgumentCaptor saveCaptor = ArgumentCaptor.forClass(EntityRelation.class); + verify(relationService, times(2)).saveRelationAsync(eq(ctx.getTenantId()), saveCaptor.capture()); + List saveValues = saveCaptor.getAllValues(); + assertThat(saveValues).hasSize(2); + + EntityRelation relationFromFirstIteration = saveValues.get(0); + assertThat(relationFromFirstIteration.getTo()).isEqualTo(ctx.getEntityId()); + assertThat(relationFromFirstIteration.getFrom()).isEqualTo(ZONE_1_ID); + assertThat(relationFromFirstIteration.getType()).isEqualTo("CurrentZone"); + + EntityRelation relationFromSecondIteration = saveValues.get(1); + assertThat(relationFromSecondIteration.getTo()).isEqualTo(ctx.getEntityId()); + assertThat(relationFromSecondIteration.getFrom()).isEqualTo(ZONE_2_ID); + assertThat(relationFromSecondIteration.getType()).isEqualTo("CurrentZone"); + + ArgumentCaptor deleteCaptor = ArgumentCaptor.forClass(EntityRelation.class); + verify(relationService, times(2)).deleteRelationAsync(eq(ctx.getTenantId()), deleteCaptor.capture()); + List deleteValues = deleteCaptor.getAllValues(); + assertThat(deleteValues).hasSize(2); + + EntityRelation deleteRelationFromFirstIteration = deleteValues.get(0); + assertThat(deleteRelationFromFirstIteration.getFrom()).isEqualTo(ZONE_2_ID); + assertThat(deleteRelationFromFirstIteration.getTo()).isEqualTo(ctx.getEntityId()); + + EntityRelation deleteRelationFromSecondIteration = deleteValues.get(1); + assertThat(deleteRelationFromSecondIteration.getFrom()).isEqualTo(ZONE_1_ID); + assertThat(deleteRelationFromSecondIteration.getTo()).isEqualTo(ctx.getEntityId()); + } + + @Test + void testPerformCalculationWithOnlyPresenceStatusReportingStrategy() throws ExecutionException, InterruptedException { + state.arguments = new HashMap<>(Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, latitudeArgEntry, + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, longitudeArgEntry, + "allowedZones", geofencingAllowedZoneArgEntry, + "restrictedZones", geofencingRestrictedZoneArgEntry + )); + + Output output = ctx.getOutput(); + + var calculatedFieldConfig = getCalculatedFieldConfig(GeofencingReportStrategy.REPORT_PRESENCE_STATUS_ONLY); + + ctx.setCalculatedField(getCalculatedField(calculatedFieldConfig)); + ctx.init(); + + var configuration = (GeofencingCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + + when(relationService.saveRelationAsync(any(), any())).thenReturn(Futures.immediateFuture(true)); + when(relationService.deleteRelationAsync(any(), any())).thenReturn(Futures.immediateFuture(true)); + + TelemetryCalculatedFieldResult result = performCalculation(); + + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(output.getType()); + assertThat(result.getScope()).isEqualTo(output.getScope()); + assertThat(result.getResult().get("values")).isEqualTo( + JacksonUtil.newObjectNode() + .put("allowedZonesStatus", "INSIDE") + .put("restrictedZonesStatus", "OUTSIDE") + ); + + SingleValueArgumentEntry newLatitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 50.4760), 146L); + SingleValueArgumentEntry newLongitude = new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", 30.5110), 166L); + + // move the device to new coordinates → leaves allowed, enters restricted + state.update(Map.of(ENTITY_ID_LATITUDE_ARGUMENT_KEY, newLatitude, ENTITY_ID_LONGITUDE_ARGUMENT_KEY, newLongitude), ctx); + + TelemetryCalculatedFieldResult result2 = performCalculation(); + + assertThat(result2).isNotNull(); + assertThat(result2.getType()).isEqualTo(output.getType()); + assertThat(result2.getScope()).isEqualTo(output.getScope()); + assertThat(result2.getResult().get("values")).isEqualTo( + JacksonUtil.newObjectNode() + .put("allowedZonesStatus", "OUTSIDE") + .put("restrictedZonesStatus", "INSIDE") + ); + + // Check relations are created and deleted correctly for both iterations. + ArgumentCaptor saveCaptor = ArgumentCaptor.forClass(EntityRelation.class); + verify(relationService, times(2)).saveRelationAsync(eq(ctx.getTenantId()), saveCaptor.capture()); + List saveValues = saveCaptor.getAllValues(); + assertThat(saveValues).hasSize(2); + + EntityRelation relationFromFirstIteration = saveValues.get(0); + assertThat(relationFromFirstIteration.getTo()).isEqualTo(ctx.getEntityId()); + assertThat(relationFromFirstIteration.getFrom()).isEqualTo(ZONE_1_ID); + assertThat(relationFromFirstIteration.getType()).isEqualTo("CurrentZone"); + + EntityRelation relationFromSecondIteration = saveValues.get(1); + assertThat(relationFromSecondIteration.getTo()).isEqualTo(ctx.getEntityId()); + assertThat(relationFromSecondIteration.getFrom()).isEqualTo(ZONE_2_ID); + assertThat(relationFromSecondIteration.getType()).isEqualTo("CurrentZone"); + + ArgumentCaptor deleteCaptor = ArgumentCaptor.forClass(EntityRelation.class); + verify(relationService, times(2)).deleteRelationAsync(eq(ctx.getTenantId()), deleteCaptor.capture()); + List deleteValues = deleteCaptor.getAllValues(); + assertThat(deleteValues).hasSize(2); + + EntityRelation deleteRelationFromFirstIteration = deleteValues.get(0); + assertThat(deleteRelationFromFirstIteration.getFrom()).isEqualTo(ZONE_2_ID); + assertThat(deleteRelationFromFirstIteration.getTo()).isEqualTo(ctx.getEntityId()); + + EntityRelation deleteRelationFromSecondIteration = deleteValues.get(1); + assertThat(deleteRelationFromSecondIteration.getFrom()).isEqualTo(ZONE_1_ID); + assertThat(deleteRelationFromSecondIteration.getTo()).isEqualTo(ctx.getEntityId()); + } + + private CalculatedField getCalculatedField() { + return getCalculatedField(getCalculatedFieldConfig(REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS)); + } + + private CalculatedField getCalculatedField(CalculatedFieldConfiguration configuration) { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setTenantId(TENANT_ID); + calculatedField.setEntityId(DEVICE_ID); + calculatedField.setType(CalculatedFieldType.GEOFENCING); + calculatedField.setName("Test Geofencing Calculated Field"); + calculatedField.setConfigurationVersion(1); + calculatedField.setConfiguration(configuration); + calculatedField.setVersion(1L); + return calculatedField; + } + + private CalculatedFieldConfiguration getCalculatedFieldConfig(GeofencingReportStrategy reportStrategy) { + var config = new GeofencingCalculatedFieldConfiguration(); + + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + config.setEntityCoordinates(entityCoordinates); + + ZoneGroupConfiguration allowedZonesGroup = new ZoneGroupConfiguration("zone", reportStrategy, true); + var allowedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.TO, "AllowedZone"))); + allowedZonesGroup.setRefDynamicSourceConfiguration(allowedZoneDynamicSourceConfiguration); + allowedZonesGroup.setRelationType("CurrentZone"); + allowedZonesGroup.setDirection(EntitySearchDirection.TO); + + ZoneGroupConfiguration restrictedZonesGroup = new ZoneGroupConfiguration("zone", reportStrategy, true); + var restrictedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + restrictedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.TO, "RestrictedZone"))); + restrictedZonesGroup.setRefDynamicSourceConfiguration(restrictedZoneDynamicSourceConfiguration); + restrictedZonesGroup.setRelationType("CurrentZone"); + restrictedZonesGroup.setDirection(EntitySearchDirection.TO); + + config.setZoneGroups(Map.of("allowedZones", allowedZonesGroup, "restrictedZones", restrictedZonesGroup)); + + config.setOutput(new TimeSeriesOutput()); + return config; + } + + private TelemetryCalculatedFieldResult performCalculation() throws InterruptedException, ExecutionException { + return (TelemetryCalculatedFieldResult) state.performCalculation(Collections.emptyMap(), ctx).get(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java new file mode 100644 index 0000000000..d7bc39f6e8 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java @@ -0,0 +1,191 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import io.hypersistence.utils.hibernate.type.json.internal.JacksonUtil; +import org.junit.jupiter.api.BeforeEach; +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.geo.PerimeterDefinition; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingZoneState; + +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@ExtendWith(MockitoExtension.class) +public class GeofencingValueArgumentEntryTest { + + private final AssetId ZONE_1_ID = new AssetId(UUID.fromString("c0e3031c-7df1-45e4-9590-cfd621a4d714")); + private final AssetId ZONE_2_ID = new AssetId(UUID.fromString("e7da6200-2096-4038-a343-ade9ea4fa3e4")); + + private final JsonDataEntry allowedZoneDataEntry = new JsonDataEntry("zone", "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"); + private final BaseAttributeKvEntry allowedZoneAttributeKvEntry = new BaseAttributeKvEntry(allowedZoneDataEntry, 363L, 155L); + + private final JsonDataEntry restrictedZoneDataEntry = new JsonDataEntry("zone", "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"); + private final BaseAttributeKvEntry restrictedZoneAttributeKvEntry = new BaseAttributeKvEntry(restrictedZoneDataEntry, 363L, 155L); + + private GeofencingArgumentEntry entry; + + @Mock + private CalculatedFieldCtx ctx; + + @BeforeEach + void setUp() { + entry = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + } + + @Test + void testArgumentEntryType() { + assertThat(entry.getType()).isEqualTo(ArgumentEntryType.GEOFENCING); + } + + @Test + void testUpdateEntryWhenSingleEntryPassed() { + assertThatThrownBy(() -> entry.updateEntry(new SingleValueArgumentEntry(), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for geofencing argument entry: SINGLE_VALUE"); + } + + @Test + void testUpdateEntryWhenRollingEntryPassed() { + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for geofencing argument entry: TS_ROLLING"); + } + + @Test + void testUpdateEntryWithTheSameTs() { + BaseAttributeKvEntry differentValueSameTs = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 363L, 156L); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueSameTs, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + assertThat(entry.updateEntry(updated, ctx)).isFalse(); + } + + @Test + @SuppressWarnings("unchecked") + void testUpdateEntryWhenNewVersionIsNull() { + BaseAttributeKvEntry differentValueNewVersionIsNull = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, null); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsNull, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + + assertThat(entry.updateEntry(updated, ctx)).isTrue(); + assertThat(entry.getValue()).isInstanceOf(Map.class); + + Map value = (Map) entry.getValue(); + assertThat(value).hasSize(2); + assertThat(value.get(ZONE_1_ID).getVersion()).isNull(); + assertThat(value.get(ZONE_1_ID).getTs()).isEqualTo(364L); + assertThat(value.get(ZONE_1_ID).getPerimeterDefinition()) + .isEqualTo(JacksonUtil.fromString(differentValueNewVersionIsNull.getJsonValue().get(), PerimeterDefinition.class)); + + assertThat(value.get(ZONE_2_ID).getVersion()).isEqualTo(155L); + assertThat(value.get(ZONE_2_ID).getTs()).isEqualTo(363L); + assertThat(value.get(ZONE_2_ID).getPerimeterDefinition()) + .isEqualTo(JacksonUtil.fromString(restrictedZoneAttributeKvEntry.getJsonValue().get(), PerimeterDefinition.class)); + } + + @Test + @SuppressWarnings("unchecked") + void testUpdateEntryWhenNewVersionIsGreaterThanCurrent() { + BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 156L); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsSet, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + + assertThat(entry.updateEntry(updated, ctx)).isTrue(); + assertThat(entry.getValue()).isInstanceOf(Map.class); + + Map value = (Map) entry.getValue(); + assertThat(value).hasSize(2); + assertThat(value.get(ZONE_1_ID).getVersion()).isEqualTo(156L); + assertThat(value.get(ZONE_1_ID).getTs()).isEqualTo(364L); + assertThat(value.get(ZONE_1_ID).getPerimeterDefinition()) + .isEqualTo(JacksonUtil.fromString(differentValueNewVersionIsSet.getJsonValue().get(), PerimeterDefinition.class)); + + assertThat(value.get(ZONE_2_ID).getVersion()).isEqualTo(155L); + assertThat(value.get(ZONE_2_ID).getTs()).isEqualTo(363L); + assertThat(value.get(ZONE_2_ID).getPerimeterDefinition()) + .isEqualTo(JacksonUtil.fromString(restrictedZoneAttributeKvEntry.getJsonValue().get(), PerimeterDefinition.class)); + } + + @Test + void testUpdateEntryWhenNewVersionIsLessThanCurrent() { + BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 154L); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsSet, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + + assertThat(entry.updateEntry(updated, ctx)).isFalse(); + } + + @Test + void testUpdateEntryWhenNewTsAndVersionIsGreaterThenCurrentAndValueWasNotChanged() { + BaseAttributeKvEntry newTsAndTheSameValue = new BaseAttributeKvEntry(allowedZoneDataEntry, 364L, 156L); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, newTsAndTheSameValue, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + + assertThat(entry.updateEntry(updated, ctx)).isTrue(); + } + + @Test + void testUpdateEntryWithOldTs() { + BaseAttributeKvEntry oldTsAndTheSameValue = new BaseAttributeKvEntry(allowedZoneDataEntry, 362L, 156L); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, oldTsAndTheSameValue, ZONE_2_ID, restrictedZoneAttributeKvEntry)); + + assertThat(entry.updateEntry(updated, ctx)).isFalse(); + } + + @Test + void testUpdateEntryWithNewZone() { + final AssetId NEW_ZONE_ID = new AssetId(UUID.fromString("a3eacf1a-6af3-4e9f-87c4-502bb25c7dc3")); + BaseAttributeKvEntry newZone = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 156L); + var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry, ZONE_2_ID, restrictedZoneAttributeKvEntry, NEW_ZONE_ID, newZone)); + assertThat(entry.updateEntry(updated, ctx)).isTrue(); + } + + @Test + void testIsEmpty() { + GeofencingArgumentEntry geofencingArgumentEntry = new GeofencingArgumentEntry(); + assertThat(geofencingArgumentEntry.isEmpty()).isTrue(); + } + + @Test + void testIsEmptyWithEmptyMap() { + GeofencingArgumentEntry geofencingArgumentEntry = new GeofencingArgumentEntry(Map.of()); + assertThat(geofencingArgumentEntry.isEmpty()).isTrue(); + } + + @Test + void testInvalidKvEntryDataTypeForZoneResultInEmptyArgument() { + BaseAttributeKvEntry invalidZoneEntry = new BaseAttributeKvEntry(new StringDataEntry("zone", "someString"), 363L, 155L); + assertThatThrownBy(() -> new GeofencingArgumentEntry(Map.of(ZONE_1_ID, invalidZoneEntry))) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid perimeter definition format for Zone with id: " + ZONE_1_ID + ". Failed to parse attribute 'zone'"); + } + + @Test + void testNotParsableToPerimeterJsonKvEntryResultInExceptionTrowed() { + BaseAttributeKvEntry invalidZoneEntry = new BaseAttributeKvEntry(new JsonDataEntry("zone", "\"{}\""), 363L, 155L); + assertThatThrownBy(() -> new GeofencingArgumentEntry(Map.of(ZONE_1_ID, invalidZoneEntry))) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid perimeter definition format for Zone with id: " + ZONE_1_ID + ". Failed to parse attribute 'zone'"); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingZoneStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingZoneStateTest.java new file mode 100644 index 0000000000..40d1ae83b1 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingZoneStateTest.java @@ -0,0 +1,169 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.thingsboard.common.util.geo.Coordinates; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingEvalResult; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingZoneState; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus.INSIDE; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus.OUTSIDE; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingTransitionEvent.ENTERED; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingTransitionEvent.LEFT; + +public class GeofencingZoneStateTest { + + private final AssetId ZONE_ID = new AssetId(UUID.fromString("628730fd-d625-417f-9c6d-ae9fe4addbdb")); + + private GeofencingZoneState state; + + @BeforeEach + void setUp() { + String POLYGON = "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"; + state = new GeofencingZoneState(ZONE_ID, new BaseAttributeKvEntry(new JsonDataEntry("zone", POLYGON), 100L, 1L)); + } + + @Test + void evaluate_initialInside_thenInsideAgain() { + var inside = new Coordinates(50.4730, 30.5050); + // first evaluation: no prior state -> INSIDE + assertThat(state.evaluate(inside)).isEqualTo(new GeofencingEvalResult(null, INSIDE)); + // same position again -> INSIDE (steady state) + assertThat(state.evaluate(inside)).isEqualTo(new GeofencingEvalResult(null, INSIDE)); + } + + @Test + void evaluate_initialOutside_thenOutsideAgain() { + var outside = new Coordinates(50.4760, 30.5110); + // first evaluation: no prior state -> OUTSIDE + assertThat(state.evaluate(outside)).isEqualTo(new GeofencingEvalResult(null, OUTSIDE)); + // same position again -> OUTSIDE (steady state) + assertThat(state.evaluate(outside)).isEqualTo(new GeofencingEvalResult(null, OUTSIDE)); + } + + @Test + void evaluate_inside_thenLeave() { + var inside = new Coordinates(50.4730, 30.5050); + var outside = new Coordinates(50.4760, 30.5110); + // initial eval + assertThat(state.evaluate(inside)).isEqualTo(new GeofencingEvalResult(null, INSIDE)); + // leave -> LEFT + assertThat(state.evaluate(outside)).isEqualTo(new GeofencingEvalResult(LEFT, OUTSIDE)); + // still outside -> OUTSIDE + assertThat(state.evaluate(outside)).isEqualTo(new GeofencingEvalResult(null, OUTSIDE)); + } + + @Test + void evaluate_outside_thenEnter() { + var outside = new Coordinates(50.4760, 30.5110); + var inside = new Coordinates(50.4730, 30.5050); + // start outside + assertThat(state.evaluate(outside)).isEqualTo(new GeofencingEvalResult(null, OUTSIDE)); + // cross boundary -> ENTERED + assertThat(state.evaluate(inside)).isEqualTo(new GeofencingEvalResult(ENTERED, INSIDE)); + // remain inside -> INSIDE + assertThat(state.evaluate(inside)).isEqualTo(new GeofencingEvalResult(null, INSIDE)); + } + + @Test + void update_withNewerVersion_updatesState_andResetsPresence() { + // arrange: establish a prior presence to ensure it’s reset on update + var inside = new Coordinates(50.4730, 30.5050); + assertThat(state.evaluate(inside)).isNotNull(); // sets lastPresence internally + + String NEW_POLYGON = "[[50.470000, 30.502000], [50.470000, 30.503000], [50.471000, 30.503000], [50.471000, 30.502000]]"; + GeofencingZoneState newer = new GeofencingZoneState( + ZONE_ID, + new BaseAttributeKvEntry(new JsonDataEntry("zone", NEW_POLYGON), 200L, 2L) + ); + + // act + boolean changed = state.update(newer); + + // assert + assertThat(changed).isTrue(); + assertThat(state.getTs()).isEqualTo(200L); + assertThat(state.getVersion()).isEqualTo(2L); + assertThat(state.getPerimeterDefinition()).isNotNull(); + assertThat(state.getLastPresence()).isNull(); // must be reset on successful update + } + + @Test + void update_withEqualVersion_doesNothing() { + // arrange: same version (1L) but different ts/polygon should still be ignored + String SOME_POLYGON = "[[50.472500, 30.504500], [50.472500, 30.505500], [50.473500, 30.505500], [50.473500, 30.504500]]"; + GeofencingZoneState sameVersion = new GeofencingZoneState( + ZONE_ID, + new BaseAttributeKvEntry(new JsonDataEntry("zone", SOME_POLYGON), 300L, 1L) + ); + + // act + boolean changed = state.update(sameVersion); + + // assert: nothing changes + assertThat(changed).isFalse(); + assertThat(state.getTs()).isEqualTo(100L); + assertThat(state.getVersion()).isEqualTo(1L); + } + + @Test + void update_withNullNewVersion_alwaysApplies_andCopiesNull() { + // arrange: the implementation updates if newVersion == null + String OTHER_POLYGON = "[[50.471000, 30.506000], [50.471000, 30.507000], [50.472000, 30.507000], [50.472000, 30.506000]]"; + GeofencingZoneState nullVersion = new GeofencingZoneState( + ZONE_ID, + new BaseAttributeKvEntry(new JsonDataEntry("zone", OTHER_POLYGON), 400L, null) + ); + + // act + boolean changed = state.update(nullVersion); + + // assert: applied and version copied as null + assertThat(changed).isTrue(); + assertThat(state.getTs()).isEqualTo(400L); + assertThat(state.getVersion()).isNull(); + assertThat(state.getLastPresence()).isNull(); + } + + @Test + void update_withNewVersionWhenExistingIsNull_alwaysApplies_andCopiesNew() { + // arrange: the implementation updates if newVersion == null + String OTHER_POLYGON = "[[50.471000, 30.506000], [50.471000, 30.507000], [50.472000, 30.507000], [50.472000, 30.506000]]"; + GeofencingZoneState newVersion = new GeofencingZoneState( + ZONE_ID, + new BaseAttributeKvEntry(new JsonDataEntry("zone", OTHER_POLYGON), 400L, 2L) + ); + state.setVersion(null); + + // act + boolean changed = state.update(newVersion); + + // assert: applied and version copied as null + assertThat(changed).isTrue(); + assertThat(state.getTs()).isEqualTo(400L); + assertThat(state.getVersion()).isEqualTo(2); + assertThat(state.getLastPresence()).isNull(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java new file mode 100644 index 0000000000..7db8bc658a --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java @@ -0,0 +1,257 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import org.junit.jupiter.api.BeforeEach; +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.script.api.tbel.TbelCfArg; +import org.thingsboard.script.api.tbel.TbelCfPropagationArg; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +public class PropagationArgumentEntryTest { + + private final AssetId ENTITY_1_ID = new AssetId(UUID.fromString("b0a8637d-6d67-43d5-a483-c0e391afe805")); + private final AssetId ENTITY_2_ID = new AssetId(UUID.fromString("7bd85073-ded5-414f-a2ef-bd56ad3dbf6a")); + private final AssetId ENTITY_3_ID = new AssetId(UUID.fromString("d64f3e51-2ec2-472f-b475-b095ef8bdc70")); + + private PropagationArgumentEntry entry; + + @Mock + private CalculatedFieldCtx ctx; + + @BeforeEach + void setUp() { + lenient().when(ctx.getMaxRelatedEntitiesPerCfArgument()).thenReturn(1000L); + + List propagationEntityIds = new ArrayList<>(); + propagationEntityIds.add(ENTITY_1_ID); + propagationEntityIds.add(ENTITY_2_ID); + entry = new PropagationArgumentEntry(propagationEntityIds); + } + + @Test + void testArgumentEntryType() { + assertThat(entry.getType()).isEqualTo(ArgumentEntryType.PROPAGATION); + } + + @Test + void testIsEmpty() { + PropagationArgumentEntry emptyEntry = new PropagationArgumentEntry(List.of()); + assertThat(emptyEntry.isEmpty()).isTrue(); + } + + @Test + void testGetValueReturnsPropagationIds() { + assertThat(entry.getValue()).isInstanceOf(Set.class); + @SuppressWarnings("unchecked") + Set value = (Set) entry.getValue(); + assertThat(value).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); + } + + @Test + void testUpdateEntryWhenSingleEntryPassed() { + assertThatThrownBy(() -> entry.updateEntry(new SingleValueArgumentEntry(), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for propagation argument entry: SINGLE_VALUE"); + } + + @Test + void testUpdateEntryWhenRollingEntryPassed() { + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for propagation argument entry: TS_ROLLING"); + } + + @Test + void testUpdateEntryReplacesWithNewIds() { + var newIds = new ArrayList(List.of(ENTITY_3_ID, ENTITY_1_ID)); + var updated = new PropagationArgumentEntry(newIds); + + boolean changed = entry.updateEntry(updated, ctx); + + assertThat(changed).isTrue(); + assertThat(entry.getEntityIds()).containsExactlyElementsOf(newIds); + } + + @Test + void testUpdateEntryClearsWhenNewEntryIsEmpty() { + var updatedEmpty = new PropagationArgumentEntry(List.of()); + + boolean changed = entry.updateEntry(updatedEmpty, ctx); + + assertThat(changed).isTrue(); + assertThat(entry.getEntityIds()).isEmpty(); + } + + @Test + void testUpdateEntryWhenAdded() { + var added = new PropagationArgumentEntry(); + added.setAdded(List.of(ENTITY_3_ID)); + + boolean changed = entry.updateEntry(added, ctx); + + assertThat(changed).isTrue(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID, ENTITY_3_ID); + assertThat(entry.getAdded()).isEqualTo(List.of(ENTITY_3_ID)); + } + + @Test + void testUpdateEntryWhenAddedExistingEntity() { + var added = new PropagationArgumentEntry(); + added.setAdded(List.of(ENTITY_2_ID)); + + boolean changed = entry.updateEntry(added, ctx); + + assertThat(changed).isFalse(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); + assertThat(entry.getAdded()).isNull(); + } + + @Test + void testUpdateEntryWhenRemoved() { + var removed = new PropagationArgumentEntry(); + removed.setRemoved(ENTITY_2_ID); + + boolean changed = entry.updateEntry(removed, ctx); + + assertThat(changed).isTrue(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID); + assertThat(entry.getRemoved()).isNull(); + } + + @Test + void testUpdateEntryWhenRemovedNonExistingEntity() { + var removed = new PropagationArgumentEntry(); + removed.setRemoved(ENTITY_3_ID); + + boolean changed = entry.updateEntry(removed, ctx); + + assertThat(changed).isFalse(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); + assertThat(entry.getRemoved()).isNull(); + } + + @Test + void testUpdateEntryWhenPartitionStateRestoreAddsMissingIds() { + var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID, ENTITY_2_ID, ENTITY_3_ID)); + restore.setSyncWithDb(true); + + boolean changed = entry.updateEntry(restore, ctx); + + assertThat(changed).isTrue(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID, ENTITY_3_ID); + assertThat(entry.getAdded()).containsExactly(ENTITY_3_ID); + assertThat(entry.getRemoved()).isNull(); + assertThat(entry.isSyncWithDb()).isFalse(); + } + + @Test + void testUpdateEntryWhenPartitionStateRestoreRemovesStaleIds() { + var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID)); + restore.setSyncWithDb(true); + + boolean changed = entry.updateEntry(restore, ctx); + + assertThat(changed).isTrue(); // expected to be changed, so we re-check readiness for the state + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID); + assertThat(entry.getAdded()).isNull(); + assertThat(entry.getRemoved()).isNull(); + assertThat(entry.isSyncWithDb()).isFalse(); + } + + @Test + void testUpdateEntryWhenPartitionStateRestoreAddsAndRemoves() { + var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID, ENTITY_3_ID)); + restore.setSyncWithDb(true); + + boolean changed = entry.updateEntry(restore, ctx); + + assertThat(changed).isTrue(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_3_ID); + assertThat(entry.getAdded()).containsExactly(ENTITY_3_ID); + assertThat(entry.getRemoved()).isNull(); + assertThat(entry.isSyncWithDb()).isFalse(); + } + + + @Test + void testUpdateEntryWhenPartitionStateRestoreNoChanges() { + var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID, ENTITY_2_ID)); + restore.setSyncWithDb(true); + + boolean changed = entry.updateEntry(restore, ctx); + + assertThat(changed).isFalse(); + assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); + assertThat(entry.getAdded()).isNull(); + assertThat(entry.getRemoved()).isNull(); + assertThat(entry.isSyncWithDb()).isFalse(); + } + + @Test + void testUpdateEntryWhenPartitionStateRestoreEmptySet() { + var restore = new PropagationArgumentEntry(List.of()); + restore.setSyncWithDb(true); + + boolean changed = entry.updateEntry(restore, ctx); + + assertThat(changed).isTrue(); // expected to be changed, so we re-check readiness for the state + assertThat(entry.getEntityIds()).isEmpty(); + assertThat(entry.getAdded()).isNull(); + assertThat(entry.getRemoved()).isNull(); + assertThat(entry.isSyncWithDb()).isFalse(); + } + + @Test + @SuppressWarnings("unchecked") + void testToTbelCfArgWithValues() { + TbelCfArg arg = entry.toTbelCfArg(); + assertThat(arg).isInstanceOf(TbelCfPropagationArg.class); + + TbelCfPropagationArg tbelCfPropagationArg = (TbelCfPropagationArg) arg; + assertThat(tbelCfPropagationArg.getValue()).isInstanceOf(Set.class); + assertThat((Set) tbelCfPropagationArg.getValue()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); + } + + + @Test + @SuppressWarnings("unchecked") + void testToTbelCfArgWithEmptyValues() { + var empty = new PropagationArgumentEntry(List.of()); + TbelCfArg emptyArg = empty.toTbelCfArg(); + assertThat(emptyArg).isInstanceOf(TbelCfPropagationArg.class); + + TbelCfPropagationArg tbelCfPropagationArg = (TbelCfPropagationArg) emptyArg; + assertThat(tbelCfPropagationArg.getValue()).isInstanceOf(Set.class); + assertThat((Set) tbelCfPropagationArg.getValue()).isEmpty(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java new file mode 100644 index 0000000000..66111caf9c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java @@ -0,0 +1,498 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; +import org.thingsboard.script.api.tbel.TbelInvokeService; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; +import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +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.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.stats.DefaultStatsFactory; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; +import org.thingsboard.server.service.cf.PropagationCalculatedFieldResult; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; + +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.UUID; +import java.util.concurrent.ExecutionException; +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.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; + +@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class}) +public class PropagationCalculatedFieldStateTest { + + private static final String TEMPERATURE_ARGUMENT_NAME = "t"; + private static final String HUMIDITY_ARGUMENT_NAME = "h"; + private static final String TEST_RESULT_EXPRESSION_KEY = "testResult"; + private static final double TEMPERATURE_VALUE = 12.5; + private static final double HUMIDITY_VALUE = 85; + + private static final PropagationArgumentEntry EMPTY_PROPAGATION_ARGUMENT = new PropagationArgumentEntry(Collections.emptyList()); + + private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("6c3513cb-85e7-4510-8746-1ba01859a8ce")); + private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("be960a50-c029-4698-b2ec-c56a543c561c")); + private final AssetId ASSET_ID_1 = new AssetId(UUID.fromString("d26f0e5b-7d7d-4a61-9f5e-08ab97b30734")); + private final AssetId ASSET_ID_2 = new AssetId(UUID.fromString("1933a317-4df5-4d36-9800-68aded74579b")); + + private final SingleValueArgumentEntry EMPTY_SINGLE_VALUE_ARGUMENT = new SingleValueArgumentEntry(); + + private final SingleValueArgumentEntry temperatureArgumentEntry = + new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", TEMPERATURE_VALUE), 99L); + + private final SingleValueArgumentEntry humidityArgumentEntry = + new SingleValueArgumentEntry(System.currentTimeMillis(), new DoubleDataEntry("humidity", HUMIDITY_VALUE), 99L); + + private final PropagationArgumentEntry propagationArgEntry = + new PropagationArgumentEntry(new ArrayList<>(List.of(ASSET_ID_2, ASSET_ID_1))); + + private PropagationCalculatedFieldState state; + private CalculatedFieldCtx ctx; + + @Autowired + private TbelInvokeService tbelInvokeService; + + @MockitoBean + private TenantProfile tenantProfile; + + @MockitoBean + private TbTenantProfileCache tenantProfileCache; + + @MockitoBean + private ActorSystemContext actorSystemContext; + + @MockitoBean + private CalculatedFieldProcessingService cfProcessingService; + + @BeforeEach + void setUp() { + when(actorSystemContext.getTbelInvokeService()).thenReturn(tbelInvokeService); + when(actorSystemContext.getCalculatedFieldProcessingService()).thenReturn(cfProcessingService); + when(actorSystemContext.getTenantProfileCache()).thenReturn(tenantProfileCache); + when(tenantProfileCache.get(any(TenantId.class))).thenReturn(tenantProfile); + when(tenantProfile.getProfileConfiguration()).thenReturn(Optional.of(new DefaultTenantProfileConfiguration())); + } + + void initCtxAndState(boolean applyExpressionToResolvedArguments) { + ctx = spy(new CalculatedFieldCtx(getCalculatedField(applyExpressionToResolvedArguments), actorSystemContext)); + ctx.init(); + + state = new PropagationCalculatedFieldState(ctx.getEntityId()); + state.setCtx(ctx, null); + state.init(false); + } + + @Test + void testType() { + initCtxAndState(false); + assertThat(state.getType()).isEqualTo(CalculatedFieldType.PROPAGATION); + } + + @Test + void testInitAddsRequiredArgument() { + initCtxAndState(false); + assertThat(state.getRequiredArguments()).containsExactlyInAnyOrder(TEMPERATURE_ARGUMENT_NAME, HUMIDITY_ARGUMENT_NAME, PROPAGATION_CONFIG_ARGUMENT); + } + + @Test + void testIsReadyReturnFalseWhenNoArgumentsSet() { + initCtxAndState(false); + assertThat(state.isReady()).isFalse(); + } + + private static Stream provideInvalidPropagationArgs() { + return Stream.of( + null, + EMPTY_PROPAGATION_ARGUMENT + ); + } + + @ParameterizedTest + @MethodSource("provideInvalidPropagationArgs") + void testIsReadyWhenPropagationArgIsNullOrEmpty(ArgumentEntry propagationEntry) { + initCtxAndState(false); + + Map args = new HashMap<>(); + args.put(TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry); // Valid user arg + args.put(HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry); // Valid user arg + + if (propagationEntry != null) { + args.put(PROPAGATION_CONFIG_ARGUMENT, propagationEntry); + } + state.update(args, ctx); + assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()) + .isEqualTo("No entities found via 'Propagation path to related entities'. Verify the configured relation type and direction."); + } + + @Test + void testIsReadyWithoutExpressionWhenAllArgumentsAreNotEmpty() { + initCtxAndState(false); + Map updatedArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry + ); + state.update(updatedArgs, ctx); + assertThat(state.isReady()).isTrue(); + assertThat(state.getReadinessStatus().errorMsg()).isNull(); + } + + + @Test + void testIsReadyWithoutExpressionWhenAtLeastOneArgumentIsNotEmpty() { + initCtxAndState(false); + Map updatedArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, EMPTY_SINGLE_VALUE_ARGUMENT, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry); + state.update(updatedArgs, ctx); + assertThat(state.isReady()).isTrue(); + assertThat(state.getReadinessStatus().errorMsg()).isNull(); + } + + @Test + void testIsNotReadyWithExpressionWhenAtLeastOneArgumentIsEmpty() { + initCtxAndState(true); + Map updatedArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, EMPTY_SINGLE_VALUE_ARGUMENT, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry); + state.update(updatedArgs, ctx); + assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).isEqualTo("Required arguments are missing: h"); + } + + @Test + void testPerformCalculationWithEmptyPropagationArg() throws Exception { + initCtxAndState(false); + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, EMPTY_PROPAGATION_ARGUMENT); + state.update(initArgs, ctx); + assertThat(state.isReady()).isFalse(); + + // test empty propagation argument calculation + PropagationCalculatedFieldResult result = performCalculation(); + + assertThat(result).isNotNull(); + assertThat(result.isEmpty()).isTrue(); + assertThat(result.getEntityIds()).isNullOrEmpty(); + } + + @Test + void testPerformCalculationWithArgumentsOnlyMode() throws Exception { + initCtxAndState(false); + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, EMPTY_SINGLE_VALUE_ARGUMENT, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry); + state.update(initArgs, ctx); + assertThat(state.isReady()).isTrue(); + + PropagationCalculatedFieldResult propagationResult = performCalculation(); + + assertThat(propagationResult).isNotNull(); + assertThat(propagationResult.isEmpty()).isFalse(); + assertThat(propagationResult.getEntityIds()).containsExactly(ASSET_ID_2, ASSET_ID_1); + + TelemetryCalculatedFieldResult result = propagationResult.getResult(); + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(OutputType.ATTRIBUTES); + assertThat(result.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + + ObjectNode expectedNode = JacksonUtil.newObjectNode(); + JacksonUtil.addKvEntry(expectedNode, temperatureArgumentEntry.getKvEntryValue(), TEMPERATURE_ARGUMENT_NAME); + + assertThat(result.getResult()).isEqualTo(expectedNode); + } + + @Test + void testPerformCalculationWithExpressionResultMode() throws Exception { + initCtxAndState(true); + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry + ); + state.update(initArgs, ctx); + assertThat(state.isReady()).isTrue(); + PropagationCalculatedFieldResult propagationResult = performCalculation(); + + assertThat(propagationResult).isNotNull(); + assertThat(propagationResult.isEmpty()).isFalse(); + assertThat(propagationResult.getEntityIds()).containsExactly(ASSET_ID_2, ASSET_ID_1); + + TelemetryCalculatedFieldResult result = propagationResult.getResult(); + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(OutputType.ATTRIBUTES); + assertThat(result.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + + ObjectNode expectedNode = JacksonUtil.newObjectNode(); + expectedNode.put(TEST_RESULT_EXPRESSION_KEY, (TEMPERATURE_VALUE + HUMIDITY_VALUE) / 2); + + assertThat(result.getResult()).isEqualTo(expectedNode); + } + + @Test + void testPropagationWithUpdatedPropagationArgument() throws ExecutionException, InterruptedException { + initCtxAndState(false); + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, EMPTY_SINGLE_VALUE_ARGUMENT, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry + ); + state.update(initArgs, ctx); + assertThat(state.isReady()).isTrue(); + + AssetId newEntityId = new AssetId(UUID.fromString("83e2c962-eeae-4708-984e-e6a24760f9c3")); + PropagationArgumentEntry propagationArgumentEntry = new PropagationArgumentEntry(); + propagationArgumentEntry.setAdded(List.of(newEntityId)); + Map updated = state.update(Map.of(PROPAGATION_CONFIG_ARGUMENT, propagationArgumentEntry), ctx); + assertThat(updated).isNotNull().containsEntry(PROPAGATION_CONFIG_ARGUMENT, propagationArgumentEntry); + + PropagationCalculatedFieldResult propagationCalculatedFieldResult = performCalculation(updated); + assertThat(propagationCalculatedFieldResult).isNotNull(); + assertThat(propagationCalculatedFieldResult.getEntityIds()).isNotNull().containsExactly(newEntityId); + assertThat(propagationCalculatedFieldResult.getResult()).isNotNull(); + assertThat(propagationCalculatedFieldResult.getResult().getResult()).isNotNull(); + assertThat(propagationCalculatedFieldResult.getResult().getResult()).isEqualTo(JacksonUtil.newObjectNode().put(TEMPERATURE_ARGUMENT_NAME, TEMPERATURE_VALUE)); + } + + @Test + void testPropapagationStateInitWithRestoredSetToFalse() { + initCtxAndState(false); + verify(cfProcessingService, never()).fetchPropagationArgumentFromDb(any(), any()); + verify(ctx, never()).scheduleReevaluation(anyLong(), any()); + } + + @Test + void testPropagationStateInitRestoredTrueNoDbChanges_noReevaluation() { + initCtxAndState(false); + + // existing state already matches DB snapshot + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry + ); + state.update(initArgs, ctx); + + // DB returns the same set + when(cfProcessingService.fetchPropagationArgumentFromDb(any(), any())).thenReturn(Optional.of(propagationArgEntry)); + + state.init(true); + + verify(cfProcessingService).fetchPropagationArgumentFromDb(ctx, state.getEntityId()); + verify(ctx, never()).scheduleReevaluation(anyLong(), any()); + } + + @Test + void testPropagationStateInitRestoredTrueOnlyRemovals_noReevaluation() { + initCtxAndState(false); + + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry + ); + state.update(initArgs, ctx); + + // DB snapshot contains only ASSET_ID_1 (ASSET_ID_2 should be removed) + var fromDb = new PropagationArgumentEntry(List.of(ASSET_ID_1)); + when(cfProcessingService.fetchPropagationArgumentFromDb(any(), any())).thenReturn(Optional.of(fromDb)); + + state.init(true); + + verify(cfProcessingService).fetchPropagationArgumentFromDb(ctx, state.getEntityId()); + verify(ctx, never()).scheduleReevaluation(anyLong(), any()); + } + + @Test + void testPropagationStateIsNotReadyInitRestoredTrueAddedEntities_scheduleReevaluation() { + initCtxAndState(false); + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, new PropagationArgumentEntry(Collections.emptyList()) + ); + state.update(initArgs, ctx); + assertThat(state.isReady()).isFalse(); + + when(cfProcessingService.fetchPropagationArgumentFromDb(any(), any())).thenReturn(Optional.of(propagationArgEntry)); + + state.init(true); + + verify(cfProcessingService).fetchPropagationArgumentFromDb(ctx, state.getEntityId()); + verify(ctx).scheduleReevaluation(0L, state.getActorCtx()); + } + + @Test + void testPropagationStateInitRestoredTrueAddedEntities_scheduleReevaluation() { + initCtxAndState(false); + + // existing missing ASSET_ID_2 + var existing = new PropagationArgumentEntry(List.of(ASSET_ID_1)); + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, existing + ); + state.update(initArgs, ctx); + assertThat(state.isReady()).isTrue(); + + // DB snapshot contains both (ASSET_ID_2 should be "added") + var fromDb = new PropagationArgumentEntry(List.of(ASSET_ID_1, ASSET_ID_2)); + when(cfProcessingService.fetchPropagationArgumentFromDb(any(), any())).thenReturn(Optional.of(fromDb)); + + state.init(true); + + verify(cfProcessingService).fetchPropagationArgumentFromDb(ctx, state.getEntityId()); + verify(ctx).scheduleReevaluation(0L, state.getActorCtx()); + } + + @Test + void testPropagationStateInitRestoredTrueDbEmpty_clearsState_noReevaluation() { + initCtxAndState(false); + + // existing has something + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry + ); + state.update(initArgs, ctx); + + // DB snapshot empty -> clear + var fromDb = new PropagationArgumentEntry(Collections.emptyList()); + when(cfProcessingService.fetchPropagationArgumentFromDb(any(), any())).thenReturn(Optional.of(fromDb)); + + state.init(true); + + verify(cfProcessingService).fetchPropagationArgumentFromDb(ctx, state.getEntityId()); + verify(ctx, never()).scheduleReevaluation(anyLong(), any()); + } + + @Test + void testPropagationStateInitRestoredTrueBothEmpty_noReevaluation() { + initCtxAndState(false); + + // Existing propagation argument is empty + Map initArgs = Map.of( + TEMPERATURE_ARGUMENT_NAME, temperatureArgumentEntry, + HUMIDITY_ARGUMENT_NAME, humidityArgumentEntry, + PROPAGATION_CONFIG_ARGUMENT, new PropagationArgumentEntry(Collections.emptyList()) + ); + state.update(initArgs, ctx); + assertThat(state.isReady()).isFalse(); + + // DB snapshot is also empty + var fromDb = new PropagationArgumentEntry(Collections.emptyList()); + when(cfProcessingService.fetchPropagationArgumentFromDb(any(), any())).thenReturn(Optional.of(fromDb)); + + state.init(true); + + verify(cfProcessingService).fetchPropagationArgumentFromDb(ctx, state.getEntityId()); + verify(ctx, never()).scheduleReevaluation(anyLong(), any()); + } + + private CalculatedField getCalculatedField(boolean applyExpressionToResolvedArguments) { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setTenantId(TENANT_ID); + calculatedField.setEntityId(DEVICE_ID); + calculatedField.setType(CalculatedFieldType.PROPAGATION); + calculatedField.setName("Test Propagation CF"); + calculatedField.setConfigurationVersion(1); + calculatedField.setConfiguration(getCalculatedFieldConfig(applyExpressionToResolvedArguments)); + calculatedField.setVersion(1L); + return calculatedField; + } + + private CalculatedFieldConfiguration getCalculatedFieldConfig(boolean applyExpressionToResolvedArguments) { + var config = new PropagationCalculatedFieldConfiguration(); + + config.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + config.setApplyExpressionToResolvedArguments(applyExpressionToResolvedArguments); + + Argument temperatureArg = new Argument(); + ReferencedEntityKey tempKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null); + temperatureArg.setRefEntityKey(tempKey); + + Argument humidityArg = new Argument(); + ReferencedEntityKey humidityKey = new ReferencedEntityKey("humidity", ArgumentType.TS_LATEST, null); + humidityArg.setRefEntityKey(humidityKey); + + config.setArguments(Map.of(TEMPERATURE_ARGUMENT_NAME, temperatureArg, HUMIDITY_ARGUMENT_NAME, humidityArg)); + config.setExpression("return { " + TEST_RESULT_EXPRESSION_KEY + ": (" + TEMPERATURE_ARGUMENT_NAME + " + " + HUMIDITY_ARGUMENT_NAME + ") / 2};"); + + AttributesOutput output = new AttributesOutput(); + output.setScope(AttributeScope.SERVER_SCOPE); + config.setOutput(output); + + return config; + } + + private PropagationCalculatedFieldResult performCalculation() throws ExecutionException, InterruptedException { + return performCalculation(Collections.emptyMap()); + } + + private PropagationCalculatedFieldResult performCalculation(Map updatedArgs) throws ExecutionException, InterruptedException { + return (PropagationCalculatedFieldResult) state.performCalculation(updatedArgs, ctx).get(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesAggregationCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesAggregationCalculatedFieldStateTest.java new file mode 100644 index 0000000000..cbde708ff8 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesAggregationCalculatedFieldStateTest.java @@ -0,0 +1,242 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import com.fasterxml.jackson.databind.JsonNode; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; +import org.thingsboard.script.api.tbel.TbelInvokeService; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunctionInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +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.BooleanDataEntry; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.stats.DefaultStatsFactory; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesAggregationCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesArgumentEntry; + +import java.util.HashMap; +import java.util.Map; +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.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class}) +public class RelatedEntitiesAggregationCalculatedFieldStateTest { + + private final long ts = System.currentTimeMillis(); + + private final TenantId tenantId = TenantId.fromUUID(UUID.fromString("cceba360-71e9-44ab-8596-c600d60ee0d0")); + private final AssetProfileId assetProfileId = new AssetProfileId(UUID.fromString("ccced83b-e5f6-4978-bba0-c2df46de9a35")); + private final AssetId assetId = new AssetId(UUID.fromString("982dfdee-b2bc-4f04-a49d-7ffd70940c69")); + private final DeviceId device1 = new DeviceId(UUID.fromString("47f1fef5-a3b7-46e7-9732-b7669d3ef885")); + private final DeviceId device2 = new DeviceId(UUID.fromString("3b097e5e-9eef-46f4-8997-937075e6f342")); + + private RelatedEntitiesAggregationCalculatedFieldState state; + private CalculatedFieldCtx ctx; + + @Autowired + private TbelInvokeService tbelInvokeService; + + @MockitoBean + private TenantProfile tenantProfile; + + @MockitoBean + private TbTenantProfileCache tenantProfileCache; + + @MockitoBean + private ActorSystemContext actorSystemContext; + + @BeforeEach + void setUp() { + when(actorSystemContext.getTbelInvokeService()).thenReturn(tbelInvokeService); + when(actorSystemContext.getTenantProfileCache()).thenReturn(tenantProfileCache); + when(tenantProfileCache.get(any(TenantId.class))).thenReturn(tenantProfile); + when(tenantProfile.getProfileConfiguration()).thenReturn(Optional.of(new DefaultTenantProfileConfiguration())); + + initCtxAndState(); + } + + void initCtxAndState() { + ctx = new CalculatedFieldCtx(getCalculatedField(), actorSystemContext); + ctx.init(); + + state = new RelatedEntitiesAggregationCalculatedFieldState(assetId); + state.setCtx(ctx, null); + state.init(false); + } + + @Test + void testType() { + assertThat(state.getType()).isEqualTo(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION); + } + + @Test + void testInitAddsRequiredArgument() { + assertThat(state.getRequiredArguments()).contains("oc"); + } + + @Test + void testIsReadyWhenNoRelatedEntities() { + assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()) + .isEqualTo("No entities found via 'Aggregation path to related entities'. Verify the configured relation type and direction."); + } + + @Test + void testUpdateArguments() { + assertThat(state.getLastArgsRefreshTs()).isEqualTo(-1); + + state.update(arguments(), ctx); + + assertThat(state.getLastArgsRefreshTs()).isGreaterThan(-1); + } + + @Test + void testPerformCalculationWhenArgsUpdatedButIntervalDidNotPass() throws Exception { + // deduplication interval 60 sec + state.setLastMetricsEvalTs(ts - TimeUnit.SECONDS.toMillis(10)); + state.setLastArgsRefreshTs(ts - TimeUnit.SECONDS.toMillis(5)); + + assertThat(state.performCalculation(arguments(), ctx).get()).isEqualTo(TelemetryCalculatedFieldResult.EMPTY); + } + + @Test + void testPerformCalculationWhenIntervalPassedButNoArgsUpdated() throws Exception { + state.setLastMetricsEvalTs(ts - TimeUnit.SECONDS.toMillis(90)); + state.setLastArgsRefreshTs(ts - TimeUnit.SECONDS.toMillis(90)); + + assertThat(state.performCalculation(arguments(), ctx).get()).isEqualTo(TelemetryCalculatedFieldResult.EMPTY); + } + + @Test + void testPerformCalculationWhenIntervalPassedAndArgsUpdated() throws Exception { + state.setLastMetricsEvalTs(ts - TimeUnit.SECONDS.toMillis(70)); + state.setLastArgsRefreshTs(ts - TimeUnit.SECONDS.toMillis(10)); + + Map arguments = arguments(); + state.update(arguments, ctx); + + TelemetryCalculatedFieldResult calculatedFieldResult = (TelemetryCalculatedFieldResult) state.performCalculation(arguments, ctx).get(); + assertThat(calculatedFieldResult).isNotNull(); + assertThat(calculatedFieldResult.isEmpty()).isFalse(); + assertThat(calculatedFieldResult.getType()).isEqualTo(OutputType.TIME_SERIES); + + JsonNode result = calculatedFieldResult.getResult(); + assertThat(result).isNotNull(); + safeAssert(result.get("ts"), String.valueOf(state.getLatestTimestamp())); + JsonNode values = result.get("values"); + safeAssert(values.get("freeSpaces"), "1"); + safeAssert(values.get("occupiedSpaces"), "1"); + safeAssert(values.get("totalSpaces"), "2"); + } + + private void safeAssert(JsonNode node, String expected) { + assertThat(node).isNotNull(); + assertThat(node.asText()).isEqualTo(expected); + } + + private Map arguments() { + Map arguments = new HashMap<>(); + Map entityArguments = new HashMap<>(); + entityArguments.put(device1, new SingleValueArgumentEntry(ts - 100, new BooleanDataEntry("occupied", true), 23L)); + entityArguments.put(device2, new SingleValueArgumentEntry(ts - 80, new BooleanDataEntry("occupied", false), 26L)); + RelatedEntitiesArgumentEntry entry = new RelatedEntitiesArgumentEntry(entityArguments, false); + arguments.put("oc", entry); + return arguments; + } + + private CalculatedField getCalculatedField() { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setTenantId(tenantId); + calculatedField.setEntityId(assetProfileId); + calculatedField.setType(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION); + calculatedField.setName("Test CF"); + calculatedField.setConfigurationVersion(1); + + var config = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + config.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE)); + + Argument oc = new Argument(); + ReferencedEntityKey refKey = new ReferencedEntityKey("occupied", ArgumentType.TS_LATEST, null); + oc.setRefEntityKey(refKey); + config.setArguments(Map.of("oc", oc)); + + Map aggMetrics = new HashMap<>(); + + AggMetric freeSpaces = new AggMetric(); + freeSpaces.setFunction(AggFunction.COUNT); + freeSpaces.setFilter("return oc == false;"); + freeSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("freeSpaces", freeSpaces); + + AggMetric occupiedSpaces = new AggMetric(); + occupiedSpaces.setFunction(AggFunction.COUNT); + occupiedSpaces.setFilter("return oc == true;"); + occupiedSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("occupiedSpaces", occupiedSpaces); + + AggMetric totalSpaces = new AggMetric(); + totalSpaces.setFunction(AggFunction.COUNT); + totalSpaces.setInput(new AggFunctionInput("return 1;")); + aggMetrics.put("totalSpaces", totalSpaces); + config.setMetrics(aggMetrics); + + config.setDeduplicationIntervalInSec(60); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + config.setOutput(output); + + config.setUseLatestTs(true); + + calculatedField.setConfiguration(config); + calculatedField.setVersion(1L); + return calculatedField; + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java new file mode 100644 index 0000000000..725860dd80 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java @@ -0,0 +1,110 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.ctx.state; + +import org.junit.jupiter.api.BeforeEach; +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.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.service.cf.ctx.state.aggregation.RelatedEntitiesArgumentEntry; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +public class RelatedEntitiesArgumentEntryTest { + + private RelatedEntitiesArgumentEntry entry; + + @Mock + private CalculatedFieldCtx ctx; + + private final DeviceId device1 = new DeviceId(UUID.fromString("1984e5f4-9ff0-4187-84ae-e4438bba4c8a")); + private final DeviceId device2 = new DeviceId(UUID.fromString("937fc062-1a9d-438f-aa22-55a93fc908b7")); + + private final long ts = System.currentTimeMillis(); + + @BeforeEach + void setUp() { + lenient().when(ctx.getMaxRelatedEntitiesPerCfArgument()).thenReturn(1000L); + + Map aggInputs = new HashMap<>(); + aggInputs.put(device1, new SingleValueArgumentEntry(device1, new BasicTsKvEntry(ts - 100, new LongDataEntry("key", 12L), 1L))); + aggInputs.put(device2, new SingleValueArgumentEntry(device2, new BasicTsKvEntry(ts - 150, new LongDataEntry("key", 16L), 6L))); + + entry = new RelatedEntitiesArgumentEntry(aggInputs, false); + } + + @Test + void testUpdateEntryWhenNotAggEntryPassed() { + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported argument entry type for aggregation argument entry: " + ArgumentEntryType.TS_ROLLING); + } + + @Test + void testUpdateEntryWhenAggArgumentEntryPasser() { + DeviceId device3 = new DeviceId(UUID.randomUUID()); + DeviceId device4 = new DeviceId(UUID.randomUUID()); + + RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry = new RelatedEntitiesArgumentEntry(Map.of( + device3, new SingleValueArgumentEntry(device3, new BasicTsKvEntry(ts - 50, new LongDataEntry("key", 16L), 13L)), + device4, new SingleValueArgumentEntry(device4, new BasicTsKvEntry(ts - 60, new LongDataEntry("key", 23L), 7L)) + ), false); + + assertThat(entry.updateEntry(relatedEntitiesArgumentEntry, ctx)).isTrue(); + + Map aggInputs = entry.getEntityInputs(); + assertThat(aggInputs.size()).isEqualTo(4); + assertThat(aggInputs.get(device3)).isEqualTo(relatedEntitiesArgumentEntry.getEntityInputs().get(device3)); + assertThat(aggInputs.get(device4)).isEqualTo(relatedEntitiesArgumentEntry.getEntityInputs().get(device4)); + } + + @Test + void testUpdateEntryWhenSingleValueArgumentEntryPassedAndNoEntriesById() { + DeviceId device3 = new DeviceId(UUID.randomUUID()); + + SingleValueArgumentEntry singleEntityArgumentEntry = new SingleValueArgumentEntry(device3, new BasicTsKvEntry(ts - 50, new LongDataEntry("key", 18L), 10L)); + + assertThat(entry.updateEntry(singleEntityArgumentEntry, ctx)).isTrue(); + + Map aggInputs = entry.getEntityInputs(); + assertThat(aggInputs.size()).isEqualTo(3); + assertThat(aggInputs.get(device3)).isEqualTo(singleEntityArgumentEntry); + } + + @Test + void testUpdateEntryWhenSingleValueArgumentEntryPassedAndEntryByIdExist() { + SingleValueArgumentEntry singleEntityArgumentEntry = new SingleValueArgumentEntry(device2, new BasicTsKvEntry(ts - 50, new LongDataEntry("key", 18L), 10L)); + + assertThat(entry.updateEntry(singleEntityArgumentEntry, ctx)).isTrue(); + + Map aggInputs = entry.getEntityInputs(); + assertThat(aggInputs.size()).isEqualTo(2); + assertThat(aggInputs.get(device2)).isEqualTo(singleEntityArgumentEntry); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java index 1c8c1e160d..e1b881fec1 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java @@ -18,20 +18,23 @@ package org.thingsboard.server.service.cf.ctx.state; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; import org.thingsboard.script.api.tbel.TbelInvokeService; +import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.AssetId; @@ -39,12 +42,15 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.stats.DefaultStatsFactory; -import org.thingsboard.server.dao.usagerecord.ApiLimitService; -import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -72,14 +78,24 @@ public class ScriptCalculatedFieldStateTest { private TbelInvokeService tbelInvokeService; @MockitoBean - private ApiLimitService apiLimitService; + private TenantProfile tenantProfile; + + @MockitoBean + private TbTenantProfileCache tenantProfileCache; @BeforeEach void setUp() { - when(apiLimitService.getLimit(any(), any())).thenReturn(1000L); - ctx = new CalculatedFieldCtx(getCalculatedField(), tbelInvokeService, apiLimitService); + ActorSystemContext systemContext = Mockito.mock(ActorSystemContext.class); + when(systemContext.getTbelInvokeService()).thenReturn(tbelInvokeService); + when(systemContext.getTenantProfileCache()).thenReturn(tenantProfileCache); + when(tenantProfileCache.get(any(TenantId.class))).thenReturn(tenantProfile); + when(tenantProfile.getProfileConfiguration()).thenReturn(Optional.of(new DefaultTenantProfileConfiguration())); + + ctx = new CalculatedFieldCtx(getCalculatedField(), systemContext); ctx.init(); - state = new ScriptCalculatedFieldState(ctx.getArgNames()); + state = new ScriptCalculatedFieldState(ctx.getEntityId()); + state.setCtx(ctx, null); + state.init(false); } @Test @@ -92,7 +108,7 @@ public class ScriptCalculatedFieldStateTest { state.arguments = new HashMap<>(Map.of("assetHumidity", assetHumidityArgEntry)); Map newArgs = Map.of("deviceTemperature", deviceTemperatureArgEntry); - boolean stateUpdated = state.updateState(ctx, newArgs); + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); assertThat(stateUpdated).isTrue(); assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( @@ -109,7 +125,7 @@ public class ScriptCalculatedFieldStateTest { SingleValueArgumentEntry newArgEntry = new SingleValueArgumentEntry(ts, new LongDataEntry("assetHumidity", 41L), 349L); Map newArgs = Map.of("assetHumidity", newArgEntry); - boolean stateUpdated = state.updateState(ctx, newArgs); + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); assertThat(stateUpdated).isTrue(); assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( @@ -124,7 +140,7 @@ public class ScriptCalculatedFieldStateTest { void testPerformCalculation() throws ExecutionException, InterruptedException { state.arguments = new HashMap<>(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry)); - CalculatedFieldResult result = state.performCalculation(ctx).get(); + TelemetryCalculatedFieldResult result = performCalculation(); assertThat(result).isNotNull(); Output output = getCalculatedFieldConfig().getOutput(); @@ -140,7 +156,7 @@ public class ScriptCalculatedFieldStateTest { "assetHumidity", new SingleValueArgumentEntry(System.currentTimeMillis() - 10, new LongDataEntry("a", 45L), 10L) )); - CalculatedFieldResult result = state.performCalculation(ctx).get(); + TelemetryCalculatedFieldResult result = performCalculation(); assertThat(result).isNotNull(); Output output = getCalculatedFieldConfig().getOutput(); @@ -152,20 +168,21 @@ public class ScriptCalculatedFieldStateTest { @Test void testIsReadyWhenNotAllArgPresent() { assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).contains(state.getRequiredArguments()); } @Test void testIsReadyWhenAllArgPresent() { - state.arguments = new HashMap<>(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry)); - + state.update(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry), ctx); assertThat(state.isReady()).isTrue(); + assertThat(state.getReadinessStatus().errorMsg()).isNull(); } @Test void testIsReadyWhenEmptyEntryPresents() { - state.arguments = new HashMap<>(Map.of("deviceTemperature", new TsRollingArgumentEntry(5, 30000L), "assetHumidity", assetHumidityArgEntry)); - + state.update(Map.of("deviceTemperature", new TsRollingArgumentEntry(5, 30000L), "assetHumidity", assetHumidityArgEntry), ctx); assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).contains("deviceTemperature"); } private TsRollingArgumentEntry createRollingArgEntry() { @@ -211,8 +228,7 @@ public class ScriptCalculatedFieldStateTest { config.setExpression("return {\"maxDeviceTemperature\": deviceTemperature.max(), \"assetHumidity\": assetHumidity / 2 }"); - Output output = new Output(); - output.setType(OutputType.ATTRIBUTES); + AttributesOutput output = new AttributesOutput(); output.setScope(AttributeScope.SERVER_SCOPE); config.setOutput(output); @@ -220,4 +236,8 @@ public class ScriptCalculatedFieldStateTest { return config; } + private TelemetryCalculatedFieldResult performCalculation() throws InterruptedException, ExecutionException { + return (TelemetryCalculatedFieldResult) state.performCalculation(Collections.emptyMap(), ctx).get(); + } + } \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java index 0001566abd..4454d41e6e 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java @@ -18,17 +18,20 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.AssetId; @@ -38,11 +41,14 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; -import org.thingsboard.server.dao.usagerecord.ApiLimitService; -import org.thingsboard.server.service.cf.CalculatedFieldResult; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -66,14 +72,21 @@ public class SimpleCalculatedFieldStateTest { private CalculatedFieldCtx ctx; @Mock - private ApiLimitService apiLimitService; + private TenantProfile tenantProfile; + @Mock + private TbTenantProfileCache tenantProfileCache; + @InjectMocks + private ActorSystemContext systemContext; @BeforeEach void setUp() { - when(apiLimitService.getLimit(any(), any())).thenReturn(1000L); - ctx = new CalculatedFieldCtx(getCalculatedField(), null, apiLimitService); + when(tenantProfileCache.get(any(TenantId.class))).thenReturn(tenantProfile); + when(tenantProfile.getProfileConfiguration()).thenReturn(Optional.of(new DefaultTenantProfileConfiguration())); + ctx = new CalculatedFieldCtx(getCalculatedField(), systemContext); ctx.init(); - state = new SimpleCalculatedFieldState(ctx.getArgNames()); + state = new SimpleCalculatedFieldState(ctx.getEntityId()); + state.setCtx(ctx, null); + state.init(false); } @Test @@ -89,7 +102,7 @@ public class SimpleCalculatedFieldStateTest { )); Map newArgs = Map.of("key3", key3ArgEntry); - boolean stateUpdated = state.updateState(ctx, newArgs); + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); assertThat(stateUpdated).isTrue(); assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( @@ -107,7 +120,7 @@ public class SimpleCalculatedFieldStateTest { SingleValueArgumentEntry newArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis(), new LongDataEntry("key1", 18L), 190L); Map newArgs = Map.of("key1", newArgEntry); - boolean stateUpdated = state.updateState(ctx, newArgs); + boolean stateUpdated = !state.update(newArgs, ctx).isEmpty(); assertThat(stateUpdated).isTrue(); assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf(Map.of("key1", newArgEntry)); @@ -121,9 +134,10 @@ public class SimpleCalculatedFieldStateTest { )); Map newArgs = Map.of("key3", new TsRollingArgumentEntry(10, 30000L)); - assertThatThrownBy(() -> state.updateState(ctx, newArgs)) + assertThatThrownBy(() -> state.update(newArgs, ctx)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Rolling argument entry is not supported for simple calculated fields."); + .hasMessage("Unsupported argument type detected for argument: key3. " + + "Rolling argument entry is not supported for simple calculated fields."); } @Test @@ -134,7 +148,7 @@ public class SimpleCalculatedFieldStateTest { "key3", key3ArgEntry )); - CalculatedFieldResult result = state.performCalculation(ctx).get(); + TelemetryCalculatedFieldResult result = performCalculation(); assertThat(result).isNotNull(); Output output = getCalculatedFieldConfig().getOutput(); @@ -151,7 +165,7 @@ public class SimpleCalculatedFieldStateTest { "key3", key3ArgEntry )); - assertThatThrownBy(() -> state.performCalculation(ctx)) + assertThatThrownBy(() -> state.performCalculation(Collections.emptyMap(), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Argument 'key2' is not a number."); } @@ -164,7 +178,7 @@ public class SimpleCalculatedFieldStateTest { "key3", key3ArgEntry )); - CalculatedFieldResult result = state.performCalculation(ctx).get(); + TelemetryCalculatedFieldResult result = performCalculation(); assertThat(result).isNotNull(); Output output = getCalculatedFieldConfig().getOutput(); @@ -185,7 +199,7 @@ public class SimpleCalculatedFieldStateTest { output.setDecimalsByDefault(3); ctx.setOutput(output); - CalculatedFieldResult result = state.performCalculation(ctx).get(); + TelemetryCalculatedFieldResult result = performCalculation(); assertThat(result).isNotNull(); assertThat(result.getType()).isEqualTo(output.getType()); @@ -196,28 +210,29 @@ public class SimpleCalculatedFieldStateTest { @Test void testIsReadyWhenNotAllArgPresent() { assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).contains(state.getRequiredArguments()); } @Test void testIsReadyWhenAllArgPresent() { - state.arguments = new HashMap<>(Map.of( + state.update(Map.of( "key1", key1ArgEntry, "key2", key2ArgEntry, "key3", key3ArgEntry - )); - + ), ctx); assertThat(state.isReady()).isTrue(); + assertThat(state.getReadinessStatus().errorMsg()).isNull(); } @Test void testIsReadyWhenEmptyEntryPresents() { - state.arguments = new HashMap<>(Map.of( + state.update(Map.of( "key1", key1ArgEntry, - "key2", key2ArgEntry - )); - state.getArguments().put("key3", new SingleValueArgumentEntry()); - + "key2", key2ArgEntry, + "key3", new SingleValueArgumentEntry() + ), ctx); assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().errorMsg()).contains("key3"); } private CalculatedField getCalculatedField() { @@ -254,9 +269,8 @@ public class SimpleCalculatedFieldStateTest { config.setExpression("key1 + key2 + key3"); - Output output = new Output(); + AttributesOutput output = new AttributesOutput(); output.setName("output"); - output.setType(OutputType.ATTRIBUTES); output.setScope(AttributeScope.SERVER_SCOPE); output.setDecimalsByDefault(0); @@ -265,4 +279,8 @@ public class SimpleCalculatedFieldStateTest { return config; } + private TelemetryCalculatedFieldResult performCalculation() throws InterruptedException, ExecutionException { + return (TelemetryCalculatedFieldResult) state.performCalculation(Collections.emptyMap(), ctx).get(); + } + } \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java index 707397f6b1..57bb161688 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java @@ -17,6 +17,9 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; 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.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; import org.thingsboard.server.common.data.kv.JsonDataEntry; @@ -29,10 +32,14 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class SingleValueArgumentEntryTest { private SingleValueArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + private final long ts = System.currentTimeMillis(); @BeforeEach @@ -47,43 +54,48 @@ public class SingleValueArgumentEntryTest { @Test void testUpdateEntryWhenRollingEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L))) + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for single value argument entry: " + ArgumentEntryType.TS_ROLLING); } @Test void testUpdateEntryWithTheSameTs() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 363L))).isFalse(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 363L), ctx)).isFalse(); + } + + @Test + void testUpdateEntryWithTheSameTsAndDifferentVersion() { + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 364L), ctx)).isTrue(); } @Test void testUpdateEntryWhenNewVersionIsNull() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 16, new LongDataEntry("key", 13L), null))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 16, new LongDataEntry("key", 13L), null), ctx)).isTrue(); assertThat(entry.getValue()).isEqualTo(13L); assertThat(entry.getVersion()).isNull(); } @Test void testUpdateEntryWhenNewVersionIsGreaterThanCurrent() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 369L))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 369L), ctx)).isTrue(); assertThat(entry.getValue()).isEqualTo(18L); assertThat(entry.getVersion()).isEqualTo(369L); } @Test void testUpdateEntryWhenNewVersionIsLessThanCurrent() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 234L))).isFalse(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 234L), ctx)).isFalse(); } @Test void testUpdateEntryWhenValueWasNotChanged() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 11L), 364L))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 11L), 364L), ctx)).isTrue(); } @Test void testUpdateEntryWithOldTs() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts - 10, new LongDataEntry("key", 14L), 365L))).isFalse(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts - 10, new LongDataEntry("key", 14L), 365L), ctx)).isFalse(); } @Test @@ -115,4 +127,5 @@ public class SingleValueArgumentEntryTest { expectedList.add(Map.of("test2", 20)); assertThat(singleValueArg.getValue()).isEqualTo(expectedList); } + } diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java index b0c30f5a84..7dea28e655 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java @@ -17,6 +17,9 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; 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.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -26,10 +29,14 @@ import java.util.TreeMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class TsRollingArgumentEntryTest { private TsRollingArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + private final long ts = System.currentTimeMillis(); @BeforeEach @@ -51,7 +58,7 @@ public class TsRollingArgumentEntryTest { void testUpdateEntryWhenSingleValueEntryPassed() { SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 10, new DoubleDataEntry("key", 23.0), 123L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(4); assertThat(entry.getTsRecords().get(ts - 10)).isEqualTo(23.0); } @@ -64,7 +71,7 @@ public class TsRollingArgumentEntryTest { values.put(ts - 5, 1.0); newEntry.setTsRecords(values); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(5); assertThat(entry.getTsRecords()).isEqualTo(Map.of( ts - 40, 10.0, @@ -79,7 +86,7 @@ public class TsRollingArgumentEntryTest { void testUpdateEntryWhenValueIsNotNumber() { SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 10, new StringDataEntry("key", "string"), 123L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords().get(ts - 10)).isNaN(); } @@ -93,7 +100,7 @@ public class TsRollingArgumentEntryTest { newEntry.setTsRecords(values); entry = new TsRollingArgumentEntry(3, 30000L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(1); assertThat(entry.getTsRecords()).isEqualTo(Map.of( ts - 5, 0.0 @@ -111,7 +118,7 @@ public class TsRollingArgumentEntryTest { newEntry.setTsRecords(values); entry = new TsRollingArgumentEntry(3, 30000L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(3); assertThat(entry.getTsRecords()).isEqualTo(Map.of( ts - 18, 0.0, diff --git a/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java b/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java index eb16623b30..4bbb51068c 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.service.edge; +import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -36,6 +38,10 @@ import org.thingsboard.rule.engine.rest.TbSendRestApiCallReplyNode; import org.thingsboard.rule.engine.telemetry.TbCalculatedFieldsNode; import org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode; import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode; +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.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.gen.edge.v1.EdgeVersion; @@ -44,6 +50,7 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import java.lang.reflect.Constructor; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Stream; import static org.thingsboard.server.service.edge.EdgeMsgConstructorUtils.EXCLUDED_NODES_BY_EDGE_VERSION; @@ -155,4 +162,120 @@ public class EdgeMsgConstructorUtilsTest { String.format("For EdgeVersion '%s', ruleNode '%s' should not be included.", edgeVersion, ruleNode.getType())); } + @Test + @DisplayName("mergeDownlinkDuplicates: latest per attribute key is retained and duplicates removed") + public void testMergeDownlinkDuplicates() { + UUID deviceId = UUID.randomUUID(); + UUID assetId = UUID.randomUUID(); + TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); + + var deviceAttrUpdate1 = createEdgeEvent(tenantId, 1, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBody(1_000L, "{\"a\":1,\"b\":1,\"d\":1}")); + var deviceAttrUpdate2 = createEdgeEvent(tenantId, 2, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBody(2_000L, "{\"a\":2,\"b\":2,\"c\":2}")); + var deviceAttrUpdate3 = createEdgeEvent(tenantId, 3, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBody(3_000L, "{\"a\":3,\"d\":3}")); + + var deviceUpdate = createEdgeEvent(tenantId, 4, EdgeEventActionType.UPDATED, + deviceId, EdgeEventType.DEVICE, null); + var deviceUpdateDup = createEdgeEvent(tenantId, 5, EdgeEventActionType.UPDATED, + deviceId, EdgeEventType.DEVICE, null); + + var assetAttrUpdate1 = createEdgeEvent(tenantId, 6, EdgeEventActionType.ATTRIBUTES_UPDATED, + assetId, EdgeEventType.ASSET, createAttrBody(6_000L, "{\"a\":6,\"d\":6}")); + var assetAttrUpdate2 = createEdgeEvent(tenantId, 7, EdgeEventActionType.ATTRIBUTES_UPDATED, + assetId, EdgeEventType.ASSET, createAttrBody(7_000L, "{\"a\":7,\"b\":7,\"c\":7}")); + var assetAttrUpdate3 = createEdgeEvent(tenantId, 8, EdgeEventActionType.ATTRIBUTES_UPDATED, + assetId, EdgeEventType.ASSET, createAttrBody(8_000L, "{\"a\":8,\"d\":8}")); + + List input = List.of(deviceAttrUpdate1, deviceAttrUpdate2, deviceAttrUpdate3, + deviceUpdate, deviceUpdateDup, + assetAttrUpdate1, assetAttrUpdate2, assetAttrUpdate3); + List merged = EdgeMsgConstructorUtils.mergeAndFilterDownlinkDuplicates(input); + + Assertions.assertEquals(5, merged.size()); + + EdgeEvent deviceMergedAttrBC = merged.get(0); + Assertions.assertEquals(2, deviceMergedAttrBC.getSeqId()); + Assertions.assertEquals(deviceId, deviceMergedAttrBC.getEntityId()); + Assertions.assertEquals(2_000L, deviceMergedAttrBC.getBody().get("ts").asLong()); + Assertions.assertEquals(2, getIntValue(deviceMergedAttrBC.getBody(), "b")); + Assertions.assertEquals(2, getIntValue(deviceMergedAttrBC.getBody(), "c")); + Assertions.assertNull(getIntValue(deviceMergedAttrBC.getBody(), "a")); + + EdgeEvent deviceMergedAttrAD = merged.get(1); + Assertions.assertEquals(3, deviceMergedAttrAD.getSeqId()); + Assertions.assertEquals(deviceId, deviceMergedAttrAD.getEntityId()); + Assertions.assertEquals(3_000L, deviceMergedAttrAD.getBody().get("ts").asLong()); + Assertions.assertEquals(3, getIntValue(deviceMergedAttrAD.getBody(), "a")); + Assertions.assertEquals(3, getIntValue(deviceMergedAttrAD.getBody(), "d")); + + EdgeEvent mergedDeviceUpdate = merged.get(2); + Assertions.assertEquals(4, mergedDeviceUpdate.getSeqId()); + Assertions.assertEquals(EdgeEventActionType.UPDATED, mergedDeviceUpdate.getAction()); + + EdgeEvent assetMergedAttrBC = merged.get(3); + Assertions.assertEquals(7, assetMergedAttrBC.getSeqId()); + Assertions.assertEquals(assetId, assetMergedAttrBC.getEntityId()); + Assertions.assertEquals(7_000L, assetMergedAttrBC.getBody().get("ts").asLong()); + Assertions.assertEquals(7, getIntValue(assetMergedAttrBC.getBody(), "b")); + Assertions.assertEquals(7, getIntValue(assetMergedAttrBC.getBody(), "c")); + Assertions.assertNull(getIntValue(assetMergedAttrBC.getBody(), "a")); + + EdgeEvent assetMergedAttrAD = merged.get(4); + Assertions.assertEquals(8, assetMergedAttrAD.getSeqId()); + Assertions.assertEquals(assetId, assetMergedAttrAD.getEntityId()); + Assertions.assertEquals(8_000L, assetMergedAttrAD.getBody().get("ts").asLong()); + Assertions.assertEquals(8, getIntValue(assetMergedAttrAD.getBody(), "a")); + Assertions.assertEquals(8, getIntValue(assetMergedAttrAD.getBody(), "d")); + } + + @Test + public void testMergeDownlinkDuplicates_attrBodyHasNoTs_returnOriginalList() { + UUID deviceId = UUID.randomUUID(); + TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); + + var deviceAttrUpdate1 = createEdgeEvent(tenantId, 1, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBodyWithoutTs("{\"a\":1,\"b\":1,\"d\":1}")); + var deviceAttrUpdate2 = createEdgeEvent(tenantId, 2, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBodyWithoutTs("{\"a\":2,\"b\":2,\"c\":2}")); + var deviceAttrUpdate3 = createEdgeEvent(tenantId, 3, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBodyWithoutTs("{\"a\":3,\"d\":3}")); + + List input = List.of(deviceAttrUpdate1, deviceAttrUpdate2, deviceAttrUpdate3); + List merged = EdgeMsgConstructorUtils.mergeAndFilterDownlinkDuplicates(input); + + Assertions.assertEquals(3, merged.size()); + Assertions.assertEquals(deviceAttrUpdate1, merged.get(0)); + Assertions.assertEquals(deviceAttrUpdate2, merged.get(1)); + Assertions.assertEquals(deviceAttrUpdate3, merged.get(2)); + } + + private Integer getIntValue(JsonNode body, String key) { + return body.get("kv").get(key) != null ? body.get("kv").get(key).asInt() : null; + } + + private static JsonNode createAttrBodyWithoutTs(String kvJson) { + return JacksonUtil.toJsonNode("{\"kv\":" + kvJson + "}"); + } + + private static JsonNode createAttrBody(long ts, String kvJson) { + return JacksonUtil.toJsonNode("{\"ts\":" + ts + ",\"kv\":" + kvJson + "}"); + } + + private static EdgeEvent createEdgeEvent(TenantId tenantId, + long seqId, + EdgeEventActionType action, + UUID entityId, + EdgeEventType type, + JsonNode body) { + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setSeqId(seqId); + edgeEvent.setTenantId(tenantId); + edgeEvent.setAction(action); + edgeEvent.setEntityId(entityId); + edgeEvent.setType(type); + edgeEvent.setBody(body); + return edgeEvent; + } } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/EdgeStatsTest.java b/application/src/test/java/org/thingsboard/server/service/edge/EdgeStatsTest.java index 01f3a08853..f1f0965a0d 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/EdgeStatsTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/EdgeStatsTest.java @@ -21,6 +21,7 @@ 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.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; @@ -44,9 +45,11 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; 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.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_ADDED; @@ -58,6 +61,16 @@ import static org.thingsboard.server.dao.edge.stats.EdgeStatsKey.DOWNLINK_MSGS_T @ExtendWith(MockitoExtension.class) public class EdgeStatsTest { + private static final int TTL_DAYS = 30; + private static final long REPORT_INTERVAL_MILLIS = 600_000L; + + private static final long EXPECTED_MSGS_ADDED = 5L; + private static final long EXPECTED_MSGS_PUSHED = 3L; + private static final long EXPECTED_MSGS_PERMANENTLY_FAILED = 1L; + private static final long EXPECTED_MSGS_TMP_FAILED = 0L; + private static final long EXPECTED_MSGS_LAG = 10L; + private static final long EXPECTED_MSGS_KAFKA_LAG = 15L; + @Mock private TimeseriesService tsService; @Mock @@ -66,108 +79,100 @@ public class EdgeStatsTest { private EdgeStatsCounterService statsCounterService; private EdgeStatsService edgeStatsService; + @Captor + private ArgumentCaptor> captor; + private final TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); private final EdgeId edgeId = new EdgeId(UUID.randomUUID()); @BeforeEach void setUp() { - edgeStatsService = new EdgeStatsService( + edgeStatsService = createEdgeStatsService(Optional.empty()); + } + + private EdgeStatsService createEdgeStatsService(Optional kafkaAdmin) { + EdgeStatsService service = new EdgeStatsService( tsService, statsCounterService, topicService, - Optional.empty() + kafkaAdmin ); - - ReflectionTestUtils.setField(edgeStatsService, "edgesStatsTtlDays", 30); - ReflectionTestUtils.setField(edgeStatsService, "reportIntervalMillis", 600_000L); + ReflectionTestUtils.setField(service, "edgesStatsTtlDays", TTL_DAYS); + ReflectionTestUtils.setField(service, "reportIntervalMillis", REPORT_INTERVAL_MILLIS); + return service; } @Test public void testReportStatsSavesTelemetry() { - // given + // GIVEN + setupCounters(); + + // WHEN + edgeStatsService.reportStats(); + + // THEN + Map counters = verifyCounters(); + Assertions.assertEquals(EXPECTED_MSGS_LAG, counters.get(DOWNLINK_MSGS_LAG.getKey()).longValue()); + } + + @Test + public void testReportStatsWithKafkaLag() { + // GIVEN + setupCounters(); + setupKafkaLag(); + + // WHEN + edgeStatsService.reportStats(); + + // THEN + Map valuesByKey = verifyCounters(); + Assertions.assertEquals(EXPECTED_MSGS_KAFKA_LAG, valuesByKey.get(DOWNLINK_MSGS_LAG.getKey())); + } + + private void setupCounters() { MsgCounters counters = new MsgCounters(tenantId); - counters.getMsgsAdded().set(5); - counters.getMsgsPushed().set(3); - counters.getMsgsPermanentlyFailed().set(1); - counters.getMsgsTmpFailed().set(0); - counters.getMsgsLag().set(10); + counters.getMsgsAdded().set(EXPECTED_MSGS_ADDED); + counters.getMsgsPushed().set(EXPECTED_MSGS_PUSHED); + counters.getMsgsPermanentlyFailed().set(EXPECTED_MSGS_PERMANENTLY_FAILED); + counters.getMsgsTmpFailed().set(EXPECTED_MSGS_TMP_FAILED); + counters.getMsgsLag().set(EXPECTED_MSGS_LAG); ConcurrentHashMap countersByEdge = new ConcurrentHashMap<>(); countersByEdge.put(edgeId, counters); - when(statsCounterService.getCounterByEdge()).thenReturn(countersByEdge); + when(statsCounterService.getMsgCountersByEdge()).thenReturn(countersByEdge); - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); when(tsService.save(eq(tenantId), eq(edgeId), captor.capture(), anyLong())) .thenReturn(Futures.immediateFuture(mock(TimeseriesSaveResult.class))); + } - // when - edgeStatsService.reportStats(); + private Map verifyCounters() { + verify(tsService, times(1)).save(eq(tenantId), eq(edgeId), anyList(), anyLong()); + verify(statsCounterService, times(1)).clear(edgeId); - // then List entries = captor.getValue(); Assertions.assertEquals(5, entries.size()); Map valuesByKey = entries.stream() .collect(Collectors.toMap(TsKvEntry::getKey, e -> e.getLongValue().orElse(-1L))); - Assertions.assertEquals(5L, valuesByKey.get(DOWNLINK_MSGS_ADDED.getKey()).longValue()); - Assertions.assertEquals(3L, valuesByKey.get(DOWNLINK_MSGS_PUSHED.getKey()).longValue()); - Assertions.assertEquals(1L, valuesByKey.get(DOWNLINK_MSGS_PERMANENTLY_FAILED.getKey()).longValue()); - Assertions.assertEquals(0L, valuesByKey.get(DOWNLINK_MSGS_TMP_FAILED.getKey()).longValue()); - Assertions.assertEquals(10L, valuesByKey.get(DOWNLINK_MSGS_LAG.getKey()).longValue()); - - - verify(statsCounterService).clear(edgeId); + Assertions.assertEquals(EXPECTED_MSGS_ADDED, valuesByKey.get(DOWNLINK_MSGS_ADDED.getKey()).longValue()); + Assertions.assertEquals(EXPECTED_MSGS_PUSHED, valuesByKey.get(DOWNLINK_MSGS_PUSHED.getKey()).longValue()); + Assertions.assertEquals(EXPECTED_MSGS_PERMANENTLY_FAILED, valuesByKey.get(DOWNLINK_MSGS_PERMANENTLY_FAILED.getKey()).longValue()); + Assertions.assertEquals(EXPECTED_MSGS_TMP_FAILED, valuesByKey.get(DOWNLINK_MSGS_TMP_FAILED.getKey()).longValue()); + return valuesByKey; } - @Test - public void testReportStatsWithKafkaLag() { - // given - MsgCounters counters = new MsgCounters(tenantId); - counters.getMsgsAdded().set(2); - counters.getMsgsPushed().set(2); - counters.getMsgsPermanentlyFailed().set(0); - counters.getMsgsTmpFailed().set(1); - counters.getMsgsLag().set(0); - - ConcurrentHashMap countersByEdge = new ConcurrentHashMap<>(); - countersByEdge.put(edgeId, counters); - - // mocks - when(statsCounterService.getCounterByEdge()).thenReturn(countersByEdge); - + private void setupKafkaLag() { String topic = "edge-topic"; TopicPartitionInfo partitionInfo = new TopicPartitionInfo(topic, tenantId, 0, false); when(topicService.buildEdgeEventNotificationsTopicPartitionInfo(tenantId, edgeId)).thenReturn(partitionInfo); KafkaAdmin kafkaAdmin = mock(KafkaAdmin.class); when(kafkaAdmin.getTotalLagForGroupsBulk(Set.of(topic))) - .thenReturn(Map.of(topic, 15L)); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - when(tsService.save(eq(tenantId), eq(edgeId), captor.capture(), anyLong())) - .thenReturn(Futures.immediateFuture(mock(TimeseriesSaveResult.class))); - - edgeStatsService = new EdgeStatsService( - tsService, - statsCounterService, - topicService, - Optional.of(kafkaAdmin) - ); - ReflectionTestUtils.setField(edgeStatsService, "edgesStatsTtlDays", 30); - ReflectionTestUtils.setField(edgeStatsService, "reportIntervalMillis", 600_000L); - - // when - edgeStatsService.reportStats(); - - // then - List entries = captor.getValue(); - Map valuesByKey = entries.stream() - .collect(Collectors.toMap(TsKvEntry::getKey, e -> e.getLongValue().orElse(-1L))); + .thenReturn(Map.of(topic, EXPECTED_MSGS_KAFKA_LAG)); - Assertions.assertEquals(15L, valuesByKey.get(DOWNLINK_MSGS_LAG.getKey())); - verify(statsCounterService).clear(edgeId); + edgeStatsService = createEdgeStatsService(Optional.of(kafkaAdmin)); } } diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/EdqsEntityServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/EdqsEntityServiceTest.java index a16cf99c1d..adf664e7cf 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/EdqsEntityServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/EdqsEntityServiceTest.java @@ -19,8 +19,8 @@ import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.CustomerId; @@ -61,7 +61,7 @@ public class EdqsEntityServiceTest extends EntityServiceTest { @Autowired private EdqsService edqsService; - @MockBean + @MockitoBean private EdqsRocksDb edqsRocksDb; @Before 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 4886310750..96e4444ffd 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 @@ -62,6 +62,9 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.ASSIGNED_TO_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.UNASSIGNED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.UNASSIGNED_FROM_DELETED_USER; @SpringJUnitConfig(DefaultTbAlarmService.class) class DefaultTbAlarmServiceTest { @@ -217,9 +220,10 @@ class DefaultTbAlarmServiceTest { 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()); + commentNode.put("text", String.format(UNASSIGNED_BY_USER.getText(), user.getTitle())); + commentNode.put("subtype", UNASSIGNED_BY_USER.name()); + commentNode.put("userName", user.getTitle()); + AlarmComment expectedAlarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) @@ -243,8 +247,10 @@ class DefaultTbAlarmServiceTest { service.unassignDeletedUserAlarms(tenantId, user.getId(), user.getTitle(), List.of(alarm.getUuidId()), 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())); + commentNode.put("text", String.format(UNASSIGNED_FROM_DELETED_USER.getText(), user.getTitle())); + commentNode.put("subtype", UNASSIGNED_FROM_DELETED_USER.name()); + commentNode.put("userName", user.getTitle()); + AlarmComment expectedAlarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) diff --git a/application/src/test/java/org/thingsboard/server/service/install/update/DefaultDataUpdateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/install/update/DefaultDataUpdateServiceTest.java deleted file mode 100644 index a82aad5b62..0000000000 --- a/application/src/test/java/org/thingsboard/server/service/install/update/DefaultDataUpdateServiceTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -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.test.context.ActiveProfiles; -import org.thingsboard.common.util.JacksonUtil; - -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.willCallRealMethod; - -@ActiveProfiles("install") -@SpringBootTest(classes = DefaultDataUpdateService.class) -class DefaultDataUpdateServiceTest { - - @MockBean - DefaultDataUpdateService service; - - @BeforeEach - void setUp() { - willCallRealMethod().given(service).convertDeviceProfileAlarmRulesForVersion330(any()); - willCallRealMethod().given(service).convertDeviceProfileForVersion330(any()); - } - - JsonNode readFromResource(String resourceName) throws IOException { - return JacksonUtil.OBJECT_MAPPER.readTree(this.getClass().getClassLoader().getResourceAsStream(resourceName)); - } - - @Test - void convertDeviceProfileAlarmRulesForVersion330FirstRun() throws IOException { - JsonNode spec = readFromResource("update/330/device_profile_001_in.json"); - JsonNode expected = readFromResource("update/330/device_profile_001_out.json"); - - assertThat(service.convertDeviceProfileForVersion330(spec.get("profileData"))).isTrue(); - assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); // use IDE feature - } - - @Test - void convertDeviceProfileAlarmRulesForVersion330SecondRun() throws IOException { - JsonNode spec = readFromResource("update/330/device_profile_001_out.json"); - JsonNode expected = readFromResource("update/330/device_profile_001_out.json"); - - assertThat(service.convertDeviceProfileForVersion330(spec.get("profileData"))).isFalse(); - assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); // use IDE feature - } - - @Test - void convertDeviceProfileAlarmRulesForVersion330EmptyJson() throws JsonProcessingException { - JsonNode spec = JacksonUtil.toJsonNode("{ }"); - JsonNode expected = JacksonUtil.toJsonNode("{ }"); - - assertThat(service.convertDeviceProfileForVersion330(spec)).isFalse(); - assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); - } - - @Test - void convertDeviceProfileAlarmRulesForVersion330AlarmNodeNull() throws JsonProcessingException { - JsonNode spec = JacksonUtil.toJsonNode("{ \"alarms\" : null }"); - JsonNode expected = JacksonUtil.toJsonNode("{ \"alarms\" : null }"); - - assertThat(service.convertDeviceProfileForVersion330(spec)).isFalse(); - assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); - } - - @Test - void convertDeviceProfileAlarmRulesForVersion330NoAlarmNode() throws JsonProcessingException { - JsonNode spec = JacksonUtil.toJsonNode("{ \"configuration\": { \"type\": \"DEFAULT\" } }"); - JsonNode expected = JacksonUtil.toJsonNode("{ \"configuration\": { \"type\": \"DEFAULT\" } }"); - - assertThat(service.convertDeviceProfileForVersion330(spec)).isFalse(); - assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); - } - -} 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 e9e239ec04..fc42ae247c 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 @@ -24,8 +24,8 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpEntity; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.ResultActions; import org.springframework.web.client.RestTemplate; import org.thingsboard.common.util.JacksonUtil; @@ -125,7 +125,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { private NotificationCenter notificationCenter; @Autowired private MicrosoftTeamsNotificationChannel microsoftTeamsNotificationChannel; - @MockBean + @MockitoBean private FirebaseService firebaseService; private static final String TEST_MOBILE_TOKEN = "tenantFcmToken"; 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 7175f0981d..e3342dbf43 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 @@ -21,13 +21,15 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Before; import org.junit.Test; import org.junit.function.ThrowingRunnable; +import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.data.util.Pair; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.SystemUtil; import org.thingsboard.server.cache.limits.RateLimitService; -import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; @@ -39,17 +41,19 @@ import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.SimpleAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; -import org.thingsboard.server.common.data.device.profile.AlarmCondition; -import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; -import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; -import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; -import org.thingsboard.server.common.data.device.profile.AlarmRule; -import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; -import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DeviceId; @@ -87,9 +91,6 @@ import org.thingsboard.server.common.data.notification.targets.platform.SystemAd 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; -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.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.Authority; @@ -106,12 +107,11 @@ import org.thingsboard.server.service.system.DefaultSystemInfoService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; -import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -123,6 +123,7 @@ 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.mockito.Mockito.mockStatic; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 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; @@ -137,7 +138,7 @@ import static org.thingsboard.server.common.data.notification.rule.trigger.confi }) public class NotificationRuleApiTest extends AbstractNotificationApiTest { - @SpyBean + @MockitoSpyBean private AlarmSubscriptionService alarmSubscriptionService; @Autowired private DefaultSystemInfoService systemInfoService; @@ -193,7 +194,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { @Test public void testNotificationRuleProcessing_alarmTrigger() throws Exception { String notificationSubject = "Alarm type: ${alarmType}, status: ${alarmStatus}, " + - "severity: ${alarmSeverity}, deviceId: ${alarmOriginatorId}"; + "severity: ${alarmSeverity}, deviceId: ${alarmOriginatorId}, details: ${details.data}."; String notificationText = "Status: ${alarmStatus}, severity: ${alarmSeverity}"; NotificationTemplate notificationTemplate = createNotificationTemplate(NotificationType.ALARM, notificationSubject, notificationText, NotificationDeliveryMethod.WEB); @@ -221,12 +222,12 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { clients.put(delay, userAndClient.getSecond()); } notificationRule.setRecipientsConfig(recipientsConfig); - notificationRule = saveNotificationRule(notificationRule); + saveNotificationRule(notificationRule); String alarmType = "myBoolIsTrue"; DeviceProfile deviceProfile = createDeviceProfileWithAlarmRules(alarmType); - Device device = createDevice("Device 1", deviceProfile.getName(), "1234"); + Device device = createDevice("Device 1", deviceProfile.getName(), "label", "1234"); clients.values().forEach(wsClient -> { wsClient.subscribeForUnreadNotifications(10).waitForReply(true); @@ -234,8 +235,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }); JsonNode attr = JacksonUtil.newObjectNode() - .set("bool", BooleanNode.TRUE); - doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); + .set("createAlarm", BooleanNode.TRUE); + postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); @@ -250,7 +251,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(actualDelay).isCloseTo(expectedDelay, offset(2.0)); assertThat(notification.getSubject()).isEqualTo("Alarm type: " + alarmType + ", status: " + AlarmStatus.ACTIVE_UNACK + ", " + - "severity: " + AlarmSeverity.CRITICAL.toString().toLowerCase() + ", deviceId: " + device.getId()); + "severity: " + AlarmSeverity.CRITICAL.toString().toLowerCase() + ", deviceId: " + device.getId() + ", details: attribute is true."); assertThat(notification.getText()).isEqualTo("Status: " + AlarmStatus.ACTIVE_UNACK + ", severity: " + AlarmSeverity.CRITICAL.toString().toLowerCase()); assertThat(notification.getType()).isEqualTo(NotificationType.ALARM); @@ -270,7 +271,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { wsClient.waitForUpdate(true); Notification updatedNotification = wsClient.getLastDataUpdate().getUpdate(); assertThat(updatedNotification.getSubject()).isEqualTo("Alarm type: " + alarmType + ", status: " + expectedStatus + ", " + - "severity: " + expectedSeverity.toString().toLowerCase() + ", deviceId: " + device.getId()); + "severity: " + expectedSeverity.toString().toLowerCase() + ", deviceId: " + device.getId() + ", details: attribute is true."); assertThat(updatedNotification.getText()).isEqualTo("Status: " + expectedStatus + ", severity: " + expectedSeverity.toString().toLowerCase()); wsClient.close(); @@ -341,8 +342,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { getWsClient().subscribeForUnreadNotifications(10).waitForReply(true); getWsClient().registerWaitForUpdate(); JsonNode attr = JacksonUtil.newObjectNode() - .set("bool", BooleanNode.TRUE); - doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); + .set("createAlarm", BooleanNode.TRUE); + postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); @@ -516,10 +517,10 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .notifyOn(Set.of(ASSIGNED, UNASSIGNED)) .build(); NotificationTarget target = createNotificationTarget(tenantAdminUserId); - String template = "${userEmail} ${action} alarm on ${alarmOriginatorEntityType} '${alarmOriginatorName}'. Assignee: ${assigneeEmail}"; + String template = "${userEmail} ${action} alarm on ${alarmOriginatorEntityType} '${alarmOriginatorName}' with label '${alarmOriginatorLabel}'. Assignee: ${assigneeEmail}"; createNotificationRule(triggerConfig, "Test", template, target.getId()); - Device device = createDevice("Device A", "123"); + Device device = createDevice("Device A", "default", "test", "123"); Alarm alarm = Alarm.builder() .tenantId(tenantId) .originator(device.getId()) @@ -536,7 +537,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { doPost("/api/alarm/" + alarmId + "/assign/" + tenantAdminUserId).andExpect(status().isOk()); }, notification -> { assertThat(notification.getText()).isEqualTo( - TENANT_ADMIN_EMAIL + " assigned alarm on Device 'Device A'. Assignee: " + TENANT_ADMIN_EMAIL + TENANT_ADMIN_EMAIL + " assigned alarm on Device 'Device A' with label 'test'. Assignee: " + TENANT_ADMIN_EMAIL ); }); @@ -544,7 +545,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { doDelete("/api/alarm/" + alarmId + "/assign").andExpect(status().isOk()); }, notification -> { assertThat(notification.getText()).isEqualTo( - TENANT_ADMIN_EMAIL + " unassigned alarm on Device 'Device A'. Assignee: " + TENANT_ADMIN_EMAIL + " unassigned alarm on Device 'Device A' with label 'test'. Assignee: " ); }); } @@ -799,9 +800,14 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId()); loginTenantAdmin(); - Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); - method.setAccessible(true); - method.invoke(systemInfoService); + // Mock SystemUtil to return 15% memory usage (exceeds 1% threshold) + try (MockedStatic mockedSystemUtil = mockStatic(SystemUtil.class)) { + mockedSystemUtil.when(SystemUtil::getMemoryUsage).thenReturn(Optional.of(15)); + + Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); + method.setAccessible(true); + method.invoke(systemInfoService); + } await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); Notification notification = getMyNotifications(false, 100).get(0); @@ -850,15 +856,23 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { loginSysAdmin(); NotificationTarget sysadmins = createNotificationTarget(new SystemAdministratorsFilter()); ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() - .ramThreshold(1f) - .cpuThreshold(1f) - .storageThreshold(1f) + .ramThreshold(0.99f) + .cpuThreshold(0.99f) + .storageThreshold(0.99f) .build(); NotificationRule rule = createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", sysadmins.getId()); - Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); - method.setAccessible(true); - method.invoke(systemInfoService); + // Mock SystemUtil to return 15% usages (not exceeds 99% threshold) + Method method; + try (MockedStatic mockedSystemUtil = mockStatic(SystemUtil.class)) { + mockedSystemUtil.when(SystemUtil::getMemoryUsage).thenReturn(Optional.of(15)); + mockedSystemUtil.when(SystemUtil::getCpuUsage).thenReturn(Optional.of(15)); + mockedSystemUtil.when(SystemUtil::getDiscSpaceUsage).thenReturn(Optional.of(15)); + + method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); + method.setAccessible(true); + method.invoke(systemInfoService); + } TimeUnit.SECONDS.sleep(5); assertThat(getMyNotifications(false, 100)).size().isZero(); @@ -942,35 +956,28 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { private DeviceProfile createDeviceProfileWithAlarmRules(String alarmType) { DeviceProfile deviceProfile = createDeviceProfile("For notification rule test"); deviceProfile.setTenantId(tenantId); + deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); - List alarms = new ArrayList<>(); - DeviceProfileAlarm alarm = new DeviceProfileAlarm(); - alarm.setAlarmType(alarmType); - alarm.setId(alarmType); + CalculatedField alarmCf = new CalculatedField(); + alarmCf.setType(CalculatedFieldType.ALARM); + alarmCf.setEntityId(deviceProfile.getId()); + alarmCf.setName(alarmType); + AlarmCalculatedFieldConfiguration configuration = new AlarmCalculatedFieldConfiguration(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("createAlarm", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + configuration.setArguments(Map.of("createAlarm", argument)); AlarmRule alarmRule = new AlarmRule(); - alarmRule.setAlarmDetails("Details"); - AlarmCondition alarmCondition = new AlarmCondition(); - alarmCondition.setSpec(new SimpleAlarmConditionSpec()); - List condition = new ArrayList<>(); - - AlarmConditionFilter alarmConditionFilter = new AlarmConditionFilter(); - alarmConditionFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "bool")); - BooleanFilterPredicate predicate = new BooleanFilterPredicate(); - predicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); - predicate.setValue(new FilterPredicateValue<>(true)); - - alarmConditionFilter.setPredicate(predicate); - alarmConditionFilter.setValueType(EntityKeyValueType.BOOLEAN); - condition.add(alarmConditionFilter); - alarmCondition.setCondition(condition); - alarmRule.setCondition(alarmCondition); - TreeMap createRules = new TreeMap<>(); - createRules.put(AlarmSeverity.CRITICAL, alarmRule); - alarm.setCreateRules(createRules); - alarms.add(alarm); - - deviceProfile.getProfileData().setAlarms(alarms); - deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + alarmRule.setAlarmDetails("attribute is ${createAlarm}"); + SimpleAlarmCondition condition = new SimpleAlarmCondition(); + TbelAlarmConditionExpression expression = new TbelAlarmConditionExpression(); + expression.setExpression("return createAlarm == true;"); + condition.setExpression(expression); + alarmRule.setCondition(condition); + configuration.setCreateRules(Map.of( + AlarmSeverity.CRITICAL, alarmRule + )); + alarmCf.setConfiguration(configuration); + saveCalculatedField(alarmCf); return deviceProfile; } 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 index 9c371ee5fc..228b987cf0 100644 --- 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 @@ -595,7 +595,7 @@ public class TbRuleEngineQueueConsumerManagerTest { 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()))) { + consumer.pollingStarted && Set.of(partition).equals(consumer.getPartitions()))) { return false; } } @@ -605,7 +605,7 @@ public class TbRuleEngineQueueConsumerManagerTest { await().atMost(5, TimeUnit.SECONDS).until(() -> { return consumers.size() == 1 && consumers.stream() .anyMatch(consumer -> consumer.subscribed && consumer.pollingStarted && - expectedPartitions.equals(consumer.getPartitions())); + expectedPartitions.equals(consumer.getPartitions())); }); } Mockito.reset(ruleEngineConsumerContext.getSubmitStrategyFactory()); @@ -667,8 +667,8 @@ public class TbRuleEngineQueueConsumerManagerTest { return await().atMost(5, TimeUnit.SECONDS) .until(() -> consumers.stream() .filter(consumer -> consumer.getPartitions() != null && - consumer.getPartitions().size() == 1 && - consumer.getPartitions().contains(tpi)) + consumer.getPartitions().size() == 1 && + consumer.getPartitions().contains(tpi)) .findFirst().orElse(null), Objects::nonNull); } @@ -676,9 +676,9 @@ public class TbRuleEngineQueueConsumerManagerTest { 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))) + consumer.getPartitions().size() == 1 && + consumer.getPartitions().stream() + .anyMatch(tpi -> tpi.getPartition().get().equals(partition))) .findFirst().orElse(null), Objects::nonNull); } @@ -778,10 +778,6 @@ public class TbRuleEngineQueueConsumerManagerTest { return false; } - public Set getPartitions() { - return partitions; - } - public void setUpTestMsg() { testMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) @@ -790,6 +786,7 @@ public class TbRuleEngineQueueConsumerManagerTest { .data("{}") .build(); } + } } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java index cdbc6d611f..3b33d91348 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; 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.common.consumer.TbQueueConsumerTask.ConsumerKey; import org.thingsboard.server.queue.discovery.QueueKey; import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory; import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory; @@ -191,6 +192,7 @@ public class TbRuleEngineStrategyTest { queue.setProcessingStrategy(processingStrategy); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); + ConsumerKey consumerKey = new ConsumerKey(queueKey, null); var consumerManager = TbRuleEngineQueueConsumerManager.create() .ctx(ruleEngineConsumerContext) .queueKey(queueKey) @@ -238,7 +240,7 @@ public class TbRuleEngineStrategyTest { .map(this::toProto) .toList(); - consumerManager.processMsgs(protoMsgs, consumer, queueKey, queue); + consumerManager.processMsgs(protoMsgs, consumer, consumerKey, queue); processingData.forEach(data -> { verify(actorContext, times(data.attempts)).tell(argThat(msg -> 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 ed9d9df095..0875bd94ee 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 @@ -60,7 +60,7 @@ import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.ai.AiModelService; import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DaoSqlTest; 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 cb7802034e..950ce4609f 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 @@ -60,7 +60,7 @@ public class JwtTokenFactoryTest { public void beforeEach() { jwtSettings = new JwtSettings(); jwtSettings.setTokenIssuer("tb"); - jwtSettings.setTokenSigningKey(Base64.getEncoder().encodeToString(RandomStringUtils.randomAlphanumeric(64).getBytes(StandardCharsets.UTF_8))); + jwtSettings.setTokenSigningKey(Base64.getEncoder().encodeToString(RandomStringUtils.secure().nextAlphanumeric(64).getBytes(StandardCharsets.UTF_8))); jwtSettings.setTokenExpirationTime((int) TimeUnit.HOURS.toSeconds(2)); jwtSettings.setRefreshTokenExpTime((int) TimeUnit.DAYS.toSeconds(7)); @@ -89,7 +89,7 @@ public class JwtTokenFactoryTest { AccessJwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); checkExpirationTime(accessToken, jwtSettings.getTokenExpirationTime()); - SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(accessToken.getToken()); + SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(accessToken.token()); assertThat(parsedSecurityUser.getId()).isEqualTo(securityUser.getId()); assertThat(parsedSecurityUser.getEmail()).isEqualTo(securityUser.getEmail()); assertThat(parsedSecurityUser.getUserPrincipal()).matches(userPrincipal -> { @@ -112,7 +112,7 @@ public class JwtTokenFactoryTest { JwtToken refreshToken = tokenFactory.createRefreshToken(securityUser); checkExpirationTime(refreshToken, jwtSettings.getRefreshTokenExpTime()); - SecurityUser parsedSecurityUser = tokenFactory.parseRefreshToken(refreshToken.getToken()); + SecurityUser parsedSecurityUser = tokenFactory.parseRefreshToken(refreshToken.token()); assertThat(parsedSecurityUser.getId()).isEqualTo(securityUser.getId()); assertThat(parsedSecurityUser.getUserPrincipal()).matches(userPrincipal -> { return userPrincipal.getType().equals(securityUser.getUserPrincipal().getType()) @@ -125,10 +125,10 @@ public class JwtTokenFactoryTest { public void testCreateAndParsePreVerificationJwtToken() { SecurityUser securityUser = createSecurityUser(); int tokenLifetime = (int) TimeUnit.MINUTES.toSeconds(30); - JwtToken preVerificationToken = tokenFactory.createPreVerificationToken(securityUser, tokenLifetime); + JwtToken preVerificationToken = tokenFactory.createMfaToken(securityUser, Authority.PRE_VERIFICATION_TOKEN, tokenLifetime); checkExpirationTime(preVerificationToken, tokenLifetime); - SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(preVerificationToken.getToken()); + SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(preVerificationToken.token()); assertThat(parsedSecurityUser.getId()).isEqualTo(securityUser.getId()); assertThat(parsedSecurityUser.getAuthority()).isEqualTo(Authority.PRE_VERIFICATION_TOKEN); assertThat(parsedSecurityUser.getTenantId()).isEqualTo(securityUser.getTenantId()); @@ -144,7 +144,7 @@ public class JwtTokenFactoryTest { SecurityUser securityUser = createSecurityUser(); String sessionId = securityUser.getSessionId(); - String accessToken = tokenFactory.createAccessJwtToken(securityUser).getToken(); + String accessToken = tokenFactory.createAccessJwtToken(securityUser).token(); securityUser = tokenFactory.parseAccessJwtToken(accessToken); assertThat(securityUser.getSessionId()).isNotNull().isEqualTo(sessionId); @@ -158,7 +158,7 @@ public class JwtTokenFactoryTest { securityUser.setId(new UserId(UUID.randomUUID())); securityUser.setEmail("tenant@thingsboard.org"); securityUser.setAuthority(Authority.TENANT_ADMIN); - securityUser.setTenantId(new TenantId(UUID.randomUUID())); + securityUser.setTenantId(TenantId.fromUUID(UUID.randomUUID())); securityUser.setEnabled(true); securityUser.setFirstName("A"); securityUser.setLastName("B"); @@ -179,7 +179,7 @@ public class JwtTokenFactoryTest { } private void checkExpirationTime(JwtToken jwtToken, int tokenLifetime) { - Claims claims = tokenFactory.parseTokenClaims(jwtToken.getToken()).getPayload(); + Claims claims = tokenFactory.parseTokenClaims(jwtToken.token()).getPayload(); 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 519c04b391..e54e36c33f 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 @@ -30,15 +30,14 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserAuthDetails; 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.event.UserCredentialsInvalidationEvent; import org.thingsboard.server.common.data.security.event.UserSessionInvalidationEvent; import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; @@ -46,6 +45,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; +import org.thingsboard.server.service.user.cache.UserAuthDetailsCache; import java.util.UUID; @@ -91,20 +91,16 @@ public class TokenOutdatingTest { UserId userId = new UserId(UUID.randomUUID()); securityUser = createMockSecurityUser(userId); - UserService userService = mock(UserService.class); + UserAuthDetailsCache userAuthDetailsCache = mock(UserAuthDetailsCache.class); User user = new User(); user.setId(userId); user.setAuthority(Authority.TENANT_ADMIN); user.setEmail("email"); - when(userService.findUserById(any(), eq(userId))).thenReturn(user); - - UserCredentials userCredentials = new UserCredentials(); - userCredentials.setEnabled(true); - when(userService.findUserCredentialsByUserId(any(), eq(userId))).thenReturn(userCredentials); + when(userAuthDetailsCache.getUserAuthDetails(any(), eq(userId))).thenReturn(new UserAuthDetails(user, true)); accessTokenAuthenticationProvider = new JwtAuthenticationProvider(tokenFactory, tokenOutdatingService); - refreshTokenAuthenticationProvider = new RefreshTokenAuthenticationProvider(tokenFactory, userService, mock(CustomerService.class), tokenOutdatingService); + refreshTokenAuthenticationProvider = new RefreshTokenAuthenticationProvider(tokenFactory, userAuthDetailsCache, mock(CustomerService.class), tokenOutdatingService); } @Test @@ -114,12 +110,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.getToken(), securityUser.getId())); + assertTrue(tokenOutdatingService.isOutdated(jwtToken.token(), securityUser.getId())); SECONDS.sleep(1); JwtToken newJwtToken = tokenFactory.createAccessJwtToken(securityUser); - assertFalse(tokenOutdatingService.isOutdated(newJwtToken.getToken(), securityUser.getId())); + assertFalse(tokenOutdatingService.isOutdated(newJwtToken.token(), securityUser.getId())); } @Test @@ -229,7 +225,7 @@ public class TokenOutdatingTest { private RawAccessJwtToken getRawJwtToken(JwtToken token) { - return new RawAccessJwtToken(token.getToken()); + return new RawAccessJwtToken(token.token()); } private SecurityUser createMockSecurityUser(UserId userId) { @@ -241,4 +237,5 @@ public class TokenOutdatingTest { securityUser.setSessionId(UUID.randomUUID().toString()); return securityUser; } + } diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProviderTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProviderTest.java new file mode 100644 index 0000000000..e9c36b1ebd --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProviderTest.java @@ -0,0 +1,174 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth.pat; + +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +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.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@DaoSqlTest +public class ApiKeyAuthenticationProviderTest extends AbstractControllerTest { + + ApiKey savedApiKey; + + @Before + public void setUp() throws Exception { + loginTenantAdmin(); + + ApiKeyInfo apiKeyInfo = constructApiKeyInfo(); + savedApiKey = doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + setApiKey(savedApiKey.getValue()); + } + + @After + public void cleanUp() throws Exception { + resetApiKey(); + doDelete("/api/apiKey/" + savedApiKey.getId()).andExpect(status().isOk()); + } + + @Test + public void testSaveEdgeWithApiKey() throws Exception { + Edge edge = constructEdge("My edge", "default"); + + Mockito.reset(tbClusterService, auditLogService); + + Edge savedEdge = doPostWithApiKey("/api/edge", edge, Edge.class); + + Assert.assertNotNull(savedEdge); + Assert.assertNotNull(savedEdge.getId()); + Assert.assertTrue(savedEdge.getCreatedTime() > 0); + Assert.assertEquals(tenantId, savedEdge.getTenantId()); + Assert.assertNotNull(savedEdge.getCustomerId()); + Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId()); + Assert.assertEquals(edge.getName(), savedEdge.getName()); + + testNotifyEdgeStateChangeEventManyTimeMsgToEdgeServiceNever(savedEdge, savedEdge.getId(), savedEdge.getId(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), + ActionType.ADDED, 2); + + savedEdge.setName("My new edge"); + doPostWithApiKey("/api/edge", savedEdge, Edge.class); + + Edge foundEdge = doGetWithApiKey("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); + Assert.assertEquals(foundEdge.getName(), savedEdge.getName()); + + testNotifyEdgeStateChangeEventManyTimeMsgToEdgeServiceNever(foundEdge, foundEdge.getId(), foundEdge.getId(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), + ActionType.UPDATED, 1); + + doDeleteWithApiKey("/api/edge/" + savedEdge.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testUnauthorizedWhenKeyDisabled() throws Exception { + ApiKeyInfo disabledApiKeyInfo = doPut("/api/apiKey/" + savedApiKey.getId().getId() + "/enabled/false", Boolean.FALSE, ApiKeyInfo.class); + Assert.assertFalse(disabledApiKeyInfo.isEnabled()); + doGetWithApiKey("/api/admin/featuresInfo").andExpect(status().isUnauthorized()); + } + + @Test + public void testUnauthorizedWhenKeyExpired() throws Exception { + ApiKeyInfo apiKeyInfo = constructApiKeyInfo(); + apiKeyInfo.setExpirationTime(System.currentTimeMillis() - 1000); + ApiKey savedApiKeyWithBad = doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + setApiKey(savedApiKeyWithBad.getValue()); + doPost("/api/apiKey", savedApiKey, ApiKeyInfo.class); + doGetWithApiKey("/api/admin/featuresInfo").andExpect(status().isUnauthorized()); + } + + @Test + public void testUnauthorizedWhenUserCredentialsDisabled() throws Exception { + User newUser = new User(); + newUser.setAuthority(Authority.TENANT_ADMIN); + newUser.setTenantId(tenantId); + newUser.setEmail("testUser" + RandomStringUtils.secure().nextAlphanumeric(10) + "@thingsboard.org"); + newUser.setFirstName("Test"); + newUser.setLastName("User"); + User savedUser = createUser(newUser, "testPassword1"); + + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); + apiKeyInfo.setDescription("Test API key for user credentials test"); + apiKeyInfo.setEnabled(true); + apiKeyInfo.setUserId(savedUser.getId()); + ApiKey testApiKey = doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + setApiKey(testApiKey.getValue()); + + doGetWithApiKey("/api/admin/repositorySettings/exists").andExpect(status().isOk()); + + doPost("/api/user/" + savedUser.getId().getId() + "/userCredentialsEnabled?userCredentialsEnabled=false").andExpect(status().isOk()); + + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> doGetWithApiKey("/api/admin/repositorySettings/exists").andExpect(status().isUnauthorized())); + + resetApiKey(); + doDelete("/api/apiKey/" + testApiKey.getId()).andExpect(status().isOk()); + loginSysAdmin(); + doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); + } + + @Test + public void testUnauthorizedWhenUserDeleted() throws Exception { + User newUser = new User(); + newUser.setAuthority(Authority.TENANT_ADMIN); + newUser.setTenantId(tenantId); + newUser.setEmail("testUser" + RandomStringUtils.secure().nextAlphanumeric(10) + "@thingsboard.org"); + newUser.setFirstName("Test"); + newUser.setLastName("User"); + User savedUser = createUser(newUser, "testPassword1"); + + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); + apiKeyInfo.setDescription("Test API key for user deletion test"); + apiKeyInfo.setEnabled(true); + apiKeyInfo.setUserId(savedUser.getId()); + ApiKey testApiKey = doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + setApiKey(testApiKey.getValue()); + + doGetWithApiKey("/api/admin/repositorySettings/exists").andExpect(status().isOk()); + + loginSysAdmin(); + doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); + + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> doGetWithApiKey("/api/admin/repositorySettings/exists").andExpect(status().isUnauthorized())); + } + + private ApiKeyInfo constructApiKeyInfo() { + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); + apiKeyInfo.setDescription("New API key description"); + apiKeyInfo.setEnabled(true); + apiKeyInfo.setUserId(tenantAdminUserId); + return apiKeyInfo; + } + +} 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 e19efdf7e0..9630dacdd4 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 @@ -48,10 +48,9 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; @@ -627,9 +626,8 @@ public class ExportImportServiceSqlTest extends AbstractControllerTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); diff --git a/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java b/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java index 7496eaf945..0305d031b8 100644 --- a/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.google.common.collect.Streams; +import org.apache.commons.lang3.tuple.Pair; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -45,17 +46,21 @@ import org.thingsboard.server.common.data.TbResource; 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.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.alarm.rule.condition.SimpleAlarmCondition; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; @@ -244,7 +249,7 @@ public class VersionControlTest extends AbstractControllerTest { DeviceProfile deviceProfile = createDeviceProfile(null, null, "Device profile v1.0"); OtaPackage firmware = createOtaPackage(tenantId1, deviceProfile.getId(), OtaPackageType.FIRMWARE); OtaPackage software = createOtaPackage(tenantId1, deviceProfile.getId(), OtaPackageType.SOFTWARE); - Device device = createDevice(null, deviceProfile.getId(), "Device v1.0", "test1", newDevice -> { + Device device = createDevice(deviceProfile.getId(), "Device v1.0", "test1", newDevice -> { newDevice.setFirmwareId(firmware.getId()); newDevice.setSoftwareId(software.getId()); }); @@ -267,7 +272,7 @@ public class VersionControlTest extends AbstractControllerTest { createVersion("profiles", EntityType.DEVICE_PROFILE); OtaPackage firmware = createOtaPackage(tenantId1, deviceProfile.getId(), OtaPackageType.FIRMWARE); OtaPackage software = createOtaPackage(tenantId1, deviceProfile.getId(), OtaPackageType.SOFTWARE); - Device device = createDevice(null, deviceProfile.getId(), "Device of tenant 1", "test1", newDevice -> { + Device device = createDevice(deviceProfile.getId(), "Device of tenant 1", "test1", newDevice -> { newDevice.setFirmwareId(firmware.getId()); newDevice.setSoftwareId(software.getId()); }); @@ -307,6 +312,54 @@ public class VersionControlTest extends AbstractControllerTest { checkImportedOtaPackageData(software, importedSoftwareOta); } + @Test + public void testDeviceVc_withAlarmRules_betweenTenants() throws Exception { + DeviceProfile deviceProfile = createDeviceProfile(null, null, "Device profile of tenant 1"); + Dashboard dashboard = createDashboard(null, "Mobile dashboard"); + createAlarmRule(deviceProfile.getId(), "Profile alarm rule", dashboard.getId()); + Device device = createDevice(deviceProfile.getId(), "Device of tenant 1", "test1"); + createAlarmRule(device.getId(), "Device alarm rule", dashboard.getId()); + String version = createVersion("devices, profiles and dashboards", EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.DASHBOARD); + + loginTenant2(); + Map result = loadVersion(version, config -> { + config.setLoadCredentials(false); + }, EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.DASHBOARD); + assertThat(result.get(EntityType.DEVICE).getCreated()).isEqualTo(1); + assertThat(result.get(EntityType.DEVICE_PROFILE).getCreated()).isEqualTo(1); + assertThat(result.get(EntityType.DASHBOARD).getCreated()).isEqualTo(1); + + Device importedDevice = findDevice(device.getName()); + checkImportedEntity(tenantId1, device, tenantId2, importedDevice); + checkImportedDeviceData(device, importedDevice); + + DeviceProfile importedDeviceProfile = findDeviceProfile(deviceProfile.getName()); + checkImportedEntity(tenantId1, deviceProfile, tenantId2, importedDeviceProfile); + checkImportedDeviceProfileData(deviceProfile, importedDeviceProfile); + assertThat(importedDevice.getDeviceProfileId()).isEqualTo(importedDeviceProfile.getId()); + + Dashboard importedDashboard = findDashboard(dashboard.getName()); + checkImportedEntity(tenantId1, dashboard, tenantId2, importedDashboard); + checkImportedDashboardData(dashboard, importedDashboard); + + getCalculatedFields(CalculatedFieldType.ALARM, EntityType.DEVICE_PROFILE, + List.of(importedDeviceProfile.getUuidId()), null).forEach(alarmRuleCf -> { + assertThat(alarmRuleCf.getName()).isEqualTo("Profile alarm rule"); + AlarmCalculatedFieldConfiguration config = (AlarmCalculatedFieldConfiguration) alarmRuleCf.getConfiguration(); + config.getAllRules().map(Pair::getValue).forEach(alarmRule -> { + assertThat(alarmRule.getDashboardId()).isEqualTo(importedDashboard.getId()); + }); + }); + getCalculatedFields(CalculatedFieldType.ALARM, EntityType.DEVICE_PROFILE, + List.of(importedDevice.getUuidId()), null).forEach(alarmRuleCf -> { + assertThat(alarmRuleCf.getName()).isEqualTo("Device alarm rule"); + AlarmCalculatedFieldConfiguration config = (AlarmCalculatedFieldConfiguration) alarmRuleCf.getConfiguration(); + config.getAllRules().map(Pair::getValue).forEach(alarmRule -> { + assertThat(alarmRule.getDashboardId()).isEqualTo(importedDashboard.getId()); + }); + }); + } + @Test public void testDashboardVc_betweenTenants() throws Exception { Dashboard dashboard = createDashboard(null, "Dashboard of tenant 1"); @@ -369,42 +422,42 @@ public class VersionControlTest extends AbstractControllerTest { String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c"; String unknownUuid = "ea0dc8b0-3d85-11ed-9200-77fc04fa14fa"; String entityAliases = "{\n" + - "\"" + aliasId + "\": {\n" + - "\"alias\": \"assets\",\n" + - "\"filter\": {\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" + - "}"; + "\"" + aliasId + "\": {\n" + + "\"alias\": \"assets\",\n" + + "\"filter\": {\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" + - "}"; + " \"" + 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)); @@ -500,11 +553,12 @@ public class VersionControlTest extends AbstractControllerTest { 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 };"); + generatorNodeConfig.setJsScript(""" + var msg = { temp: 42, humidity: 77 }; + var metadata = { data: 40 }; + var msgType = "POST_TELEMETRY_REQUEST"; + var someUuid = "%s"; + return { msg: msg, metadata: metadata, msgType: msgType };""".formatted(someUuid)); generatorNode.setConfiguration(JacksonUtil.valueToTree(generatorNodeConfig)); nodes.add(generatorNode); metaData.setNodes(nodes); @@ -528,7 +582,7 @@ public class VersionControlTest extends AbstractControllerTest { @Test public void testVcWithRelations_betweenTenants() throws Exception { Asset asset = createAsset(null, null, "Asset 1"); - Device device = createDevice(null, null, "Device 1", "test1"); + Device device = createDevice("Device 1", "test1"); EntityRelation relation = createRelation(asset.getId(), device.getId()); String versionId = createVersion("assets and devices", EntityType.ASSET, EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE); @@ -554,11 +608,11 @@ public class VersionControlTest extends AbstractControllerTest { @Test public void testVcWithRelations_sameTenant() throws Exception { Asset asset = createAsset(null, null, "Asset 1"); - Device device1 = createDevice(null, null, "Device 1", "test1"); + Device device1 = createDevice("Device 1", "test1"); EntityRelation relation1 = createRelation(device1.getId(), asset.getId()); String versionId = createVersion("assets", EntityType.ASSET); - Device device2 = createDevice(null, null, "Device 2", "test2"); + Device device2 = createDevice("Device 2", "test2"); EntityRelation relation2 = createRelation(device2.getId(), asset.getId()); List relations = findRelationsByTo(asset.getId()); assertThat(relations).contains(relation1, relation2); @@ -591,7 +645,7 @@ public class VersionControlTest extends AbstractControllerTest { @Test public void testVcWithCalculatedFields_betweenTenants() throws Exception { Asset asset = createAsset(null, null, "Asset 1"); - Device device = createDevice(null, null, "Device 1", "test1"); + Device device = createDevice("Device 1", "test1"); CalculatedField calculatedField = createCalculatedField("CalculatedField1", device.getId(), asset.getId()); String versionId = createVersion("calculated fields of asset and device", EntityType.ASSET, EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE); @@ -617,7 +671,7 @@ public class VersionControlTest extends AbstractControllerTest { @Test public void testVcWithReferencedCalculatedFields_betweenTenants() throws Exception { Asset asset = createAsset(null, null, "Asset 1"); - Device device = createDevice(null, null, "Device 1", "test1"); + Device device = createDevice("Device 1", "test1"); CalculatedField deviceCalculatedField = createCalculatedField("CalculatedField1", device.getId(), asset.getId()); CalculatedField assetCalculatedField = createCalculatedField("CalculatedField2", asset.getId(), device.getId()); String versionId = createVersion("calculated fields of asset and device", EntityType.ASSET, EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE); @@ -638,7 +692,9 @@ public class VersionControlTest extends AbstractControllerTest { assertThat(importedField.getName()).isEqualTo(deviceCalculatedField.getName()); assertThat(importedField.getType()).isEqualTo(deviceCalculatedField.getType()); assertThat(importedField.getId()).isNotEqualTo(deviceCalculatedField.getId()); - assertThat(importedField.getConfiguration().getArguments().get("T").getRefEntityId()).isEqualTo(importedAsset.getId()); + assertThat(importedField.getConfiguration()).isInstanceOf(SimpleCalculatedFieldConfiguration.class); + SimpleCalculatedFieldConfiguration simpleCfg = (SimpleCalculatedFieldConfiguration) importedField.getConfiguration(); + assertThat(simpleCfg.getArguments().get("T").getRefEntityId()).isEqualTo(importedAsset.getId()); }); List importedAssetCalculatedFields = findCalculatedFieldsByEntityId(importedAsset.getId()); @@ -647,7 +703,9 @@ public class VersionControlTest extends AbstractControllerTest { assertThat(importedField.getName()).isEqualTo(assetCalculatedField.getName()); assertThat(importedField.getType()).isEqualTo(assetCalculatedField.getType()); assertThat(importedField.getId()).isNotEqualTo(assetCalculatedField.getId()); - assertThat(importedField.getConfiguration().getArguments().get("T").getRefEntityId()).isEqualTo(importedDevice.getId()); + assertThat(importedField.getConfiguration()).isInstanceOf(SimpleCalculatedFieldConfiguration.class); + SimpleCalculatedFieldConfiguration simpleCfg = (SimpleCalculatedFieldConfiguration) importedField.getConfiguration(); + assertThat(simpleCfg.getArguments().get("T").getRefEntityId()).isEqualTo(importedDevice.getId()); }); } @@ -907,9 +965,8 @@ public class VersionControlTest extends AbstractControllerTest { login(tenantAdmin2.getEmail(), tenantAdmin2.getEmail()); } - private Device createDevice(CustomerId customerId, DeviceProfileId deviceProfileId, String name, String accessToken, Consumer... modifiers) { + private Device createDevice(DeviceProfileId deviceProfileId, String name, String accessToken, Consumer... modifiers) { Device device = new Device(); - device.setCustomerId(customerId); device.setName(name); device.setLabel("lbl"); device.setDeviceProfileId(deviceProfileId); @@ -1016,20 +1073,21 @@ public class VersionControlTest extends AbstractControllerTest { protected Dashboard createDashboard(CustomerId customerId, String name, AssetId assetForEntityAlias) { Dashboard dashboard = createDashboard(customerId, name); - String entityAliases = "{\n" + - "\t\"23c4185d-1497-9457-30b2-6d91e69a5b2c\": {\n" + - "\t\t\"alias\": \"assets\",\n" + - "\t\t\"filter\": {\n" + - "\t\t\t\"entityList\": [\n" + - "\t\t\t\t\"" + assetForEntityAlias.getId().toString() + "\"\n" + - "\t\t\t],\n" + - "\t\t\t\"entityType\": \"ASSET\",\n" + - "\t\t\t\"resolveMultiple\": true,\n" + - "\t\t\t\"type\": \"entityList\"\n" + - "\t\t},\n" + - "\t\t\"id\": \"23c4185d-1497-9457-30b2-6d91e69a5b2c\"\n" + - "\t}\n" + - "}"; + String entityAliases = """ + { + "23c4185d-1497-9457-30b2-6d91e69a5b2c": { + "alias": "assets", + "filter": { + "entityList": [ + "%s" + ], + "entityType": "ASSET", + "resolveMultiple": true, + "type": "entityList" + }, + "id": "23c4185d-1497-9457-30b2-6d91e69a5b2c" + } + }""".formatted(assetForEntityAlias.getId().toString()); ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode(); dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases)); dashboardConfiguration.set("description", new TextNode("hallo")); @@ -1132,6 +1190,33 @@ public class VersionControlTest extends AbstractControllerTest { return doPost("/api/calculatedField", calculatedField, CalculatedField.class); } + private CalculatedField createAlarmRule(EntityId entityId, String alarmType, DashboardId mobileDashboardId) { + Argument temperatureArgument = new Argument(); + temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + temperatureArgument.setDefaultValue("0"); + Map arguments = Map.of( + "temperature", temperatureArgument + ); + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(entityId); + calculatedField.setName(alarmType); + calculatedField.setType(CalculatedFieldType.ALARM); + AlarmCalculatedFieldConfiguration configuration = new AlarmCalculatedFieldConfiguration(); + configuration.setArguments(arguments); + SimpleAlarmCondition createCondition = new SimpleAlarmCondition(); + createCondition.setExpression(new TbelAlarmConditionExpression("return temperature >= 50;")); + configuration.setCreateRules(Map.of( + AlarmSeverity.CRITICAL, new AlarmRule(createCondition, "", mobileDashboardId) + )); + SimpleAlarmCondition clearCondition = new SimpleAlarmCondition(); + clearCondition.setExpression(new TbelAlarmConditionExpression("return temperature < 50;")); + configuration.setClearRule(new AlarmRule(clearCondition, "", mobileDashboardId)); + calculatedField.setConfiguration(configuration); + calculatedField.setDebugSettings(DebugSettings.all()); + return saveCalculatedField(calculatedField); + } + private CalculatedFieldConfiguration getCalculatedFieldConfig(EntityId referencedEntityId) { SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); @@ -1144,9 +1229,8 @@ public class VersionControlTest extends AbstractControllerTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); diff --git a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java index dcfe9bd2bd..f278153ed8 100644 --- a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java @@ -162,7 +162,7 @@ class DefaultTelemetrySubscriptionServiceTest { apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED); lenient().when(apiUsageStateService.getApiUsageState(tenantId)).thenReturn(apiUsageState); - lenient().when(partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId)).thenReturn(tpi); + lenient().when(partitionService.resolve(eq(ServiceType.TB_CORE), eq(tenantId), any())).thenReturn(tpi); lenient().when(tsService.save(tenantId, entityId, sampleTimeseries, sampleTtl)).thenReturn(immediateFuture(TimeseriesSaveResult.of(sampleTimeseries.size(), listOfNNumbers(sampleTimeseries.size())))); lenient().when(tsService.saveWithoutLatest(tenantId, entityId, sampleTimeseries, sampleTtl)).thenReturn(immediateFuture(TimeseriesSaveResult.of(sampleTimeseries.size(), null))); @@ -310,8 +310,6 @@ class DefaultTelemetrySubscriptionServiceTest { given(tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId)).willReturn(immediateFuture(List.of(entityView))); // mock that save latest call for entity view is successful given(tsService.saveLatest(tenantId, entityView.getId(), sampleTimeseries)).willReturn(immediateFuture(TimeseriesSaveResult.of(sampleTimeseries.size(), listOfNNumbers(sampleTimeseries.size())))); - // mock TPI for entity view - given(partitionService.resolve(ServiceType.TB_CORE, tenantId, entityView.getId())).willReturn(tpi); var request = TimeseriesSaveRequest.builder() .tenantId(tenantId) @@ -373,8 +371,6 @@ class DefaultTelemetrySubscriptionServiceTest { lenient().when(tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId)).thenReturn(immediateFuture(List.of(entityView))); // mock that save latest call for entity view is successful lenient().when(tsService.saveLatest(tenantId, entityView.getId(), sampleTimeseries)).thenReturn(immediateFuture(TimeseriesSaveResult.of(sampleTimeseries.size(), listOfNNumbers(sampleTimeseries.size())))); - // mock TPI for entity view - lenient().when(partitionService.resolve(ServiceType.TB_CORE, tenantId, entityView.getId())).thenReturn(tpi); var request = TimeseriesSaveRequest.builder() .tenantId(tenantId) 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 8d0f0f2af0..f3a3212e67 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 @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import jnr.ffi.annotations.In; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.awaitility.core.ConditionTimeoutException; @@ -125,11 +126,11 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfil import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources; import static org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest.CLIENT_LWM2M_SETTINGS_19; -@TestPropertySource(properties = { - "transport.lwm2m.enabled=true", -}) @Slf4j @DaoSqlTest +@TestPropertySource(properties = { + "transport.lwm2m.enabled=true" +}) public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportIntegrationTest { @SpyBean @@ -164,9 +165,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte } public static final Integer shortServerId = 123; - public static final Integer shortServerIdBs0 = 0; - public static final int serverId = 1; - public static final int serverIdBs = 0; public static final String COAP = "coap://"; public static final String COAPS = "coaps://"; @@ -336,7 +334,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte @After public void after() throws Exception { - this.clientDestroy(true); + this.clientDestroy(); if (executor != null && !executor.isShutdown()) { executor.shutdownNow(); } @@ -585,7 +583,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte public void createNewClient(Security security, Security securityBs, boolean isRpc, String endpoint, Integer clientDtlsCidLength, boolean queueMode, String deviceIdStr, Integer value3_0_9) throws Exception { - this.clientDestroy(false); + this.clientDestroy(); lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources); int clientPort = PortFinder.findAvailableUdpPort(); @@ -671,13 +669,29 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte } - private void clientDestroy(boolean isAfter) { + private void clientDestroy() { try { if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) { - if (isAfter) { - sendObserveCancelAllWithAwait(lwM2MTestClient.getDeviceIdStr()); - awaitDeleteDevice(lwM2MTestClient.getDeviceIdStr()); + boolean serverAlive = false; + List ports = List.of(LWM2M_PORT, LWM2MS_PORT, LWM2MS_BOOTSTRAP_PORT, LWM2MS_BOOTSTRAP_PORT); + for (Integer port : ports) { + if (!PortFinder.isUDPPortAvailable(port)) { + log.debug("Port {} is busy — CoAP server still active.", port); + serverAlive = true; + break; + } + } + if (serverAlive) { + try { + sendObserveCancelAllWithAwait(lwM2MTestClient.getDeviceIdStr()); + awaitDeleteDevice(lwM2MTestClient.getDeviceIdStr()); + } catch (Exception e) { + log.warn("Failed to cleanup LwM2M observations before destroy: {}", e.getMessage()); + } + } else { + log.info("No active CoAP server found on ports 5685–5688. Skipping observe cleanup."); } + lwM2MTestClient.destroy(); } } catch (Exception e) { @@ -725,10 +739,10 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte return bootstrap; } - private AbstractLwM2MBootstrapServerCredential getBootstrapServerCredentialNoSec(boolean isBootstrap) { + protected AbstractLwM2MBootstrapServerCredential getBootstrapServerCredentialNoSec(boolean isBootstrap) { AbstractLwM2MBootstrapServerCredential bootstrapServerCredential = new NoSecLwM2MBootstrapServerCredential(); bootstrapServerCredential.setServerPublicKey(""); - bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId); + bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId); bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST); bootstrapServerCredential.setPort(isBootstrap ? LWM2M_BOOTSTRAP_PORT : LWM2M_PORT); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 1a4af66d20..a307d53054 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -24,8 +24,6 @@ public class Lwm2mTestHelper { public static final int TEMPERATURE_SENSOR = 3303; // Ids in Client - public static final int OBJECT_ID_0 = 0; - public static final int OBJECT_ID_1 = 1; public static final int OBJECT_INSTANCE_ID_0 = 0; public static final int OBJECT_INSTANCE_ID_1 = 1; public static final int OBJECT_INSTANCE_ID_2 = 2; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 908c2d52d3..c9c39bc701 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -33,7 +33,6 @@ import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.resource.DummyInstanceEnabler; -import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter; @@ -68,6 +67,9 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; @@ -77,6 +79,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static org.awaitility.Awaitility.await; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; @@ -90,10 +93,7 @@ import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder; import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS; -import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.serverId; -import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.serverIdBs; import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId; -import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerIdBs0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE; @@ -141,11 +141,11 @@ public class LwM2MTestClient { private Map clientDtlsCid; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mClientContext clientContext; - private LwM2mTemperatureSensor lwM2mTemperatureSensor12; + private LwM2mTemperatureSensor lwM2MTemperatureSensor12; private String deviceIdStr; private int clientPort; - public void init(Security security, Security securityBs, int clientPort, boolean isRpc, + public void init(Security securityLwm2m, Security securityBs, int clientPort, boolean isRpc, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode, boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException { @@ -154,55 +154,33 @@ public class LwM2MTestClient { this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler; this.clientContext = clientContext; - List models = ObjectLoader.loadAllDefault(); - for (String resourceName : lwm2mClientResources) { - models.addAll(ObjectLoader.loadDdfFile(LwM2MTestClient.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName), resourceName)); + ObjectsInitializer initializer = createFreshInitializer(); + + // SECURITY + if (securityLwm2m != null && securityLwm2m.getId() != null) { + forceNullSecurityId(securityLwm2m); } - if (this.modelResources != null) { - List modelsRes = new ArrayList<>(); - for (String resourceName : this.modelResources) { - modelsRes.addAll(ObjectLoader.loadDdfFile(LwM2MTestClient.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName), resourceName)); - } - Set idsToRemove = new HashSet<>(); - for (ObjectModel model : modelsRes) { - idsToRemove.add(model.id); - } - models.removeIf(model -> idsToRemove.contains(model.id)); - models.addAll(modelsRes); + if (securityBs != null && securityBs.getId() != null) { + forceNullSecurityId(securityBs); } - - LwM2mModel model = new StaticModel(models); - ObjectsInitializer initializer = new ObjectsInitializer(model); - if (securityBs != null && security != null) { - // SECURITY - security.setId(serverId); - securityBs.setId(serverIdBs); - LwM2mInstanceEnabler[] instances = new LwM2mInstanceEnabler[]{securityBs, security}; - initializer.setClassForObject(SECURITY, Security.class); - initializer.setInstancesForObject(SECURITY, instances); - // SERVER - Server lwm2mServer = new Server(shortServerId, TimeUnit.MINUTES.toSeconds(60)); - lwm2mServer.setId(serverId); - Server serverBs = new Server(shortServerIdBs0, TimeUnit.MINUTES.toSeconds(60)); - serverBs.setId(serverIdBs); - instances = new LwM2mInstanceEnabler[]{serverBs, lwm2mServer}; - initializer.setClassForObject(SERVER, Server.class); - initializer.setInstancesForObject(SERVER, instances); + if (securityBs != null && securityLwm2m != null) { + log.warn("Security Both: securityBs: [{}] and security Lwm2m [{}]", securityBs.getId(), securityLwm2m.getId()); + initializer.setInstancesForObject(SECURITY, securityBs, securityLwm2m); } else if (securityBs != null) { - // SECURITY + log.warn("Security BS only: securityBs: [{}] ", securityBs.getId()); initializer.setInstancesForObject(SECURITY, securityBs); - // SERVER - initializer.setClassForObject(SERVER, Server.class); - } else { + } else if (securityLwm2m != null) { // SECURITY - initializer.setInstancesForObject(SECURITY, security); - // SERVER - Server lwm2mServer = new Server(shortServerId, TimeUnit.MINUTES.toSeconds(60)); - lwm2mServer.setId(serverId); - initializer.setInstancesForObject(SERVER, lwm2mServer); + log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId()); + initializer.setInstancesForObject(SECURITY, securityLwm2m); } - + // SERVER + Server serverLwm2m = new Server(shortServerId, TimeUnit.MINUTES.toSeconds(60)); + initializer.setInstancesForObject(SERVER, serverLwm2m); + // DEVICE initializer.setInstancesForObject(DEVICE, lwM2MDevice = new SimpleLwM2MDevice(executor, value3_0_9)); + + // OTHER t initializer.setInstancesForObject(FIRMWARE, fwLwM2MDevice = new FwLwM2MDevice()); initializer.setInstancesForObject(SOFTWARE_MANAGEMENT, swLwM2MDevice = new SwLwM2MDevice()); initializer.setClassForObject(ACCESS_CONTROL, DummyInstanceEnabler.class); @@ -212,8 +190,8 @@ public class LwM2MTestClient { locationParams.getPos(); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0); - lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); - initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12); + lwM2MTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); + initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2MTemperatureSensor12); List enablers = initializer.createAll(); @@ -234,7 +212,9 @@ public class LwM2MTestClient { builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid)); return builder; - }; + } + + ; }; } }; @@ -449,28 +429,45 @@ public class LwM2MTestClient { public void destroy() { if (leshanClient != null) { - leshanClient.destroy(true); - } - if (lwM2MDevice != null) { - lwM2MDevice.destroy(); - } - if (fwLwM2MDevice != null) { - fwLwM2MDevice.destroy(); - } - if (swLwM2MDevice != null) { - swLwM2MDevice.destroy(); - } - if (lwM2MBinaryAppDataContainer != null) { - lwM2MBinaryAppDataContainer.destroy(); - } - if (lwM2MTemperatureSensor != null) { - lwM2MTemperatureSensor.destroy(); + try { + leshanClient.destroy(true); + } catch (Exception e) { + log.warn("Failed to destroy Leshan client", e); + } finally { + leshanClient = null; + } } - if (lwM2mTemperatureSensor12 != null) { - lwM2mTemperatureSensor12.destroy(); + + // ThingsBoard custom LwM2M objects + destroySafe(lwM2MDevice); + destroySafe(fwLwM2MDevice); + destroySafe(swLwM2MDevice); + destroySafe(lwM2MBinaryAppDataContainer); + destroySafe(lwM2MTemperatureSensor); + destroySafe(lwM2MTemperatureSensor12); + + lwM2MDevice = null; + fwLwM2MDevice = null; + swLwM2MDevice = null; + lwM2MBinaryAppDataContainer = null; + lwM2MTemperatureSensor = null; + lwM2MTemperatureSensor12 = null; + } + + + private void destroySafe(Object obj) { + if (obj == null) return; + try { + Method destroy = obj.getClass().getMethod("destroy"); + destroy.invoke(obj); + } catch (NoSuchMethodException e) { + // не має destroy() — ігноруємо + } catch (Exception e) { + log.warn("Failed to destroy {}", obj.getClass().getSimpleName(), e); } } + public void start(boolean isStartLw) { if (leshanClient != null) { if (clientPort > 0) { @@ -483,7 +480,7 @@ public class LwM2MTestClient { if (isStartLw) { this.awaitClientAfterStartConnectLw(); } - lwM2mTemperatureSensor12.setLeshanClient(leshanClient); + lwM2MTemperatureSensor12.setLeshanClient(leshanClient); fwLwM2MDevice.setLeshanClient(leshanClient); } } @@ -504,4 +501,60 @@ public class LwM2MTestClient { LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(endpoint); Mockito.doAnswer(invocationOnMock -> null).when(defaultLwM2mUplinkMsgHandlerTest).initAttributes(lwM2MClient, true); } + + private ObjectsInitializer createFreshInitializer() { + List models = new ArrayList<>(ObjectLoader.loadAllDefault()); + for (String resourceName : lwm2mClientResources) { + try (InputStream in = LwM2MTestClient.class.getClassLoader() + .getResourceAsStream("lwm2m/" + resourceName)) { + models.addAll(ObjectLoader.loadDdfFile(in, resourceName)); + } catch (IOException | InvalidDDFFileException e) { + log.warn("Failed to load resource {}", resourceName, e); + } + } + if (this.modelResources != null) { + List modelsRes = new ArrayList<>(); + for (String resourceName : this.modelResources) { + try (InputStream in = LwM2MTestClient.class.getClassLoader() + .getResourceAsStream("lwm2m/" + resourceName)) { + modelsRes.addAll(ObjectLoader.loadDdfFile(in, resourceName)); + } catch (IOException | InvalidDDFFileException e) { + log.warn("Failed to load resource {}", resourceName, e); + } + } + Set idsToRemove = modelsRes.stream() + .map(m -> m.id) + .collect(Collectors.toSet()); + models.removeIf(m -> idsToRemove.contains(m.id)); + models.addAll(modelsRes); + } + LwM2mModel model = new StaticModel(models); + return new ObjectsInitializer(model); + } + + private void forceNullSecurityId(Security security) { + if (security == null) { + return; + } + try { + Field field = security.getClass().getDeclaredField("id"); + field.setAccessible(true); + field.set(security, null); + log.info("[forceNullSecurityId] Set id=null for {}", security); + } catch (NoSuchFieldException e) { + try { + //(SecurityObjectInstance) + Field field = security.getClass().getSuperclass().getDeclaredField("id"); + field.setAccessible(true); + field.set(security, null); + log.info("[forceNullSecurityId] Set id=null for {} (via superclass)", security); + } catch (Exception ex) { + log.error("[forceNullSecurityId] Field 'id' not found for {}", security.getClass(), ex); + } + } catch (Exception e) { + log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e); + } + } + } + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index ad02b8f8fc..3c4bdcd3e7 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -48,12 +48,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; +import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_ID_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_ID_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; @@ -116,6 +115,10 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg initRpc(0); } else if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationReadCollectedValueTest")) { initRpc(3303); + } else if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationInitReadCompositeAllTest")) { + initRpc(2); + }else if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationInitReadCompositeByObjectTest")) { + initRpc(3); } else { initRpc(1); } @@ -141,10 +144,10 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg }); } }); - String ver_Id_0 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_0).version; - String ver_Id_1 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_1).version; - objectIdVer_0 = "/" + OBJECT_ID_0 + "_" + ver_Id_0; - objectIdVer_1 = "/" + OBJECT_ID_1 + "_" + ver_Id_1; + String ver_Id_0 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(SECURITY).version; + String ver_Id_1 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(SERVER).version; + objectIdVer_0 = "/" + SECURITY + "_" + ver_Id_0; + objectIdVer_1 = "/" + SERVER + "_" + ver_Id_1; objectIdVer_2 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).startsWith("/" + ACCESS_CONTROL)).findFirst().get(); objectIdVer_3 = (String) expectedObjectIdVers.stream().filter(PREDICATE_3).findFirst().get(); objectIdVer_19 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).startsWith("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); @@ -222,10 +225,67 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg " ],\n" + " \"attributeLwm2m\": {}\n" + " }"; + String INIT_READ_TELEMETRY_ATTRIBUTE_AS_OBSERVE_STRATEGY_ALL = + " {\n" + + " \"keyName\": {\n" + + " \"/3_1.2/0/9\": \"batteryLevel\",\n" + + " \"/3_1.2/0/20\": \"batteryStatus\",\n" + + " \"/19_1.1/0/2\": \"dataCreationTime\",\n" + + " \"/5_1.2/0/6\": \"pkgname\",\n" + + " \"/5_1.2/0/7\": \"pkgversion\",\n" + + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/3_1.2/0/20\"\n" + + " ],\n" + + " \"attribute\": [\n" + + " \"/5_1.2/0/6\",\n" + + " \"/5_1.2/0/7\"\n" + + " ],\n" + + " \"telemetry\": [\n" + + " \"/3_1.2/0/9\",\n" + + " \"/3_1.2/0/20\",\n" + + " \"/5_1.2/0/9\",\n" + + " \"/19_1.1/0/2\"\n" + + " ],\n" + + " \"attributeLwm2m\": {},\n" + + " \"initAttrTelAsObsStrategy\": true,\n" + + " \"observeStrategy\": 1\n" + + " }"; + String INIT_READ_TELEMETRY_ATTRIBUTE_AS_OBSERVE_STRATEGY_BY_OBJECT = + " {\n" + + " \"keyName\": {\n" + + " \"/3_1.2/0/9\": \"batteryLevel\",\n" + + " \"/3_1.2/0/20\": \"batteryStatus\",\n" + + " \"/19_1.1/0/2\": \"dataCreationTime\",\n" + + " \"/5_1.2/0/6\": \"pkgname\",\n" + + " \"/5_1.2/0/7\": \"pkgversion\",\n" + + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/3_1.2/0/9\"\n" + + " ],\n" + + " \"attribute\": [\n" + + " \"/5_1.2/0/6\",\n" + + " \"/5_1.2/0/7\"\n" + + " ],\n" + + " \"telemetry\": [\n" + + " \"/3_1.2/0/9\",\n" + + " \"/3_1.2/0/20\",\n" + + " \"/5_1.2/0/9\",\n" + + " \"/19_1.1/0/2\"\n" + + " ],\n" + + " \"attributeLwm2m\": {},\n" + + " \"initAttrTelAsObsStrategy\": true,\n" + + " \"observeStrategy\": 2\n" + + " }"; + CONFIG_PROFILE_WITH_PARAMS_RPC = switch (typeConfigProfile) { case 0 -> ATTRIBUTES_TELEMETRY_WITH_PARAMS_RPC_WITH_OBSERVE; case 1 -> TELEMETRY_WITH_PARAMS_RPC_WITHOUT_OBSERVE; + case 2 -> INIT_READ_TELEMETRY_ATTRIBUTE_AS_OBSERVE_STRATEGY_ALL; + case 3 -> INIT_READ_TELEMETRY_ATTRIBUTE_AS_OBSERVE_STRATEGY_BY_OBJECT; case 3303 -> TELEMETRY_WITH_PARAMS_RPC_COLLECTED_VALUE; default -> throw new IllegalStateException("Unexpected value: " + typeConfigProfile); }; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationInitReadCompositeAllTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationInitReadCompositeAllTest.java new file mode 100644 index 0000000000..ea23d2a61d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationInitReadCompositeAllTest.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.rpc.sql; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_6; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_7; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; + +@Slf4j +public class RpcLwm2mIntegrationInitReadCompositeAllTest extends AbstractRpcLwM2MIntegrationTest { + + /** + " \"/3_1.2/0/9\": \"batteryLevel\", - Telemetry + " \"/3_1.2/0/20\": \"batteryStatus\" - Observe, Telemetry + " \"/5_1.2/0/6\": \"pkgname\" - Attributes + " \"/5_1.2/0/7\": \"pkgversion\" - Attributes + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\"\ - Telemetry + " \"/19_1.1/0/2\": \"dataCreationTime\" - Telemetry + * "observeStrategy": 1 + */ + @Test + public void testInitReadCompositeAsObserveStrategyCompositeAll() throws Exception { + + + // init test + String RESOURCE_3_9 = "batteryLevel"; + String RESOURCE_3_20 = "batteryStatus"; + String RESOURCE_5_6 = "pkgname"; + String RESOURCE_5_7 = "pkgversion"; + String RESOURCE_5_9 = "firmwareUpdateDeliveryMethod"; + String RESOURCE_19_2 = "dataCreationTime"; + + String idVwr_3_0_20 = idVer_3_0_9 = objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + 20; + String IdVer5_0_6 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_6; + String IdVer5_0_7 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_7; + String IdVer5_0_9 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_9; + String idVer_19_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2; + countUpdateAttrTelemetryResource(idVer_3_0_9); + countUpdateAttrTelemetryResource(idVwr_3_0_20); + countUpdateAttrTelemetryResource(IdVer5_0_6); + countUpdateAttrTelemetryResource(IdVer5_0_7); + countUpdateAttrTelemetryResource(IdVer5_0_9); + countUpdateAttrTelemetryResource(idVer_19_0_2); + + + AtomicReference actualValues = new AtomicReference<>(); + await().atMost(40, SECONDS).until(() -> { + actualValues.set(doGetAsync( + "/api/plugins/telemetry/DEVICE/" + lwM2MTestClient.getDeviceIdStr() + "/values/timeseries?keys=" + + RESOURCE_3_9 + "," + RESOURCE_3_20 + "," + RESOURCE_5_9 + "," + RESOURCE_19_2, ObjectNode.class)); + return actualValues.get() != null && !actualValues.get().isEmpty() + && !actualValues.get().get(RESOURCE_3_9).isEmpty() + && !actualValues.get().get(RESOURCE_3_20).isEmpty() + && !actualValues.get().get(RESOURCE_5_9).isEmpty() + && !actualValues.get().get(RESOURCE_19_2).isEmpty(); + }); + + AtomicReference> actualKeys =new AtomicReference<>(); + await().atMost(40, SECONDS).until(() -> { + actualKeys.set(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + lwM2MTestClient.getDeviceIdStr() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() { + })); + return actualKeys.get() != null && !actualKeys.get().isEmpty() && !actualKeys.get().isEmpty() + && actualKeys.get().contains(RESOURCE_5_6)&& actualKeys.get().contains(RESOURCE_5_7); + }); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationInitReadCompositeByObjectTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationInitReadCompositeByObjectTest.java new file mode 100644 index 0000000000..6c5f04da77 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationInitReadCompositeByObjectTest.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.rpc.sql; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_6; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_7; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; + +@Slf4j +public class RpcLwm2mIntegrationInitReadCompositeByObjectTest extends AbstractRpcLwM2MIntegrationTest { + + /** + " \"/3_1.2/0/9\": \"batteryLevel\", - Telemetry + " \"/3_1.2/0/20\": \"batteryStatus\" - Observe, Telemetry + " \"/5_1.2/0/6\": \"pkgname\" - Attributes + " \"/5_1.2/0/7\": \"pkgversion\" - Attributes + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\"\ - Telemetry + " \"/19_1.1/0/2\": \"dataCreationTime\" - Telemetry + * "observeStrategy": 2 + */ + @Test + public void testInitReadCompositeAsObserveStrategyCompositeByObject() throws Exception { + + + // init test + String RESOURCE_3_9 = "batteryLevel"; + String RESOURCE_3_20 = "batteryStatus"; + String RESOURCE_5_6 = "pkgname"; + String RESOURCE_5_7 = "pkgversion"; + String RESOURCE_5_9 = "firmwareUpdateDeliveryMethod"; + String RESOURCE_19_2 = "dataCreationTime"; + + String idVwr_3_0_20 = idVer_3_0_9 = objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + 20; + String IdVer5_0_6 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_6; + String IdVer5_0_7 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_7; + String IdVer5_0_9 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_9; + String idVer_19_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2; + countUpdateAttrTelemetryResource(idVer_3_0_9); + countUpdateAttrTelemetryResource(idVwr_3_0_20); + countUpdateAttrTelemetryResource(IdVer5_0_6); + countUpdateAttrTelemetryResource(IdVer5_0_7); + countUpdateAttrTelemetryResource(IdVer5_0_9); + countUpdateAttrTelemetryResource(idVer_19_0_2); + + + AtomicReference actualValues = new AtomicReference<>(); + await().atMost(40, SECONDS).until(() -> { + actualValues.set(doGetAsync( + "/api/plugins/telemetry/DEVICE/" + lwM2MTestClient.getDeviceIdStr() + "/values/timeseries?keys=" + + RESOURCE_3_9 + "," + RESOURCE_3_20 + "," + RESOURCE_5_9 + "," + RESOURCE_19_2, ObjectNode.class)); + return actualValues.get() != null && !actualValues.get().isEmpty() + && !actualValues.get().get(RESOURCE_3_9).isEmpty() + && !actualValues.get().get(RESOURCE_3_20).isEmpty() + && !actualValues.get().get(RESOURCE_5_9).isEmpty() + && !actualValues.get().get(RESOURCE_19_2).isEmpty(); + }); + + AtomicReference> actualKeys =new AtomicReference<>(); + await().atMost(40, SECONDS).until(() -> { + actualKeys.set(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + lwM2MTestClient.getDeviceIdStr() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() { + })); + return actualKeys.get() != null && !actualKeys.get().isEmpty() && !actualKeys.get().isEmpty() + && actualKeys.get().contains(RESOURCE_5_6)&& actualKeys.get().contains(RESOURCE_5_7); + }); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java index 5e28f9bf41..fde11785c3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -21,8 +21,10 @@ import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mPath; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; +import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -53,18 +55,18 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest try { expectedObjectIdVers.forEach(expected -> { try { - String actualResult = sendRPCById((String) expected); String expectedObjectId = pathIdVerToObjectId((String) expected); LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources="; - if (expectedPath.getObjectId() == 1) { - expectedObjectInstances = "LwM2mObject [id=1, instances={1="; - } else if (expectedPath.getObjectId() == 2) { - expectedObjectInstances = "LwM2mObject [id=2, instances={}]"; + if (expectedPath.getObjectId() > ACCESS_CONTROL) { + String actualResult = sendRPCByIdSync((String) expected); + if (StringUtils.isNoneBlank(actualResult)) { + log.warn(" expectedPath: [{}]", expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String expectedObjectInstances = "LwM2mObject [id=" + expectedPath.getObjectId() + ", instances={0=LwM2mObjectInstance [id=0, resources="; + assertTrue(rpcActualResult.get("value").asText().contains(expectedObjectInstances)); + } } - assertTrue(rpcActualResult.get("value").asText().contains(expectedObjectInstances)); } catch (Exception e) { e.printStackTrace(); } @@ -83,7 +85,7 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest public void testReadAllInstancesInClientById_Result_CONTENT_Value_IsInstances_IsResources() throws Exception { expectedObjectIdVerInstances.forEach(expected -> { try { - String actualResult = sendRPCById((String) expected); + String actualResult = sendRPCByIdAsync((String) expected); String expectedObjectId = pathIdVerToObjectId((String) expected); LwM2mPath expectedPath = new LwM2mPath(expectedObjectId); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -104,7 +106,7 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest @Test public void testReadMultipleResourceById_Result_CONTENT_Value_IsLwM2mMultipleResource() throws Exception { String expectedIdVer = objectInstanceIdVer_3 + "/" + RESOURCE_ID_11; - String actualResult = sendRPCById(expectedIdVer); + String actualResult = sendRPCByIdAsync(expectedIdVer); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String expected = "LwM2mMultipleResource [id=" + RESOURCE_ID_11 + ", values={"; @@ -117,7 +119,7 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest @Test public void testReadSingleResourceById_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception { String expectedIdVer = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14; - String actualResult = sendRPCById(expectedIdVer); + String actualResult = sendRPCByIdAsync(expectedIdVer); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value="; @@ -228,10 +230,14 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest assertEquals(actualValue, expectedValue); } - private String sendRPCById(String path) throws Exception { + private String sendRPCByIdAsync(String path) throws Exception { String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; return doPostAsync("/api/plugins/rpc/twoway/" + lwM2MTestClient.getDeviceIdStr(), setRpcRequest, String.class, status().isOk()); } + private String sendRPCByIdSync(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPost("/api/plugins/rpc/twoway/" + lwM2MTestClient.getDeviceIdStr(), setRpcRequest, String.class, status().isOk()); + } private String sendRPCByKey(String key) throws Exception { String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"key\": \"" + key + "\"}}"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index 4adc398fa8..52bef912f2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -70,6 +70,7 @@ import java.util.concurrent.TimeUnit; import static org.awaitility.Awaitility.await; import static org.eclipse.leshan.client.object.Security.noSecBootstrap; import static org.eclipse.leshan.client.object.Security.psk; +import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.junit.Assert.assertEquals; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode.PSK; @@ -78,7 +79,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClient import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_STARTED; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_UPDATE_SUCCESS; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_ID_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; @DaoSqlTest @@ -96,7 +97,6 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M protected static final String SERVER_STORE_PWD = "server_ks_password"; protected static final String SERVER_CERT_ALIAS = "server"; protected static final String SERVER_CERT_ALIAS_BS = "bootstrap"; - protected static final Security SECURITY_NO_SEC_BS = noSecBootstrap(URI_BS);; protected final X509Certificate serverX509Cert; // server certificate signed by rootCA protected final X509Certificate serverX509CertBs; // serverBs certificate signed by rootCA protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK @@ -190,13 +190,13 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M LwM2MClientState finishState) throws Exception { Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(type)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); - this.basicTestConnection(null , SECURITY_NO_SEC_BS, + this.basicTestConnection(null , noSecBootstrap(URI_BS), deviceCredentials, clientEndpoint, transportConfiguration, awaitAlias, expectedStatuses, - true, + false, finishState, false); } @@ -236,12 +236,19 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M } - public void basicTestConnectionBootstrapRequestTriggerBefore(String clientEndpoint, String awaitAlias, LwM2MProfileBootstrapConfigType type) throws Exception { - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(type)); + public void basicTestConnectionBootstrapRequestTriggerBefore(String clientEndpoint, String awaitAlias, LwM2MProfileBootstrapConfigType type, int cnt) throws Exception { + List bootstrapServerCredentialsNoSec = getBootstrapServerCredentialsNoSec(type); + for (int i = 2; i <= cnt; i++) { + AbstractLwM2MBootstrapServerCredential bsCredential = getBootstrapServerCredentialNoSec(false); + bsCredential.setHost("0.0.0." + i); + bsCredential.setShortServerId(bsCredential.getShortServerId() + i); + bootstrapServerCredentialsNoSec.add(bsCredential); + } + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, bootstrapServerCredentialsNoSec); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); this.basicTestConnectionBootstrapRequestTrigger( SECURITY_NO_SEC, - SECURITY_NO_SEC_BS, + noSecBootstrap(URI_BS), deviceCredentials, clientEndpoint, transportConfiguration, @@ -280,8 +287,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M }); Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatusesLwm2m)); - String executedPath = "/" + OBJECT_ID_1 + "_" + lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_1).version - + "/" +serverId + "/" + RESOURCE_ID_9; + String executedPath = "/" + SERVER + "_" + lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(SERVER).version + + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9; lwM2MTestClient.setClientStates(new HashSet<>()); String actualResult = sendRPCSecurityExecuteById(executedPath, deviceIdStr, endpoint); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -357,7 +364,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M default: throw new IllegalStateException("Unexpected value: " + mode); } - bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId); + bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId); bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST); bootstrapServerCredential.setPort(isBootstrap ? LWM2MS_BOOTSTRAP_PORT : LWM2MS_PORT); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength0Test.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength0Test.java index 1676c04fcc..19e6a5c6b3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength0Test.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength0Test.java @@ -29,10 +29,12 @@ import org.thingsboard.server.dao.service.DaoSqlTest; public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLength0Test extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { + private static final Integer serverDtlsCidLength = 0; + protected void testNoSecDtlsCidLength(Integer dtlsCidLength) throws Exception { - testNoSecDtlsCidLength(dtlsCidLength, 0); + testNoSecDtlsCidLength(dtlsCidLength, serverDtlsCidLength); } protected void testPskDtlsCidLength(Integer dtlsCidLength) throws Exception { - testPskDtlsCidLength(dtlsCidLength, 0); + testPskDtlsCidLength(dtlsCidLength, serverDtlsCidLength); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength16Test.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength16Test.java new file mode 100644 index 0000000000..faa6872b3b --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength16Test.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.cid; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; + + +@TestPropertySource(properties = { + "transport.lwm2m.dtls.connection_id_length=16" +}) + +@DaoSqlTest +@Slf4j +public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLength16Test extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { + + private static final Integer serverDtlsCidLength = 16; + + protected void testNoSecDtlsCidLength(Integer clientDtlsCidLength) throws Exception { + testNoSecDtlsCidLength(clientDtlsCidLength, serverDtlsCidLength); + } + + protected void testPskDtlsCidLength(Integer clientDtlsCidLength) throws Exception { + testPskDtlsCidLength(clientDtlsCidLength, serverDtlsCidLength); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength3Test.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength1Test.java similarity index 78% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength3Test.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength1Test.java index c45614ca7d..004702f19f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength3Test.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength1Test.java @@ -21,17 +21,20 @@ import org.thingsboard.server.dao.service.DaoSqlTest; @TestPropertySource(properties = { - "transport.lwm2m.dtls.connection_id_length=3" + "transport.lwm2m.dtls.connection_id_length=1" }) @DaoSqlTest @Slf4j -public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLength3Test extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { +public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLength1Test extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { + + + private static final Integer serverDtlsCidLength = 1; protected void testNoSecDtlsCidLength(Integer dtlsCidLength) throws Exception { - testNoSecDtlsCidLength(dtlsCidLength, 3); + testNoSecDtlsCidLength(dtlsCidLength, serverDtlsCidLength); } protected void testPskDtlsCidLength(Integer dtlsCidLength) throws Exception { - testPskDtlsCidLength(dtlsCidLength, 3); + testPskDtlsCidLength(dtlsCidLength, serverDtlsCidLength); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength2Test.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength2Test.java new file mode 100644 index 0000000000..3ab6de4cd3 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength2Test.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.cid; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; + + +@TestPropertySource(properties = { + "transport.lwm2m.dtls.connection_id_length=2" +}) + +@DaoSqlTest +@Slf4j +public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLength2Test extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { + + private static final Integer serverDtlsCidLength = 2; + + protected void testNoSecDtlsCidLength(Integer dtlsCidLength) throws Exception { + testNoSecDtlsCidLength(dtlsCidLength, serverDtlsCidLength); + } + protected void testPskDtlsCidLength(Integer dtlsCidLength) throws Exception { + testPskDtlsCidLength(dtlsCidLength, serverDtlsCidLength); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength4Test.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength4Test.java new file mode 100644 index 0000000000..168b260227 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLength4Test.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.cid; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; + + +@TestPropertySource(properties = { + "transport.lwm2m.dtls.connection_id_length=4" +}) + +@DaoSqlTest +@Slf4j +public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLength4Test extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { + + private static final Integer serverDtlsCidLength = 4; + + protected void testNoSecDtlsCidLength(Integer dtlsCidLength) throws Exception { + testNoSecDtlsCidLength(dtlsCidLength, serverDtlsCidLength); + } + protected void testPskDtlsCidLength(Integer dtlsCidLength) throws Exception { + testPskDtlsCidLength(dtlsCidLength, serverDtlsCidLength); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthNullTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthNullTest.java index b543a012d4..b91362e21b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthNullTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthNullTest.java @@ -28,11 +28,12 @@ import org.thingsboard.server.dao.service.DaoSqlTest; @Slf4j public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLengthNullTest extends AbstractSecurityLwM2MIntegrationDtlsCidLengthTest { + private static final Integer serverDtlsCidLength = null; protected void testNoSecDtlsCidLength(Integer dtlsCidLength) throws Exception { - testNoSecDtlsCidLength(dtlsCidLength, null); + testNoSecDtlsCidLength(dtlsCidLength, serverDtlsCidLength); } protected void testPskDtlsCidLength(Integer dtlsCidLength) throws Exception { - testPskDtlsCidLength(dtlsCidLength, null); + testPskDtlsCidLength(dtlsCidLength, serverDtlsCidLength); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthTest.java index 2c7212fce9..f07b2d4284 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/AbstractSecurityLwM2MIntegrationDtlsCidLengthTest.java @@ -17,14 +17,23 @@ package org.thingsboard.server.transport.lwm2m.security.cid; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.elements.config.Configuration; +import org.eclipse.californium.scandium.DTLSConnector; +import org.eclipse.californium.scandium.dtls.Connection; +import org.eclipse.californium.scandium.dtls.ConnectionId; +import org.eclipse.californium.scandium.dtls.InMemoryReadWriteLockConnectionStore; +import org.eclipse.californium.scandium.dtls.ResumptionSupportingConnectionStore; import org.eclipse.leshan.client.californium.endpoint.CaliforniumClientEndpoint; import org.eclipse.leshan.client.californium.endpoint.CaliforniumClientEndpointsProvider; +import org.eclipse.leshan.client.servers.LwM2mServer; +import org.eclipse.leshan.core.peer.IpPeer; import org.junit.Assert; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import static org.awaitility.Awaitility.await; @@ -39,13 +48,13 @@ public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLengthTest extends protected String awaitAlias; - protected void testNoSecDtlsCidLength(Integer dtlsCidLength, Integer serverDtlsCidLength) throws Exception { + protected void testNoSecDtlsCidLength(Integer clientDtlsCidLength, Integer serverDtlsCidLength) throws Exception { initDeviceCredentialsNoSek(); - basicTestConnectionDtlsCidLength(dtlsCidLength, serverDtlsCidLength); + basicTestConnectionDtlsCidLength(clientDtlsCidLength, serverDtlsCidLength); } - protected void testPskDtlsCidLength(Integer dtlsCidLength, Integer serverDtlsCidLength) throws Exception { + protected void testPskDtlsCidLength(Integer clientDtlsCidLength, Integer serverDtlsCidLength) throws Exception { initDeviceCredentialsPsk(); - basicTestConnectionDtlsCidLength(dtlsCidLength, serverDtlsCidLength); + basicTestConnectionDtlsCidLength(clientDtlsCidLength, serverDtlsCidLength); } protected void basicTestConnectionDtlsCidLength(Integer clientDtlsCidLength, @@ -69,19 +78,55 @@ public abstract class AbstractSecurityLwM2MIntegrationDtlsCidLengthTest extends Assert.assertTrue(lwM2MTestClient.getClientDtlsCid().isEmpty()); } else { Assert.assertEquals(2L, lwM2MTestClient.getClientDtlsCid().size()); - Assert.assertTrue(lwM2MTestClient.getClientDtlsCid().keySet().contains(ON_READ_CONNECTION_ID)); - Assert.assertTrue(lwM2MTestClient.getClientDtlsCid().keySet().contains(ON_WRITE_CONNECTION_ID)); - if (serverDtlsCidLength == null) { + Assert.assertTrue(lwM2MTestClient.getClientDtlsCid().containsKey(ON_READ_CONNECTION_ID)); + Assert.assertTrue(lwM2MTestClient.getClientDtlsCid().containsKey(ON_WRITE_CONNECTION_ID)); + + LwM2mServer lwM2mServer = lwM2MTestClient.getLeshanClient().getRegisteredServers().entrySet().stream().findFirst().get().getValue(); + CaliforniumClientEndpoint lwM2mClientEndpoint = (CaliforniumClientEndpoint) lwM2MTestClient.getLeshanClient().getEndpoint(lwM2mServer); + Connection connection = getConnection(lwM2mClientEndpoint, lwM2mServer); + ConnectionId clientCid = connection.getConnectionId(); + ConnectionId readCid = connection.getEstablishedDtlsContext().getReadConnectionId(); + ConnectionId serverCid = connection.getEstablishedDtlsContext().getWriteConnectionId(); + if (serverDtlsCidLength == null || clientDtlsCidLength == null) { + // cid is not used Assert.assertNull(lwM2MTestClient.getClientDtlsCid().get(ON_WRITE_CONNECTION_ID)); Assert.assertNull(lwM2MTestClient.getClientDtlsCid().get(ON_READ_CONNECTION_ID)); + Assert.assertNull(readCid); + Assert.assertNull(serverCid); } else { + Assert.assertEquals(serverDtlsCidLength, lwM2MTestClient.getClientDtlsCid().get(ON_WRITE_CONNECTION_ID)); Assert.assertEquals(clientDtlsCidLength, lwM2MTestClient.getClientDtlsCid().get(ON_READ_CONNECTION_ID)); - if (clientDtlsCidLength == null) { - Assert.assertNull(lwM2MTestClient.getClientDtlsCid().get(ON_READ_CONNECTION_ID)); + // cid used + Assert.assertNotNull(clientCid); + Assert.assertNotNull(readCid); + if (clientDtlsCidLength > 0) { + Assert.assertEquals(clientCid, readCid); + } + Assert.assertNotNull(serverCid); + int actualServerCidLength = serverCid.getBytes().length; + int expectedServerCidLength = serverDtlsCidLength; + Assert.assertEquals(expectedServerCidLength, actualServerCidLength); + } + + if (clientCid != null) { + int actualClientCidLength = clientCid.getBytes().length; + int expectedClientCidLength; + if (clientDtlsCidLength == null || clientDtlsCidLength == 0) { + expectedClientCidLength = 3; } else { - Assert.assertEquals(Integer.valueOf(serverDtlsCidLength), lwM2MTestClient.getClientDtlsCid().get(ON_WRITE_CONNECTION_ID)); + expectedClientCidLength = clientDtlsCidLength; } + Assert.assertEquals(expectedClientCidLength, actualClientCidLength); } } } + + private static Connection getConnection(CaliforniumClientEndpoint lwM2mClientEndpoint, LwM2mServer lwM2mServer) throws NoSuchFieldException, IllegalAccessException { + DTLSConnector connector = (DTLSConnector) lwM2mClientEndpoint.getCoapEndpoint().getConnector(); + Field field = DTLSConnector.class.getDeclaredField("connectionStore"); + field.setAccessible(true); + ResumptionSupportingConnectionStore connectionStore = (InMemoryReadWriteLockConnectionStore) field.get(connector); + InetSocketAddress serverAddr = ((IpPeer) lwM2mServer.getTransportData()).getSocketAddress(); + return connectionStore.get(serverAddr); + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/NoSecLwM2MIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/NoSecLwM2MIntegrationDtlsCidLengthTest.java index 2e6d40fc7e..5c2a4ceb29 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/NoSecLwM2MIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/NoSecLwM2MIntegrationDtlsCidLengthTest.java @@ -27,7 +27,7 @@ public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2 @Before public void setUpNoSecDtlsCidLength() { transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(NO_SEC, NONE)); - awaitAlias = "await on client state (NoSec_Lwm2m) DtlsCidLength = 0"; + awaitAlias = "await on client state (NoSec_Lwm2m) serverDtlsCidLength = 0"; } @Test @@ -40,8 +40,23 @@ public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2 testNoSecDtlsCidLength(0); } + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testNoSecDtlsCidLength(1); + } + @Test public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { - testNoSecDtlsCidLength(2); + testNoSecDtlsCidLength(1); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testNoSecDtlsCidLength(4); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testNoSecDtlsCidLength(16); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/PskLwm2mIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/PskLwm2mIntegrationDtlsCidLengthTest.java index 8dd020c448..d080e1bc65 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/PskLwm2mIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_0/PskLwm2mIntegrationDtlsCidLengthTest.java @@ -27,7 +27,7 @@ public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MI @Before public void createProfileRpc() { transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); - awaitAlias = "await on client state (Psk_Lwm2m) DtlsCidLength = 0"; + awaitAlias = "await on client state (Psk_Lwm2m) serverDtlsCidLength = 0"; } @Test @@ -39,10 +39,24 @@ public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MI public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_0() throws Exception { testPskDtlsCidLength(0); } + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testPskDtlsCidLength(1); + } @Test public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { testPskDtlsCidLength(2); } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testPskDtlsCidLength(4); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testPskDtlsCidLength(16); + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_1/PskLwm2mIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_1/PskLwm2mIntegrationDtlsCidLengthTest.java new file mode 100644 index 0000000000..d39afdd504 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_1/PskLwm2mIntegrationDtlsCidLengthTest.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.cid.serverDtlsCidLength_1; + +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength0Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength1Test; + +import static org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode.PSK; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; + +public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength1Test { + + @Before + public void createProfileRpc() { + transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); + awaitAlias = "await on client state (Psk_Lwm2m) serverDtlsCidLength = 1"; + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_Null() throws Exception { + testPskDtlsCidLength(null); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_0() throws Exception { + testPskDtlsCidLength(0); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testPskDtlsCidLength(1); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { + testPskDtlsCidLength(2); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testPskDtlsCidLength(4); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testPskDtlsCidLength(16); + } +} + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_3/NoSecLwM2MIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_16/NoSecLwM2MIntegrationDtlsCidLengthTest.java similarity index 69% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_3/NoSecLwM2MIntegrationDtlsCidLengthTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_16/NoSecLwM2MIntegrationDtlsCidLengthTest.java index 0d74787993..ca36137295 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_3/NoSecLwM2MIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_16/NoSecLwM2MIntegrationDtlsCidLengthTest.java @@ -13,21 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.security.cid.serverDtlsCidLength_3; +package org.thingsboard.server.transport.lwm2m.security.cid.serverDtlsCidLength_16; import org.junit.Before; import org.junit.Test; -import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength3Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength16Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength4Test; import static org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; -public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength3Test { +public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength16Test { @Before public void setUpNoSecDtlsCidLength() { transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(NO_SEC, NONE)); - awaitAlias = "await on client state (NoSec_Lwm2m) DtlsCidLength = 3"; + awaitAlias = "await on client state (NoSec_Lwm2m) serverDtlsCidLength = 16"; } @Test @@ -40,8 +41,23 @@ public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2 testNoSecDtlsCidLength(0); } + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testNoSecDtlsCidLength(1); + } + @Test public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { testNoSecDtlsCidLength(2); } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testNoSecDtlsCidLength(4); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testNoSecDtlsCidLength(16); + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_16/PskLwm2mIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_16/PskLwm2mIntegrationDtlsCidLengthTest.java new file mode 100644 index 0000000000..45b52c6ddd --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_16/PskLwm2mIntegrationDtlsCidLengthTest.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.cid.serverDtlsCidLength_16; + +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength16Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength4Test; + +import static org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode.PSK; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; + +public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength16Test { + + @Before + public void createProfileRpc() { + transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); + awaitAlias = "await on client state (Psk_Lwm2m) serverDtlsCidLength = 16"; + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_Null() throws Exception { + testPskDtlsCidLength(null); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_0() throws Exception { + testPskDtlsCidLength(0); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testPskDtlsCidLength(1); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { + testPskDtlsCidLength(2); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testPskDtlsCidLength(4); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testPskDtlsCidLength(16); + } +} + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_3/PskLwm2mIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_2/PskLwm2mIntegrationDtlsCidLengthTest.java similarity index 73% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_3/PskLwm2mIntegrationDtlsCidLengthTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_2/PskLwm2mIntegrationDtlsCidLengthTest.java index 5312c872b8..2e0b40da3d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_3/PskLwm2mIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_2/PskLwm2mIntegrationDtlsCidLengthTest.java @@ -13,21 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.security.cid.serverDtlsCidLength_3; +package org.thingsboard.server.transport.lwm2m.security.cid.serverDtlsCidLength_2; import org.junit.Before; import org.junit.Test; -import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength3Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength2Test; import static org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode.PSK; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; -public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength3Test { +public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength2Test { @Before public void createProfileRpc() { transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); - awaitAlias = "await on client state (Psk_Lwm2m) DtlsCidLength = 3"; + awaitAlias = "await on client state (Psk_Lwm2m) serverDtlsCidLength = 2"; } @Test @@ -40,9 +40,24 @@ public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MI testPskDtlsCidLength(0); } + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testPskDtlsCidLength(1); + } + @Test public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { testPskDtlsCidLength(2); } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testPskDtlsCidLength(4); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testPskDtlsCidLength(16); + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_4/PskLwm2mIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_4/PskLwm2mIntegrationDtlsCidLengthTest.java new file mode 100644 index 0000000000..aa281020a7 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_4/PskLwm2mIntegrationDtlsCidLengthTest.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.cid.serverDtlsCidLength_4; + +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.cid.AbstractSecurityLwM2MIntegrationDtlsCidLength4Test; + +import static org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode.PSK; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; + +public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MIntegrationDtlsCidLength4Test { + + @Before + public void createProfileRpc() { + transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); + awaitAlias = "await on client state (Psk_Lwm2m) serverDtlsCidLength = 4"; + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_Null() throws Exception { + testPskDtlsCidLength(null); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_0() throws Exception { + testPskDtlsCidLength(0); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testPskDtlsCidLength(1); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { + testPskDtlsCidLength(2); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testPskDtlsCidLength(4); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testPskDtlsCidLength(16); + } +} + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/NoSecLwM2MIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/NoSecLwM2MIntegrationDtlsCidLengthTest.java index 0c928b2237..88bc5a2622 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/NoSecLwM2MIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/NoSecLwM2MIntegrationDtlsCidLengthTest.java @@ -27,7 +27,7 @@ public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2 @Before public void setUpNoSecDtlsCidLength() { transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(NO_SEC, NONE)); - awaitAlias = "await on client state (NoSec_Lwm2m) DtlsCidLength = Null"; + awaitAlias = "await on client state (NoSec_Lwm2m) serverDtlsCidLength = Null"; } @Test @@ -41,7 +41,22 @@ public class NoSecLwM2MIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2 } @Test - public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { - testNoSecDtlsCidLength(2); + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testNoSecDtlsCidLength(1); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testNoSecDtlsCidLength(4); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_8() throws Exception { + testNoSecDtlsCidLength(8); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testNoSecDtlsCidLength(16); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/PskLwm2mIntegrationDtlsCidLengthTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/PskLwm2mIntegrationDtlsCidLengthTest.java index e84e7fdb8d..37611c2bdb 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/PskLwm2mIntegrationDtlsCidLengthTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/cid/serverDtlsCidLength_null/PskLwm2mIntegrationDtlsCidLengthTest.java @@ -27,7 +27,7 @@ public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MI @Before public void createProfileRpc() { transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); - awaitAlias = "await on client state (Psk_Lwm2m) DtlsCidLength = Null"; + awaitAlias = "await on client state (Psk_Lwm2m) serverDtlsCidLength = Null"; } @Test @@ -40,9 +40,24 @@ public class PskLwm2mIntegrationDtlsCidLengthTest extends AbstractSecurityLwM2MI testPskDtlsCidLength(0); } + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_1() throws Exception { + testPskDtlsCidLength(1); + } + @Test public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_2() throws Exception { testPskDtlsCidLength(2); } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_4() throws Exception { + testPskDtlsCidLength(4); + } + + @Test + public void testWithPskConnectLwm2mSuccessClientDtlsCidLength_16() throws Exception { + testPskDtlsCidLength(16); + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBS3SectionTriggerTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBS3SectionTriggerTest.java new file mode 100644 index 0000000000..8b7195141a --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBS3SectionTriggerTest.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.sql; + +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.LWM2M_ONLY; +public class NoSecLwM2MIntegrationBS3SectionTriggerTest extends AbstractSecurityLwM2MIntegrationTest { + + @Test + public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTrigger_3_ConnectBsSuccess_UpdateLwm2mSection_3_AndLm2m_1_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger_3" + LWM2M_ONLY.name(); + String awaitAlias = "await on client state (NoSecBS Trigger Lwm2m section)"; + basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, LWM2M_ONLY, 3); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSLwm2mOnlyNoneTriggerOneSectionTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSLwm2mOnlyNoneTriggerOneSectionTest.java new file mode 100644 index 0000000000..d386c60135 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSLwm2mOnlyNoneTriggerOneSectionTest.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.sql; + +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.LWM2M_ONLY; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +public class NoSecLwM2MIntegrationBSLwm2mOnlyNoneTriggerOneSectionTest extends AbstractSecurityLwM2MIntegrationTest { + + @Test + public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateLwm2mSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + LWM2M_ONLY.name(); + String awaitAlias = "await on client state (NoSecBS Trigger Lwm2m section)"; + basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, LWM2M_ONLY, 1); + } + + @Test + public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateNoneSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + NONE.name(); + String awaitAlias = "await on client state (NoSecBS Trigger None section)"; + basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, NONE, 1); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSNoTriggerTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSNoTriggerTest.java new file mode 100644 index 0000000000..46deafd0a4 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSNoTriggerTest.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.sql; + +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.LWM2M_ONLY; + +public class NoSecLwM2MIntegrationBSNoTriggerTest extends AbstractSecurityLwM2MIntegrationTest { + + @Test + public void testWithNoSecConnectBsSuccess_UpdateTwoSectionsBootstrapAndLm2m_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "NoTrigger" + BOTH.name(); + String awaitAlias = "await on client state (NoSecBS two section)"; + basicTestConnectionStartBS(clientEndpoint, awaitAlias, BOTH, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS); + } + + @Test + public void testWithNoSecConnectBsSuccess_UpdateLwm2mSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "NoTrigger" + LWM2M_ONLY.name(); + String awaitAlias = "await on client state (NoSecBS Lwm2m section)"; + basicTestConnectionStartBS(clientEndpoint, awaitAlias, LWM2M_ONLY, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSOnlyTriggerOneSectionTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSOnlyTriggerOneSectionTest.java new file mode 100644 index 0000000000..deacc8167e --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSOnlyTriggerOneSectionTest.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.sql; + +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY; +public class NoSecLwM2MIntegrationBSOnlyTriggerOneSectionTest extends AbstractSecurityLwM2MIntegrationTest { + + @Test + public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateBootstrapSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + BOOTSTRAP_ONLY.name(); + String awaitAlias = "await on client state (NoSecBS Trigger Bootstrap section)"; + basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, BOOTSTRAP_ONLY, 1); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSTriggerTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSTriggerTest.java new file mode 100644 index 0000000000..9ba19fbbfa --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationBSTriggerTest.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.security.sql; + +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; + +public class NoSecLwM2MIntegrationBSTriggerTest extends AbstractSecurityLwM2MIntegrationTest { + + @Test + public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateTwoSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + BOTH.name(); + String awaitAlias = "await on client state (NoSecBS Trigger Two section)"; + basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, BOTH, 1); + } +} + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index 633092b9b7..21b4fcbce0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -19,12 +19,6 @@ import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.LWM2M_ONLY; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; - public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { //Lwm2m only @@ -34,48 +28,4 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); super.basicTestConnectionObserveSingleTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, false, false); } - - // Bootstrap + Lwm2m - @Test - public void testWithNoSecConnectBsSuccess_UpdateTwoSectionsBootstrapAndLm2m_ConnectLwm2mSuccess() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + BOTH.name(); - String awaitAlias = "await on client state (NoSecBS two section)"; - basicTestConnectionStartBS(clientEndpoint, awaitAlias, BOTH, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS); - } - - @Test - public void testWithNoSecConnectBsSuccess_UpdateLwm2mSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + LWM2M_ONLY.name(); - String awaitAlias = "await on client state (NoSecBS Lwm2m section)"; - basicTestConnectionStartBS(clientEndpoint, awaitAlias, LWM2M_ONLY, expectedStatusesRegistrationBsSuccess, ON_REGISTRATION_SUCCESS); - } - - // Bs trigger - @Test - public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateTwoSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + BOTH.name(); - String awaitAlias = "await on client state (NoSecBS Trigger Two section)"; - basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, BOTH); - } - - @Test - public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateBootstrapSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + BOOTSTRAP_ONLY.name(); - String awaitAlias = "await on client state (NoSecBS Trigger Bootstrap section)"; - basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, BOOTSTRAP_ONLY); - } - - @Test - public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateLwm2mSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + LWM2M_ONLY.name(); - String awaitAlias = "await on client state (NoSecBS Trigger Lwm2m section)"; - basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, LWM2M_ONLY); - } - - @Test - public void testWithNoSecConnectLwm2mSuccessBootstrapRequestTriggerConnectBsSuccess_UpdateNoneSectionAndLm2m_ConnectLwm2mSuccess() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC_BS + "Trigger" + NONE.name(); - String awaitAlias = "await on client state (NoSecBS Trigger None section)"; - basicTestConnectionBootstrapRequestTriggerBefore(clientEndpoint, awaitAlias, NONE); - } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index 29beace952..6083966837 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -58,8 +58,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes Hex.decodeHex(keyPsk.toCharArray())); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, NONE)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); - this.basicTestConnection(security, - null, + this.basicTestConnection(security, null, deviceCredentials, clientEndpoint, transportConfiguration, @@ -87,8 +86,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_ONE_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); String awaitAlias = "await on client state (Psk_Lwm2m)"; - Device lwm2mDevice = this.basicTestConnection(security, - null, + Device lwm2mDevice = this.basicTestConnection(security, null, deviceCredentials, clientEndpoint, transportConfiguration, @@ -125,8 +123,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_ONE_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); String awaitAlias = "await on client state (Psk_Lwm2m)"; - Device lwm2mDevice = this.basicTestConnection(security, - null, + Device lwm2mDevice = this.basicTestConnection(security, null, deviceCredentials, clientEndpoint, transportConfiguration, @@ -179,12 +176,12 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes clientCredentials.setEndpoint(clientEndpoint); clientCredentials.setIdentity(identity); clientCredentials.setKey(keyPsk); - Security securityBs = pskBootstrap(SECURE_URI_BS, + Security securityPskBs = pskBootstrap(SECURE_URI_BS, identity.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(keyPsk.toCharArray())); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(PSK, BOTH)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); - this.basicTestConnection(null, securityBs, + this.basicTestConnection(null, securityPskBs, deviceCredentials, clientEndpoint, transportConfiguration, diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index 666457c7f9..ce942090e9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -113,13 +113,13 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes RPKClientCredential clientCredentials = new RPKClientCredential(); clientCredentials.setEndpoint(clientEndpoint); clientCredentials.setKey(Base64.encodeBase64String(certificate.getPublicKey().getEncoded())); - Security securityBs = rpkBootstrap(SECURE_URI_BS, + Security securityRpkBs = rpkBootstrap(SECURE_URI_BS, certificate.getPublicKey().getEncoded(), privateKey.getEncoded(), serverX509CertBs.getPublicKey().getEncoded()); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(RPK, BOTH)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, clientPrivateKeyFromCertTrust, certificate, RPK, false); - this.basicTestConnection(null, securityBs, + this.basicTestConnection(null, securityRpkBs, deviceCredentials, clientEndpoint, transportConfiguration, diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java index e5786388e4..5b385dfea0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java @@ -51,15 +51,14 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg X509ClientCredential clientCredentials = new X509ClientCredential(); clientCredentials.setEndpoint(clientEndpoint); clientCredentials.setCert(Base64.getEncoder().encodeToString(certificate.getEncoded())); - Security security = x509(SECURE_URI, + Security securityX509 = x509(SECURE_URI, shortServerId, certificate.getEncoded(), privateKey.getEncoded(), serverX509Cert.getEncoded()); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, NONE)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); - this.basicTestConnection(security, - null, + this.basicTestConnection(securityX509, null, deviceCredentials, clientEndpoint, transportConfiguration, @@ -119,8 +118,7 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg serverX509CertBs.getEncoded()); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); - this.basicTestConnection(security, - null, + this.basicTestConnection(security, null, deviceCredentials, clientEndpoint, transportConfiguration, diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java index 7c2063301d..44eeb96d5f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java @@ -50,8 +50,7 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra serverX509Cert.getEncoded()); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, NONE)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); - this.basicTestConnection(security, - null, + this.basicTestConnection(security, null, deviceCredentials, clientEndpoint, transportConfiguration, @@ -77,8 +76,7 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra serverX509CertBs.getEncoded()); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsSecure(X509, BOTH)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, privateKey, certificate, X509, false); - this.basicTestConnection(security, - null, + this.basicTestConnection(security,null, deviceCredentials, clientEndpoint, transportConfiguration, diff --git a/application/src/test/java/org/thingsboard/server/utils/CalculatedFieldUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/CalculatedFieldUtilsTest.java new file mode 100644 index 0000000000..1e2273af7c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/CalculatedFieldUtilsTest.java @@ -0,0 +1,167 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingPresenceStatus; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CalculatedFieldId; +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.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; +import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; +import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculatedFieldState; +import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingZoneState; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationArgumentEntry; +import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; +import static org.thingsboard.server.utils.CalculatedFieldUtils.toProto; + +@ExtendWith(MockitoExtension.class) +class CalculatedFieldUtilsTest { + + private static final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("0a69e1e2-fcbc-4234-a4cd-3844bf54035c")); + private static final CalculatedFieldId CF_ID = CalculatedFieldId.fromString("ec0e91b9-6f27-4e93-946a-5fbc2707d8bc"); + private static final DeviceId DEVICE_ID = DeviceId.fromString("1e03bd38-2010-4739-9362-160c288e36c4"); + + @Test + void toProtoAndFromProto_shouldMapGeofencingArgumentsAndZones() { + // given + CalculatedFieldEntityCtxId stateId = mock(CalculatedFieldEntityCtxId.class); + given(stateId.tenantId()).willReturn(TENANT_ID); + given(stateId.cfId()).willReturn(CF_ID); + given(stateId.entityId()).willReturn(DEVICE_ID); + + // Build a geofencing argument with two zones (one with inside=true, one with inside=null) + GeofencingArgumentEntry geofencingArgumentEntry = new GeofencingArgumentEntry(); + Map zoneStates = new LinkedHashMap<>(); + + UUID zoneId1 = UUID.fromString("624a8fff-71a2-4847-a100-ff1cf52dbe71"); + UUID zoneId2 = UUID.fromString("e2adf6ce-9478-40b1-b0e9-4a6860cc46bb"); + + AssetId z1 = new AssetId(zoneId1); + AssetId z2 = new AssetId(zoneId2); + + JsonDataEntry zone1 = new JsonDataEntry("zone", "[[50.472000, 30.504000], [50.472000, 30.506000], [50.474000, 30.506000], [50.474000, 30.504000]]"); + JsonDataEntry zone2 = new JsonDataEntry("zone", "[[50.475000, 30.510000], [50.475000, 30.512000], [50.477000, 30.512000], [50.477000, 30.510000]]"); + + BaseAttributeKvEntry zone1PerimeterAttribute = new BaseAttributeKvEntry(zone1, System.currentTimeMillis(), 0L); + BaseAttributeKvEntry zone2PerimeterAttribute = new BaseAttributeKvEntry(zone2, System.currentTimeMillis(), 0L); + + GeofencingZoneState s1 = new GeofencingZoneState(z1, zone1PerimeterAttribute); + s1.setLastPresence(GeofencingPresenceStatus.INSIDE); + GeofencingZoneState s2 = new GeofencingZoneState(z2, zone2PerimeterAttribute); + + zoneStates.put(z1, s1); + zoneStates.put(z2, s2); + geofencingArgumentEntry.setZoneStates(zoneStates); + + // Create cf state with the geofencing argument and add it to the state map + CalculatedFieldState state = new GeofencingCalculatedFieldState(DEVICE_ID); + + CalculatedFieldCtx cfCtxMock = mock(CalculatedFieldCtx.class); + when(cfCtxMock.getArgNames()).thenReturn(List.of("geofencingArgumentTest")); + + state.setCtx(cfCtxMock, null); + + Map updatedArguments = state.update(Map.of("geofencingArgumentTest", geofencingArgumentEntry), cfCtxMock); + assertThat(updatedArguments).hasSize(1); + assertThat(updatedArguments.get("geofencingArgumentTest")).isEqualTo(geofencingArgumentEntry); + + CalculatedFieldStateProto proto = toProto(stateId, state); + CalculatedFieldState fromProto = CalculatedFieldUtils.fromProto(stateId, proto); + + assertThat(fromProto) + .usingRecursiveComparison() + .ignoringFields("ctx", "requiredArguments", "readinessStatus", "latestTimestamp") + .isEqualTo(state); + + ArgumentEntry fromProtoArgument = fromProto.getArguments().get("geofencingArgumentTest"); + assertThat(fromProtoArgument).isInstanceOf(GeofencingArgumentEntry.class); + GeofencingArgumentEntry fromProtoGeoArgument = (GeofencingArgumentEntry) fromProtoArgument; + assertThat(fromProtoGeoArgument.getZoneStates()).hasSize(2); + assertThat(fromProtoGeoArgument.getZoneStates().get(z1).getLastPresence()).isEqualTo(GeofencingPresenceStatus.INSIDE); + assertThat(fromProtoGeoArgument.getZoneStates().get(z2).getLastPresence()).isNull(); + } + + @Test + void toProtoAndFromProto_shouldCreatePropagationStateWithNotEmptyPropagationArgument() { + // given + CalculatedFieldEntityCtxId stateId = mock(CalculatedFieldEntityCtxId.class); + given(stateId.tenantId()).willReturn(TENANT_ID); + given(stateId.cfId()).willReturn(CF_ID); + given(stateId.entityId()).willReturn(DEVICE_ID); + + AssetId propagationAssetId = new AssetId(UUID.fromString("17bbf99c-3b87-4d21-b07d-da7409bb2bb7")); + PropagationArgumentEntry propagationArgumentEntry = new PropagationArgumentEntry(List.of(propagationAssetId)); + + long lastUpdateTs = System.currentTimeMillis(); + SingleValueArgumentEntry singleValueArgumentEntry = new SingleValueArgumentEntry(new BaseAttributeKvEntry(new StringDataEntry("state", "active"), lastUpdateTs, 1L)); + + CalculatedFieldCtx cfCtxMock = mock(CalculatedFieldCtx.class); + when(cfCtxMock.getArgNames()).thenReturn(List.of("state")); + + CalculatedFieldState state = new PropagationCalculatedFieldState(DEVICE_ID); + + state.setCtx(cfCtxMock, null); + + Map updatedArguments = state.update(Map.of(PROPAGATION_CONFIG_ARGUMENT, propagationArgumentEntry, "state", singleValueArgumentEntry), cfCtxMock); + assertThat(updatedArguments).hasSize(2); + assertThat(updatedArguments.get(PROPAGATION_CONFIG_ARGUMENT)).isEqualTo(propagationArgumentEntry); + assertThat(updatedArguments.get("state")).isEqualTo(singleValueArgumentEntry); + + // when + CalculatedFieldStateProto proto = toProto(stateId, state); + + // then + CalculatedFieldState restored = CalculatedFieldUtils.fromProto(stateId, proto); + + // Propagation argument is not persisted -> should be absent after restore + assertThat(restored).isNotNull(); + assertThat(restored).isInstanceOf(PropagationCalculatedFieldState.class); + + PropagationCalculatedFieldState propagationState = (PropagationCalculatedFieldState) restored; + + assertThat(propagationState.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(propagationState.getArguments()).isNotNull(); + assertThat(propagationState.getArguments().get(PROPAGATION_CONFIG_ARGUMENT)).isEqualTo(propagationArgumentEntry); + assertThat(propagationState.getArguments().get("state")).isNotNull().isEqualTo(singleValueArgumentEntry); + assertThat(propagationState.getRequiredArguments()).isNull(); + assertThat(propagationState.getReadinessStatus()).isNull(); + } + +} diff --git a/common/actor/pom.xml b/common/actor/pom.xml index d0a11006d0..64727ab3f7 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 0144606128..163c47356d 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/TbCaffeineCacheConfiguration.java b/common/cache/src/main/java/org/thingsboard/server/cache/TbCaffeineCacheConfiguration.java index 7ab81680dc..da04969ed1 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/TbCaffeineCacheConfiguration.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/TbCaffeineCacheConfiguration.java @@ -70,11 +70,10 @@ public class TbCaffeineCacheConfiguration { } private CaffeineCache buildCache(String name, CacheSpecs cacheSpec) { - - final Caffeine caffeineBuilder - = Caffeine.newBuilder() + Caffeine caffeineBuilder = Caffeine.newBuilder() .weigher(collectionSafeWeigher()) .maximumWeight(cacheSpec.getMaxSize()) + .recordStats() .ticker(ticker()); if (!cacheSpec.getTimeToLiveInMinutes().equals(0)) { caffeineBuilder.expireAfterWrite(cacheSpec.getTimeToLiveInMinutes(), TimeUnit.MINUTES); diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 38aad36189..9be12f3b18 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common 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 8b3ae02c1d..e537b3127a 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 @@ -16,6 +16,7 @@ package org.thingsboard.server.cluster; import org.thingsboard.server.common.data.ApiUsageState; +import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.TbResourceInfo; @@ -29,6 +30,7 @@ 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.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; @@ -38,7 +40,6 @@ import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 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.RestApiCallResponseMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; @@ -81,7 +82,7 @@ public interface TbClusterService extends TbQueueClusterService { void pushNotificationToTransport(String targetServiceId, ToTransportMsg response, TbQueueCallback callback); - void pushMsgToCalculatedFields(TenantId tenantId, EntityId entityId, TransportProtos.ToCalculatedFieldMsg msg, TbQueueCallback callback); + void pushMsgToCalculatedFields(TenantId tenantId, EntityId entityId, ToCalculatedFieldMsg msg, TbQueueCallback callback); void pushMsgToCalculatedFields(TopicPartitionInfo tpi, UUID msgId, ToCalculatedFieldMsg msg, TbQueueCallback callback); @@ -131,8 +132,14 @@ public interface TbClusterService extends TbQueueClusterService { void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action, EdgeId sourceEdgeId); + void onCustomerUpdated(Customer customer, Customer oldCustomer); + void onCalculatedFieldUpdated(CalculatedField calculatedField, CalculatedField oldCalculatedField, TbQueueCallback callback); void onCalculatedFieldDeleted(CalculatedField calculatedField, TbQueueCallback callback); + void onRelationUpdated(TenantId tenantId, EntityRelation entityRelation, TbQueueCallback callback); + + void onRelationDeleted(TenantId tenantId, EntityRelation entityRelation, TbQueueCallback callback); + } 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 5ad9babb58..60d4d532be 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 @@ -38,6 +38,8 @@ public interface TbQueueConsumer { boolean isStopped(); + Set getPartitions(); + List getFullTopicNames(); } diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 82d60a241c..2e8aca2ba4 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 672de6bacd..af9dc2829f 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -66,7 +66,7 @@ public class TbCoapDtlsSettings { @Value("${coap.dtls.retransmission_timeout:9000}") private int dtlsRetransmissionTimeout; - @Value("${coap.dtls.connection_id_length:}") + @Value("${coap.dtls.connection_id_length:8}") private Integer cIdLength; @Value("${coap.dtls.max_transmission_unit:1024}") diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 34799550bc..3468f51be0 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ai/AiModelService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ai/AiModelService.java index ac87ed2afa..bd0367d871 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/ai/AiModelService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ai/AiModelService.java @@ -29,6 +29,8 @@ public interface AiModelService extends EntityDaoService { AiModel save(AiModel model); + AiModel save(AiModel model, boolean doValidate); + Optional findAiModelById(TenantId tenantId, AiModelId modelId); PageData findAiModelsByTenantId(TenantId tenantId, PageLink pageLink); @@ -37,6 +39,8 @@ public interface AiModelService extends EntityDaoService { FluentFuture> findAiModelByTenantIdAndIdAsync(TenantId tenantId, AiModelId modelId); + Optional findAiModelByTenantIdAndName(TenantId tenantId, String name); + boolean deleteByTenantIdAndId(TenantId tenantId, AiModelId modelId); } 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 afbdd4eda7..b9173ef863 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 @@ -51,10 +51,6 @@ import java.util.UUID; public interface AlarmService extends EntityDaoService { - /* - * New API, since 3.5. - */ - /** * Designed for atomic operations over active alarms. * Only one active alarm may exist for the pair {originatorId, alarmType} @@ -74,7 +70,7 @@ public interface AlarmService extends EntityDaoService { AlarmApiCallResult acknowledgeAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); - AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details); + AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details, boolean pushEvent); AlarmApiCallResult assignAlarm(TenantId tenantId, AlarmId alarmId, UserId assigneeId, long ts); 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 ad0aebddd2..2294e4fa09 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 @@ -62,4 +62,6 @@ public interface AssetProfileService extends EntityDaoService { List findAssetProfileNamesByTenantId(TenantId tenantId, boolean activeOnly); + List findAssetProfilesByIds(TenantId tenantId, List assetProfileIds); + } 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 8af1b22604..a785140294 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 @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.asset; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.ProfileEntityIdInfo; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; @@ -48,6 +49,8 @@ public interface AssetService extends EntityDaoService { Asset saveAsset(Asset asset); + Asset saveAsset(Asset asset, NameConflictStrategy nameConflictStrategy); + Asset assignAssetToCustomer(TenantId tenantId, AssetId assetId, CustomerId customerId); Asset unassignAssetFromCustomer(TenantId tenantId, AssetId assetId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index 2cca29e5c7..3166c64835 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -48,7 +48,7 @@ public interface AttributesService { List findAllKeysByEntityIds(TenantId tenantId, List entityIds); - List findAllKeysByEntityIds(TenantId tenantId, List entityIds, String scope); + List findAllKeysByEntityIds(TenantId tenantId, List entityIds, AttributeScope scope); int removeAllByEntityId(TenantId tenantId, EntityId entityId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldService.java index 420bf01dd2..1603385094 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldService.java @@ -16,9 +16,10 @@ package org.thingsboard.server.dao.cf; import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; +import org.thingsboard.server.common.data.cf.CalculatedFieldFilter; +import org.thingsboard.server.common.data.cf.CalculatedFieldInfo; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -35,7 +36,7 @@ public interface CalculatedFieldService extends EntityDaoService { CalculatedField findById(TenantId tenantId, CalculatedFieldId calculatedFieldId); - CalculatedField findByEntityIdAndName(EntityId entityId, String name); + CalculatedField findByEntityIdAndTypeAndName(EntityId entityId, CalculatedFieldType type, String name); List findCalculatedFieldIdsByEntityId(TenantId tenantId, EntityId entityId); @@ -45,23 +46,15 @@ public interface CalculatedFieldService extends EntityDaoService { PageData findCalculatedFieldsByTenantId(TenantId tenantId, PageLink pageLink); - PageData findAllCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink); + PageData findCalculatedFieldsByTenantIdAndFilter(TenantId tenantId, CalculatedFieldFilter filter, PageLink pageLink); - void deleteCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId); - - int deleteAllCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId); - - CalculatedFieldLink saveCalculatedFieldLink(TenantId tenantId, CalculatedFieldLink calculatedFieldLink); + PageData findCalculatedFieldNamesByTenantIdAndType(TenantId tenantId, CalculatedFieldType type, PageLink pageLink); - CalculatedFieldLink findCalculatedFieldLinkById(TenantId tenantId, CalculatedFieldLinkId calculatedFieldLinkId); + PageData findCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId, CalculatedFieldType type, PageLink pageLink); - List findAllCalculatedFieldLinksById(TenantId tenantId, CalculatedFieldId calculatedFieldId); - - List findAllCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId); - - PageData findAllCalculatedFieldLinksByTenantId(TenantId tenantId, PageLink pageLink); + void deleteCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId); - PageData findAllCalculatedFieldLinks(PageLink pageLink); + int deleteAllCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId); boolean referencedInAnyCalculatedField(TenantId tenantId, EntityId referencedEntityId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java index 4673f47a7e..b365f41830 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java @@ -17,12 +17,14 @@ package org.thingsboard.server.dao.customer; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.NameConflictStrategy; 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.entity.EntityDaoService; +import java.util.List; import java.util.Optional; public interface CustomerService extends EntityDaoService { @@ -37,6 +39,8 @@ public interface CustomerService extends EntityDaoService { Customer saveCustomer(Customer customer); + Customer saveCustomer(Customer customer, NameConflictStrategy nameConflictStrategy); + void deleteCustomer(TenantId tenantId, CustomerId customerId); Customer findOrCreatePublicCustomer(TenantId tenantId); @@ -47,4 +51,6 @@ public interface CustomerService extends EntityDaoService { void deleteCustomersByTenantId(TenantId tenantId); + List findCustomersByTenantIdAndIds(TenantId tenantId, List customerIds); + } 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 1866ffe3d1..11f860d6f6 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 @@ -80,4 +80,6 @@ public interface DashboardService extends EntityDaoService { PageData findAllDashboardsIds(PageLink pageLink); + List findDashboardInfoByIds(TenantId tenantId, List dashboardIds); + } 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 c054653235..be708f9733 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 @@ -64,4 +64,6 @@ public interface DeviceProfileService extends EntityDaoService { List findDeviceProfileNamesByTenantId(TenantId tenantId, boolean activeOnly); + List findDeviceProfilesByIds(TenantId tenantId, List deviceProfileIds); + } 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 64b9be5149..13bff8d4e4 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 @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.DeviceInfoFilter; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.ProfileEntityIdInfo; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; @@ -58,8 +59,12 @@ public interface DeviceService extends EntityDaoService { Device saveDeviceWithAccessToken(Device device, String accessToken); + Device saveDeviceWithAccessToken(Device device, String accessToken, NameConflictStrategy nameConflictStrategy); + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials); + Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials, NameConflictStrategy nameConflictStrategy); + Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile); Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId); 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 2156cc1860..990a2e4621 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 @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; @@ -35,6 +36,8 @@ public interface EntityViewService extends EntityDaoService { EntityView saveEntityView(EntityView entityView); + EntityView saveEntityView(EntityView entityView, NameConflictStrategy nameConflictStrategy); + EntityView saveEntityView(EntityView entityView, boolean doValidate); EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, CustomerId customerId); @@ -65,6 +68,8 @@ public interface EntityViewService extends EntityDaoService { PageData findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink); + List findEntityViewsByTenantIdAndIds(TenantId tenantId, List entityViewIds); + PageData findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink); PageData findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, PageLink pageLink, String type); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/pat/ApiKeyService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/pat/ApiKeyService.java new file mode 100644 index 0000000000..798549cf85 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/pat/ApiKeyService.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +import org.thingsboard.server.common.data.id.ApiKeyId; +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.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.dao.entity.EntityDaoService; + +public interface ApiKeyService extends EntityDaoService { + + ApiKey saveApiKey(TenantId tenantId, ApiKeyInfo apiKey); + + void deleteApiKey(TenantId tenantId, ApiKey apiKey, boolean force); + + void deleteByUserId(TenantId tenantId, UserId userId); + + ApiKey findApiKeyByValue(String value); + + ApiKey findApiKeyById(TenantId tenantId, ApiKeyId apiKeyId); + + PageData findApiKeysByUserId(TenantId tenantId, UserId userId, 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 030830d312..21d84f6930 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 @@ -20,15 +20,14 @@ 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.EntityRelationInfo; +import org.thingsboard.server.common.data.relation.EntityRelationPathQuery; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChainType; import java.util.List; +import java.util.function.Predicate; -/** - * Created by ashvayka on 27.04.17. - */ public interface RelationService { ListenableFuture checkRelationAsync(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); @@ -83,6 +82,12 @@ public interface RelationService { List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit); + ListenableFuture> findByRelationPathQueryAsync(TenantId tenantId, EntityRelationPathQuery relationPathQuery); + + ListenableFuture> findFilteredRelationsByPathQueryAsync(TenantId tenantId, EntityRelationPathQuery relationPathQuery, Predicate relationFilter); + + List findByRelationPathQuery(TenantId tenantId, EntityRelationPathQuery relationPathQuery); + // TODO: This method may be useful for some validations in the future // ListenableFuture checkRecursiveRelation(EntityId from, EntityId to); 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 1bfc75e7e7..8f76e7444f 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 @@ -117,4 +117,6 @@ public interface RuleChainService extends EntityDaoService { void deleteRuleNodes(TenantId tenantId, RuleChainId ruleChainId); + List findRuleChainsByIds(TenantId tenantId, List ruleChainIds); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java index 8910ed38b3..a4f4e1d715 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.tenant; -import org.thingsboard.server.common.data.SystemParams; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java index 8b57fd2495..40a3b4bd2a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java @@ -55,4 +55,6 @@ public interface TenantService extends EntityDaoService { PageData findTenantsIds(PageLink pageLink); + List findTenantsByIds(TenantId callerId, List tenantIds); + } 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 6538a5ea26..5bf2c63176 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 @@ -63,5 +63,8 @@ public interface TimeseriesService { List findAllKeysByEntityIds(TenantId tenantId, List entityIds); + ListenableFuture> findAllKeysByEntityIdsAsync(TenantId tenantId, List entityIds); + void cleanup(long systemTtl); + } 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 60a54037ab..29811a3c6d 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 @@ -17,12 +17,15 @@ package org.thingsboard.server.dao.user; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserAuthDetails; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.mobile.MobileSessionInfo; +import org.thingsboard.server.common.data.notification.targets.platform.SystemLevelUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.UserCredentials; @@ -45,6 +48,8 @@ public interface UserService extends EntityDaoService { User saveUser(TenantId tenantId, User user); + User saveUser(TenantId tenantId, User user, boolean doValidate); + UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken); @@ -53,6 +58,8 @@ public interface UserService extends EntityDaoService { UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); + UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials, boolean doValidate); + UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password); UserCredentials requestPasswordReset(TenantId tenantId, String email); @@ -67,6 +74,8 @@ public interface UserService extends EntityDaoService { UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials); + void deleteUserCredentials(TenantId tenantId, UserCredentials userCredentials); + void deleteUser(TenantId tenantId, User user); PageData findUsersByTenantId(TenantId tenantId, PageLink pageLink); @@ -110,4 +119,14 @@ public interface UserService extends EntityDaoService { void removeMobileSession(TenantId tenantId, String mobileToken); int countTenantAdmins(TenantId tenantId); + + PageData findUsersByFilter(TenantId tenantId, UsersFilter filter, PageLink pageLink); + + boolean matchesFilter(TenantId tenantId, SystemLevelUsersFilter filter, User user); + + UserAuthDetails findUserAuthDetailsByUserId(TenantId tenantId, UserId userId); + + + List findUsersByTenantIdAndIds(TenantId tenantId, List userIds); + } 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 4838fc176a..180fed9eb3 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 @@ -52,4 +52,6 @@ public interface WidgetsBundleService extends EntityDaoService { void updateSystemWidgets(Stream bundles, Stream widgets); + List findSystemOrTenantWidgetsBundlesByIds(TenantId tenantId, List widgetsBundleIds); + } diff --git a/common/data/pom.xml b/common/data/pom.xml index e4cbf00b7a..ccb0eda1f3 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common 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 942a5d4227..e5ca526938 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 @@ -40,6 +40,7 @@ public final class CacheConstants { public static final String SENT_NOTIFICATIONS_CACHE = "sentNotifications"; public static final String TRENDZ_SETTINGS_CACHE = "trendzSettings"; public static final String AI_MODEL_CACHE = "aiModel"; + public static final String API_KEYS_CACHE = "apiKeys"; public static final String ASSET_PROFILE_CACHE = "assetProfiles"; public static final String ATTRIBUTES_CACHE = "attributes"; 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 7ac8e434ef..de46575e06 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 @@ -41,6 +41,7 @@ public class DataConstants { 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 CF_NAME_METADATA_KEY = "calculatedFieldName"; public static final String OVERWRITE_ACTIVITY_TIME_PARAMETER = "overwriteActivityTime"; public static final String COAP_TRANSPORT_NAME = "COAP"; 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 9ab3ee28cf..3039e992b0 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 @@ -28,6 +28,7 @@ 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.EntityId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; @@ -142,6 +143,11 @@ public class Device extends BaseDataWithAdditionalInfo implements HasL this.customerId = customerId; } + @JsonIgnore + public EntityId getOwnerId() { + return customerId != null && !customerId.isNullUid() ? customerId : tenantId; + } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Unique Device Name in scope of Tenant", example = "A4B72CCDFF33") @Override public String getName() { 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 a4317b8aa6..8d0c44800c 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 @@ -114,6 +114,9 @@ public class DeviceProfile extends BaseData implements HasName, this.description = deviceProfile.getDescription(); this.image = deviceProfile.getImage(); this.isDefault = deviceProfile.isDefault(); + this.type = deviceProfile.getType(); + this.transportType = deviceProfile.getTransportType(); + this.provisionType = deviceProfile.getProvisionType(); this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); this.defaultDashboardId = deviceProfile.getDefaultDashboardId(); this.defaultQueueName = deviceProfile.getDefaultQueueName(); 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 a95208e457..deb0f8c66e 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 @@ -17,12 +17,14 @@ package org.thingsboard.server.common.data; import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import java.util.Arrays; import java.util.EnumSet; import java.util.List; public enum EntityType { + TENANT(1), CUSTOMER(2), USER(3, "tb_user"), @@ -61,7 +63,7 @@ public enum EntityType { MOBILE_APP(37), MOBILE_APP_BUNDLE(38), CALCULATED_FIELD(39), - CALCULATED_FIELD_LINK(40), + // CALCULATED_FIELD_LINK(40), - was removed in 4.3 JOB(41), ADMIN_SETTINGS(42), AI_MODEL(43, "ai_model") { @@ -69,14 +71,15 @@ public enum EntityType { public String getNormalName() { return "AI model"; } - }; + }, + API_KEY(44); @Getter private final int protoNumber; // Corresponds to EntityTypeProto @Getter private final String tableName; @Getter - private final String normalName = StringUtils.capitalize(StringUtils.removeStart(name(), "TB_") + private final String normalName = StringUtils.capitalize(Strings.CS.removeStart(name(), "TB_") .toLowerCase().replaceAll("_", " ")); public static final List NORMAL_NAMES = EnumSet.allOf(EntityType.class).stream() diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/NameConflictPolicy.java b/common/data/src/main/java/org/thingsboard/server/common/data/NameConflictPolicy.java new file mode 100644 index 0000000000..8386c32387 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/NameConflictPolicy.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 enum NameConflictPolicy { + + FAIL, + UNIQUIFY; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/NameConflictStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/NameConflictStrategy.java new file mode 100644 index 0000000000..ab43448f28 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/NameConflictStrategy.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.v3.oas.annotations.media.Schema; + +@Schema +public record NameConflictStrategy(NameConflictPolicy policy, String separator, UniquifyStrategy uniquifyStrategy) { + + public static final NameConflictStrategy DEFAULT = new NameConflictStrategy(NameConflictPolicy.FAIL, null, null); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ProfileEntityIdInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/ProfileEntityIdInfo.java index 3d162de1e7..cd467b01d2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ProfileEntityIdInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ProfileEntityIdInfo.java @@ -34,21 +34,23 @@ public class ProfileEntityIdInfo implements Serializable, HasTenantId { private static final long serialVersionUID = 8532058281983868003L; private final TenantId tenantId; + private final EntityId ownerId; private final EntityId profileId; private final EntityId entityId; - private ProfileEntityIdInfo(UUID tenantId, EntityId profileId, EntityId entityId) { + private ProfileEntityIdInfo(UUID tenantId, EntityId ownerId, EntityId profileId, EntityId entityId) { this.tenantId = TenantId.fromUUID(tenantId); + this.ownerId = ownerId; this.profileId = profileId; this.entityId = entityId; } - public static ProfileEntityIdInfo create(UUID tenantId, DeviceProfileId profileId, DeviceId entityId) { - return new ProfileEntityIdInfo(tenantId, profileId, entityId); + public static ProfileEntityIdInfo create(UUID tenantId, EntityId ownerId, DeviceProfileId profileId, DeviceId entityId) { + return new ProfileEntityIdInfo(tenantId, ownerId, profileId, entityId); } - public static ProfileEntityIdInfo create(UUID tenantId, AssetProfileId profileId, AssetId entityId) { - return new ProfileEntityIdInfo(tenantId, profileId, entityId); + public static ProfileEntityIdInfo create(UUID tenantId, EntityId ownerId, AssetProfileId profileId, AssetId entityId) { + return new ProfileEntityIdInfo(tenantId, ownerId, profileId, entityId); } } 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 37242dfa1d..3cb6e024fc 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 @@ -17,12 +17,14 @@ package org.thingsboard.server.common.data; import com.google.common.base.Splitter; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.Strings; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; +import java.util.Objects; import java.util.function.Function; import static org.apache.commons.lang3.StringUtils.repeat; @@ -131,7 +133,7 @@ public class StringUtils { } public static boolean endsWith(String str, String suffix) { - return org.apache.commons.lang3.StringUtils.endsWith(str, suffix); + return Strings.CS.endsWith(str, suffix); } public static boolean hasLength(String str) { @@ -147,7 +149,7 @@ public class StringUtils { } public static String defaultString(String s, String defaultValue) { - return org.apache.commons.lang3.StringUtils.defaultString(s, defaultValue); + return Objects.toString(s, defaultValue); } public static boolean isNumeric(String str) { @@ -155,7 +157,7 @@ public class StringUtils { } public static boolean equals(String str1, String str2) { - return org.apache.commons.lang3.StringUtils.equals(str1, str2); + return Strings.CS.equals(str1, str2); } public static boolean equalsAny(String string, String... otherStrings) { @@ -199,7 +201,7 @@ public class StringUtils { } public static boolean contains(final CharSequence seq, final CharSequence searchSeq) { - return org.apache.commons.lang3.StringUtils.contains(seq, searchSeq); + return Strings.CS.contains(seq, searchSeq); } /** @@ -210,23 +212,23 @@ public class StringUtils { } public static String randomNumeric(int length) { - return RandomStringUtils.randomNumeric(length); + return RandomStringUtils.secure().nextNumeric(length); } public static String random(int length) { - return RandomStringUtils.random(length); + return RandomStringUtils.secure().next(length); } public static String random(int length, String chars) { - return RandomStringUtils.random(length, chars); + return RandomStringUtils.secure().next(length, chars); } public static String randomAlphanumeric(int count) { - return RandomStringUtils.randomAlphanumeric(count); + return RandomStringUtils.secure().nextAlphanumeric(count); } public static String randomAlphabetic(int count) { - return RandomStringUtils.randomAlphabetic(count); + return RandomStringUtils.secure().nextAlphabetic(count); } public static String generateSafeToken(int length) { 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 c31b49325e..3cd2ada38f 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 @@ -38,5 +38,11 @@ public class SystemParams { String calculatedFieldDebugPerTenantLimitsConfiguration; long maxArgumentsPerCF; long maxDataPointsPerRollingArg; + int minAllowedScheduledUpdateIntervalInSecForCF; + int maxRelationLevelPerCfArgument; + int maxRelatedEntitiesToReturnPerCfArgument; + long minAllowedDeduplicationIntervalInSecForCF; + long minAllowedAggregationIntervalInSecForCF; + long intermediateAggregationIntervalInSecForCF; TrendzSettings trendzSettings; } 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 0aa0010231..f1cca4e80c 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 @@ -28,6 +28,7 @@ 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.io.Serial; import java.util.function.UnaryOperator; @Schema @@ -36,6 +37,7 @@ import java.util.function.UnaryOperator; @EqualsAndHashCode(callSuper = true) public class TbResourceInfo extends BaseData implements HasName, HasTenantId, ExportableEntity { + @Serial private static final long serialVersionUID = 7282664529021651736L; @Schema(description = "JSON object with Tenant Id. Tenant Id of the resource can't be changed.", accessMode = Schema.AccessMode.READ_ONLY) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/UniquifyStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/UniquifyStrategy.java new file mode 100644 index 0000000000..addf0b8b07 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/UniquifyStrategy.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 enum UniquifyStrategy { + + RANDOM, + INCREMENTAL; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/UserAuthDetails.java b/common/data/src/main/java/org/thingsboard/server/common/data/UserAuthDetails.java new file mode 100644 index 0000000000..f71b1a7f73 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/UserAuthDetails.java @@ -0,0 +1,18 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 record UserAuthDetails(User user, boolean credentialsEnabled) {} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentSubType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentSubType.java new file mode 100644 index 0000000000..80d08a594d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentSubType.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm; + +import lombok.Getter; + +public enum AlarmCommentSubType { + + ACKED_BY_USER("Alarm was acknowledged by user %s"), + CLEARED_BY_USER("Alarm was cleared by user %s"), + ASSIGNED_TO_USER("Alarm was assigned by user %s to user %s"), + UNASSIGNED_BY_USER("Alarm was unassigned by user %s"), + UNASSIGNED_FROM_DELETED_USER("Alarm was unassigned because user %s - was deleted"), + COMMENT_DELETED("User %s deleted his comment"), + SEVERITY_CHANGED("Alarm severity was updated from %s to %s"); + + @Getter + private final String text; + + AlarmCommentSubType(String text) { + this.text = text; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java index d1481f756d..bb85f772e7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java @@ -21,11 +21,14 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import java.io.Serial; + @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Schema public class AlarmInfo extends Alarm { + @Serial private static final long serialVersionUID = 2807343093519543363L; @Getter @@ -38,6 +41,10 @@ public class AlarmInfo extends Alarm { @Schema(description = "Alarm originator label", example = "Thermostat label") private String originatorLabel; + @Setter + @Schema(description = "Originator display name", example = "Thermostat") + private String originatorDisplayName; + @Getter @Setter @Schema(description = "Alarm assignee") @@ -51,10 +58,15 @@ public class AlarmInfo extends Alarm { super(alarm); } + public String getOriginatorDisplayName() { + return originatorDisplayName != null ? originatorDisplayName : (originatorLabel != null ? originatorLabel : originatorName); + } + public AlarmInfo(AlarmInfo alarmInfo) { super(alarmInfo); - this.originatorName = alarmInfo.originatorName; - this.originatorLabel = alarmInfo.originatorLabel; + this.originatorName = alarmInfo.getOriginatorName(); + this.originatorLabel = alarmInfo.getOriginatorLabel(); + this.originatorDisplayName = alarmInfo.getOriginatorDisplayName(); this.assignee = alarmInfo.getAssignee(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java new file mode 100644 index 0000000000..93370c0c30 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmCondition; +import org.thingsboard.server.common.data.id.DashboardId; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AlarmRule { + + @Valid + @NotNull + private AlarmCondition condition; + private String alarmDetails; + private DashboardId dashboardId; + + @JsonIgnore + public boolean requiresScheduledReevaluation() { + return condition.requiresScheduledReevaluation(); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java new file mode 100644 index 0000000000..1d50f3343c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition; + +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 jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSchedule; +import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AnyTimeSchedule; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(name = "SIMPLE", value = SimpleAlarmCondition.class), + @Type(name = "DURATION", value = DurationAlarmCondition.class), + @Type(name = "REPEATING", value = RepeatingAlarmCondition.class), +}) +@Data +@NoArgsConstructor +public abstract class AlarmCondition { + + @NotNull + @Valid + private AlarmConditionExpression expression; + @Valid + private AlarmConditionValue schedule; + + @JsonIgnore + public boolean hasSchedule() { + return schedule != null && !(schedule.getStaticValue() instanceof AnyTimeSchedule); + } + + @JsonIgnore + public boolean requiresScheduledReevaluation() { + return hasSchedule() || expression.requiresScheduledReevaluation(); + } + + @JsonIgnore + @AssertTrue(message = "Expressions requiring scheduled reevaluation can only be used with simple alarm conditions") + public boolean isValid() { + if (getType() != AlarmConditionType.SIMPLE && expression.requiresScheduledReevaluation()) { + return false; + } + return true; + } + + @JsonIgnore + public abstract AlarmConditionType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmConditionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmConditionType.java new file mode 100644 index 0000000000..4810a74f99 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmConditionType.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition; + +public enum AlarmConditionType { + SIMPLE, + DURATION, + REPEATING +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmConditionValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmConditionValue.java new file mode 100644 index 0000000000..d55faf6fbf --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmConditionValue.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.AssertTrue; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AlarmConditionValue { + + private T staticValue; + private String dynamicValueArgument; + + @JsonIgnore + @AssertTrue(message = "Either staticValue or dynamicValueArgument must be set") + public boolean isValid() { + return staticValue != null ^ dynamicValueArgument != null; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/DurationAlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/DurationAlarmCondition.java new file mode 100644 index 0000000000..0a4a995ab0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/DurationAlarmCondition.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.concurrent.TimeUnit; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DurationAlarmCondition extends AlarmCondition { + + @NotNull + private TimeUnit unit; + @Valid + @NotNull + private AlarmConditionValue value; + + @Override + public AlarmConditionType getType() { + return AlarmConditionType.DURATION; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/RepeatingAlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/RepeatingAlarmCondition.java new file mode 100644 index 0000000000..28472d0556 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/RepeatingAlarmCondition.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RepeatingAlarmCondition extends AlarmCondition { + + @Valid + @NotNull + private AlarmConditionValue count; + + @Override + public AlarmConditionType getType() { + return AlarmConditionType.REPEATING; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/SimpleAlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/SimpleAlarmCondition.java new file mode 100644 index 0000000000..73dfac9e60 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/SimpleAlarmCondition.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition; + +public class SimpleAlarmCondition extends AlarmCondition { + + @Override + public AlarmConditionType getType() { + return AlarmConditionType.SIMPLE; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpression.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpression.java new file mode 100644 index 0000000000..e02d438c0d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpression.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression; + +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; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(name = "SIMPLE", value = SimpleAlarmConditionExpression.class), + @Type(name = "TBEL", value = TbelAlarmConditionExpression.class), +}) +public interface AlarmConditionExpression { + + @JsonIgnore + AlarmConditionExpressionType getType(); + + @JsonIgnore + default boolean requiresScheduledReevaluation() { + return false; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpressionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpressionType.java new file mode 100644 index 0000000000..b683188436 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpressionType.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression; + +public enum AlarmConditionExpressionType { + SIMPLE, + TBEL +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java new file mode 100644 index 0000000000..448d4586ff --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.ComplexFilterPredicate; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.FilterPredicateType; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.KeyFilterPredicate; +import org.thingsboard.server.common.data.query.EntityKeyValueType; + +import java.io.Serializable; +import java.util.List; + +@Data +public class AlarmConditionFilter implements Serializable { + + @NotBlank + private String argument; + @NotNull + private EntityKeyValueType valueType; + private ComplexOperation operation; + @Valid + @NotEmpty + private List predicates; + + public boolean hasPredicate(FilterPredicateType type) { + return containsPredicate(predicates, type); + } + + private boolean containsPredicate(List predicates, FilterPredicateType type) { + return predicates.stream().anyMatch(predicate -> { + if (predicate instanceof ComplexFilterPredicate complexPredicate) { + return containsPredicate(complexPredicate.getPredicates(), type); + } else { + return predicate.getType() == type; + } + }); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/ComplexOperation.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/ComplexOperation.java new file mode 100644 index 0000000000..492fc683dd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/ComplexOperation.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression; + +public enum ComplexOperation { + AND, + OR +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java new file mode 100644 index 0000000000..ec5407b19d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.FilterPredicateType; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SimpleAlarmConditionExpression implements AlarmConditionExpression { + + @Valid + @NotEmpty + private List filters; + private ComplexOperation operation; + + @Override + public AlarmConditionExpressionType getType() { + return AlarmConditionExpressionType.SIMPLE; + } + + @Override + public boolean requiresScheduledReevaluation() { + return filters.stream().anyMatch(filter -> filter.hasPredicate(FilterPredicateType.NO_DATA)); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/TbelAlarmConditionExpression.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/TbelAlarmConditionExpression.java new file mode 100644 index 0000000000..e7a24dff72 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/TbelAlarmConditionExpression.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TbelAlarmConditionExpression implements AlarmConditionExpression { + + @NotBlank + private String expression; + + @Override + public AlarmConditionExpressionType getType() { + return AlarmConditionExpressionType.TBEL; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java new file mode 100644 index 0000000000..15731ae1fb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; + +@Data +public class BooleanFilterPredicate implements SimpleKeyFilterPredicate { + + @NotNull + private BooleanOperation operation; + @Valid + @NotNull + private AlarmConditionValue value; + + @Override + public FilterPredicateType getType() { + return FilterPredicateType.BOOLEAN; + } + + public enum BooleanOperation { + EQUAL, + NOT_EQUAL + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java new file mode 100644 index 0000000000..5fa7c17107 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import lombok.Data; +import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation; + +import java.util.List; + +@Data +public class ComplexFilterPredicate implements KeyFilterPredicate { + + private ComplexOperation operation; + private List predicates; + + @Override + public FilterPredicateType getType() { + return FilterPredicateType.COMPLEX; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java new file mode 100644 index 0000000000..3b5fa638d8 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +public enum FilterPredicateType { + STRING, + NUMERIC, + BOOLEAN, + NO_DATA, + COMPLEX +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java new file mode 100644 index 0000000000..24f7a6acf6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.io.Serializable; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(value = StringFilterPredicate.class, name = "STRING"), + @Type(value = NumericFilterPredicate.class, name = "NUMERIC"), + @Type(value = BooleanFilterPredicate.class, name = "BOOLEAN"), + @Type(value = NoDataFilterPredicate.class, name = "NO_DATA"), + @Type(value = ComplexFilterPredicate.class, name = "COMPLEX") +}) +public interface KeyFilterPredicate extends Serializable { + + @JsonIgnore + FilterPredicateType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NoDataFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NoDataFilterPredicate.java new file mode 100644 index 0000000000..d6f733d2c1 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NoDataFilterPredicate.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; + +import java.util.concurrent.TimeUnit; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class NoDataFilterPredicate implements KeyFilterPredicate { + + @NotNull + private TimeUnit unit; + @Valid + @NotNull + private AlarmConditionValue duration; + + @Override + public FilterPredicateType getType() { + return FilterPredicateType.NO_DATA; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java new file mode 100644 index 0000000000..5622836c93 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class NumericFilterPredicate implements SimpleKeyFilterPredicate { + + @NotNull + private NumericOperation operation; + @Valid + @NotNull + private AlarmConditionValue value; + + @Override + public FilterPredicateType getType() { + return FilterPredicateType.NUMERIC; + } + + public enum NumericOperation { + EQUAL, + NOT_EQUAL, + GREATER, + LESS, + GREATER_OR_EQUAL, + LESS_OR_EQUAL + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java new file mode 100644 index 0000000000..952a5c2d46 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; + +public interface SimpleKeyFilterPredicate extends KeyFilterPredicate { + + AlarmConditionValue getValue(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java new file mode 100644 index 0000000000..1053f21271 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.expression.predicate; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue; + +@Data +public class StringFilterPredicate implements SimpleKeyFilterPredicate { + + @NotNull + private StringOperation operation; + @Valid + @NotNull + private AlarmConditionValue value; + private boolean ignoreCase; + + @Override + public FilterPredicateType getType() { + return FilterPredicateType.STRING; + } + + public enum StringOperation { + EQUAL, + NOT_EQUAL, + STARTS_WITH, + ENDS_WITH, + CONTAINS, + NOT_CONTAINS, + IN, + NOT_IN + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmSchedule.java new file mode 100644 index 0000000000..e764a9fb46 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmSchedule.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.schedule; + +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 = "type") +@JsonSubTypes({ + @Type(value = AnyTimeSchedule.class, name = "ANY_TIME"), + @Type(value = SpecificTimeSchedule.class, name = "SPECIFIC_TIME"), + @Type(value = CustomTimeSchedule.class, name = "CUSTOM") +}) +public interface AlarmSchedule extends Serializable { + + @JsonIgnore + AlarmScheduleType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmScheduleType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmScheduleType.java new file mode 100644 index 0000000000..73e9e80159 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmScheduleType.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.schedule; + +public enum AlarmScheduleType { + ANY_TIME, + SPECIFIC_TIME, + CUSTOM +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AnyTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AnyTimeSchedule.java new file mode 100644 index 0000000000..2d0454c16e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AnyTimeSchedule.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.schedule; + +public class AnyTimeSchedule implements AlarmSchedule { + + @Override + public AlarmScheduleType getType() { + return AlarmScheduleType.ANY_TIME; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeSchedule.java new file mode 100644 index 0000000000..bbb1fd0390 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeSchedule.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.schedule; + +import lombok.Data; + +import java.util.List; + +@Data +public class CustomTimeSchedule implements AlarmSchedule { + + private String timezone; + private List items; + + @Override + public AlarmScheduleType getType() { + return AlarmScheduleType.CUSTOM; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeScheduleItem.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeScheduleItem.java new file mode 100644 index 0000000000..47d60e732b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeScheduleItem.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.schedule; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class CustomTimeScheduleItem implements Serializable { + + private boolean enabled; + private int dayOfWeek; + private long startsOn; + private long endsOn; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/SpecificTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/SpecificTimeSchedule.java new file mode 100644 index 0000000000..733081b81f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/SpecificTimeSchedule.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.alarm.rule.condition.schedule; + +import lombok.Data; + +import java.util.Set; + +@Data +public class SpecificTimeSchedule implements AlarmSchedule { + + private String timezone; + private Set daysOfWeek; + private long startsOn; + private long endsOn; + + @Override + public AlarmScheduleType getType() { + return AlarmScheduleType.SPECIFIC_TIME; + } + +} 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 d2845cdd66..ad53f070f6 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.asset; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; import lombok.EqualsAndHashCode; @@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.HasVersion; 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.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @@ -125,6 +127,11 @@ public class Asset extends BaseDataWithAdditionalInfo implements HasLab this.customerId = customerId; } + @JsonIgnore + public EntityId getOwnerId() { + return customerId != null && !customerId.isNullUid() ? customerId : tenantId; + } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Unique Asset Name in scope of Tenant", example = "Empire State Building") @Override public String getName() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java index 5ab8e45b93..36fd677299 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java @@ -18,11 +18,14 @@ package org.thingsboard.server.common.data.cf; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasDebugSettings; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; @@ -37,6 +40,10 @@ import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import java.io.Serial; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; @Schema @Data @@ -46,6 +53,22 @@ public class CalculatedField extends BaseData implements HasN @Serial private static final long serialVersionUID = 4491966747773381420L; + public static final Map> SUPPORTED_ENTITIES = Map.of( + EntityType.DEVICE, CalculatedFieldType.all, + EntityType.ASSET, CalculatedFieldType.all, + EntityType.DEVICE_PROFILE, CalculatedFieldType.all, + EntityType.ASSET_PROFILE, CalculatedFieldType.all, + EntityType.CUSTOMER, Set.of(CalculatedFieldType.ALARM) + ); + + public static final Set SUPPORTED_REFERENCED_ENTITIES = Collections.unmodifiableSet(EnumSet.of( + EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.TENANT + )); + + public static boolean isSupportedRefEntity(EntityId entity) { + return SUPPORTED_REFERENCED_ENTITIES.contains(entity.getEntityType()); + } + private TenantId tenantId; private EntityId entityId; @@ -64,6 +87,8 @@ public class CalculatedField extends BaseData implements HasN @Schema(description = "Version of calculated field configuration.", example = "0") private int configurationVersion; @Schema(implementation = SimpleCalculatedFieldConfiguration.class) + @Valid + @NotNull private CalculatedFieldConfiguration configuration; @Getter @Setter diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldEventType.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldEventType.java new file mode 100644 index 0000000000..e671bfd27b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldEventType.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +public enum CalculatedFieldEventType { + + INITIALIZED, + UPDATED, + + TENANT_PROFILE_UPDATED, + OWNER_CHANGED, + RELATION_ADD_OR_UPDATE, + RELATION_DELETED, + + REEVALUATION_MSG + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldFilter.java new file mode 100644 index 0000000000..81a50e69c4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldFilter.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +import lombok.Builder; +import lombok.Data; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.thingsboard.server.common.data.EntityType; + +import java.util.Set; +import java.util.UUID; + +@Data +@Builder +public class CalculatedFieldFilter { + + @NonNull + private final Set types; + @NonNull + private final Set entityTypes; + @Nullable + private final Set entityIds; + @Nullable + private final Set names; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldInfo.java new file mode 100644 index 0000000000..68b60d30a5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldInfo.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class CalculatedFieldInfo extends CalculatedField { + + private String entityName; + + public CalculatedFieldInfo(CalculatedField calculatedField, String entityName) { + super(calculatedField); + this.entityName = entityName; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldLink.java index bf2e6dcd22..809d5dfb31 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldLink.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldLink.java @@ -15,52 +15,8 @@ */ package org.thingsboard.server.common.data.cf; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -@Schema -@Data -@EqualsAndHashCode(callSuper = true) -public class CalculatedFieldLink extends BaseData { - - private static final long serialVersionUID = 6492846246722091530L; - - private TenantId tenantId; - private EntityId entityId; - - @Schema(description = "JSON object with the Calculated Field Id. ", accessMode = Schema.AccessMode.READ_ONLY) - private CalculatedFieldId calculatedFieldId; - - public CalculatedFieldLink() { - super(); - } - - public CalculatedFieldLink(CalculatedFieldLinkId id) { - super(id); - } - - public CalculatedFieldLink(TenantId tenantId, EntityId entityId, CalculatedFieldId calculatedFieldId) { - this.tenantId = tenantId; - this.entityId = entityId; - this.calculatedFieldId = calculatedFieldId; - } - - @Override - public String toString() { - return new StringBuilder() - .append("CalculatedFieldLink[") - .append("tenantId=").append(tenantId) - .append(", entityId=").append(entityId) - .append(", calculatedFieldId=").append(calculatedFieldId) - .append(", createdTime=").append(createdTime) - .append(", id=").append(id).append(']') - .toString(); - } - -} +public record CalculatedFieldLink(TenantId tenantId, EntityId entityId, CalculatedFieldId calculatedFieldId) {} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java index c533aad712..fb944344e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java @@ -15,8 +15,20 @@ */ package org.thingsboard.server.common.data.cf; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + public enum CalculatedFieldType { - SIMPLE, SCRIPT + SIMPLE, + SCRIPT, + GEOFENCING, + ALARM, + PROPAGATION, + RELATED_ENTITIES_AGGREGATION, + ENTITY_AGGREGATION; + + public static final Set all = Collections.unmodifiableSet(EnumSet.allOf(CalculatedFieldType.class)); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..ef3869fccb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.apache.commons.lang3.tuple.Pair; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.rule.AlarmRule; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.util.CollectionsUtil; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.stream.Stream; + +import static java.util.Map.Entry.comparingByKey; + +@Data +public class AlarmCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration { + + private Map arguments; + + @Valid + @NotEmpty + private Map createRules; + @Valid + private AlarmRule clearRule; + + private boolean propagate; + private boolean propagateToOwner; + private boolean propagateToTenant; + private List propagateRelationTypes; + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.ALARM; + } + + @Override + public Output getOutput() { + return null; + } + + @JsonIgnore + @Override + public boolean requiresScheduledReevaluation() { + return getAllRules().anyMatch(entry -> entry.getValue().requiresScheduledReevaluation()); + } + + @JsonIgnore + public Stream> getAllRules() { + Stream> rules = createRules.entrySet().stream() + .map(entry -> Pair.of(entry.getKey(), entry.getValue())); + if (clearRule != null) { + rules = Stream.concat(rules, Stream.of(Pair.of(null, clearRule))); + } + return rules.sorted(comparingByKey(Comparator.nullsLast(Comparator.naturalOrder()))); + } + + public boolean rulesEqual(AlarmCalculatedFieldConfiguration other, BiPredicate equalityCheck) { + List> thisRules = this.getAllRules().toList(); + List> otherRules = other.getAllRules().toList(); + return CollectionsUtil.elementsEqual(thisRules, otherRules, (thisRule, otherRule) -> { + if (!Objects.equals(thisRule.getKey(), otherRule.getKey())) { + return false; + } + return equalityCheck.test(thisRule.getValue(), otherRule.getValue()); + }); + } + + public boolean propagationSettingsEqual(AlarmCalculatedFieldConfiguration other) { + return this.propagate == other.propagate && + this.propagateToOwner == other.propagateToOwner && + this.propagateToTenant == other.propagateToTenant && + Objects.equals(this.propagateRelationTypes, other.propagateRelationTypes); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java index f4e050fa75..9c5f84f75b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java @@ -26,10 +26,27 @@ public class Argument { @Nullable private EntityId refEntityId; + private CfArgumentDynamicSourceConfiguration refDynamicSourceConfiguration; private ReferencedEntityKey refEntityKey; private String defaultValue; private Integer limit; private Long timeWindow; + public boolean hasDynamicSource() { + return refDynamicSourceConfiguration != null; + } + + public boolean hasRelationQuerySource() { + return hasDynamicSource() && refDynamicSourceConfiguration.getType() == CFArgumentDynamicSourceType.RELATION_PATH_QUERY; + } + + public boolean hasOwnerSource() { + return hasDynamicSource() && refDynamicSourceConfiguration.getType() == CFArgumentDynamicSourceType.CURRENT_OWNER; + } + + public boolean hasTsRollingArgument() { + return ArgumentType.TS_ROLLING.equals(refEntityKey.getType()); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ArgumentsBasedCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ArgumentsBasedCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..1dd87a3974 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ArgumentsBasedCalculatedFieldConfiguration.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static java.util.stream.Collectors.toSet; + +public interface ArgumentsBasedCalculatedFieldConfiguration extends CalculatedFieldConfiguration { + + @Valid + @NotEmpty + Map getArguments(); + + default Set getReferencedEntities() { + var args = getArguments(); + if (args == null) { + return Collections.emptySet(); + } + return args.values().stream() + .map(Argument::getRefEntityId) + .filter(Objects::nonNull) + .collect(toSet()); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesImmediateOutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesImmediateOutputStrategy.java new file mode 100644 index 0000000000..2a17232f0f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesImmediateOutputStrategy.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AttributesImmediateOutputStrategy implements AttributesOutputStrategy { + + private boolean sendAttributesUpdatedNotification; + private boolean updateAttributesOnlyOnValueChange; + + private boolean saveAttribute; + private boolean sendWsUpdate; + private boolean processCfs; + + @Override + public OutputStrategyType getType() { + return OutputStrategyType.IMMEDIATE; + } + + @Override + public boolean hasContextOnlyChanges(OutputStrategy other) { + if (!(other instanceof AttributesImmediateOutputStrategy otherStrategy)) { + return true; + } + boolean saveTimeSeriesUpdated = saveAttribute != otherStrategy.isSaveAttribute(); + boolean sendWsUpdateUpdated = sendWsUpdate != otherStrategy.isSendWsUpdate(); + boolean processCfsUpdated = processCfs != otherStrategy.isProcessCfs(); + return saveTimeSeriesUpdated || sendWsUpdateUpdated || processCfsUpdated; + } + + @Override + public boolean hasRefreshContextOnlyChanges(OutputStrategy other) { + if (!(other instanceof AttributesImmediateOutputStrategy otherStrategy)) { + return true; + } + boolean updateAttrOnValueChangedChanged = updateAttributesOnlyOnValueChange != otherStrategy.isUpdateAttributesOnlyOnValueChange(); + boolean sendAttrUpdatedNotificationChanged = sendAttributesUpdatedNotification != otherStrategy.isSendAttributesUpdatedNotification(); + return updateAttrOnValueChangedChanged || sendAttrUpdatedNotificationChanged; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesOutput.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesOutput.java new file mode 100644 index 0000000000..e672c90ed0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesOutput.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.Data; +import org.thingsboard.server.common.data.AttributeScope; + +@Data +public class AttributesOutput implements Output { + + private String name; + private AttributeScope scope; + private Integer decimalsByDefault; + + private AttributesOutputStrategy strategy; + + public AttributesOutput() { + this.strategy = new AttributesRuleChainOutputStrategy(); + } + + @Override + public OutputType getType() { + return OutputType.ATTRIBUTES; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesOutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesOutputStrategy.java new file mode 100644 index 0000000000..02b453b20a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesOutputStrategy.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = AttributesImmediateOutputStrategy.class, name = "IMMEDIATE"), + @JsonSubTypes.Type(value = AttributesRuleChainOutputStrategy.class, name = "RULE_CHAIN"), +}) +public interface AttributesOutputStrategy extends OutputStrategy { +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesRuleChainOutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesRuleChainOutputStrategy.java new file mode 100644 index 0000000000..5d0b4a3abe --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AttributesRuleChainOutputStrategy.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class AttributesRuleChainOutputStrategy implements AttributesOutputStrategy { + + @Override + public OutputStrategyType getType() { + return OutputStrategyType.RULE_CHAIN; + } + + @Override + public boolean hasContextOnlyChanges(OutputStrategy other) { + return !(other instanceof AttributesRuleChainOutputStrategy); + } + + @Override + public boolean hasRefreshContextOnlyChanges(OutputStrategy other) { + return false; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java index dbb7856e16..d2c6ebb78c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java @@ -15,47 +15,32 @@ */ package org.thingsboard.server.common.data.cf.configuration; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; @Data -public abstract class BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration { +public abstract class BaseCalculatedFieldConfiguration implements ExpressionBasedCalculatedFieldConfiguration { protected Map arguments; protected String expression; - protected Output output; - @Override - public List getReferencedEntities() { - return arguments.values().stream() - .map(Argument::getRefEntityId) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } + @NotNull + protected Output output; @Override - public List buildCalculatedFieldLinks(TenantId tenantId, EntityId cfEntityId, CalculatedFieldId calculatedFieldId) { - return getReferencedEntities().stream() - .filter(referencedEntity -> !referencedEntity.equals(cfEntityId)) - .map(referencedEntityId -> buildCalculatedFieldLink(tenantId, referencedEntityId, calculatedFieldId)) - .collect(Collectors.toList()); + public void validate() { + baseCalculatedFieldRestriction(); + if (arguments.values().stream().anyMatch(Argument::hasRelationQuerySource)) { + throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' doesn't support relation query configuration!"); + } } - @Override - public CalculatedFieldLink buildCalculatedFieldLink(TenantId tenantId, EntityId referencedEntityId, CalculatedFieldId calculatedFieldId) { - CalculatedFieldLink link = new CalculatedFieldLink(); - link.setTenantId(tenantId); - link.setEntityId(referencedEntityId); - link.setCalculatedFieldId(calculatedFieldId); - return link; + protected void baseCalculatedFieldRestriction() { + if (arguments.containsKey("ctx")) { + throw new IllegalArgumentException("Argument name 'ctx' is reserved and cannot be used."); + } } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CFArgumentDynamicSourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CFArgumentDynamicSourceType.java new file mode 100644 index 0000000000..192694b32a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CFArgumentDynamicSourceType.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +public enum CFArgumentDynamicSourceType { + + CURRENT_OWNER, + RELATION_PATH_QUERY + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java index 2bf7e0a403..18213b0b7b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java @@ -18,15 +18,21 @@ package org.thingsboard.server.common.data.cf.configuration; 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 org.thingsboard.server.common.data.cf.CalculatedFieldLink; import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import java.util.Collections; import java.util.List; -import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -34,8 +40,13 @@ import java.util.Map; property = "type" ) @JsonSubTypes({ - @JsonSubTypes.Type(value = SimpleCalculatedFieldConfiguration.class, name = "SIMPLE"), - @JsonSubTypes.Type(value = ScriptCalculatedFieldConfiguration.class, name = "SCRIPT") + @Type(value = SimpleCalculatedFieldConfiguration.class, name = "SIMPLE"), + @Type(value = ScriptCalculatedFieldConfiguration.class, name = "SCRIPT"), + @Type(value = GeofencingCalculatedFieldConfiguration.class, name = "GEOFENCING"), + @Type(value = AlarmCalculatedFieldConfiguration.class, name = "ALARM"), + @Type(value = PropagationCalculatedFieldConfiguration.class, name = "PROPAGATION"), + @Type(value = RelatedEntitiesAggregationCalculatedFieldConfiguration.class, name = "RELATED_ENTITIES_AGGREGATION"), + @Type(value = EntityAggregationCalculatedFieldConfiguration.class, name = "ENTITY_AGGREGATION") }) @JsonIgnoreProperties(ignoreUnknown = true) public interface CalculatedFieldConfiguration { @@ -43,19 +54,29 @@ public interface CalculatedFieldConfiguration { @JsonIgnore CalculatedFieldType getType(); - Map getArguments(); - - String getExpression(); - - void setExpression(String expression); - Output getOutput(); + default void validate() {} + @JsonIgnore - List getReferencedEntities(); + default Set getReferencedEntities() { + return Collections.emptySet(); + } + + default CalculatedFieldLink buildCalculatedFieldLink(TenantId tenantId, EntityId referencedEntityId, CalculatedFieldId calculatedFieldId) { + return new CalculatedFieldLink(tenantId, referencedEntityId, calculatedFieldId); + } - List buildCalculatedFieldLinks(TenantId tenantId, EntityId cfEntityId, CalculatedFieldId calculatedFieldId); + default List buildCalculatedFieldLinks(TenantId tenantId, EntityId cfEntityId, CalculatedFieldId calculatedFieldId) { + return getReferencedEntities().stream() + .filter(referencedEntity -> !referencedEntity.equals(cfEntityId)) + .map(referencedEntityId -> buildCalculatedFieldLink(tenantId, referencedEntityId, calculatedFieldId)) + .collect(Collectors.toList()); + } - CalculatedFieldLink buildCalculatedFieldLink(TenantId tenantId, EntityId referencedEntityId, CalculatedFieldId calculatedFieldId); + @JsonIgnore + default boolean requiresScheduledReevaluation() { + return false; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CfArgumentDynamicSourceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CfArgumentDynamicSourceConfiguration.java new file mode 100644 index 0000000000..90c4b3fa39 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CfArgumentDynamicSourceConfiguration.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = RelationPathQueryDynamicSourceConfiguration.class, name = "RELATION_PATH_QUERY"), + @JsonSubTypes.Type(value = CurrentOwnerDynamicSourceConfiguration.class, name = "CURRENT_OWNER") +}) +@JsonIgnoreProperties(ignoreUnknown = true) +public interface CfArgumentDynamicSourceConfiguration { + + @JsonIgnore + CFArgumentDynamicSourceType getType(); + + default void validate() {} + +} diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password.component.scss b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CurrentOwnerDynamicSourceConfiguration.java similarity index 66% rename from ui-ngx/src/app/modules/login/pages/login/reset-password.component.scss rename to common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CurrentOwnerDynamicSourceConfiguration.java index b4a09bd8aa..61efa11557 100644 --- a/ui-ngx/src/app/modules/login/pages/login/reset-password.component.scss +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CurrentOwnerDynamicSourceConfiguration.java @@ -13,22 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import '../../../../../scss/constants'; +package org.thingsboard.server.common.data.cf.configuration; -:host { - display: flex; - flex: 1 1 0; - .tb-reset-password-content { - background-color: #eee; - .tb-reset-password-card { - @media #{$mat-gt-sm} { - width: 450px !important; - } - } +import lombok.Data; + +@Data +public class CurrentOwnerDynamicSourceConfiguration implements CfArgumentDynamicSourceConfiguration { - .tb-card-title{ - padding-top: 0; - padding-bottom: 0; + @Override + public CFArgumentDynamicSourceType getType() { + return CFArgumentDynamicSourceType.CURRENT_OWNER; } - } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ExpressionBasedCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ExpressionBasedCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..5b25d7ca11 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ExpressionBasedCalculatedFieldConfiguration.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +public interface ExpressionBasedCalculatedFieldConfiguration extends ArgumentsBasedCalculatedFieldConfiguration { + + String getExpression(); + + void setExpression(String expression); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasRelationPathLevel.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasRelationPathLevel.java new file mode 100644 index 0000000000..0a71bb7bad --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasRelationPathLevel.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import org.thingsboard.server.common.data.relation.RelationPathLevel; + +public interface HasRelationPathLevel { + + RelationPathLevel getRelation(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasUseLatestTsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasUseLatestTsConfig.java new file mode 100644 index 0000000000..f8fc449171 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasUseLatestTsConfig.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +public interface HasUseLatestTsConfig { + + boolean isUseLatestTs(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Output.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Output.java index 1339678df7..46df6cbad7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Output.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Output.java @@ -15,17 +15,60 @@ */ package org.thingsboard.server.common.data.cf.configuration; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.Data; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.thingsboard.server.common.data.AttributeScope; -@Data +import java.util.Objects; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = TimeSeriesOutput.class, name = "TIME_SERIES"), + @JsonSubTypes.Type(value = AttributesOutput.class, name = "ATTRIBUTES") +}) @JsonInclude(JsonInclude.Include.NON_NULL) -public class Output { +@JsonIgnoreProperties(ignoreUnknown = true) +public interface Output { + + @JsonIgnore + OutputType getType(); + + String getName(); + + OutputStrategy getStrategy(); + + default AttributeScope getScope() { + return null; + } + + Integer getDecimalsByDefault(); + + void setDecimalsByDefault(Integer decimalsByDefault); - private String name; - private OutputType type; - private AttributeScope scope; - private Integer decimalsByDefault; + default boolean hasContextOnlyChanges(Output other) { + if (!getType().equals(other.getType())) { + return true; + } + if (!Objects.equals(getName(), other.getName())) { + return true; + } + if (getScope() != (other.getScope())) { + return true; + } + if (!Objects.equals(getDecimalsByDefault(), other.getDecimalsByDefault())) { + return true; + } + if (getStrategy().hasContextOnlyChanges(other.getStrategy())) { + return true; + } + return false; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/OutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/OutputStrategy.java new file mode 100644 index 0000000000..c34c1040e2 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/OutputStrategy.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public interface OutputStrategy { + + @JsonIgnore + OutputStrategyType getType(); + + boolean hasContextOnlyChanges(OutputStrategy other); + + boolean hasRefreshContextOnlyChanges(OutputStrategy other); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/OutputStrategyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/OutputStrategyType.java new file mode 100644 index 0000000000..454f312ebd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/OutputStrategyType.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +public enum OutputStrategyType { + + IMMEDIATE, RULE_CHAIN + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..d0ae8789b9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java @@ -0,0 +1,101 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.relation.RelationPathLevel; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PropagationCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements HasRelationPathLevel, HasUseLatestTsConfig { + + public static final String PROPAGATION_CONFIG_ARGUMENT = "propagationCtx"; + + @Valid + @NotNull + private RelationPathLevel relation; + + private boolean applyExpressionToResolvedArguments; + + @Override + @JsonIgnore + public boolean isUseLatestTs() { + return output.getType() == OutputType.TIME_SERIES; + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.PROPAGATION; + } + + @Override + public void validate() { + baseCalculatedFieldRestriction(); + propagationRestriction(); + if (!applyExpressionToResolvedArguments) { + arguments.forEach((name, argument) -> { + if (!currentEntitySource(argument)) { + throw new IllegalArgumentException("Arguments in 'Arguments only' propagation mode support only the 'Current entity' source entity type!"); + } + if (argument.getRefEntityKey() == null) { + throw new IllegalArgumentException("Argument: '" + name + "' doesn't have reference entity key configured!"); + } + if (argument.getRefEntityKey().getType() == ArgumentType.TS_ROLLING) { + throw new IllegalArgumentException("Argument type: 'Time series rolling' detected for argument: '" + name + "'. " + + "Only 'Attribute' or 'Latest telemetry' arguments are allowed for 'Arguments only' propagation mode!"); + } + }); + } else { + boolean noneMatchCurrentEntitySource = arguments.entrySet() + .stream() + .noneMatch(entry -> currentEntitySource(entry.getValue())); + if (noneMatchCurrentEntitySource) { + throw new IllegalArgumentException("At least one argument must be configured with the 'Current entity' " + + "source entity type for 'Expression result' propagation mode!"); + } + if (StringUtils.isBlank(expression)) { + throw new IllegalArgumentException("Expression must be specified for 'Expression result' propagation mode!"); + } + } + } + + public Argument toPropagationArgument() { + var refDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + refDynamicSourceConfiguration.setLevels(List.of(relation)); + var propagationArgument = new Argument(); + propagationArgument.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration); + return propagationArgument; + } + + private void propagationRestriction() { + if (arguments.entrySet().stream().anyMatch(entry -> entry.getKey().equals(PROPAGATION_CONFIG_ARGUMENT))) { + throw new IllegalArgumentException("Argument name '" + PROPAGATION_CONFIG_ARGUMENT + "' is reserved and cannot be used."); + } + } + + private boolean currentEntitySource(Argument argument) { + return argument.getRefEntityId() == null && argument.getRefDynamicSourceConfiguration() == null; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationPathQueryDynamicSourceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationPathQueryDynamicSourceConfiguration.java new file mode 100644 index 0000000000..208bb3cc80 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationPathQueryDynamicSourceConfiguration.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationPathQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.util.CollectionsUtil; + +import java.util.List; + +@Data +public class RelationPathQueryDynamicSourceConfiguration implements CfArgumentDynamicSourceConfiguration { + + private List levels; + + @Override + public CFArgumentDynamicSourceType getType() { + return CFArgumentDynamicSourceType.RELATION_PATH_QUERY; + } + + @Override + public void validate() { + if (CollectionsUtil.isEmpty(levels)) { + throw new IllegalArgumentException("At least one relation level must be specified!"); + } + levels.forEach(RelationPathLevel::validate); + } + + public List resolveEntityIds(List relations) { + EntitySearchDirection lastLevelDirection = getLastLevel().direction(); + return switch (lastLevelDirection) { + case FROM -> relations.stream().map(EntityRelation::getTo).toList(); + case TO -> relations.stream().map(EntityRelation::getFrom).toList(); + }; + } + + public void validateMaxRelationLevel(String argumentName, int maxAllowedRelationLevel) { + if (levels.size() > maxAllowedRelationLevel) { + throw new IllegalArgumentException("Max relation level is greater than configured " + + "maximum allowed relation level in tenant profile: " + maxAllowedRelationLevel + " for argument: " + argumentName); + } + } + + public EntityRelationPathQuery toRelationPathQuery(EntityId entityId) { + return new EntityRelationPathQuery(entityId, levels); + } + + private RelationPathLevel getLastLevel() { + return levels.get(levels.size() - 1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScheduledUpdateSupportedCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScheduledUpdateSupportedCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..f5b45c6d5c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScheduledUpdateSupportedCalculatedFieldConfiguration.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import jakarta.validation.constraints.PositiveOrZero; + +public interface ScheduledUpdateSupportedCalculatedFieldConfiguration extends CalculatedFieldConfiguration { + + boolean isScheduledUpdateEnabled(); + + @PositiveOrZero + int getScheduledUpdateInterval(); + + void setScheduledUpdateInterval(int interval); + + default void validate(long minAllowedScheduledUpdateInterval) { + if (getScheduledUpdateInterval() < minAllowedScheduledUpdateInterval) { + throw new IllegalArgumentException("Scheduled update interval is less than configured " + + "minimum allowed interval in tenant profile: " + minAllowedScheduledUpdateInterval); + } + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScriptCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScriptCalculatedFieldConfiguration.java index c4475026de..464f7f54e4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScriptCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScriptCalculatedFieldConfiguration.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; @Data @EqualsAndHashCode(callSuper = true) -public class ScriptCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration { +public class ScriptCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration { @Override public CalculatedFieldType getType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java index cb73f0ae65..53210d1cf8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; @Data @EqualsAndHashCode(callSuper = true) -public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration { +public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements ExpressionBasedCalculatedFieldConfiguration, HasUseLatestTsConfig { private boolean useLatestTs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesImmediateOutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesImmediateOutputStrategy.java new file mode 100644 index 0000000000..33be67e5a6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesImmediateOutputStrategy.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TimeSeriesImmediateOutputStrategy implements TimeSeriesOutputStrategy { + + private long ttl; + + private boolean saveTimeSeries; + private boolean saveLatest; + private boolean sendWsUpdate; + private boolean processCfs; + + @Override + public OutputStrategyType getType() { + return OutputStrategyType.IMMEDIATE; + } + + @Override + public boolean hasContextOnlyChanges(OutputStrategy other) { + if (!(other instanceof TimeSeriesImmediateOutputStrategy otherStrategy)) { + return true; + } + boolean saveTimeSeriesUpdated = saveTimeSeries != otherStrategy.isSaveTimeSeries(); + boolean saveLatestUpdated = saveLatest != otherStrategy.isSaveLatest(); + boolean sendWsUpdateUpdated = sendWsUpdate != otherStrategy.isSendWsUpdate(); + boolean processCfsUpdated = processCfs != otherStrategy.isProcessCfs(); + return saveTimeSeriesUpdated || saveLatestUpdated || sendWsUpdateUpdated || processCfsUpdated; + } + + @Override + public boolean hasRefreshContextOnlyChanges(OutputStrategy other) { + if (!(other instanceof TimeSeriesImmediateOutputStrategy otherStrategy)) { + return true; + } + return ttl != otherStrategy.getTtl(); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesOutput.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesOutput.java new file mode 100644 index 0000000000..a16b9cab89 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesOutput.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.Data; + +@Data +public class TimeSeriesOutput implements Output { + + private String name; + private Integer decimalsByDefault; + + private TimeSeriesOutputStrategy strategy; + + public TimeSeriesOutput() { + this.strategy = new TimeSeriesRuleChainOutputStrategy(); + } + + @Override + public OutputType getType() { + return OutputType.TIME_SERIES; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesOutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesOutputStrategy.java new file mode 100644 index 0000000000..327cad3d5f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesOutputStrategy.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = TimeSeriesImmediateOutputStrategy.class, name = "IMMEDIATE"), + @JsonSubTypes.Type(value = TimeSeriesRuleChainOutputStrategy.class, name = "RULE_CHAIN") +}) +public interface TimeSeriesOutputStrategy extends OutputStrategy { +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesRuleChainOutputStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesRuleChainOutputStrategy.java new file mode 100644 index 0000000000..d4ebb231a2 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/TimeSeriesRuleChainOutputStrategy.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class TimeSeriesRuleChainOutputStrategy implements TimeSeriesOutputStrategy { + + @Override + public OutputStrategyType getType() { + return OutputStrategyType.RULE_CHAIN; + } + + @Override + public boolean hasContextOnlyChanges(OutputStrategy other) { + return !(other instanceof TimeSeriesRuleChainOutputStrategy); + } + + @Override + public boolean hasRefreshContextOnlyChanges(OutputStrategy other) { + return false; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggFunction.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggFunction.java new file mode 100644 index 0000000000..c29ef61beb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggFunction.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +public enum AggFunction { + MIN, MAX, SUM, AVG, COUNT, COUNT_UNIQUE +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggFunctionInput.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggFunctionInput.java new file mode 100644 index 0000000000..ce29e20ea4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggFunctionInput.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AggFunctionInput implements AggInput { + + private String function; + + @Override + public String getType() { + return "function"; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggInput.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggInput.java new file mode 100644 index 0000000000..32db324db9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggInput.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = AggKeyInput.class, name = "key"), + @JsonSubTypes.Type(value = AggFunctionInput.class, name = "function") +}) +@JsonIgnoreProperties(ignoreUnknown = true) +public interface AggInput { + + @JsonIgnore + String getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggKeyInput.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggKeyInput.java new file mode 100644 index 0000000000..55aed3b59a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggKeyInput.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AggKeyInput implements AggInput { + + private String key; + + @Override + public String getType() { + return "key"; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java new file mode 100644 index 0000000000..8a322bd9ce --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AggMetric { + + private AggFunction function; + private String filter; + private AggInput input; + private Double defaultValue; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..d905248aa5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.HasRelationPathLevel; +import org.thingsboard.server.common.data.cf.configuration.HasUseLatestTsConfig; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.relation.RelationPathLevel; + +import java.util.Map; + +@Data +public class RelatedEntitiesAggregationCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration, ScheduledUpdateSupportedCalculatedFieldConfiguration, HasRelationPathLevel, HasUseLatestTsConfig { + + @NotNull + private RelationPathLevel relation; + private Map arguments; + private long deduplicationIntervalInSec; + @Valid + @NotEmpty + private Map metrics; + @Valid + @NotNull + private Output output; + private boolean useLatestTs; + + private int scheduledUpdateInterval; + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.RELATED_ENTITIES_AGGREGATION; + } + + @Override + public boolean isScheduledUpdateEnabled() { + return true; + } + + @Override + public void validate() { + validateRelation(); + validateArguments(); + validateMetrics(); + } + + private void validateRelation() { + if (relation == null) { + throw new IllegalArgumentException("Relation must be specified!"); + } + relation.validate(); + } + + private void validateArguments() { + if (arguments == null || arguments.isEmpty()) { + throw new IllegalArgumentException("Arguments map cannot be empty."); + } + if (arguments.containsKey("ctx")) { + throw new IllegalArgumentException("Argument name 'ctx' is reserved and cannot be used."); + } + if (arguments.values().stream().anyMatch(Argument::hasTsRollingArgument)) { + throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' doesn't support TS_ROLLING arguments."); + } + } + + private void validateMetrics() { + if (metrics == null || metrics.isEmpty()) { + throw new IllegalArgumentException("Metrics map cannot be empty."); + } + + for (AggMetric metric : metrics.values()) { + if (metric.getInput() instanceof AggKeyInput aggKeyInput) { + if (!arguments.containsKey(aggKeyInput.getKey())) { + throw new IllegalArgumentException( + "Metric references unknown argument: '" + aggKeyInput.getKey() + "'." + ); + } + } + } + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..53235136bb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfiguration.java @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; + +import java.util.Map; + +@Data +public class EntityAggregationCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration { + + private Map arguments; + @Valid + @NotEmpty + private Map metrics; + @Valid + @NotNull + private AggInterval interval; + @Valid + private Watermark watermark; + private boolean produceIntermediateResult; + @Valid + @NotNull + private Output output; + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.ENTITY_AGGREGATION; + } + + @Override + public void validate() { + validateArguments(); + validateMetrics(); + validateInterval(); + } + + private void validateArguments() { + if (arguments.containsKey("ctx")) { + throw new IllegalArgumentException("Argument name 'ctx' is reserved and cannot be used."); + } + if (arguments.values().stream().anyMatch(argument -> !ArgumentType.TS_LATEST.equals(argument.getRefEntityKey().getType()))) { + throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' support only TS_LATEST arguments."); + } + } + + private void validateMetrics() { + if (metrics == null || metrics.isEmpty()) { + throw new IllegalArgumentException("Metrics map cannot be empty."); + } + + for (AggMetric metric : metrics.values()) { + if (metric.getInput() instanceof AggKeyInput aggKeyInput) { + if (!arguments.containsKey(aggKeyInput.getKey())) { + throw new IllegalArgumentException( + "Metric references unknown argument: '" + aggKeyInput.getKey() + "'." + ); + } + } else { + throw new IllegalArgumentException("Metric key can only refer to argument."); + } + } + } + + private void validateInterval() { + if (interval == null) { + throw new IllegalArgumentException("Interval must be defined."); + } + interval.validate(); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggInterval.java new file mode 100644 index 0000000000..2eada7f165 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggInterval.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = HourInterval.class, name = "HOUR"), + @JsonSubTypes.Type(value = DayInterval.class, name = "DAY"), + @JsonSubTypes.Type(value = WeekInterval.class, name = "WEEK"), + @JsonSubTypes.Type(value = WeekSunSatInterval.class, name = "WEEK_SUN_SAT"), + @JsonSubTypes.Type(value = MonthInterval.class, name = "MONTH"), + @JsonSubTypes.Type(value = QuarterInterval.class, name = "QUARTER"), + @JsonSubTypes.Type(value = YearInterval.class, name = "YEAR"), + @JsonSubTypes.Type(value = CustomInterval.class, name = "CUSTOM") +}) +@JsonIgnoreProperties(ignoreUnknown = true) +public interface AggInterval { + + @JsonIgnore + AggIntervalType getType(); + + @JsonIgnore + ZoneId getZoneId(); + + @JsonIgnore + long getCurrentIntervalDurationMillis(); + + @JsonIgnore + long getCurrentIntervalStartTs(); + + long getDateTimeIntervalStartTs(ZonedDateTime dateTime); + + @JsonIgnore + long getCurrentIntervalEndTs(); + + long getDateTimeIntervalEndTs(ZonedDateTime dateTime); + + ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart); + + void validate(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggIntervalType.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggIntervalType.java new file mode 100644 index 0000000000..4f98bc08de --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggIntervalType.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +public enum AggIntervalType { + + HOUR, + DAY, + WEEK, + WEEK_SUN_SAT, + MONTH, + QUARTER, + YEAR, + CUSTOM + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/BaseAggInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/BaseAggInterval.java new file mode 100644 index 0000000000..8f77215568 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/BaseAggInterval.java @@ -0,0 +1,108 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.concurrent.TimeUnit; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@AllArgsConstructor +@NoArgsConstructor +public abstract class BaseAggInterval implements AggInterval { + + @NotBlank + protected String tz; + protected Long offsetSec; // delay seconds since start of interval + + @Override + public ZoneId getZoneId() { + return ZoneId.of(tz); + } + + protected long getOffsetSafe() { + return offsetSec != null ? offsetSec : 0L; + } + + @Override + public long getCurrentIntervalDurationMillis() { + return getCurrentIntervalEndTs() - getCurrentIntervalStartTs(); + } + + @Override + public long getCurrentIntervalStartTs() { + ZoneId zoneId = getZoneId(); + ZonedDateTime now = ZonedDateTime.now(zoneId); + return getDateTimeIntervalStartTs(now); + } + + @Override + public long getDateTimeIntervalStartTs(ZonedDateTime dateTime) { + long offset = getOffsetSafe(); + ZonedDateTime shiftedNow = dateTime.minusSeconds(offset); + ZonedDateTime alignedStart = getAlignedBoundary(shiftedNow, false); + ZonedDateTime actualStart = alignedStart.plusSeconds(offset); + return actualStart.toInstant().toEpochMilli(); + } + + @Override + public long getCurrentIntervalEndTs() { + ZoneId zoneId = getZoneId(); + ZonedDateTime now = ZonedDateTime.now(zoneId); + return getDateTimeIntervalEndTs(now); + } + + @Override + public long getDateTimeIntervalEndTs(ZonedDateTime dateTime) { + long offset = getOffsetSafe(); + ZonedDateTime shiftedNow = dateTime.minusSeconds(offset); + ZonedDateTime alignedEnd = getAlignedBoundary(shiftedNow, true); + ZonedDateTime actualEnd = alignedEnd.plusSeconds(offset); + return actualEnd.toInstant().toEpochMilli(); + } + + protected abstract ZonedDateTime alignToIntervalStart(ZonedDateTime reference); + + protected ZonedDateTime getAlignedBoundary(ZonedDateTime reference, boolean next) { + ZonedDateTime base = alignToIntervalStart(reference); + return next ? getNextIntervalStart(base) : base; + } + + @Override + public void validate() { + try { + getZoneId(); + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid timezone in interval: " + ex.getMessage()); + } + if (offsetSec != null) { + if (offsetSec < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); + } + if (TimeUnit.SECONDS.toMillis(offsetSec) >= getCurrentIntervalDurationMillis()) { + throw new IllegalArgumentException("Offset must be greater than interval duration."); + } + } + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/CustomInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/CustomInterval.java new file mode 100644 index 0000000000..3b72f309d8 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/CustomInterval.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.time.Duration; +import java.time.ZonedDateTime; + +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +public class CustomInterval extends BaseAggInterval { + + @NotNull + @Min(1) + private Long durationSec; + + public CustomInterval(String tz, Long offsetSec, Long durationSec) { + super(tz, offsetSec); + this.durationSec = durationSec; + } + + @Override + public AggIntervalType getType() { + return AggIntervalType.CUSTOM; + } + + @Override + public long getCurrentIntervalDurationMillis() { + return getDurationMillis(); + } + + private long getDurationMillis() { + return Duration.ofSeconds(durationSec).toMillis(); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + ZonedDateTime localMidnight = reference.toLocalDate().atStartOfDay(reference.getZone()); + long secondsFromMidnight = Duration.between(localMidnight, reference).getSeconds(); + long alignedSecondsFromMidnight = (secondsFromMidnight / durationSec) * durationSec; + return localMidnight.plusSeconds(alignedSecondsFromMidnight); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusSeconds(durationSec); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/DayInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/DayInterval.java new file mode 100644 index 0000000000..1ff5ed25c2 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/DayInterval.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +@Data +@NoArgsConstructor +public class DayInterval extends BaseAggInterval { + + @Override + public AggIntervalType getType() { + return AggIntervalType.DAY; + } + + public DayInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + return reference.truncatedTo(ChronoUnit.DAYS); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusDays(1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/HourInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/HourInterval.java new file mode 100644 index 0000000000..f20387db82 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/HourInterval.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +public class HourInterval extends BaseAggInterval { + + public HourInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + public AggIntervalType getType() { + return AggIntervalType.HOUR; + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + return reference.truncatedTo(ChronoUnit.HOURS); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusHours(1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/MonthInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/MonthInterval.java new file mode 100644 index 0000000000..3e2dee2eaf --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/MonthInterval.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +@Data +@NoArgsConstructor +public class MonthInterval extends BaseAggInterval { + + @Override + public AggIntervalType getType() { + return AggIntervalType.MONTH; + } + + public MonthInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + return reference.withDayOfMonth(1).truncatedTo(ChronoUnit.DAYS); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusMonths(1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/QuarterInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/QuarterInterval.java new file mode 100644 index 0000000000..6a9123f76f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/QuarterInterval.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; + +@Data +@NoArgsConstructor +public class QuarterInterval extends BaseAggInterval { + + @Override + public AggIntervalType getType() { + return AggIntervalType.QUARTER; + } + + public QuarterInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + int month = reference.getMonthValue(); + int quarterStartMonth = ((month - 1) / 3) * 3 + 1; // 1, 4, 7, 10 + return ZonedDateTime.of( + LocalDate.of(reference.getYear(), quarterStartMonth, 1), + LocalTime.MIDNIGHT, + reference.getZone()); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusMonths(3); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/Watermark.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/Watermark.java new file mode 100644 index 0000000000..7f1a53fd57 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/Watermark.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import jakarta.validation.constraints.Min; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Watermark { + + @Min(0) + private long duration; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/WeekInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/WeekInterval.java new file mode 100644 index 0000000000..311a5254ef --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/WeekInterval.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.DayOfWeek; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; + +@Data +@NoArgsConstructor +public class WeekInterval extends BaseAggInterval { + + @Override + public AggIntervalType getType() { + return AggIntervalType.WEEK; + } + + public WeekInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + return reference.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).truncatedTo(ChronoUnit.DAYS); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusWeeks(1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/WeekSunSatInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/WeekSunSatInterval.java new file mode 100644 index 0000000000..732b82446a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/WeekSunSatInterval.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.DayOfWeek; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; + +@Data +@NoArgsConstructor +public class WeekSunSatInterval extends BaseAggInterval { + + @Override + public AggIntervalType getType() { + return AggIntervalType.WEEK_SUN_SAT; + } + + public WeekSunSatInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + return reference.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)).truncatedTo(ChronoUnit.DAYS); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusWeeks(1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/YearInterval.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/YearInterval.java new file mode 100644 index 0000000000..9f1015f01e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/YearInterval.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; + +@Data +@NoArgsConstructor +public class YearInterval extends BaseAggInterval { + + @Override + public AggIntervalType getType() { + return AggIntervalType.YEAR; + } + + public YearInterval(String tz, Long offsetSec) { + super(tz, offsetSec); + } + + @Override + protected ZonedDateTime alignToIntervalStart(ZonedDateTime reference) { + return ZonedDateTime.of( + LocalDate.of(reference.getYear(), 1, 1), + LocalTime.MIDNIGHT, + reference.getZone()); + } + + @Override + public ZonedDateTime getNextIntervalStart(ZonedDateTime currentStart) { + return currentStart.plusYears(1); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/EntityCoordinates.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/EntityCoordinates.java new file mode 100644 index 0000000000..83d6275441 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/EntityCoordinates.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; + +import java.util.Map; + +@Data +public class EntityCoordinates { + + public static final String ENTITY_ID_LATITUDE_ARGUMENT_KEY = "latitude"; + public static final String ENTITY_ID_LONGITUDE_ARGUMENT_KEY = "longitude"; + + @NotBlank + private final String latitudeKeyName; + @NotBlank + private final String longitudeKeyName; + + public Map toArguments() { + return Map.of( + ENTITY_ID_LATITUDE_ARGUMENT_KEY, toArgument(latitudeKeyName), + ENTITY_ID_LONGITUDE_ARGUMENT_KEY, toArgument(longitudeKeyName) + ); + } + + private Argument toArgument(String keyName) { + var argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey(keyName, ArgumentType.TS_LATEST, null)); + return argument; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java new file mode 100644 index 0000000000..cb408c5dbf --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java @@ -0,0 +1,94 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.HasUseLatestTsConfig; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static java.util.stream.Collectors.toSet; + +@Data +public class GeofencingCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration, ScheduledUpdateSupportedCalculatedFieldConfiguration, HasUseLatestTsConfig { + + @Valid + @NotNull + private EntityCoordinates entityCoordinates; + + @Valid + @NotNull + private Map zoneGroups; + + private boolean scheduledUpdateEnabled; + private int scheduledUpdateInterval; + + @NotNull + private Output output; + + @Override + @JsonIgnore + public boolean isUseLatestTs() { + return output.getType() == OutputType.TIME_SERIES; + } + + @Override + public CalculatedFieldType getType() { + return CalculatedFieldType.GEOFENCING; + } + + @Override + @JsonIgnore + public Map getArguments() { + Map args = new HashMap<>(entityCoordinates.toArguments()); + zoneGroups.forEach((zgName, zgConfig) -> args.put(zgName, zgConfig.toArgument())); + return args; + } + + + @Override + public Set getReferencedEntities() { + return zoneGroups == null ? Collections.emptySet() : zoneGroups.values().stream() + .map(ZoneGroupConfiguration::getRefEntityId) + .filter(Objects::nonNull) + .collect(toSet()); + } + + @Override + public Output getOutput() { + return output; + } + + @Override + public void validate() { + zoneGroups.forEach((key, value) -> value.validate(key)); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingEvent.java new file mode 100644 index 0000000000..973524c9f8 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingEvent.java @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +public sealed interface GeofencingEvent + permits GeofencingTransitionEvent, GeofencingPresenceStatus { } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingPresenceStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingPresenceStatus.java new file mode 100644 index 0000000000..e17d97aa3f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingPresenceStatus.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +import lombok.Getter; + +@Getter +public enum GeofencingPresenceStatus implements GeofencingEvent { + + INSIDE, OUTSIDE; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingReportStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingReportStrategy.java new file mode 100644 index 0000000000..3acb67d12a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingReportStrategy.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +public enum GeofencingReportStrategy { + + REPORT_TRANSITION_EVENTS_ONLY, + REPORT_PRESENCE_STATUS_ONLY, + REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingTransitionEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingTransitionEvent.java new file mode 100644 index 0000000000..a4af68a519 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingTransitionEvent.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +public enum GeofencingTransitionEvent implements GeofencingEvent { + ENTERED, LEFT +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfiguration.java new file mode 100644 index 0000000000..cf2827a47e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfiguration.java @@ -0,0 +1,98 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.lang.Nullable; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.CfArgumentDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ZoneGroupConfiguration { + + @Nullable + private EntityId refEntityId; + private CfArgumentDynamicSourceConfiguration refDynamicSourceConfiguration; + + @NotBlank + private final String perimeterKeyName; + + @NotNull + private final GeofencingReportStrategy reportStrategy; + private final boolean createRelationsWithMatchedZones; + + private String relationType; + private EntitySearchDirection direction; + + public void validate(String name) { + if (EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY.equals(name) || EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY.equals(name)) { + throw new IllegalArgumentException("Name '" + name + "' is reserved and cannot be used for zone group!"); + } + if (refDynamicSourceConfiguration != null) { + refDynamicSourceConfiguration.validate(); + } + if (!createRelationsWithMatchedZones) { + return; + } + if (StringUtils.isBlank(relationType)) { + throw new IllegalArgumentException("Relation type must be specified for '" + name + "' zone group!"); + } + if (direction == null) { + throw new IllegalArgumentException("Relation direction must be specified for '" + name + "' zone group!"); + } + } + + public boolean hasRelationQuerySource() { + return toArgument().hasRelationQuerySource(); + } + + public boolean hasCurrentOwnerSource() { + return toArgument().hasOwnerSource(); + } + + @JsonIgnore + public boolean isCfEntitySource(EntityId cfEntityId) { + if (refEntityId == null && refDynamicSourceConfiguration == null) { + return true; + } + return refEntityId != null && refEntityId.equals(cfEntityId); + } + + @JsonIgnore + public boolean isLinkedCfEntitySource(EntityId cfEntityId) { + return refEntityId != null && !refEntityId.equals(cfEntityId); + } + + public Argument toArgument() { + var argument = new Argument(); + argument.setRefEntityId(refEntityId); + argument.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration); + argument.setRefEntityKey(new ReferencedEntityKey(perimeterKeyName, ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + return argument; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/Lwm2mServerIdentifier.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/Lwm2mServerIdentifier.java new file mode 100644 index 0000000000..33a845074c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/Lwm2mServerIdentifier.java @@ -0,0 +1,112 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.device.credentials.lwm2m; + +/** + * Enum representing predefined LwM2M Short Server Identifiers. + *

+ * See OMA Lightweight M2M Specification for details about the server identifier space. + */ +public enum Lwm2mServerIdentifier { + + /** + * Not used for identifying an LwM2M Server (0). + */ + NOT_USED_IDENTIFYING_LWM2M_SERVER_MIN(0, "Bootstrap Short Server ID", false), + + /** + * Primary LwM2M Server Short Server ID (1). + * Upper boundary for valid LwM2M Server Identifiers (1–65534). + */ + PRIMARY_LWM2M_SERVER(1, "LwM2M Server Short Server ID", true), + + /** + * Maximum valid LwM2M Server ID (65534). + * Upper boundary for valid LwM2M Server Identifiers (1–65534). + */ + LWM2M_SERVER_MAX(65534, "LwM2M Server Short Server ID", true), + + /** + * Not used for identifying an LwM2M Server (65535). + * Reserved sentinel value representing "no server associated" or "invalid ID". + * MUST NOT be assigned to any LwM2M Server according to OMA-TS-LightweightM2M-Core, §6.2.1. + * OMA LwM2M Core / v1.2: Server / Short Server ID): «MAX_ID 65535 is a reserved value and MUST NOT be used for identifying an Object» + */ + NOT_USED_IDENTIFYING_LWM2M_SERVER_MAX(65535, "Reserved sentinel value (no active server)", false); + + private final Integer id; + private final String description; + private final boolean isLwm2mServer; + + Lwm2mServerIdentifier(Integer id, String description, boolean isLwm2mServer) { + this.id = id; + this.description = description; + this.isLwm2mServer = isLwm2mServer; + } + + /** + * @return the integer value of this Short Server ID. + */ + public Integer getId() { + return id; + } + + /** + * @return a human-readable description of this Server ID. + */ + public String getDescription() { + return description; + } + + /** + * @return true if this ID represents a Lwm2m Server. + */ + public boolean isLwm2mServer() { + return isLwm2mServer; + } + + /** + * Checks whether a given ID represents a valid LwM2M Server (1–65534). + * @param id Short Server ID value. + * @return true if the ID belongs to a standard LwM2M Server. + */ + public static boolean isLwm2mServer(Integer id) { + return id != null && id >= PRIMARY_LWM2M_SERVER.id && id <= LWM2M_SERVER_MAX.id; + } + public static boolean isNotLwm2mServer(Integer id) { + return id == null || id < PRIMARY_LWM2M_SERVER.id || id > LWM2M_SERVER_MAX.id; + } + + /** + * Returns a {@link Lwm2mServerIdentifier} instance matching the given ID. + * @param id numeric ID. + * @return corresponding enum constant. + * @throws IllegalArgumentException if no constant matches the given ID. + */ + public static Lwm2mServerIdentifier fromId(Integer id) { + for (Lwm2mServerIdentifier s : values()) { + if (s.id == id) { + return s; + } + } + throw new IllegalArgumentException("Unknown Lwm2mServerIdentifier: " + id); + } + + @Override + public String toString() { + return name() + "(" + id + ") - " + description; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java index a4da4ceae0..838239b091 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java @@ -26,6 +26,7 @@ import java.util.List; @Schema @Data @JsonIgnoreProperties(ignoreUnknown = true) +@Deprecated public class AlarmCondition implements Serializable { @Valid diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java index 17ca7c094f..b2a3083f6f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java @@ -26,6 +26,7 @@ import java.io.Serializable; @Schema @Data +@Deprecated public class AlarmConditionFilter implements Serializable { @Valid diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java index f656ad2a73..df711fd2a2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java @@ -23,6 +23,7 @@ import java.io.Serializable; @Schema @Data +@Deprecated public class AlarmConditionFilterKey implements Serializable { @Schema(description = "The key type", example = "TIME_SERIES") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionKeyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionKeyType.java index 80fdadc68a..7e4ca78de7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionKeyType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionKeyType.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.device.profile; +@Deprecated public enum AlarmConditionKeyType { ATTRIBUTE, TIME_SERIES, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java index 5bb1cd752f..d3066ec8cc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java @@ -31,6 +31,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = SimpleAlarmConditionSpec.class, name = "SIMPLE"), @JsonSubTypes.Type(value = DurationAlarmConditionSpec.class, name = "DURATION"), @JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "REPEATING")}) +@Deprecated public interface AlarmConditionSpec extends Serializable { @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java index f8ce5947ed..b666bf821f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.device.profile; +@Deprecated public enum AlarmConditionSpecType { SIMPLE, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java index d2401082de..9ceea43677 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java @@ -21,12 +21,17 @@ import lombok.Data; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.validation.NoXss; +import java.io.Serial; import java.io.Serializable; @Schema @Data +@Deprecated public class AlarmRule implements Serializable { + @Serial + private static final long serialVersionUID = -7617427132423304707L; + @Valid @Schema(description = "JSON object representing the alarm rule condition") private AlarmCondition condition; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java index 00faaef712..d46fedf87b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java @@ -31,6 +31,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = AnyTimeSchedule.class, name = "ANY_TIME"), @JsonSubTypes.Type(value = SpecificTimeSchedule.class, name = "SPECIFIC_TIME"), @JsonSubTypes.Type(value = CustomTimeSchedule.class, name = "CUSTOM")}) +@Deprecated public interface AlarmSchedule extends Serializable { AlarmScheduleType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java index 56148844fa..7f78dbc46d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.device.profile; +@Deprecated public enum AlarmScheduleType { ANY_TIME, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java index 57003396ef..e7acb04cd3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AnyTimeSchedule.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.profile; import org.thingsboard.server.common.data.query.DynamicValue; +@Deprecated public class AnyTimeSchedule implements AlarmSchedule { @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java index cb22bc775f..878a85360b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.query.DynamicValue; import java.util.List; @Data +@Deprecated public class CustomTimeSchedule implements AlarmSchedule { private String timezone; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeScheduleItem.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeScheduleItem.java index 575f45dc17..0d4f5cf018 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeScheduleItem.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeScheduleItem.java @@ -20,6 +20,7 @@ import lombok.Data; import java.io.Serializable; @Data +@Deprecated public class CustomTimeScheduleItem implements Serializable { private boolean enabled; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java index 0b18e8cf25..c793f6c452 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java @@ -28,6 +28,7 @@ import java.util.TreeMap; @Schema @Data +@Deprecated public class DeviceProfileAlarm implements Serializable { @Schema(description = "String value representing the alarm rule id", example = "highTemperatureAlarmID") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java index b23d5f9a6e..202c11019c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; @Data @JsonIgnoreProperties(ignoreUnknown = true) +@Deprecated public class DurationAlarmConditionSpec implements AlarmConditionSpec { private TimeUnit unit; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java index ccaa730f45..d73e4bf1fe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java @@ -50,6 +50,6 @@ public class Lwm2mDeviceProfileTransportConfiguration implements DeviceProfileTr private void updateDefault(){ this.setBootstrap(Collections.emptyList()); this.setClientLwM2mSettings(new OtherConfiguration(false,1, 1, 1, PowerMode.DRX, null, null, null, null, null, V1_0.toString())); - this.setObserveAttr(new TelemetryMappingConfiguration(Collections.emptyMap(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptyMap(), SINGLE)); + this.setObserveAttr(new TelemetryMappingConfiguration(Collections.emptyMap(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptyMap(), false, SINGLE)); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java index cdbb7a80df..323b29a327 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; @Data @JsonIgnoreProperties(ignoreUnknown = true) +@Deprecated public class RepeatingAlarmConditionSpec implements AlarmConditionSpec { private FilterPredicateValue predicate; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java index 5f492060d0..53a87a6b99 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java @@ -20,6 +20,7 @@ import lombok.Data; @Data @JsonIgnoreProperties(ignoreUnknown = true) +@Deprecated public class SimpleAlarmConditionSpec implements AlarmConditionSpec { @Override public AlarmConditionSpecType getType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java index 5cb546ebd8..dfaf6c763c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.query.DynamicValue; import java.util.Set; @Data +@Deprecated public class SpecificTimeSchedule implements AlarmSchedule { private String timezone; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java index a166d992a4..7362ac3ac3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java @@ -36,6 +36,7 @@ public class TelemetryMappingConfiguration implements Serializable { private Set attribute; private Set telemetry; private Map attributeLwm2m; + private Boolean initAttrTelAsObsStrategy; private TelemetryObserveStrategy observeStrategy; @JsonCreator @@ -45,6 +46,7 @@ public class TelemetryMappingConfiguration implements Serializable { @JsonProperty("attribute") Set attribute, @JsonProperty("telemetry") Set telemetry, @JsonProperty("attributeLwm2m") Map attributeLwm2m, + @JsonProperty("initAttrTelAsObsStrategy") Boolean initAttrTelAsObsStrategy, @JsonProperty("observeStrategy") TelemetryObserveStrategy observeStrategy) { this.keyName = keyName != null ? keyName : Collections.emptyMap(); @@ -52,6 +54,7 @@ public class TelemetryMappingConfiguration implements Serializable { this.attribute = attribute != null ? attribute : Collections.emptySet(); this.telemetry = telemetry != null ? telemetry : Collections.emptySet(); this.attributeLwm2m = attributeLwm2m != null ? attributeLwm2m : Collections.emptyMap(); + this.initAttrTelAsObsStrategy = initAttrTelAsObsStrategy != null ? initAttrTelAsObsStrategy : false; this.observeStrategy = observeStrategy != null ? observeStrategy : TelemetryObserveStrategy.SINGLE; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java index b160c735a9..8d826b6e5c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java @@ -26,7 +26,7 @@ public class LwM2MServerSecurityConfig implements Serializable { @Schema(description = "Server short Id. Used as link to associate server Object Instance. This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client. " + "This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. " + - "The values ID:1 and ID:65534 values MUST NOT be used for identifying the LwM2M Server.", example = "123", accessMode = Schema.AccessMode.READ_ONLY) + "The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.", example = "123", accessMode = Schema.AccessMode.READ_ONLY) protected Integer shortServerId = 123; /** Security -> ObjectId = 0 'LWM2M Security' */ @Schema(description = "Is Bootstrap Server or Lwm2m Server. " + 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 eff86ee5d8..b07c159fa9 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 @@ -47,7 +47,8 @@ public enum EdgeEventType { TB_RESOURCE(true, EntityType.TB_RESOURCE), OAUTH2_CLIENT(true, EntityType.OAUTH2_CLIENT), DOMAIN(true, EntityType.DOMAIN), - CALCULATED_FIELD(false, EntityType.CALCULATED_FIELD); + CALCULATED_FIELD(false, EntityType.CALCULATED_FIELD), + AI_MODEL(true, EntityType.AI_MODEL); private final boolean allEdgesRelated; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsSyncRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsSyncRequest.java index 7d5e268ba1..ee0ff30778 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsSyncRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsSyncRequest.java @@ -16,9 +16,17 @@ package org.thingsboard.server.common.data.edqs; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.ObjectType; + +import java.util.Set; @Data +@AllArgsConstructor +@NoArgsConstructor @JsonIgnoreProperties public class EdqsSyncRequest { + Set objectTypes; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEvent.java index 5aa9cf9ebe..f57bf8861c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEvent.java @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.id.TenantId; import java.util.UUID; -@ToString +@ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) public class CalculatedFieldDebugEvent extends Event { 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 4974b550e4..9750e5edf3 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 @@ -31,6 +31,7 @@ public enum ThingsboardErrorCode { TOO_MANY_UPDATES(34), VERSION_CONFLICT(35), SUBSCRIPTION_VIOLATION(40), + ENTITIES_LIMIT_EXCEEDED(41), PASSWORD_VIOLATION(45), DATABASE(46); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/CalculatedFieldLinkId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/ApiKeyId.java similarity index 67% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/CalculatedFieldLinkId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/ApiKeyId.java index 29bee49ef7..514c29665c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/CalculatedFieldLinkId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/ApiKeyId.java @@ -20,26 +20,27 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import org.thingsboard.server.common.data.EntityType; +import java.io.Serial; import java.util.UUID; -@Schema -public class CalculatedFieldLinkId extends UUIDBased implements EntityId { +public class ApiKeyId extends UUIDBased implements EntityId { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = -273913539653684641L; @JsonCreator - public CalculatedFieldLinkId(@JsonProperty("id") UUID id) { + public ApiKeyId(@JsonProperty("id") UUID id) { super(id); } - public static CalculatedFieldLinkId fromString(String calculatedFieldLinkId) { - return new CalculatedFieldLinkId(UUID.fromString(calculatedFieldLinkId)); + public static ApiKeyId fromString(String secretId) { + return new ApiKeyId(UUID.fromString(secretId)); } - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "CALCULATED_FIELD_LINK", allowableValues = "CALCULATED_FIELD_LINK") @Override + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "API_KEY", allowableValues = "API_KEY") public EntityType getEntityType() { - return EntityType.CALCULATED_FIELD_LINK; + return EntityType.API_KEY; } } 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 11c33c9cdd..93851fa813 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 @@ -80,10 +80,10 @@ public class EntityIdFactory { case DOMAIN -> new DomainId(uuid); case MOBILE_APP_BUNDLE -> new MobileAppBundleId(uuid); case CALCULATED_FIELD -> new CalculatedFieldId(uuid); - case CALCULATED_FIELD_LINK -> new CalculatedFieldLinkId(uuid); case JOB -> new JobId(uuid); case ADMIN_SETTINGS -> new AdminSettingsId(uuid); case AI_MODEL -> new AiModelId(uuid); + case API_KEY -> new ApiKeyId(uuid); }; } @@ -113,6 +113,7 @@ public class EntityIdFactory { case OAUTH2_CLIENT -> new OAuth2ClientId(uuid); case DOMAIN -> new DomainId(uuid); case CALCULATED_FIELD -> new CalculatedFieldId(uuid); + case AI_MODEL -> new AiModelId(uuid); default -> throw new IllegalArgumentException("EdgeEventType " + edgeEventType + " is not supported!"); }; } 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 index 28720a3335..3e60576c27 100644 --- 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 @@ -38,7 +38,10 @@ public enum TbMsgType { ENTITY_UNASSIGNED("Entity Unassigned"), ATTRIBUTES_UPDATED("Attributes Updated"), ATTRIBUTES_DELETED("Attributes Deleted"), - ALARM, + ALARM("Alarm"), + ALARM_CREATED("Alarm Created"), + ALARM_UPDATED("Alarm Updated"), + ALARM_SEVERITY_UPDATED("Alarm Severity Updated"), ALARM_ACK("Alarm Acknowledged"), ALARM_CLEAR("Alarm Cleared"), ALARM_DELETE, 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 921a972225..3584b92441 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 @@ -32,6 +32,7 @@ public enum NotificationType { ALARM_ASSIGNMENT, NEW_PLATFORM_VERSION, ENTITIES_LIMIT, + ENTITIES_LIMIT_INCREASE_REQUEST, API_USAGE_LIMIT, RULE_NODE, RATE_LIMITS, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java index 3fa21a2b7f..941bb6a0a2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java @@ -53,6 +53,7 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati private UUID alarmId; private EntityId alarmOriginator; private String alarmOriginatorName; + private String alarmOriginatorLabel; private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; private CustomerId alarmCustomerId; @@ -77,7 +78,8 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(), "alarmOriginatorId", alarmOriginator.getId().toString(), - "alarmOriginatorName", alarmOriginatorName + "alarmOriginatorName", alarmOriginatorName, + "alarmOriginatorLabel", alarmOriginatorLabel ); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java index dfd59e4e03..b2514c128b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java @@ -48,6 +48,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI private UUID alarmId; private EntityId alarmOriginator; private String alarmOriginatorName; + private String alarmOriginatorLabel; private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; private CustomerId alarmCustomerId; @@ -68,7 +69,8 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(), "alarmOriginatorId", alarmOriginator.getId().toString(), - "alarmOriginatorName", alarmOriginatorName + "alarmOriginatorName", alarmOriginatorName, + "alarmOriginatorLabel", alarmOriginatorLabel ); } 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 6001de0d30..3adcf12f55 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 @@ -25,11 +25,10 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; +import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; - @Data @NoArgsConstructor @AllArgsConstructor @@ -41,25 +40,28 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo { private UUID alarmId; private EntityId alarmOriginator; private String alarmOriginatorName; + private String alarmOriginatorLabel; private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; private boolean acknowledged; private boolean cleared; private CustomerId alarmCustomerId; private DashboardId dashboardId; + private Map details; @Override public Map getTemplateData() { - return mapOf( - "alarmType", alarmType, - "action", action, - "alarmId", alarmId.toString(), - "alarmSeverity", alarmSeverity.name().toLowerCase(), - "alarmStatus", alarmStatus.toString(), - "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(), - "alarmOriginatorName", alarmOriginatorName, - "alarmOriginatorId", alarmOriginator.getId().toString() - ); + Map templateData = details != null ? new HashMap<>(details) : new HashMap<>(); + templateData.put("alarmType", alarmType); + templateData.put("action", action); + templateData.put("alarmId", alarmId.toString()); + templateData.put("alarmSeverity", alarmSeverity.name().toLowerCase()); + templateData.put("alarmStatus", alarmStatus.toString()); + templateData.put("alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName()); + templateData.put("alarmOriginatorName", alarmOriginatorName); + templateData.put("alarmOriginatorLabel", alarmOriginatorLabel); + templateData.put("alarmOriginatorId", alarmOriginator.getId().toString()); + return templateData; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitIncreaseRequestNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitIncreaseRequestNotificationInfo.java new file mode 100644 index 0000000000..f00014567d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitIncreaseRequestNotificationInfo.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.EntityType; + +import java.util.Map; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EntitiesLimitIncreaseRequestNotificationInfo implements NotificationInfo { + + private EntityType entityType; + private String userEmail; + private String increaseLimitActionLabel; + private String increaseLimitLink; + private String baseUrl; + + @Override + public Map getTemplateData() { + return mapOf( + "entityType", entityType.getNormalName(), + "userEmail", userEmail, + "increaseLimitActionLabel", increaseLimitActionLabel, + "increaseLimitLink", increaseLimitLink, + "baseUrl", baseUrl + ); + } +} 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 index 2a07250a4c..db945b9041 100644 --- 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 @@ -25,10 +25,15 @@ 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.Serial; + @Data @Builder public class AlarmCommentTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = -8614770559491757202L; + private final TenantId tenantId; private final AlarmComment comment; private final Alarm alarm; 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 index cfc8d6b00b..ed2fd7b6c1 100644 --- 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 @@ -22,10 +22,15 @@ 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.Serial; + @Data @Builder public class AlarmTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = -466810297904938644L; + private final TenantId tenantId; private final AlarmApiCallResult alarmUpdate; 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 index d9acca4930..e6deee9838 100644 --- 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 @@ -23,6 +23,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import java.io.Serial; import java.util.Set; @Data @@ -31,6 +32,9 @@ import java.util.Set; @Builder public class AlarmAssignmentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @Serial + private static final long serialVersionUID = -5313556049809972096L; + private Set alarmTypes; private Set alarmSeverities; private Set alarmStatuses; 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 index f220002fc0..3aa3e27638 100644 --- 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 @@ -22,6 +22,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import java.io.Serial; import java.util.Set; @Data @@ -30,6 +31,9 @@ import java.util.Set; @Builder public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @Serial + private static final long serialVersionUID = -9164282098882339645L; + private Set alarmTypes; private Set alarmSeverities; private Set alarmStatuses; 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 index 041d10ba90..20d484efa7 100644 --- 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 @@ -23,6 +23,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import java.io.Serial; import java.io.Serializable; import java.util.Set; @@ -32,6 +33,9 @@ import java.util.Set; @Builder public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @Serial + private static final long serialVersionUID = -7382883720381542344L; + private Set alarmTypes; private Set alarmSeverities; @NotEmpty @@ -46,6 +50,8 @@ public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTrigg @Data public static class ClearRule implements Serializable { + @Serial + private static final long serialVersionUID = 7922533150038105124L; private Set alarmStatuses; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java index 3b603f43e9..fbe42938f0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java @@ -18,7 +18,7 @@ package org.thingsboard.server.common.data.notification.targets.platform; import lombok.Data; @Data -public class AllUsersFilter implements UsersFilter { +public class AllUsersFilter implements SystemLevelUsersFilter { @Override public UsersFilterType getType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java index a0b49b1072..8c5330d165 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java @@ -18,7 +18,7 @@ package org.thingsboard.server.common.data.notification.targets.platform; import lombok.Data; @Data -public class SystemAdministratorsFilter implements UsersFilter { +public class SystemAdministratorsFilter implements SystemLevelUsersFilter { @Override public UsersFilterType getType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemLevelUsersFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemLevelUsersFilter.java new file mode 100644 index 0000000000..9d5cfed5a8 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemLevelUsersFilter.java @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.platform; + +public interface SystemLevelUsersFilter extends UsersFilter { +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java index 09641cf069..6f73f45e08 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java @@ -21,7 +21,7 @@ import java.util.Set; import java.util.UUID; @Data -public class TenantAdministratorsFilter implements UsersFilter { +public class TenantAdministratorsFilter implements SystemLevelUsersFilter { private Set tenantsIds; private Set tenantProfilesIds; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/pat/ApiKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/pat/ApiKey.java new file mode 100644 index 0000000000..d305c218ce --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/pat/ApiKey.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.ApiKeyId; +import org.thingsboard.server.common.data.validation.NoXss; + +import java.io.Serial; + +@Schema +@Data +@EqualsAndHashCode(callSuper = true) +public class ApiKey extends ApiKeyInfo { + + @Serial + private static final long serialVersionUID = -2313196723950490263L; + + @NoXss + @Schema(description = "API key value", requiredMode = Schema.RequiredMode.REQUIRED) + private String value; + + public ApiKey() { + super(); + } + + public ApiKey(ApiKeyId id) { + super(id); + } + + public ApiKey(ApiKey apiKey) { + super(apiKey); + this.value = apiKey.getValue(); + } + + public ApiKey(ApiKeyInfo apiKeyInfo) { + super(apiKeyInfo); + this.value = null; + } + + public ApiKey(ApiKeyInfo apiKeyInfo, String value) { + super(apiKeyInfo); + this.value = value; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/pat/ApiKeyInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/pat/ApiKeyInfo.java new file mode 100644 index 0000000000..bfb9e3cabb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/pat/ApiKeyInfo.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.ApiKeyId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; + +import java.io.Serial; + +@Schema +@Data +@EqualsAndHashCode(callSuper = true) +public class ApiKeyInfo extends BaseData implements HasTenantId { + + @Serial + private static final long serialVersionUID = -2313196723950490263L; + + @Schema(description = "JSON object with Tenant Id. Tenant Id of the API key cannot be changed.", accessMode = Schema.AccessMode.READ_ONLY) + private TenantId tenantId; + + @Schema(description = "JSON object with User Id. User Id of the API key cannot be changed.") + private UserId userId; + + @Schema(description = "Expiration time of the API key.") + private long expirationTime; + + @NoXss + @NotBlank + @Length(fieldName = "description") + @Schema(description = "API Key description.", example = "API Key description") + private String description; + + @Schema(description = "Enabled/disabled API key.", example = "true") + private boolean enabled; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @Schema(description = "Indicates if the API key is expired based on current time. Returns false if expirationTime is 0 (no expiry).", + example = "false", + accessMode = Schema.AccessMode.READ_ONLY) + public boolean isExpired() { + if (expirationTime == 0) { + return false; + } + return System.currentTimeMillis() > expirationTime; + } + + @Schema(description = "JSON object with the API Key Id. " + + "Specify this field to update the API Key. " + + "Referencing non-existing API Key Id will cause error. " + + "Omit this field to create new API Key.") + @Override + public ApiKeyId getId() { + return super.getId(); + } + + public ApiKeyInfo() { + super(); + } + + public ApiKeyInfo(ApiKeyId id) { + super(id); + } + + public ApiKeyInfo(ApiKeyInfo apiKeyInfo) { + super(apiKeyInfo); + this.tenantId = apiKeyInfo.getTenantId(); + this.userId = apiKeyInfo.getUserId(); + this.expirationTime = apiKeyInfo.getExpirationTime(); + this.enabled = apiKeyInfo.isEnabled(); + this.description = apiKeyInfo.getDescription(); + } + +} 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 4a4c5d89bb..59bce53ab9 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 @@ -32,7 +32,9 @@ public enum ComponentLifecycleEvent implements Serializable { STOPPED(5), DELETED(6), FAILED(7), - DEACTIVATED(8); + DEACTIVATED(8), + RELATION_UPDATED(9), + RELATION_DELETED(10); @Getter private final int protoNumber; // corresponds to ComponentLifecycleEvent proto diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/AvailableEntityKeys.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/AvailableEntityKeys.java new file mode 100644 index 0000000000..a20d0dca55 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/AvailableEntityKeys.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.query; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import org.thingsboard.server.common.data.EntityType; + +import java.util.List; +import java.util.Set; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Objects.requireNonNullElse; + +@Schema( + description = "Contains unique time series and attribute key names discovered from entities matching a query. Used primarily for UI hints such as autocomplete suggestions." +) +public record AvailableEntityKeys( + @Schema( + description = "Set of entity types found among the matched entities.", + example = "[\"DEVICE\", \"ASSET\"]", + requiredMode = Schema.RequiredMode.REQUIRED + ) + Set entityTypes, + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @ArraySchema( + arraySchema = @Schema(description = "List of unique time series key names available on the matched entities."), + schema = @Schema(implementation = String.class, example = "temperature"), + uniqueItems = true + ) + List timeseries, + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @ArraySchema( + arraySchema = @Schema(description = "List of unique attribute key names available on the matched entities."), + schema = @Schema(implementation = String.class, example = "serialNumber"), + uniqueItems = true + ) + List attribute +) { + + public AvailableEntityKeys { + entityTypes = requireNonNullElse(entityTypes, emptySet()); + timeseries = requireNonNullElse(timeseries, emptyList()); + attribute = requireNonNullElse(attribute, emptyList()); + } + + public static AvailableEntityKeys none() { + return new AvailableEntityKeys(emptySet(), emptyList(), emptyList()); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index e775dc6ddf..76918cc8d9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -18,11 +18,11 @@ package org.thingsboard.server.common.data.relation; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; import lombok.ToString; -import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.HasVersion; import org.thingsboard.server.common.data.ObjectType; @@ -30,16 +30,19 @@ import org.thingsboard.server.common.data.edqs.EdqsObject; import org.thingsboard.server.common.data.edqs.EdqsObjectKey; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoNullChar; +import java.io.Serial; import java.io.Serializable; import java.util.UUID; -@Slf4j +@Data @Schema @EqualsAndHashCode(exclude = "additionalInfoBytes") @ToString(exclude = {"additionalInfoBytes"}) public class EntityRelation implements HasVersion, Serializable, EdqsObject { + @Serial private static final long serialVersionUID = 2807343040519543363L; public static final String EDGE_TYPE = "ManagedByEdge"; @@ -47,18 +50,26 @@ public class EntityRelation implements HasVersion, Serializable, EdqsObject { public static final String MANAGES_TYPE = "Manages"; public static final String USES_TYPE = "Uses"; - @Setter + @NotNull + @Schema(description = "JSON object with [from] Entity Id.", accessMode = Schema.AccessMode.READ_WRITE) private EntityId from; - @Setter + + @NotNull + @Schema(description = "JSON object with [to] Entity Id.", accessMode = Schema.AccessMode.READ_WRITE) private EntityId to; - @Setter - @Length(fieldName = "type") + + @NotBlank + @NoNullChar + @Length(max = 255, fieldName = "type") + @Schema(description = "String value of relation type.", example = "Contains") private String type; - @Setter + + @NotNull + @Schema(description = "Represents the type group of the relation.", example = "COMMON") private RelationTypeGroup typeGroup; - @Getter - @Setter + private Long version; + private transient JsonNode additionalInfo; @JsonIgnore private byte[] additionalInfoBytes; @@ -92,27 +103,7 @@ public class EntityRelation implements HasVersion, Serializable, EdqsObject { this.version = entityRelation.getVersion(); } - @Schema(description = "JSON object with [from] Entity Id.", accessMode = Schema.AccessMode.READ_ONLY) - public EntityId getFrom() { - return from; - } - - @Schema(description = "JSON object with [to] Entity Id.", accessMode = Schema.AccessMode.READ_ONLY) - public EntityId getTo() { - return to; - } - - @Schema(description = "String value of relation type.", example = "Contains") - public String getType() { - return type; - } - - @Schema(description = "Represents the type group of the relation.", example = "COMMON") - public RelationTypeGroup getTypeGroup() { - return typeGroup; - } - - @Schema(description = "Additional parameters of the relation", implementation = com.fasterxml.jackson.databind.JsonNode.class) + @Schema(description = "Additional parameters of the relation", implementation = JsonNode.class) public JsonNode getAdditionalInfo() { return BaseDataWithAdditionalInfo.getJson(() -> additionalInfo, () -> additionalInfoBytes); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationPathQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationPathQuery.java new file mode 100644 index 0000000000..3a4d8f0f99 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationPathQuery.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.relation; + +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.List; + +public record EntityRelationPathQuery(EntityId rootEntityId, List levels) { + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java new file mode 100644 index 0000000000..cff16586e5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.relation; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import org.thingsboard.server.common.data.StringUtils; + +public record RelationPathLevel(@NotNull EntitySearchDirection direction, @NotBlank String relationType) { + + public void validate() { + if (direction == null) { + throw new IllegalArgumentException("Direction must be specified!"); + } + if (StringUtils.isBlank(relationType)) { + throw new IllegalArgumentException("Relation type must be specified!"); + } + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java index 171bb18077..9c7a3bd189 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java @@ -20,8 +20,10 @@ public enum Authority { SYS_ADMIN(0), TENANT_ADMIN(1), CUSTOMER_USER(2), + REFRESH_TOKEN(10), - PRE_VERIFICATION_TOKEN(11); + PRE_VERIFICATION_TOKEN(11), + MFA_CONFIGURATION_TOKEN(12); private int code; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/JwtToken.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/JwtToken.java index 4ced536626..fa35c35735 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/JwtToken.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/JwtToken.java @@ -18,5 +18,7 @@ package org.thingsboard.server.common.data.security.model; import java.io.Serializable; public interface JwtToken extends Serializable { - String getToken(); + + String token(); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index 6528f581cd..90b777c11c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -21,6 +21,7 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Data; +import org.thingsboard.server.common.data.notification.targets.platform.SystemLevelUsersFilter; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; @@ -46,6 +47,8 @@ public class PlatformTwoFaSettings { @Min(value = 60) private Integer totalAllowedTimeForVerification; + private boolean enforceTwoFa; + private SystemLevelUsersFilter enforcedUsersFilter; public Optional getProviderConfig(TwoFaProviderType providerType) { return Optional.ofNullable(providers) 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 e8dbfd1584..a04816365b 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 @@ -16,7 +16,7 @@ package org.thingsboard.server.common.data.tenant.profile; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Positive; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -172,14 +172,37 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxCalculatedFieldsPerEntity = 5; @Schema(example = "10") private long maxArgumentsPerCF = 10; + @Schema(example = "10") + private int minAllowedScheduledUpdateIntervalInSecForCF = 10; + @Builder.Default + @Schema(example = "2") + @Positive + private int maxRelationLevelPerCfArgument = 2; + @Builder.Default + @Schema(example = "100") + @Positive + private int maxRelatedEntitiesToReturnPerCfArgument = 100; @Builder.Default - @Min(value = 1, message = "must be at least 1") + @Positive @Schema(example = "1000") private long maxDataPointsPerRollingArg = 1000; @Schema(example = "32") private long maxStateSizeInKBytes = 32; @Schema(example = "2") private long maxSingleValueArgumentSizeInKBytes = 2; + @Schema(example = "10") + private long minAllowedDeduplicationIntervalInSecForCF = 10; + @Schema(example = "60") + private long minAllowedAggregationIntervalInSecForCF = 60; + @Builder.Default + @Schema(example = "300") + private long intermediateAggregationIntervalInSecForCF = 300; + @Builder.Default + @Schema(example = "60") + private long cfReevaluationCheckInterval = 60; + @Builder.Default + @Schema(example = "60") + private long alarmsReevaluationInterval = 60; @Override public long getProfileThreshold(ApiUsageRecordKey key) { @@ -235,4 +258,16 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura return maxRuleNodeExecutionsPerMessage; } + public long getCfReevaluationCheckInterval() { + return cfReevaluationCheckInterval <= 0 ? 60 : cfReevaluationCheckInterval; + } + + public long getAlarmsReevaluationInterval() { + return alarmsReevaluationInterval <= 0 ? 60 : alarmsReevaluationInterval; + } + + public long getIntermediateAggregationIntervalInSecForCF() { + return intermediateAggregationIntervalInSecForCF <= 0 ? 300 : intermediateAggregationIntervalInSecForCF; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java index d0d8828649..7541be5078 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java @@ -18,9 +18,11 @@ package org.thingsboard.server.common.data.util; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiPredicate; import java.util.stream.Collectors; public class CollectionsUtil { @@ -95,6 +97,33 @@ public class CollectionsUtil { return false; } + public static boolean elementsEqual(Iterable iterable1, Iterable iterable2, BiPredicate equalityCheck) { + if (iterable1 instanceof Collection collection1 && iterable2 instanceof Collection collection2) { + if (collection1.size() != collection2.size()) { + return false; + } + } + + Iterator iterator1 = iterable1.iterator(); + Iterator iterator2 = iterable2.iterator(); + while (true) { + if (iterator1.hasNext()) { + if (!iterator2.hasNext()) { + return false; + } + + T o1 = iterator1.next(); + T o2 = iterator2.next(); + if (equalityCheck.test(o1, o2)) { + continue; + } else { + return false; + } + } + return !iterator2.hasNext(); + } + } + public static Set addToSet(Set existing, T value) { if (existing == null || existing.isEmpty()) { return Set.of(value); @@ -108,4 +137,12 @@ public class CollectionsUtil { return (Set) Set.of(newSet.toArray()); } + public static boolean isEmpty(Map map) { + return map == null || map.isEmpty(); + } + + public static boolean isNotEmpty(Map map) { + return !isEmpty(map); + } + } 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 index 78ff4b0eba..9fec33b482 100644 --- 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.util; +import com.google.gson.JsonParser; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.tuple.Pair; import org.thingsboard.server.common.data.kv.DataType; @@ -40,6 +41,11 @@ public class TypeCastUtil { } catch (RuntimeException ignored) {} } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { return Pair.of(DataType.BOOLEAN, Boolean.parseBoolean(value)); + } else if (looksLikeJson(value)) { + try { + return Pair.of(DataType.JSON, JsonParser.parseString(value)); + } catch (Exception ignored) { + } } return Pair.of(DataType.STRING, value); } @@ -70,4 +76,10 @@ public class TypeCastUtil { return valueAsString.contains(".") && !valueAsString.contains("E") && !valueAsString.contains("e"); } + private static boolean looksLikeJson(String value) { + String trimmed = value.trim(); + return (trimmed.startsWith("{") && trimmed.endsWith("}")) || + (trimmed.startsWith("[") && trimmed.endsWith("]")); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/exception/DataValidationException.java b/common/data/src/main/java/org/thingsboard/server/exception/DataValidationException.java similarity index 88% rename from dao/src/main/java/org/thingsboard/server/dao/exception/DataValidationException.java rename to common/data/src/main/java/org/thingsboard/server/exception/DataValidationException.java index 47cbee7f63..8a5d0ad965 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/exception/DataValidationException.java +++ b/common/data/src/main/java/org/thingsboard/server/exception/DataValidationException.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.exception; +package org.thingsboard.server.exception; public class DataValidationException extends RuntimeException { - private static final long serialVersionUID = 7659985660312721830L; - public DataValidationException(String message) { super(message); } @@ -26,4 +24,5 @@ public class DataValidationException extends RuntimeException { public DataValidationException(String message, Throwable cause) { super(message, cause); } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java b/common/data/src/main/java/org/thingsboard/server/exception/EntitiesLimitExceededException.java similarity index 77% rename from dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java rename to common/data/src/main/java/org/thingsboard/server/exception/EntitiesLimitExceededException.java index 96e2bd6890..a7ed2f37d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java +++ b/common/data/src/main/java/org/thingsboard/server/exception/EntitiesLimitExceededException.java @@ -13,23 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.exception; +package org.thingsboard.server.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; +public class EntitiesLimitExceededException extends DataValidationException { @Getter private final TenantId tenantId; @Getter private final EntityType entityType; - public EntitiesLimitException(TenantId tenantId, EntityType entityType) { + @Getter + private final long limit; + + public EntitiesLimitExceededException(TenantId tenantId, EntityType entityType, long limit) { super(entityType.getNormalName() + "s limit reached"); this.tenantId = tenantId; this.entityType = entityType; + this.limit = limit; } + } diff --git a/common/data/src/main/java/org/thingsboard/server/exception/ThingsboardRuntimeException.java b/common/data/src/main/java/org/thingsboard/server/exception/ThingsboardRuntimeException.java new file mode 100644 index 0000000000..3ae5f23e82 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/exception/ThingsboardRuntimeException.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.thingsboard.server.common.data.exception.ThingsboardErrorCode; + +public class ThingsboardRuntimeException extends RuntimeException { + + private ThingsboardErrorCode errorCode; + + public ThingsboardRuntimeException() { + super(); + } + + public ThingsboardRuntimeException(ThingsboardErrorCode errorCode) { + this.errorCode = errorCode; + } + + public ThingsboardRuntimeException(String message, ThingsboardErrorCode errorCode) { + super(message); + this.errorCode = errorCode; + } + + public ThingsboardRuntimeException(String message, Throwable cause, ThingsboardErrorCode errorCode) { + super(message, cause); + this.errorCode = errorCode; + } + + public ThingsboardRuntimeException(Throwable cause, ThingsboardErrorCode errorCode) { + super(cause); + this.errorCode = errorCode; + } + + public ThingsboardErrorCode getErrorCode() { + return errorCode; + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java new file mode 100644 index 0000000000..2fbbd66478 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ArgumentTest { + + @Test + void validateShouldReturnFalseIfDynamicSourceConfigurationIsNull() { + var argument = new Argument(); + assertThat(argument.hasDynamicSource()).isFalse(); + } + + @Test + void validateWhenRelationQuerySourceConfigurationIsNotNull() { + var argument = new Argument(); + argument.setRefDynamicSourceConfiguration(new RelationPathQueryDynamicSourceConfiguration()); + assertThat(argument.hasDynamicSource()).isTrue(); + assertThat(argument.hasRelationQuerySource()).isTrue(); + assertThat(argument.hasOwnerSource()).isFalse(); + } + + @Test + void validateWhenCurrentOwnerSourceConfigurationIsNotNull() { + var argument = new Argument(); + argument.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + assertThat(argument.hasDynamicSource()).isTrue(); + assertThat(argument.hasOwnerSource()).isTrue(); + assertThat(argument.hasRelationQuerySource()).isFalse(); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldOutputTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldOutputTest.java new file mode 100644 index 0000000000..ebf56824fe --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldOutputTest.java @@ -0,0 +1,202 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.AttributeScope; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +public class CalculatedFieldOutputTest { + + @Test + public void testHasContextOnlyChanges_whenTypeChanged_shouldReturnTrue() { + TimeSeriesOutput output = new TimeSeriesOutput(); + AttributesOutput newOutput = new AttributesOutput(); + + assertThat(output.hasContextOnlyChanges(newOutput)).isTrue(); + } + + @Test + public void testHasContextOnlyChanges_whenNameChanged_shouldReturnTrue() { + TimeSeriesOutput output = new TimeSeriesOutput(); + TimeSeriesOutput newOutput = new TimeSeriesOutput(); + newOutput.setName("new"); + + assertThat(output.hasContextOnlyChanges(newOutput)).isTrue(); + } + + @Test + public void testHasContextOnlyChanges_whenScopeChanged_shouldReturnTrue() { + AttributesOutput output = new AttributesOutput(); + output.setScope(AttributeScope.SHARED_SCOPE); + AttributesOutput newOutput = new AttributesOutput(); + newOutput.setScope(AttributeScope.SERVER_SCOPE); + + assertThat(output.hasContextOnlyChanges(newOutput)).isTrue(); + } + + @Test + public void testHasContextOnlyChanges_whenDecimalsByDefaultChanged_shouldReturnTrue() { + AttributesOutput output = new AttributesOutput(); + AttributesOutput newOutput = new AttributesOutput(); + newOutput.setDecimalsByDefault(2); + + assertThat(output.hasContextOnlyChanges(newOutput)).isTrue(); + } + + @Test + public void testHasContextOnlyChanges_whenStrategyHasContextOnlyChanges_shouldReturnTrue() { + AttributesOutputStrategy outputStrategy = mock(AttributesRuleChainOutputStrategy.class); + given(outputStrategy.hasContextOnlyChanges(any())).willReturn(true); + + AttributesOutput output = new AttributesOutput(); + output.setStrategy(outputStrategy); + AttributesOutput newOutput = new AttributesOutput(); + + assertThat(output.hasContextOnlyChanges(newOutput)).isTrue(); + } + + @Test + public void testHasContextOnlyChanges_whenStrategyDoesNotHaveContextOnlyChanges_shouldReturnTrue() { + AttributesOutputStrategy outputStrategy = mock(AttributesRuleChainOutputStrategy.class); + given(outputStrategy.hasContextOnlyChanges(any())).willReturn(false); + + AttributesOutput output = new AttributesOutput(); + output.setStrategy(outputStrategy); + AttributesOutput newOutput = new AttributesOutput(); + + assertThat(output.hasContextOnlyChanges(newOutput)).isFalse(); + } + + /* .hasContextOnlyChanges() tests*/ + + @Test + public void testAttributesImmediateOutputStrategyHasContextOnlyChanges_whenTypeChanged_shouldReturnTrue() { + AttributesImmediateOutputStrategy strategy = new AttributesImmediateOutputStrategy(true, true, false, true, true); + AttributesRuleChainOutputStrategy newStrategy = new AttributesRuleChainOutputStrategy(); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testAttributesImmediateOutputStrategyHasContextOnlyChanges_whenSaveAttributesChanged_shouldReturnTrue() { + AttributesImmediateOutputStrategy strategy = new AttributesImmediateOutputStrategy(true, true, false, true, true); + AttributesImmediateOutputStrategy newStrategy = new AttributesImmediateOutputStrategy(true, true, true, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testAttributesImmediateOutputStrategyHasContextOnlyChanges_whenSendWsUpdateChanged_shouldReturnTrue() { + AttributesImmediateOutputStrategy strategy = new AttributesImmediateOutputStrategy(true, true, true, false, true); + AttributesImmediateOutputStrategy newStrategy = new AttributesImmediateOutputStrategy(true, true, true, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testAttributesImmediateOutputStrategyHasContextOnlyChanges_whenProcessCfsChanged_shouldReturnTrue() { + AttributesImmediateOutputStrategy strategy = new AttributesImmediateOutputStrategy(true, true, true, false, false); + AttributesImmediateOutputStrategy newStrategy = new AttributesImmediateOutputStrategy(true, true, true, false, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testAttributesRuleChainOutputStrategyHasContextOnlyChanges_whenTypeChanged_shouldReturnTrue() { + AttributesRuleChainOutputStrategy strategy = new AttributesRuleChainOutputStrategy(); + AttributesImmediateOutputStrategy newStrategy = new AttributesImmediateOutputStrategy(true, true, false, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testTimeSeriesImmediateOutputStrategyHasContextOnlyChanges_whenTypeChanged_shouldReturnTrue() { + TimeSeriesImmediateOutputStrategy strategy = new TimeSeriesImmediateOutputStrategy(0, false, true, true, true); + TimeSeriesRuleChainOutputStrategy newStrategy = new TimeSeriesRuleChainOutputStrategy(); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testTimeSeriesImmediateOutputStrategyHasContextOnlyChanges_whenSaveLatestChanged_shouldReturnTrue() { + TimeSeriesImmediateOutputStrategy strategy = new TimeSeriesImmediateOutputStrategy(0, false, false, true, true); + TimeSeriesImmediateOutputStrategy newStrategy = new TimeSeriesImmediateOutputStrategy(0, false, true, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testTimeSeriesImmediateOutputStrategyHasContextOnlyChanges_whenSendWsUpdateChanged_shouldReturnTrue() { + TimeSeriesImmediateOutputStrategy strategy = new TimeSeriesImmediateOutputStrategy(0, true, true, false, true); + TimeSeriesImmediateOutputStrategy newStrategy = new TimeSeriesImmediateOutputStrategy(0, true, true, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testTimeSeriesImmediateOutputStrategyHasContextOnlyChanges_whenProcessCfsChanged_shouldReturnTrue() { + TimeSeriesImmediateOutputStrategy strategy = new TimeSeriesImmediateOutputStrategy(0, true, true, true, false); + TimeSeriesImmediateOutputStrategy newStrategy = new TimeSeriesImmediateOutputStrategy(0, true, true, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testTimeSeriesRuleChainOutputStrategyHasContextOnlyChanges_whenProcessCfsChanged_shouldReturnTrue() { + TimeSeriesRuleChainOutputStrategy strategy = new TimeSeriesRuleChainOutputStrategy(); + TimeSeriesImmediateOutputStrategy newStrategy = new TimeSeriesImmediateOutputStrategy(0, true, true, true, false); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isTrue(); + } + + /* .hasRefreshContextOnlyChanges() tests*/ + + @Test + public void testAttributesImmediateOutputStrategyHasRefreshContextOnlyChanges_whenUpdateAttrOnValueChangedChanged_shouldReturnTrue() { + AttributesImmediateOutputStrategy strategy = new AttributesImmediateOutputStrategy(true, false, true, false, true); + AttributesImmediateOutputStrategy newStrategy = new AttributesImmediateOutputStrategy(true, true, true, false, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isFalse(); + assertThat(strategy.hasRefreshContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testAttributesImmediateOutputStrategyHasRefreshContextOnlyChanges_whenSendAttrUpdatedNotificationChanged_shouldReturnTrue() { + AttributesImmediateOutputStrategy strategy = new AttributesImmediateOutputStrategy(false, true, true, false, true); + AttributesImmediateOutputStrategy newStrategy = new AttributesImmediateOutputStrategy(true, true, true, false, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isFalse(); + assertThat(strategy.hasRefreshContextOnlyChanges(newStrategy)).isTrue(); + } + + @Test + public void testTimeSeriesImmediateOutputStrategyHasRefreshContextOnlyChanges_whenTtlChanged_shouldReturnTrue() { + TimeSeriesImmediateOutputStrategy strategy = new TimeSeriesImmediateOutputStrategy(0, true, true, true, true); + TimeSeriesImmediateOutputStrategy newStrategy = new TimeSeriesImmediateOutputStrategy(300, true, true, true, true); + + assertThat(strategy.hasContextOnlyChanges(newStrategy)).isFalse(); + assertThat(strategy.hasRefreshContextOnlyChanges(newStrategy)).isTrue(); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java new file mode 100644 index 0000000000..7430c6eec0 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java @@ -0,0 +1,154 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; + +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration.PROPAGATION_CONFIG_ARGUMENT; + +@ExtendWith(MockitoExtension.class) +public class PropagationCalculatedFieldConfigurationTest { + + @Test + void typeShouldBePropagation() { + var cfg = new PropagationCalculatedFieldConfiguration(); + assertThat(cfg.getType()).isEqualTo(CalculatedFieldType.PROPAGATION); + } + + @Test + void validateShouldThrowWhenConfigurationDisallowArgumentsWithReferencedEntity() { + var cfg = new PropagationCalculatedFieldConfiguration(); + Argument argumentWithRefEntityIdSet = new Argument(); + argumentWithRefEntityIdSet.setRefEntityId(new DeviceId(UUID.fromString("bda14084-f40e-4acc-9b85-9d1dd209bb64"))); + cfg.setArguments(Map.of("argumentWithRefEntityIdSet", argumentWithRefEntityIdSet)); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Arguments in 'Arguments only' propagation mode support only the 'Current entity' source entity type!"); + } + + @Test + void validateShouldThrowWhenConfigurationDisallowArgumentsWithDynamicReferenceConfiguration() { + var cfg = new PropagationCalculatedFieldConfiguration(); + Argument argumentWithDynamicRefEntitySource = new Argument(); + argumentWithDynamicRefEntitySource.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + cfg.setArguments(Map.of("argumentWithDynamicRefEntitySource", argumentWithDynamicRefEntitySource)); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Arguments in 'Arguments only' propagation mode support only the 'Current entity' source entity type!"); + } + + @Test + void validateShouldThrowWhenConfigurationHasNoArgumentsWithCurrentEntitySource() { + var cfg = new PropagationCalculatedFieldConfiguration(); + Argument argumentWithRefEntityIdSet = new Argument(); + argumentWithRefEntityIdSet.setRefEntityId(new DeviceId(UUID.fromString("3703e895-3f9b-4b75-a715-b68f1ad51944"))); + cfg.setArguments(Map.of("argumentWithRefEntityIdSet", argumentWithRefEntityIdSet)); + cfg.setApplyExpressionToResolvedArguments(true); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("At least one argument must be configured with the 'Current entity' " + + "source entity type for 'Expression result' propagation mode!"); + } + + @Test + void validateShouldThrowWhenUsedReservedPropagationArgumentName() { + var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setArguments(Map.of(PROPAGATION_CONFIG_ARGUMENT, new Argument())); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Argument name '" + PROPAGATION_CONFIG_ARGUMENT + "' is reserved and cannot be used."); + } + + @Test + void validateShouldThrowWhenUsedReservedCtxArgumentName() { + var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setArguments(Map.of("ctx", new Argument())); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Argument name 'ctx' is reserved and cannot be used."); + } + + @Test + void validateShouldThrowWhenReferencedEntityKeyIsNotSet() { + var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + Argument argument = new Argument(); + cfg.setArguments(Map.of("someArgumentName", argument)); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Argument: 'someArgumentName' doesn't have reference entity key configured!"); + } + + @Test + void validateShouldThrowWhenReferencedEntityKeyTypeIsTsRolling() { + var cfg = new PropagationCalculatedFieldConfiguration(); + ReferencedEntityKey referencedEntityKey = new ReferencedEntityKey("someKey", ArgumentType.TS_ROLLING, null); + Argument argument = new Argument(); + argument.setRefEntityKey(referencedEntityKey); + cfg.setArguments(Map.of("someArgumentName", argument)); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Argument type: 'Time series rolling' detected for argument: 'someArgumentName'. " + + "Only 'Attribute' or 'Latest telemetry' arguments are allowed for 'Arguments only' propagation mode!"); + } + + @Test + void validateShouldThrowWhenExpressionIsNotSet() { + var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setArguments(Map.of("someArgumentName", new Argument())); + cfg.setApplyExpressionToResolvedArguments(true); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Expression must be specified for 'Expression result' propagation mode!"); + } + + @Test + void validateToPropagationArgumentMethodCallReturnCorrectArgument() { + var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + + Argument propagationArgument = cfg.toPropagationArgument(); + assertThat(propagationArgument).isNotNull(); + assertThat(propagationArgument.getRefEntityId()).isNull(); + assertThat(propagationArgument.getRefEntityKey()).isNull(); + assertThat(propagationArgument.getDefaultValue()).isNull(); + assertThat(propagationArgument.getTimeWindow()).isNull(); + assertThat(propagationArgument.getLimit()).isNull(); + + assertThat(propagationArgument.getRefDynamicSourceConfiguration()) + .isNotNull() + .isInstanceOf(RelationPathQueryDynamicSourceConfiguration.class); + var refDynamicSourceConfiguration = (RelationPathQueryDynamicSourceConfiguration) propagationArgument.getRefDynamicSourceConfiguration(); + assertThat(refDynamicSourceConfiguration.getLevels()).isNotEmpty().hasSize(1); + + var relationPathLevel = refDynamicSourceConfiguration.getLevels().get(0); + assertThat(relationPathLevel.direction()).isEqualTo(EntitySearchDirection.TO); + assertThat(relationPathLevel.relationType()).isEqualTo(EntityRelation.CONTAINS_TYPE); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationPathQueryDynamicSourceConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationPathQueryDynamicSourceConfigurationTest.java new file mode 100644 index 0000000000..9e89bf7045 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationPathQueryDynamicSourceConfigurationTest.java @@ -0,0 +1,126 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +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.NullAndEmptySource; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class RelationPathQueryDynamicSourceConfigurationTest { + + @Test + void typeShouldBeRelationQuery() { + var cfg = new RelationPathQueryDynamicSourceConfiguration(); + assertThat(cfg.getType()).isEqualTo(CFArgumentDynamicSourceType.RELATION_PATH_QUERY); + } + + @ParameterizedTest + @NullAndEmptySource + void validateShouldThrowWhenLevelsIsNull(List levels) { + var cfg = new RelationPathQueryDynamicSourceConfiguration(); + cfg.setLevels(levels); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("At least one relation level must be specified!"); + } + + @Test + void validateShouldCallValidateForPathLevels() { + List levels = new ArrayList<>(); + + RelationPathLevel lvl1 = mock(RelationPathLevel.class); + RelationPathLevel lvl2 = mock(RelationPathLevel.class); + levels.add(lvl1); + levels.add(lvl2); + + var cfg = new RelationPathQueryDynamicSourceConfiguration(); + cfg.setLevels(levels); + + assertThatCode(cfg::validate).doesNotThrowAnyException(); + + verify(lvl1).validate(); + verify(lvl2).validate(); + } + + @Test + void resolveEntityIds_whenDirectionFROM_thenReturnsToIds() { + List levels = new ArrayList<>(); + + RelationPathLevel lvl1 = mock(RelationPathLevel.class); + RelationPathLevel lvl2 = mock(RelationPathLevel.class); + levels.add(lvl1); + levels.add(lvl2); + + when(lvl2.direction()).thenReturn(EntitySearchDirection.FROM); + + EntityRelation rel1 = mock(EntityRelation.class); + EntityRelation rel2 = mock(EntityRelation.class); + + when(rel1.getTo()).thenReturn(mock(EntityId.class)); + when(rel2.getTo()).thenReturn(mock(EntityId.class)); + + var cfg = new RelationPathQueryDynamicSourceConfiguration(); + cfg.setLevels(levels); + + var out = cfg.resolveEntityIds(List.of(rel1, rel2)); + + assertThat(out).containsExactly(rel1.getTo(), rel2.getTo()); + } + + @Test + void resolveEntityIds_whenDirectionTO_thenReturnsFromIds() { + List levels = new ArrayList<>(); + + RelationPathLevel lvl1 = mock(RelationPathLevel.class); + RelationPathLevel lvl2 = mock(RelationPathLevel.class); + levels.add(lvl1); + levels.add(lvl2); + + when(lvl2.direction()).thenReturn(EntitySearchDirection.TO); + + EntityRelation rel1 = mock(EntityRelation.class); + EntityRelation rel2 = mock(EntityRelation.class); + + when(rel1.getFrom()).thenReturn(mock(EntityId.class)); + when(rel2.getFrom()).thenReturn(mock(EntityId.class)); + + var cfg = new RelationPathQueryDynamicSourceConfiguration(); + cfg.setLevels(levels); + + var out = cfg.resolveEntityIds(List.of(rel1, rel2)); + + assertThat(out).containsExactly(rel1.getFrom(), rel2.getFrom()); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ScheduledUpdateSupportedCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ScheduledUpdateSupportedCalculatedFieldConfigurationTest.java new file mode 100644 index 0000000000..23e0f1add3 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ScheduledUpdateSupportedCalculatedFieldConfigurationTest.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@ExtendWith(MockitoExtension.class) +public class ScheduledUpdateSupportedCalculatedFieldConfigurationTest { + + @Test + void validateDoesNotThrowAnyExceptionWhenScheduledUpdateIntervalIsGreaterThanMinAllowedIntervalInTenantProfile() { + int scheduledUpdateInterval = 60; + int minAllowedInterval = scheduledUpdateInterval - 1; + + var cfg = new GeofencingCalculatedFieldConfiguration(); + cfg.setScheduledUpdateInterval(scheduledUpdateInterval); + assertThatCode(() -> cfg.validate(minAllowedInterval)).doesNotThrowAnyException(); + } + + @Test + void validateShouldThrowWhenScheduledUpdateIntervalIsLessThanMinAllowedIntervalInTenantProfile() { + int minAllowedInterval = (int) TimeUnit.HOURS.toSeconds(2); + + var cfg = new GeofencingCalculatedFieldConfiguration(); + cfg.setScheduledUpdateInterval(1); + + assertThatThrownBy(() -> cfg.validate(minAllowedInterval)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Scheduled update interval is less than configured " + + "minimum allowed interval in tenant profile: " + minAllowedInterval); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfigurationTest.java new file mode 100644 index 0000000000..f68bef5ef2 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfigurationTest.java @@ -0,0 +1,123 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class RelatedEntitiesAggregationCalculatedFieldConfigurationTest { + + @Test + void typeShouldBeEntityAggregation() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + assertThat(cfg.getType()).isEqualTo(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION); + } + + @Test + void validateShouldThrowWhenRelationIsNotSet() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relation must be specified!"); + } + + @Test + void validateShouldThrowWhenRelationIsNotValid() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + cfg.setRelation(new RelationPathLevel(null, null)); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Direction must be specified!"); + } + + @Test + void validateShouldThrowWhenArgumentsMapIsEmpty() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE)); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Arguments map cannot be empty."); + } + + @Test + void validateShouldThrowWhenTsRollingArgumentUsed() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE)); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("key", ArgumentType.TS_ROLLING, null)); + cfg.setArguments(Map.of("k", argument)); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Calculated field with type: '" + CalculatedFieldType.RELATED_ENTITIES_AGGREGATION + "' doesn't support TS_ROLLING arguments."); + } + + @Test + void validateShouldThrowWhenMetricMapIsEmpty() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE)); + cfg.setArguments(Map.of("k", validArgument(ArgumentType.TS_LATEST))); + cfg.setMetrics(Map.of()); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Metrics map cannot be empty."); + } + + @Test + void validateShouldThrowWhenMetricReferencesUnknownArgument() { + var cfg = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE)); + cfg.setArguments(Map.of("k", validArgument(ArgumentType.TS_LATEST))); + + AggMetric metric = new AggMetric(); + metric.setInput(new AggKeyInput("unknown")); + cfg.setMetrics(Map.of("m", metric)); + + cfg.setOutput(new TimeSeriesOutput()); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Metric references unknown argument: 'unknown'."); + } + + private Argument validArgument(ArgumentType type) { + Argument a = new Argument(); + a.setRefEntityKey(new ReferencedEntityKey("key", type, null)); + return a; + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfigurationTest.java new file mode 100644 index 0000000000..8a1509b650 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfigurationTest.java @@ -0,0 +1,128 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunctionInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.HourInterval; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class EntityAggregationCalculatedFieldConfigurationTest { + + @Test + void typeShouldBeEntityAggregation() { + var cfg = new EntityAggregationCalculatedFieldConfiguration(); + assertThat(cfg.getType()).isEqualTo(CalculatedFieldType.ENTITY_AGGREGATION); + } + + @ParameterizedTest + @ValueSource(strings = {"ATTRIBUTE", "TS_ROLLING"}) + void validateShouldThrowWhenNotTsLatestArgumentUsed(String argumentType) { + var cfg = new EntityAggregationCalculatedFieldConfiguration(); + cfg.setArguments(Map.of("k", validArgument(ArgumentType.valueOf(argumentType)))); + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Calculated field with type: '" + cfg.getType() + "' support only TS_LATEST arguments."); + } + + @Test + void validateShouldThrowWhenMetricMapIsEmpty() { + var cfg = new EntityAggregationCalculatedFieldConfiguration(); + + cfg.setArguments(Map.of("k", validArgument(ArgumentType.TS_LATEST))); + cfg.setMetrics(Map.of()); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Metrics map cannot be empty."); + } + + @Test + void validateShouldThrowWhenMetricInputIsNotAggKeyInput() { + var cfg = new EntityAggregationCalculatedFieldConfiguration(); + + cfg.setArguments(Map.of("k", validArgument(ArgumentType.TS_LATEST))); + + AggMetric metric = new AggMetric(); + metric.setInput(new AggFunctionInput()); // cannot be function + cfg.setMetrics(Map.of("m", metric)); + + cfg.setInterval(new HourInterval("Europe/Kiev", null)); + cfg.setOutput(new TimeSeriesOutput()); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Metric key can only refer to argument."); + } + + @Test + void validateShouldThrowWhenMetricReferencesUnknownArgument() { + var cfg = new EntityAggregationCalculatedFieldConfiguration(); + + cfg.setArguments(Map.of("k", validArgument(ArgumentType.TS_LATEST))); + + AggMetric metric = new AggMetric(); + metric.setInput(new AggKeyInput("unknown")); + cfg.setMetrics(Map.of("m", metric)); + + cfg.setInterval(new HourInterval("Europe/Kiev", null)); + cfg.setOutput(new TimeSeriesOutput()); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Metric references unknown argument: 'unknown'."); + } + + @Test + void validateShouldThrowWhenIntervalIsNull() { + var cfg = new EntityAggregationCalculatedFieldConfiguration(); + + cfg.setArguments(Map.of("k", validArgument(ArgumentType.TS_LATEST))); + cfg.setMetrics(Map.of("m", validMetric())); + cfg.setInterval(null); + cfg.setOutput(new TimeSeriesOutput()); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Interval must be defined."); + } + + private Argument validArgument(ArgumentType type) { + Argument a = new Argument(); + a.setRefEntityKey(new ReferencedEntityKey("key", type, null)); + return a; + } + + private AggMetric validMetric() { + AggMetric metric = new AggMetric(); + metric.setInput(new AggKeyInput("k")); + return metric; + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggIntervalTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggIntervalTest.java new file mode 100644 index 0000000000..fd1a70d02f --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/AggIntervalTest.java @@ -0,0 +1,168 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.aggregation.single.interval; + +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 java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.LongFunction; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class AggIntervalTest { + + private static final String TZ = "Europe/Kiev"; + + @Test + void validateShouldThrowWhenInvalidTimZone() { + AggInterval interval = new HourInterval("TimeZone", null); + + assertThatThrownBy(interval::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid timezone in interval: "); + } + + @Test + void validateShouldThrowWhenOffsetIsNegative() { + AggInterval interval = new CustomInterval(TZ, -100L, TimeUnit.HOURS.toSeconds(2)); + + assertThatThrownBy(interval::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Offset cannot be negative."); + } + + @Test + void validateShouldThrowWhenOffsetGreaterThanIntervalDuration() { + AggInterval interval = new CustomInterval(TZ, TimeUnit.HOURS.toSeconds(2), TimeUnit.HOURS.toSeconds(2)); + + assertThatThrownBy(interval::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Offset must be greater than interval duration."); + } + + @ParameterizedTest + @MethodSource("intervals") + void testGetStartAndEndWithoutOffset(LongFunction intervalCreator, long expectedDuration) { + AggInterval interval = intervalCreator.apply(0L); + + ZonedDateTime dateTime = ZonedDateTime.of( + // 2025.11.11 00:00:00 + 2025, 11, 11, 0, 0, 0, 0, ZoneId.of(TZ) + ); + long startTs = interval.getDateTimeIntervalStartTs(dateTime); + long endTs = interval.getDateTimeIntervalEndTs(dateTime); + + assertThat(endTs).isGreaterThan(startTs); + assertThat(endTs - startTs).isEqualTo(expectedDuration); + } + + @ParameterizedTest + @MethodSource("intervals") + void testApplyOffset(LongFunction intervalCreator) { + long offsetSec = TimeUnit.MINUTES.toSeconds(15); + AggInterval intervalWithOffset = intervalCreator.apply(offsetSec); + AggInterval intervalNoOffset = intervalCreator.apply(0L); + + ZonedDateTime dateTime = ZonedDateTime.of( + // 2025.11.11 11:20:00 - chosen so 15m offset shifts into a new interval + 2025, 6, 6, 6, 20, 0, 0, ZoneId.of(TZ) + ); + + long startWithOffsetTs = intervalWithOffset.getDateTimeIntervalStartTs(dateTime); + long startNoOffsetTs = intervalNoOffset.getDateTimeIntervalStartTs(dateTime); + + ZonedDateTime startWithOffset = Instant.ofEpochMilli(startWithOffsetTs).atZone(intervalWithOffset.getZoneId()); + ZonedDateTime startNoOffset = Instant.ofEpochMilli(startNoOffsetTs).atZone(intervalNoOffset.getZoneId()); + + long actualOffset = Duration.between(startNoOffset, startWithOffset).toSeconds(); + assertThat(actualOffset).isEqualTo(offsetSec); + } + + private static Stream intervals() { + return Stream.of( + Arguments.of((LongFunction) offset -> new HourInterval(TZ, offset), TimeUnit.HOURS.toMillis(1)), + Arguments.of((LongFunction) offset -> new DayInterval(TZ, offset), TimeUnit.DAYS.toMillis(1)), + Arguments.of((LongFunction) offset -> new WeekInterval(TZ, offset), TimeUnit.DAYS.toMillis(7)), + Arguments.of((LongFunction) offset -> new WeekSunSatInterval(TZ, offset), TimeUnit.DAYS.toMillis(7)), + Arguments.of((LongFunction) offset -> new MonthInterval(TZ, offset), TimeUnit.DAYS.toMillis(30)), + Arguments.of((LongFunction) offset -> new QuarterInterval(TZ, offset), TimeUnit.DAYS.toMillis(92) + TimeUnit.HOURS.toMillis(1)),// Includes DST fallback (2025-10-26), so duration = 92 days + 1 hour(expected for Europe/Kyiv timezone). + Arguments.of((LongFunction) offset -> new YearInterval(TZ, offset), TimeUnit.DAYS.toMillis(365)), + Arguments.of((LongFunction) offset -> new CustomInterval(TZ, offset, TimeUnit.HOURS.toSeconds(4)), TimeUnit.HOURS.toMillis(4)) + ); + } + + @ParameterizedTest + @MethodSource("nextIntervalFromExactDate") + void testNextIntervalFromExactDate(LongFunction intervalCreator, Function expectedDateTimeFunction) { + AggInterval interval = intervalCreator.apply(0L); + + ZonedDateTime currentStart = ZonedDateTime.of( + 2025, 11, 11, 0, 0, 0, 0, ZoneId.of(TZ) + ); + + ZonedDateTime nextStart = interval.getNextIntervalStart(currentStart); + + assertThat(nextStart).isEqualTo(expectedDateTimeFunction.apply(currentStart)); + } + + private static Stream nextIntervalFromExactDate() { + return Stream.of( + Arguments.of( + (LongFunction) offset -> new HourInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusHours(1) + ), + Arguments.of( + (LongFunction) offset -> new DayInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusDays(1) + ), + Arguments.of( + (LongFunction) offset -> new WeekInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusWeeks(1) + ), + Arguments.of( + (LongFunction) offset -> new WeekSunSatInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusWeeks(1) + ), + Arguments.of( + (LongFunction) offset -> new MonthInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusMonths(1) + ), + Arguments.of( + (LongFunction) offset -> new QuarterInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusMonths(3) + ), + Arguments.of( + (LongFunction) offset -> new YearInterval(TZ, offset), + (Function) currentInterval -> currentInterval.plusYears(1) + ), + Arguments.of( + (LongFunction) offset -> new CustomInterval(TZ, offset, TimeUnit.HOURS.toSeconds(4)), + (Function) currentInterval -> currentInterval.plusHours(4) + ) + ); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/EntityCoordinatesTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/EntityCoordinatesTest.java new file mode 100644 index 0000000000..5f4655dd46 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/EntityCoordinatesTest.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; + +public class EntityCoordinatesTest { + + @Test + void validateToArgumentsMethodCallWithoutRefEntityId() { + var entityCoordinates = new EntityCoordinates("xPos", "yPos"); + + var arguments = entityCoordinates.toArguments(); + assertThat(arguments).isNotNull().hasSize(2); + + Argument latitudeArgument = arguments.get(ENTITY_ID_LATITUDE_ARGUMENT_KEY); + assertThat(latitudeArgument).isNotNull(); + assertThat(latitudeArgument.getRefEntityKey()).isEqualTo(new ReferencedEntityKey("xPos", ArgumentType.TS_LATEST, null)); + assertThat(latitudeArgument.getRefEntityId()).isNull(); + assertThat(latitudeArgument.getRefDynamicSourceConfiguration()).isNull(); + + Argument longitudeArgument = arguments.get(ENTITY_ID_LONGITUDE_ARGUMENT_KEY); + assertThat(longitudeArgument).isNotNull(); + assertThat(longitudeArgument.getRefEntityKey()).isEqualTo(new ReferencedEntityKey("yPos", ArgumentType.TS_LATEST, null)); + assertThat(longitudeArgument.getRefEntityId()).isNull(); + assertThat(longitudeArgument.getRefDynamicSourceConfiguration()).isNull(); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfigurationTest.java new file mode 100644 index 0000000000..2f391435b2 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfigurationTest.java @@ -0,0 +1,106 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; + +@ExtendWith(MockitoExtension.class) +public class GeofencingCalculatedFieldConfigurationTest { + + @Test + void typeShouldBeGeofencing() { + var cfg = new GeofencingCalculatedFieldConfiguration(); + assertThat(cfg.getType()).isEqualTo(CalculatedFieldType.GEOFENCING); + } + + @Test + void validateShouldCallValidateOnZoneGroups() { + var cfg = new GeofencingCalculatedFieldConfiguration(); + EntityCoordinates entityCoordinatesMock = mock(EntityCoordinates.class); + cfg.setEntityCoordinates(entityCoordinatesMock); + var zoneGroupConfiguration = mock(ZoneGroupConfiguration.class); + cfg.setZoneGroups(Map.of("someGroupName", zoneGroupConfiguration)); + + cfg.validate(); + verify(zoneGroupConfiguration).validate("someGroupName"); + } + + @Test + void validateShouldCallValidateOnZoneGroupsWithoutAnyExceptions() { + var cfg = new GeofencingCalculatedFieldConfiguration(); + EntityCoordinates entityCoordinatesMock = mock(EntityCoordinates.class); + cfg.setEntityCoordinates(entityCoordinatesMock); + var zoneGroupConfigurationA = mock(ZoneGroupConfiguration.class); + var zoneGroupConfigurationB = mock(ZoneGroupConfiguration.class); + + String zoneGroupAName = "zoneGroupA"; + String zoneGroupBName = "zoneGroupB"; + + cfg.setZoneGroups(Map.of("zoneGroupA", zoneGroupConfigurationA, "zoneGroupB", zoneGroupConfigurationB)); + + assertThatCode(cfg::validate).doesNotThrowAnyException(); + + verify(zoneGroupConfigurationA).validate(zoneGroupAName); + verify(zoneGroupConfigurationB).validate(zoneGroupBName); + } + + @Test + void testGetArgumentsOverride() { + var cfg = new GeofencingCalculatedFieldConfiguration(); + cfg.setEntityCoordinates(new EntityCoordinates(ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY)); + cfg.setZoneGroups(Map.of("allowedZones", new ZoneGroupConfiguration("perimeter", GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false))); + + Map arguments = cfg.getArguments(); + + assertThat(arguments).isNotNull().hasSize(3); + assertThat(arguments).containsKeys(ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY, "allowedZones"); + + Argument latitudeArgument = arguments.get(ENTITY_ID_LATITUDE_ARGUMENT_KEY); + assertThat(latitudeArgument).isNotNull(); + assertThat(latitudeArgument.getRefDynamicSourceConfiguration()).isNull(); + assertThat(latitudeArgument.getRefEntityId()).isNull(); + assertThat(latitudeArgument.getRefEntityKey()).isEqualTo(new ReferencedEntityKey(ENTITY_ID_LATITUDE_ARGUMENT_KEY, ArgumentType.TS_LATEST, null)); + + Argument longitudeArgument = arguments.get(ENTITY_ID_LONGITUDE_ARGUMENT_KEY); + assertThat(longitudeArgument).isNotNull(); + assertThat(longitudeArgument.getRefDynamicSourceConfiguration()).isNull(); + assertThat(longitudeArgument.getRefEntityId()).isNull(); + assertThat(longitudeArgument.getRefEntityKey()).isEqualTo(new ReferencedEntityKey(ENTITY_ID_LONGITUDE_ARGUMENT_KEY, ArgumentType.TS_LATEST, null)); + + Argument allowedZonesArgument = arguments.get("allowedZones"); + assertThat(allowedZonesArgument).isNotNull(); + assertThat(allowedZonesArgument.getRefDynamicSourceConfiguration()).isNull(); + assertThat(allowedZonesArgument.getRefEntityId()).isNull(); + assertThat(allowedZonesArgument.getRefEntityKey()).isEqualTo(new ReferencedEntityKey("perimeter", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java new file mode 100644 index 0000000000..158eb9762a --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java @@ -0,0 +1,112 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.configuration.geofencing; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.CurrentOwnerDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; + +public class ZoneGroupConfigurationTest { + + @ParameterizedTest + @ValueSource(strings = {EntityCoordinates.ENTITY_ID_LATITUDE_ARGUMENT_KEY, EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY}) + void validateShouldThrowWhenUsedReservedEntityCoordinateNames(String name) { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + assertThatThrownBy(() -> zoneGroupConfiguration.validate(name)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Name '" + name + "' is reserved and cannot be used for zone group!"); + } + + @ParameterizedTest + @ValueSource(strings = " ") + @NullAndEmptySource + void validateShouldThrowWhenRelationCreationEnabledAndRelationTypeIsNullEmptyOrBlank(String relationType) { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, true); + zoneGroupConfiguration.setRelationType(relationType); + assertThatThrownBy(() -> zoneGroupConfiguration.validate("allowedZonesGroup")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relation type must be specified for 'allowedZonesGroup' zone group!"); + } + + @Test + void validateShouldThrowWhenRelationCreationEnabledAndDirectionIsNull() { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, true); + zoneGroupConfiguration.setRelationType(EntityRelation.CONTAINS_TYPE); + zoneGroupConfiguration.setDirection(null); + assertThatThrownBy(() -> zoneGroupConfiguration.validate("allowedZonesGroup")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relation direction must be specified for 'allowedZonesGroup' zone group!"); + } + + @Test + void validateShouldDoesNotThrowAnyExceptionWhenRelationCreationDisabledAndConfigValid() { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + assertThatCode(() -> zoneGroupConfiguration.validate("allowedZonesGroup")).doesNotThrowAnyException(); + } + + @Test + void validateShouldDoesNotThrowAnyExceptionWhenRelationCreationEnabledAndConfigValid() { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, true); + zoneGroupConfiguration.setRelationType(EntityRelation.CONTAINS_TYPE); + zoneGroupConfiguration.setDirection(EntitySearchDirection.TO); + assertThatCode(() -> zoneGroupConfiguration.validate("allowedZonesGroup")).doesNotThrowAnyException(); + } + + @Test + void whenHasRelationQuerySourceCalled_shouldReturnTrueIfRelationQuerySourceConfigurationIsNotNull() { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(new RelationPathQueryDynamicSourceConfiguration()); + assertThat(zoneGroupConfiguration.hasRelationQuerySource()).isTrue(); + } + + @Test + void whenHasRelationQuerySourceCalled_shouldReturnFalseIfRelationQuerySourceConfigurationIsNull() { + var zoneGroupConfiguration = mock(ZoneGroupConfiguration.class); + assertThat(zoneGroupConfiguration.getRefDynamicSourceConfiguration()).isNull(); + assertThat(zoneGroupConfiguration.hasRelationQuerySource()).isFalse(); + } + + @Test + void whenHasRelationQuerySourceCalled_shouldReturnFalseIfCurrentOwnerSourceConfigured() { + var zoneGroupConfiguration = mock(ZoneGroupConfiguration.class); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); + assertThat(zoneGroupConfiguration.hasRelationQuerySource()).isFalse(); + } + + @Test + void validateToArgumentsMethodCallWithoutRefEntityId() { + var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + Argument zoneGroupArgument = zoneGroupConfiguration.toArgument(); + assertThat(zoneGroupArgument).isNotNull(); + assertThat(zoneGroupArgument.getRefEntityKey()).isEqualTo(new ReferencedEntityKey("perimeter", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + } + +} 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 index 95368e3240..fbcb92e4dd 100644 --- 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 @@ -20,7 +20,6 @@ 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.DEDUPLICATION_TIMEOUT_SELF_MSG; import static org.thingsboard.server.common.data.msg.TbMsgType.DELAY_TIMEOUT_SELF_MSG; @@ -39,7 +38,6 @@ 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, diff --git a/common/discovery-api/pom.xml b/common/discovery-api/pom.xml index b6ffe0499e..9bcf02dd94 100644 --- a/common/discovery-api/pom.xml +++ b/common/discovery-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index c2d5d028b6..d52074ace6 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common 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 267fd2b3f2..a1a7b5462f 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 @@ -41,4 +41,5 @@ public interface EdgeRpcClient { void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg); int getServerMaxInboundMessageSize(); + } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 803bb98330..a01e0e561a 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -44,6 +44,7 @@ enum EdgeVersion { V_4_0_0 = 10; V_4_1_0 = 11; V_4_2_0 = 12; + V_4_3_0 = 13; V_LATEST = 999; } @@ -133,6 +134,12 @@ message CalculatedFieldUpdateMsg{ string entity = 4; } +message AiModelUpdateMsg{ + UpdateMsgType msgType = 1; + int64 idMSB = 2; + int64 idLSB = 3; + string entity = 4; +} message EntityDataProto { int64 entityIdMSB = 1; @@ -441,6 +448,9 @@ message UplinkMsg { repeated RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 24; repeated CalculatedFieldUpdateMsg calculatedFieldUpdateMsg = 25; repeated CalculatedFieldRequestMsg calculatedFieldRequestMsg = 26; + repeated AiModelUpdateMsg aiModelUpdateMsg = 27; + repeated UserUpdateMsg userUpdateMsg = 28; + repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 29; } message UplinkResponseMsg { @@ -491,4 +501,5 @@ message DownlinkMsg { repeated NotificationTemplateUpdateMsg notificationTemplateUpdateMsg = 33; repeated OAuth2DomainUpdateMsg oAuth2DomainUpdateMsg = 34; repeated CalculatedFieldUpdateMsg calculatedFieldUpdateMsg = 35; + repeated AiModelUpdateMsg aiModelUpdateMsg = 36; } diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index 7916b50d3f..b953da6d11 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/data/BaseEntityData.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/data/BaseEntityData.java index 7ebf93fae5..1385e616a0 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/data/BaseEntityData.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/data/BaseEntityData.java @@ -20,6 +20,7 @@ import lombok.Setter; import lombok.ToString; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edqs.fields.EntityFields; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.permission.QueryContext; @@ -139,11 +140,32 @@ public abstract class BaseEntityData implements EntityDa case "name" -> getEntityName(); case "ownerName" -> getOwnerName(); case "ownerType" -> getOwnerType(); + case "displayName" -> getDisplayName(); case "entityType" -> Optional.ofNullable(getEntityType()).map(EntityType::name).orElse(""); default -> fields.getAsString(name); }; } + public String getDisplayName(){ + return switch (getEntityType()) { + case DEVICE, ASSET -> StringUtils.isNotBlank(fields.getLabel()) ? fields.getLabel() : fields.getName(); + case USER -> { + boolean firstNameSet = StringUtils.isNotBlank(fields.getFirstName()); + boolean lastNameSet = StringUtils.isNotBlank(fields.getLastName()); + if(firstNameSet && lastNameSet) { + yield fields.getFirstName() + " " + fields.getLastName(); + } else if(firstNameSet) { + yield fields.getFirstName(); + } else if (lastNameSet) { + yield fields.getLastName(); + } else { + yield fields.getEmail(); + } + } + default -> fields.getName(); + }; + } + public String getEntityName() { return getFields().getName(); } diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/data/DeviceData.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/data/DeviceData.java index 90143a0465..ab183bc3bd 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/data/DeviceData.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/data/DeviceData.java @@ -18,6 +18,7 @@ package org.thingsboard.server.edqs.data; import lombok.ToString; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edqs.fields.DeviceFields; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.edqs.DataPoint; diff --git a/common/message/pom.xml b/common/message/pom.xml index 9cacded661..615989da81 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/CalculatedFieldStatePartitionRestoreMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/CalculatedFieldStatePartitionRestoreMsg.java new file mode 100644 index 0000000000..948c68108c --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/CalculatedFieldStatePartitionRestoreMsg.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; + +@Data +public class CalculatedFieldStatePartitionRestoreMsg implements ToCalculatedFieldSystemMsg { + + private final TopicPartitionInfo partition; + + @Override + public TenantId getTenantId() { + return TenantId.SYS_TENANT_ID; + } + + @Override + public MsgType getMsgType() { + return MsgType.CF_STATE_PARTITION_RESTORE_MSG; + } + +} 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 591662b136..7e7aee226f 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 @@ -138,16 +138,24 @@ public enum MsgType { CF_CACHE_INIT_MSG, // Sent to init caches for CF actor; CF_STATE_RESTORE_MSG, // Sent to restore particular calculated field entity state; + CF_STATE_PARTITION_RESTORE_MSG, CF_PARTITIONS_CHANGE_MSG, // Sent when cluster event occures; CF_ENTITY_LIFECYCLE_MSG, // Sent on CF/Device/Asset create/update/delete; + CF_ENTITY_ACTION_EVENT_MSG, + CF_ALARM_ACTION_MSG, CF_TELEMETRY_MSG, // Sent from queue to actor system; CF_LINKED_TELEMETRY_MSG, // Sent from queue to actor system; /* CF Manager Actor -> CF Entity actor */ CF_ENTITY_TELEMETRY_MSG, CF_ENTITY_INIT_CF_MSG, - CF_ENTITY_DELETE_MSG; + CF_ENTITY_DELETE_MSG, + + CF_RELATION_ACTION_MSG, + + CF_ARGUMENT_RESET_MSG, // Sent to reset argument; + CF_REEVALUATE_MSG; @Getter private final boolean ignoreOnStart; 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 831d0557cc..b1dd55676a 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 @@ -41,9 +41,6 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; -/** - * Created by ashvayka on 13.01.18. - */ @Data @Slf4j public final class TbMsg implements Serializable { @@ -500,11 +497,11 @@ public final class TbMsg implements Serializable { public String toString() { return "TbMsg.TbMsgBuilder(queueName=" + this.queueName + ", id=" + this.id + ", ts=" + this.ts + - ", type=" + this.type + ", internalType=" + this.internalType + ", originator=" + this.originator + - ", customerId=" + this.customerId + ", metaData=" + this.metaData + ", dataType=" + this.dataType + - ", data=" + this.data + ", ruleChainId=" + this.ruleChainId + ", ruleNodeId=" + this.ruleNodeId + - ", correlationId=" + this.correlationId + ", partition=" + this.partition + ", previousCalculatedFields=" + this.previousCalculatedFieldIds + - ", ctx=" + this.ctx + ", callback=" + this.callback + ")"; + ", type=" + this.type + ", internalType=" + this.internalType + ", originator=" + this.originator + + ", customerId=" + this.customerId + ", metaData=" + this.metaData + ", dataType=" + this.dataType + + ", data=" + this.data + ", ruleChainId=" + this.ruleChainId + ", ruleNodeId=" + this.ruleNodeId + + ", correlationId=" + this.correlationId + ", partition=" + this.partition + ", previousCalculatedFields=" + this.previousCalculatedFieldIds + + ", ctx=" + this.ctx + ", callback=" + this.callback + ")"; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/ToCalculatedFieldSystemMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/ToCalculatedFieldSystemMsg.java index 4f73f0c480..ab22653f1e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/ToCalculatedFieldSystemMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/ToCalculatedFieldSystemMsg.java @@ -16,12 +16,7 @@ package org.thingsboard.server.common.msg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg; -import org.thingsboard.server.common.msg.queue.TbCallback; public interface ToCalculatedFieldSystemMsg extends TenantAwareMsg { - default TbCallback getCallback() { - return TbCallback.EMPTY; - } - } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java index 5728940087..4bccc3fbbd 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java @@ -17,9 +17,14 @@ package org.thingsboard.server.common.msg.aware; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; public interface TenantAwareMsg extends TbActorMsg { TenantId getTenantId(); - + + default TbCallback getCallback() { + return TbCallback.EMPTY; + } + } 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 b4444543bf..cccd39a9d8 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 @@ -30,9 +30,6 @@ import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; import java.io.Serial; import java.util.Optional; -/** - * @author Andrew Shvayka - */ @Data public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { @@ -46,14 +43,15 @@ public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { private final String name; private final EntityId oldProfileId; private final EntityId profileId; + private final boolean ownerChanged; private final JsonNode info; public ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event) { - this(tenantId, entityId, event, null, null, null, null, null); + this(tenantId, entityId, event, null, null, null, null, false, null); } @Builder - private ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event, String oldName, String name, EntityId oldProfileId, EntityId profileId, JsonNode info) { + private ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event, String oldName, String name, EntityId oldProfileId, EntityId profileId, boolean ownerChanged, JsonNode info) { this.tenantId = tenantId; this.entityId = entityId; this.event = event; @@ -61,6 +59,7 @@ public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { this.name = name; this.oldProfileId = oldProfileId; this.profileId = profileId; + this.ownerChanged = ownerChanged; this.info = info; } diff --git a/common/pom.xml b/common/pom.xml index f0b2bf6958..211578b1d2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 2de3d937be..f024f41992 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common 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 index 4e0604239f..784bd97c79 100644 --- 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 @@ -129,6 +129,7 @@ public class ProtoUtils { builder.setOldProfileIdMSB(msg.getOldProfileId().getId().getMostSignificantBits()); builder.setOldProfileIdLSB(msg.getOldProfileId().getId().getLeastSignificantBits()); } + builder.setOwnerChanged(msg.isOwnerChanged()); if (msg.getName() != null) { builder.setName(msg.getName()); } @@ -165,6 +166,7 @@ public class ProtoUtils { var profileType = EntityType.DEVICE.equals(entityId.getEntityType()) ? EntityType.DEVICE_PROFILE : EntityType.ASSET_PROFILE; builder.oldProfileId(EntityIdFactory.getByTypeAndUuid(profileType, new UUID(proto.getOldProfileIdMSB(), proto.getOldProfileIdLSB()))); } + builder.ownerChanged(proto.getOwnerChanged()); if (proto.hasInfo()) { builder.info(JacksonUtil.toJsonNode(proto.getInfo())); } @@ -1377,6 +1379,18 @@ public class ProtoUtils { return TbMsg.fromProto(queueName, getTbMsgProto(ruleEngineMsg), callback); } + public static TransportProtos.EntityIdProto toProto(EntityId entityId) { + return TransportProtos.EntityIdProto.newBuilder() + .setEntityIdMSB(getMsb(entityId)) + .setEntityIdLSB(getLsb(entityId)) + .setType(toProto(entityId.getEntityType())) + .build(); + } + + public static EntityId fromProto(TransportProtos.EntityIdProto entityIdProto) { + return EntityIdFactory.getByTypeAndUuid(fromProto(entityIdProto.getType()), new UUID(entityIdProto.getEntityIdMSB(), entityIdProto.getEntityIdLSB())); + } + private static boolean isNotNull(Object obj) { return obj != null; } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 29f4777c3e..8a44a19e17 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -62,10 +62,11 @@ enum EntityTypeProto { MOBILE_APP = 37; MOBILE_APP_BUNDLE = 38; CALCULATED_FIELD = 39; - CALCULATED_FIELD_LINK = 40; + // CALCULATED_FIELD_LINK = 40; - was removed in 4.3 JOB = 41; ADMIN_SETTINGS = 42; AI_MODEL = 43; + API_KEY = 44; } enum ApiUsageRecordKeyProto { @@ -82,6 +83,12 @@ enum ApiUsageRecordKeyProto { INACTIVE_DEVICES = 10; } +message EntityIdProto { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + EntityTypeProto type = 4; +} + /** * Service Discovery Data Structures; */ @@ -882,6 +889,7 @@ message SingleValueArgumentProto { string argName = 1; TsValueProto value = 2; int64 version = 3; + EntityIdProto entityId = 4; } message TsDoubleValProto { @@ -896,11 +904,38 @@ message TsRollingArgumentProto { repeated TsDoubleValProto tsValue = 4; } +message GeofencingZoneProto { + EntityIdProto zoneId = 1; + int64 ts = 2; + string perimeterDefinition = 3; + int64 version = 4; + optional bool inside = 5; +} + +message GeofencingArgumentProto { + string argName = 1; + repeated GeofencingZoneProto zones = 2; +} + +message ArgumentIntervalProto { + string argName = 1; + int64 startTs = 2; + int64 endTs = 3; + int64 lastArgsRefreshTs = 4; + int64 lastMetricsEvalTs = 5; +} + message CalculatedFieldStateProto { CalculatedFieldEntityCtxIdProto id = 1; string type = 2; repeated SingleValueArgumentProto singleValueArguments = 3; repeated TsRollingArgumentProto rollingValueArguments = 4; + repeated GeofencingArgumentProto geofencingArguments = 5; + AlarmStateProto alarmState = 6; + int64 lastArgsUpdateTs = 7; + int64 lastMetricsEvalTs = 8; + repeated ArgumentIntervalProto aggregationArguments = 9; + repeated EntityIdProto propagationEntityIds = 10; } //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. @@ -1253,6 +1288,8 @@ enum ComponentLifecycleEvent { DELETED = 6; FAILED = 7; DEACTIVATED = 8; + RELATION_UPDATED = 9; + RELATION_DELETED = 10; } message ComponentLifecycleMsgProto { @@ -1270,6 +1307,7 @@ message ComponentLifecycleMsgProto { int64 profileIdMSB = 11; int64 profileIdLSB = 12; optional string info = 13; + bool ownerChanged = 100; } message EdgeEventMsgProto { @@ -1701,6 +1739,7 @@ message ToEdgeEventNotificationMsg { message ToCalculatedFieldMsg { CalculatedFieldTelemetryMsgProto telemetryMsg = 1; CalculatedFieldLinkedTelemetryMsgProto linkedTelemetryMsg = 2; + EntityActionEventProto eventMsg = 3; } message ToCalculatedFieldNotificationMsg { @@ -1874,3 +1913,22 @@ message JobStatsMsg { message TaskResultProto { string value = 1; } + +message EntityActionEventProto { + EntityIdProto tenantId = 1; + EntityIdProto entityId = 2; + string entity = 3; + string action = 4; +} + +message AlarmStateProto { + repeated AlarmRuleStateProto createRuleStates = 1; + AlarmRuleStateProto clearRuleState = 2; +} + +message AlarmRuleStateProto { + string severity = 1; + int64 eventCount = 2; + int64 firstEventTs = 3; + int64 lastCheckTs = 4; +} 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 index e779632088..78e30aa480 100644 --- 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 @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; import org.thingsboard.common.util.JacksonUtil; @@ -43,6 +44,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventType; 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.EntityIdFactory; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKey; @@ -344,4 +346,24 @@ class ProtoUtilsTest { assertThat(toDeviceRpcRequestActorMsg.getMsg()).isEqualTo(request); } + @ParameterizedTest + @EnumSource(EntityType.class) + void testEntityIdProto_toProto_fromProto(EntityType entityType) { + UUID uuid = UUID.fromString("51a514d7-ea8f-496d-b567-f6e76f0f9b83"); + + EntityId original = EntityIdFactory.getByTypeAndUuid(entityType, uuid); + assertThat(original).isNotNull(); + + // toProto + TransportProtos.EntityIdProto proto = ProtoUtils.toProto(original); + assertThat(proto).isNotNull(); + assertThat(proto.getType().getNumber()).isEqualTo(entityType.getProtoNumber()); + assertThat(proto.getEntityIdMSB()).isEqualTo(uuid.getMostSignificantBits()); + assertThat(proto.getEntityIdLSB()).isEqualTo(uuid.getLeastSignificantBits()); + + // fromProto + EntityId restored = ProtoUtils.fromProto(proto); + assertThat(restored).isNotNull().isEqualTo(original); + } + } diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 83635e7372..4cb3f449a3 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common 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 07726f0254..87f566c90f 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 @@ -194,6 +194,11 @@ public abstract class AbstractTbQueueConsumerTemplate i abstract protected void doUnsubscribe(); + @Override + public Set getPartitions() { + return partitions; + } + @Override public List getFullTopicNames() { if (partitions == null) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java index 1a5cae1109..62bd6460a9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java @@ -25,6 +25,7 @@ import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsg; import org.thingsboard.server.queue.common.consumer.TbQueueConsumerManagerTask.UpdateConfigTask; import org.thingsboard.server.queue.common.consumer.TbQueueConsumerManagerTask.UpdatePartitionsTask; +import org.thingsboard.server.queue.common.consumer.TbQueueConsumerTask.ConsumerKey; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; import java.util.Collection; @@ -218,7 +219,7 @@ public class MainQueueConsumerManager consumer) { + private void consumerLoop(ConsumerKey consumerKey, TbQueueConsumer consumer) { try { while (!stopped && !consumer.isStopped()) { try { @@ -250,7 +251,7 @@ public class MainQueueConsumerManager msgs, TbQueueConsumer consumer, Object consumerKey, C config) throws Exception { + protected void processMsgs(List msgs, TbQueueConsumer consumer, ConsumerKey consumerKey, C config) throws Exception { log.trace("Processing {} messages", msgs.size()); msgPackProcessor.process(msgs, consumer, consumerKey, config); log.trace("Processed {} messages", msgs.size()); @@ -273,7 +274,7 @@ public class MainQueueConsumerManager { - void process(List msgs, TbQueueConsumer consumer, Object consumerKey, C config) throws Exception; + void process(List msgs, TbQueueConsumer consumer, ConsumerKey consumerKey, C config) throws Exception; } public interface ConsumerWrapper { @@ -285,6 +286,7 @@ public class MainQueueConsumerManager { + private final Map> consumers = new HashMap<>(); @Override @@ -307,8 +309,7 @@ public class MainQueueConsumerManager partitions, Consumer onStop, Function startOffsetProvider) { partitions.forEach(tpi -> { - Integer partitionId = tpi.getPartition().orElse(-1); - String key = queueKey + "-" + partitionId; + ConsumerKey key = new ConsumerKey(queueKey, tpi); Runnable callback = onStop != null ? () -> onStop.accept(tpi) : null; TbQueueConsumerTask consumer = new TbQueueConsumerTask<>(key, () -> { @@ -328,9 +329,11 @@ public class MainQueueConsumerManager> getConsumers() { return consumers.values(); } + } class SingleConsumerWrapper implements ConsumerWrapper { + private TbQueueConsumerTask consumer; @Override @@ -346,7 +349,7 @@ public class MainQueueConsumerManager(queueKey, () -> consumerCreator.apply(config, null), null); // no partitionId passed + consumer = new TbQueueConsumerTask<>(new ConsumerKey(queueKey, null), () -> consumerCreator.apply(config, null), null); // no partitionId passed } consumer.subscribe(partitions); if (!consumer.isRunning()) { @@ -361,5 +364,7 @@ public class MainQueueConsumerManager { @Getter - private final Object key; + private final ConsumerKey key; private volatile TbQueueConsumer consumer; private volatile Supplier> consumerSupplier; @Getter @@ -41,7 +41,7 @@ public class TbQueueConsumerTask { @Setter private Future task; - public TbQueueConsumerTask(Object key, Supplier> consumerSupplier, Runnable callback) { + public TbQueueConsumerTask(ConsumerKey key, Supplier> consumerSupplier, Runnable callback) { this.key = key; this.consumer = null; this.consumerSupplier = consumerSupplier; @@ -97,4 +97,18 @@ public class TbQueueConsumerTask { return task != null; } + public record ConsumerKey(Object queueKey, TopicPartitionInfo partition) { + + @Override + public String toString() { + if (partition != null) { + Integer partitionId = partition.getPartition().orElse(-1); + return queueKey + "-" + partitionId; + } else { + return queueKey.toString(); + } + } + + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/state/DefaultQueueStateService.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/state/DefaultQueueStateService.java index ea15ac5c18..2a83ef9b72 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/state/DefaultQueueStateService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/state/DefaultQueueStateService.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.queue.common.state; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.TbQueueMsg; import org.thingsboard.server.queue.common.consumer.PartitionedQueueConsumerManager; +import org.thingsboard.server.queue.discovery.QueueKey; import java.util.Collections; +import java.util.Set; + +import static org.thingsboard.server.common.msg.queue.TopicPartitionInfo.withTopic; public class DefaultQueueStateService extends QueueStateService { @@ -26,4 +31,18 @@ public class DefaultQueueStateService partitions, RestoreCallback callback) { + if (callback != null) { + for (TopicPartitionInfo partition : partitions) { + callback.onPartitionRestored(partition); + } + callback.onAllPartitionsRestored(); + } + eventConsumer.addPartitions(partitions); + for (PartitionedQueueConsumerManager consumer : otherConsumers) { + consumer.addPartitions(withTopic(partitions, consumer.getTopic())); + } + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/state/KafkaQueueStateService.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/state/KafkaQueueStateService.java index c586da8842..d5ce0a55b8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/state/KafkaQueueStateService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/state/KafkaQueueStateService.java @@ -50,7 +50,7 @@ public class KafkaQueueStateService } @Override - protected void addPartitions(QueueKey queueKey, Set partitions, Runnable whenAllProcessed) { + protected void addPartitions(QueueKey queueKey, Set partitions, RestoreCallback callback) { Map eventsStartOffsets = eventsStartOffsetsProvider != null ? eventsStartOffsetsProvider.get() : null; // remembering the offsets before subscribing to states Set statePartitions = withTopic(partitions, stateConsumer.getTopic()); @@ -61,10 +61,13 @@ public class KafkaQueueStateService try { partitionsInProgress.remove(statePartition); log.info("Finished partition {} (still in progress: {})", statePartition, partitionsInProgress); + if (callback != null) { + callback.onPartitionRestored(statePartition); + } if (partitionsInProgress.isEmpty()) { log.info("All partitions processed"); - if (whenAllProcessed != null) { - whenAllProcessed.run(); + if (callback != null) { + callback.onAllPartitionsRestored(); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/state/QueueStateService.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/state/QueueStateService.java index 1cbf7ee016..234d20c32e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/state/QueueStateService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/state/QueueStateService.java @@ -49,7 +49,7 @@ public abstract class QueueStateService newPartitions, Runnable whenAllProcessed) { + public void update(QueueKey queueKey, Set newPartitions, RestoreCallback callback) { newPartitions = withTopic(newPartitions, eventConsumer.getTopic()); var writeLock = partitionsLock.writeLock(); writeLock.lock(); @@ -71,23 +71,15 @@ public abstract class QueueStateService partitions, Runnable whenAllProcessed) { - if (whenAllProcessed != null) { - whenAllProcessed.run(); - } - eventConsumer.addPartitions(partitions); - for (PartitionedQueueConsumerManager consumer : otherConsumers) { - consumer.addPartitions(withTopic(partitions, consumer.getTopic())); - } - } + protected abstract void addPartitions(QueueKey queueKey, Set partitions, RestoreCallback callback) ; protected void removePartitions(QueueKey queueKey, Set partitions) { eventConsumer.removePartitions(partitions); @@ -122,4 +114,12 @@ public abstract class QueueStateService implements TbQueueCon return stopped; } + @Override + public Set getPartitions() { + return partitions; + } + @Override public List getFullTopicNames() { return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); diff --git a/common/script/pom.xml b/common/script/pom.xml index b00e7957bf..5f352fb70b 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 7db10cb1cc..d1c71bbd9d 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 - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 990b57f41f..0ba928ebe1 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 - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT script org.thingsboard.common.script 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 6340b02dd0..7fb56a89ff 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 @@ -26,6 +26,7 @@ import org.mvel2.execution.ExecutionHashMap; import org.mvel2.execution.ExecutionLinkedHashSet; import org.mvel2.util.MethodStub; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.NumberUtils; import org.thingsboard.common.util.geo.Coordinates; import org.thingsboard.common.util.geo.GeoUtil; import org.thingsboard.common.util.geo.RangeUnit; @@ -35,7 +36,6 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; -import java.math.RoundingMode; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.ByteBuffer; @@ -258,13 +258,13 @@ public class TbUtils { byte[].class, int.class, int.class))); parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", byte[].class, int.class, int.class, boolean.class))); - parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", + parserConfig.addImport("toFixed", new MethodStub(NumberUtils.class.getMethod("toFixed", double.class, int.class))); - parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", + parserConfig.addImport("toFixed", new MethodStub(NumberUtils.class.getMethod("toFixed", float.class, int.class))); - parserConfig.addImport("toInt", new MethodStub(TbUtils.class.getMethod("toInt", + parserConfig.addImport("toInt", new MethodStub(NumberUtils.class.getMethod("toInt", double.class))); - parserConfig.addImport("isNaN", new MethodStub(TbUtils.class.getMethod("isNaN", + parserConfig.addImport("isNaN", new MethodStub(NumberUtils.class.getMethod("isNaN", double.class))); parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes", ExecutionContext.class, String.class))); @@ -1174,22 +1174,6 @@ public class TbUtils { return new String(hexChars, StandardCharsets.UTF_8); } - public static double toFixed(double value, int precision) { - 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(); - } - - public static int toInt(double value) { - return BigDecimal.valueOf(value).setScale(0, RoundingMode.HALF_UP).intValue(); - } - - public static boolean isNaN(double value) { - return Double.isNaN(value); - } - public static ExecutionHashMap toFlatMap(ExecutionContext ctx, Map json) { return toFlatMap(ctx, json, new ArrayList<>(), true); } @@ -1208,7 +1192,6 @@ public class TbUtils { return map; } - public static String encodeURI(String uri) { String encoded = URLEncoder.encode(uri, StandardCharsets.UTF_8); for (var entry : mdnEncodingReplacements.entrySet()) { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfArg.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfArg.java index 47fb994089..cacd9b9426 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfArg.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfArg.java @@ -26,7 +26,10 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; ) @JsonSubTypes({ @JsonSubTypes.Type(value = TbelCfSingleValueArg.class, name = "SINGLE_VALUE"), - @JsonSubTypes.Type(value = TbelCfTsRollingArg.class, name = "TS_ROLLING") + @JsonSubTypes.Type(value = TbelCfTsRollingArg.class, name = "TS_ROLLING"), + @JsonSubTypes.Type(value = TbelCfGeofencingArg.class, name = "GEOFENCING_CF_ARGUMENT_VALUE"), + @JsonSubTypes.Type(value = TbelCfPropagationArg.class, name = "PROPAGATION_CF_ARGUMENT_VALUE"), + @JsonSubTypes.Type(value = TbelCfRelatedEntitiesArgumentValue.class, name = "RELATED_ENTITIES_ARGUMENT_VALUE") }) public interface TbelCfArg extends TbelCfObject { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfGeofencingArg.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfGeofencingArg.java new file mode 100644 index 0000000000..16748f79f9 --- /dev/null +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfGeofencingArg.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TbelCfGeofencingArg implements TbelCfArg { + + private final Object value; + + @JsonCreator + public TbelCfGeofencingArg(@JsonProperty("value") Object value) { + this.value = value; + } + + @Override + public String getType() { + return "GEOFENCING_CF_ARGUMENT_VALUE"; + } + + + @Override + public long memorySize() { + return OBJ_SIZE; + } + +} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfPropagationArg.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfPropagationArg.java new file mode 100644 index 0000000000..b6cfa2c863 --- /dev/null +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfPropagationArg.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TbelCfPropagationArg implements TbelCfArg { + + private final Object value; + + @JsonCreator + public TbelCfPropagationArg(@JsonProperty("value") Object value) { + this.value = value; + } + + @Override + public String getType() { + return "PROPAGATION_CF_ARGUMENT_VALUE"; + } + + @Override + public long memorySize() { + return OBJ_SIZE; + } + +} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfRelatedEntitiesArgumentValue.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfRelatedEntitiesArgumentValue.java new file mode 100644 index 0000000000..58a310b3f5 --- /dev/null +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfRelatedEntitiesArgumentValue.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + +@Data +public class TbelCfRelatedEntitiesArgumentValue implements TbelCfArg { + + private final Map entityInputs; + + @JsonCreator + public TbelCfRelatedEntitiesArgumentValue(@JsonProperty("entityInputs") Map values) { + this.entityInputs = Collections.unmodifiableMap(values); + } + + @Override + public String getType() { + return "RELATED_ENTITIES_ARGUMENT_VALUE"; + } + + @Override + public long memorySize() { + return OBJ_SIZE; + } +} 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 01ccd5d579..bdafeac8a7 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 @@ -279,13 +279,6 @@ public class TbUtilsTest { Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseLittleEndianHexToFloat(floatValHexRev))); } - @Test - public void toFixedFloat() { - float actualF = TbUtils.toFixed(floatVal, 3); - Assertions.assertEquals(1, Float.compare(floatVal, actualF)); - Assertions.assertEquals(0, Float.compare(29.298f, actualF)); - } - @Test public void parseBytesToFloat() { byte[] floatValByte = {0x0A}; @@ -461,13 +454,6 @@ public class TbUtilsTest { Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseLittleEndianHexToDouble(longValHexRev))); } - @Test - public void toFixedDouble() { - double actualD = TbUtils.toFixed(doubleVal, 3); - Assertions.assertEquals(-1, Double.compare(doubleVal, actualD)); - Assertions.assertEquals(0, Double.compare(1729.173, actualD)); - } - @Test public void parseBytesToDouble() { byte[] doubleValByte = {0x0A}; @@ -1147,19 +1133,6 @@ public class TbUtilsTest { Assertions.assertEquals(expected, actual); } - @Test - public void toInt() { - Assertions.assertEquals(1729, TbUtils.toInt(doubleVal)); - Assertions.assertEquals(13, TbUtils.toInt(12.8)); - Assertions.assertEquals(28, TbUtils.toInt(28.0)); - } - - @Test - public void isNaN() { - assertFalse(TbUtils.isNaN(doubleVal)); - assertTrue(TbUtils.isNaN(Double.NaN)); - } - @Test public void isInsidePolygon() { // outside the polygon diff --git a/common/stats/pom.xml b/common/stats/pom.xml index db4c0582d7..d2942cd813 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index ff6620832a..273e7e2a7d 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index b019ebaa69..cd64cc36d2 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 051bcb119d..6f61468ed7 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java index a0f9300d81..45b3148219 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java @@ -127,7 +127,9 @@ public class LwM2MBootstrapConfig implements Serializable { private BootstrapConfig.ServerConfig setServerConfig (AbstractLwM2MBootstrapServerCredential serverCredential) { BootstrapConfig.ServerConfig serverConfig = new BootstrapConfig.ServerConfig(); - serverConfig.shortId = serverCredential.getShortServerId(); + if (serverCredential.getShortServerId() != null) { + serverConfig.shortId = serverCredential.getShortServerId(); + } serverConfig.lifetime = serverCredential.getLifetime(); serverConfig.defaultMinPeriod = serverCredential.getDefaultMinPeriod(); serverConfig.notifIfDisabled = serverCredential.isNotifIfDisabled(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java index 8104a62abf..28d4c5b7b7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure; import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.Request; import org.eclipse.leshan.core.peer.IpPeer; import org.eclipse.leshan.core.peer.LwM2mPeer; import org.eclipse.leshan.core.peer.PskIdentity; @@ -112,8 +113,10 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession } catch (InvalidConfigurationException e){ log.error("Failed put to lwM2MBootstrapSessionClients by endpoint [{}]", request.getEndpointName(), e); } + String msg = String.format("Bootstrap session started... %s", ((Request) request.getCoapRequest()).getLocalAddress().toString()); + log.warn(String.format("%s: %s", request.getEndpointName(), msg)); this.sendLogs(request.getEndpointName(), - String.format("%s: Bootstrap session started...", LOG_LWM2M_INFO, request.getEndpointName())); + String.format("%s: %s", LOG_LWM2M_INFO, msg)); } return session; } @@ -135,7 +138,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession session.setModel(modelProvider.getObjectModel(session, tasks.supportedObjects)); // set Requests to Send - log.info("tasks.requestsToSend = [{}]", tasks.requestsToSend); + log.warn("tasks.requestsToSend = [{}]", tasks.requestsToSend); session.setRequests(tasks.requestsToSend); // prepare list where we will store Responses @@ -182,14 +185,16 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession session.getResponses().add(response); String msg = String.format("%s: receives success response for: %s %s %s", LOG_LWM2M_INFO, request.getClass().getSimpleName(), request.getPath().toString(), response.toString()); + log.warn(msg); this.sendLogs(bsSession.getEndpoint(), msg); // on success for NOT bootstrap finish request we send next request return BootstrapPolicy.continueWith(nextRequest(bsSession)); } else { // on success for bootstrap finish request we stop the session - this.sendLogs(bsSession.getEndpoint(), - String.format("%s: receives success response for bootstrap finish.", LOG_LWM2M_INFO)); + String msg = String.format("%s: receives success response for bootstrap finish.", LOG_LWM2M_INFO); + log.info(msg); + this.sendLogs(bsSession.getEndpoint(), msg); this.tasksProvider.remove(bsSession.getEndpoint()); return BootstrapPolicy.finished(); } @@ -228,7 +233,9 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession @Override public void end(BootstrapSession bsSession) { - this.sendLogs(bsSession.getEndpoint(), String.format("%s: Bootstrap session finished.", LOG_LWM2M_INFO)); + String msg = String.format("%s: Bootstrap session finished.", LOG_LWM2M_INFO); + log.warn(msg); + this.sendLogs(bsSession.getEndpoint(), msg); this.tasksProvider.remove(bsSession.getEndpoint()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java index cd54e9a0f1..a40bf9ec27 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java @@ -17,15 +17,12 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.store; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.link.Link; -import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.request.BootstrapDeleteRequest; import org.eclipse.leshan.core.request.BootstrapDiscoverRequest; import org.eclipse.leshan.core.request.BootstrapDownlinkRequest; -import org.eclipse.leshan.core.request.BootstrapReadRequest; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.BootstrapDiscoverResponse; -import org.eclipse.leshan.core.response.BootstrapReadResponse; import org.eclipse.leshan.core.response.LwM2mResponse; import org.eclipse.leshan.server.bootstrap.BootstrapConfig; import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore; @@ -33,7 +30,6 @@ import org.eclipse.leshan.server.bootstrap.BootstrapSession; import org.eclipse.leshan.server.bootstrap.BootstrapUtil; import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; -import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -43,14 +39,18 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.stream.Collectors; -import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; +import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; +import static org.eclipse.leshan.core.LwM2mId.SECURITY; +import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.server.bootstrap.BootstrapUtil.toWriteRequest; -import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.BOOTSTRAP_DEFAULT_SHORT_ID_0; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.LWM2M_SERVER_MAX; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.PRIMARY_LWM2M_SERVER; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.isLwm2mServer; @Slf4j public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTaskProvider { @@ -77,11 +77,11 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask @Override public Tasks getTasks(BootstrapSession session, List previousResponse) { // BootstrapConfig config = store.get(session.getEndpoint(), session.getClientTransportData().getIdentity(), session); - BootstrapConfig config = store.get(session); - if (config == null) { + BootstrapConfig configNew = store.get(session); + if (configNew == null) { return null; } - if (previousResponse == null && shouldStartWithDiscover(config)) { + if (previousResponse == null && shouldStartWithDiscover(configNew)) { Tasks tasks = new Tasks(); tasks.requestsToSend = new ArrayList<>(1); tasks.requestsToSend.add(new BootstrapDiscoverRequest()); @@ -96,47 +96,27 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask tasks.supportedObjects = this.supportedObjects; // handle bootstrap discover response if (previousResponse != null) { - if (previousResponse.get(0) instanceof BootstrapDiscoverResponse) { - BootstrapDiscoverResponse discoverResponse = (BootstrapDiscoverResponse) previousResponse.get(0); + if (previousResponse.get(0) instanceof BootstrapDiscoverResponse discoverResponse) { if (discoverResponse.isSuccess()) { - this.initAfterBootstrapDiscover(discoverResponse); - findSecurityInstanceId(discoverResponse.getObjectLinks(), session.getEndpoint()); - } else { + this.initAfterBootstrapDiscover(discoverResponse); + /// Short Server Ids - in old config + findInstancesIdOldByServerId(discoverResponse, session.getEndpoint()); log.warn( - "Bootstrap Discover return error {} : to continue bootstrap session without autoIdForSecurityObject mode. {}", + "Bootstrap server instance successfully found in Security Object (0) in response {}. Continuing bootstrap session. Session: {}", discoverResponse, session); - } - if (this.lwM2MBootstrapSessionClients.get(session.getEndpoint()).getSecurityInstances().get(BOOTSTRAP_DEFAULT_SHORT_ID_0) == null) { - log.error( - "Unable to find bootstrap server instance in Security Object (0) in response {}: unable to continue bootstrap session with autoIdForSecurityObject mode. {}", + } else { + log.warn( + "Unable to find bootstrap server instance in Security Object (0) in response {}. Continuing bootstrap session with autoIdForSecurityObject mode, ignoring information from discoverResponse. Session: {}", discoverResponse, session); - return null; - } - tasks.requestsToSend = new ArrayList<>(1); - tasks.requestsToSend.add(new BootstrapReadRequest("/1")); - tasks.last = false; - return tasks; - } - BootstrapReadResponse readResponse = (BootstrapReadResponse) previousResponse.get(0); - Integer bootstrapServerIdOld = null; - if (readResponse.isSuccess()) { - findServerInstanceId(readResponse, session.getEndpoint()); - if (this.lwM2MBootstrapSessionClients.get(session.getEndpoint()).getSecurityInstances().size() > 0 && this.lwM2MBootstrapSessionClients.get(session.getEndpoint()).getServerInstances().size() > 0) { - bootstrapServerIdOld = this.findBootstrapServerId(session.getEndpoint()); } - } else { - log.warn( - "Bootstrap ReadResponse return error {} : to continue bootstrap session without find Server Instance Id. {}", - readResponse, session); } // create requests from config - tasks.requestsToSend = this.toRequests(config, - config.contentFormat != null ? config.contentFormat : session.getContentFormat(), - bootstrapServerIdOld, session.getEndpoint()); + tasks.requestsToSend = this.toRequests(configNew, + configNew.contentFormat != null ? configNew.contentFormat : session.getContentFormat(), session.getEndpoint()); } else { // create requests from config - tasks.requestsToSend = BootstrapUtil.toRequests(config, - config.contentFormat != null ? config.contentFormat : session.getContentFormat()); + tasks.requestsToSend = BootstrapUtil.toRequests(configNew, + configNew.contentFormat != null ? configNew.contentFormat : session.getContentFormat()); } return tasks; } @@ -148,81 +128,57 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask /** * "Short Server ID": This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. - * The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server. - * "Short Server ID": + * "Short Lwm2m Server ID": * - Link Instance (lwm2m Server) hase linkParams with key = "ssid" value = "shortId" (ver lvm2m = 1.1). - * - Link Instance (bootstrap Server) hase not linkParams with key = "ssid" (ver lvm2m = 1.0). + * The values ID:0 values MUST NOT be used for identifying the LwM2M Server only BS. */ - protected void findSecurityInstanceId(Link[] objectLinks, String endpoint) { - log.info("Object after discover: [{}]", objectLinks); - for (Link link : objectLinks) { - if (link.getUriReference().startsWith("/0/")) { - try { - LwM2mPath path = new LwM2mPath(link.getUriReference()); - if (path.isObjectInstance()) { - if (link.getAttributes().get("ssid") != null) { - int serverId = Integer.parseInt(link.getAttributes().get("ssid").getCoreLinkValue()); - if (!lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(serverId)) { - lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(serverId, path.getObjectInstanceId()); - } else { - log.error("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId()); - } - lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(serverId, path.getObjectInstanceId()); + protected void findInstancesIdOldByServerId(BootstrapDiscoverResponse discoverResponses, String endpoint) { + log.info("Object after discover: [{}]", Arrays.toString(discoverResponses.getObjectLinks())); + for (Link link : discoverResponses.getObjectLinks()) { + LwM2mPath path = new LwM2mPath(link.getUriReference()); + if (path.isObjectInstance()) { + int lwm2mShortServerId = 0; + if (path.getObjectId() == 0) { + if (link.getAttributes().get("ssid") != null) { + lwm2mShortServerId = Integer.parseInt(link.getAttributes().get("ssid").getCoreLinkValue()); + if (validateLwm2mShortServerId(lwm2mShortServerId)) { + this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().putIfAbsent(lwm2mShortServerId, path.getObjectInstanceId()); + } else { + log.error("Invalid lwm2mSecurityInstance [{}] by short server id [{}]", path.getObjectInstanceId(), lwm2mShortServerId); + } + } else { + this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().putIfAbsent(null, path.getObjectInstanceId()); + } + } else if (path.getObjectId() == 1) { + if (link.getAttributes().get("ssid") != null) { + lwm2mShortServerId = Integer.parseInt(link.getAttributes().get("ssid").getCoreLinkValue()); + if (validateLwm2mShortServerId(lwm2mShortServerId)) { + this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().putIfAbsent(lwm2mShortServerId, path.getObjectInstanceId()); } else { - if (!this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(0)) { - this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().put(BOOTSTRAP_DEFAULT_SHORT_ID_0, path.getObjectInstanceId()); - } else { - log.error("Invalid bootstrapSecurityInstance by [{}]", path.getObjectInstanceId()); - } + log.error("Invalid lwm2mServerInstance [{}] by short server id [{}]", path.getObjectInstanceId(), lwm2mShortServerId); } } - } catch (Exception e) { - // ignore if this is not a LWM2M path - log.error("Invalid LwM2MPath starting by \"/0/\""); } } } } - protected void findServerInstanceId(BootstrapReadResponse readResponse, String endpoint) { - try { - ((LwM2mObject) readResponse.getContent()).getInstances().values().forEach(instance -> { - var shId = OPAQUE.equals(instance.getResource(0).getType()) ? new BigInteger((byte[]) instance.getResource(0).getValue()).intValue() : instance.getResource(0).getValue(); - int shortId; - if (shId instanceof Long) { - shortId = ((Long) shId).intValue(); - } else { - shortId = (int) shId; - } - this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().put(shortId, instance.getId()); - }); - } catch (Exception e) { - log.error("Failed find Server Instance Id. ", e); - } - } - - protected Integer findBootstrapServerId(String endpoint) { - Integer bootstrapServerIdOld = null; - Map filteredMap = this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().entrySet() - .stream().filter(x -> !this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(x.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - if (filteredMap.size() > 0) { - bootstrapServerIdOld = filteredMap.keySet().stream().findFirst().get(); - } - return bootstrapServerIdOld; - } - public BootstrapConfigStore getStore() { return this.store; } private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) { Link[] links = response.getObjectLinks(); + AtomicReference verDefault = new AtomicReference<>("1.0"); Arrays.stream(links).forEach(link -> { LwM2mPath path = new LwM2mPath(link.getUriReference()); - if (!path.isRoot() && path.getObjectId() < 3) { + if (path.isRoot()) { + if (link.hasAttribute() && link.getAttributes().get("lwm2m") != null) { + verDefault.set(link.getAttributes().get("lwm2m").getValue().toString()); + } + } else if (path.getObjectId() <= ACCESS_CONTROL) { if (path.isObject()) { - String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver").getCoreLinkValue() : "1.0"; + String ver = (link.hasAttribute() && link.getAttributes().get("ver") != null) ? link.getAttributes().get("ver").getCoreLinkValue() : verDefault.get(); this.supportedObjects.put(path.getObjectId(), ver); } } @@ -230,94 +186,124 @@ public class LwM2MBootstrapConfigStoreTaskProvider implements LwM2MBootstrapTask } - public List> toRequests(BootstrapConfig bootstrapConfig, + /** Map => LwM2MBootstrapClientInstanceIds + * 1) Both + * - (Short) Server ID == null bs) + * SECURITY = 0; InstanceId = 0 + * - Short Server ID == 1 - 65534 lwm2m) + * SECURITY = 0; InstanceId = 1 + * SERVER = 1; InstanceId = 0 + * 2) Only BS Server + * - Short Server ID == null bs) + * SECURITY = 0; InstanceId = 0 + * 3) Only Lwm2m Server + * - Short Server ID == 1 - 65534 lwm2m) + * SECURITY = 0; InstanceId = 0 + * SERVER = 1; InstanceId = 0 + * */ + public List> toRequests(BootstrapConfig bootstrapConfigNew, ContentFormat contentFormat, - Integer bootstrapServerIdOld, String endpoint) { + Integer bootstrapSecurityInstanceId = this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(null) == null ? + -2 : this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(null); List> requests = new ArrayList<>(); Set pathsDelete = new HashSet<>(); - List> requestsWrite = new ArrayList<>(); - boolean isBsServer = false; - boolean isLwServer = false; - /** Map */ - Map instances = new HashMap<>(); - Integer bootstrapServerIdNew = null; - // handle security - int lwm2mSecurityInstanceId = 0; - int bootstrapSecurityInstanceId = this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(BOOTSTRAP_DEFAULT_SHORT_ID_0); - for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) { - if (security.bootstrapServer) { - requestsWrite.add(toWriteRequest(bootstrapSecurityInstanceId, security, contentFormat)); - isBsServer = true; - bootstrapServerIdNew = security.serverId; - instances.put(security.serverId, bootstrapSecurityInstanceId); - } else { - if (lwm2mSecurityInstanceId == bootstrapSecurityInstanceId) { - lwm2mSecurityInstanceId++; - } - requestsWrite.add(toWriteRequest(lwm2mSecurityInstanceId, security, contentFormat)); - instances.put(security.serverId, lwm2mSecurityInstanceId); - isLwServer = true; - if (!isBsServer && this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().containsKey(security.serverId) && - lwm2mSecurityInstanceId != this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(security.serverId)) { - pathsDelete.add("/0/" + this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(security.serverId)); - } - /** - * If there is an instance in the serverInstances with serverId which we replace in the securityInstances - */ - // find serverId in securityInstances by id (instance) - Integer serverIdOld = null; - for (Map.Entry entry : this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().entrySet()) { - if (entry.getValue().equals(lwm2mSecurityInstanceId)) { - serverIdOld = entry.getKey(); - } - } - if (!isBsServer && serverIdOld != null && this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().containsKey(serverIdOld)) { - pathsDelete.add("/1/" + this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(serverIdOld)); - } - lwm2mSecurityInstanceId++; + ConcurrentHashMap> requestsWrite = new ConcurrentHashMap<>(); + + /// handle security & handle + // bootstrap Security new - There can only be one instance of bootstrap at a time. + /// bs: handle security only + for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfigNew.security).values()) { + if (security.bootstrapServer && bootstrapSecurityInstanceId > -1) { + // delete old bootstrap Security + String path = "/" + SECURITY + "/" + bootstrapSecurityInstanceId; + pathsDelete.add(path); + security.serverId = null; + requestsWrite.put(path, toWriteRequest(bootstrapSecurityInstanceId, security, contentFormat)); } } - // handle server - for (Map.Entry server : bootstrapConfig.servers.entrySet()) { - int securityInstanceId = instances.get(server.getValue().shortId); - requestsWrite.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat)); - if (!isBsServer) { - /** Delete instance if bootstrapServerIdNew not equals bootstrapServerIdOld or securityInstanceBsIdNew not equals serverInstanceBsIdOld */ - if (bootstrapServerIdNew != null && server.getValue().shortId == bootstrapServerIdNew && - (bootstrapServerIdNew != bootstrapServerIdOld || securityInstanceId != this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(bootstrapServerIdOld))) { - pathsDelete.add("/1/" + this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(bootstrapServerIdOld)); - /** Delete instance if serverIdNew is present in serverInstances and securityInstanceIdOld by serverIdNew not equals serverInstanceIdOld */ - } else if (this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().containsKey(server.getValue().shortId) && - securityInstanceId != this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(server.getValue().shortId)) { - pathsDelete.add("/1/" + this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(server.getValue().shortId)); - } + + /** lwm2m servers: Multiple instances of lwm2m servers can run simultaneously by SHORT_ID + if update -> delete and write by InstanceId + if new -> only write with InstanceIdMax++ + */ + + /// lwm2m server: handle security & server + //max Lwm2m Security instance old id if new + int lwm2mSecurityInstanceIdMax = -1; + for (Integer shortId : this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().keySet()) { + if (isLwm2mServer(shortId)) { + lwm2mSecurityInstanceIdMax = Math.max( + this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(shortId), + lwm2mSecurityInstanceIdMax); } } - // handle acl - for (Map.Entry acl : bootstrapConfig.acls.entrySet()) { - requestsWrite.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); + //max Lwm2m Server instance old id if new + int lwm2mServerInstanceIdMax = -1; + for (Integer shortId : this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().keySet()) { + if (isLwm2mServer(shortId)) { + lwm2mServerInstanceIdMax = Math.max( + this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(shortId), + lwm2mServerInstanceIdMax); + } } - // handle delete - if (isBsServer && isLwServer) { - requests.add(new BootstrapDeleteRequest("/0")); - requests.add(new BootstrapDeleteRequest("/1")); - } else { - pathsDelete.forEach(pathDelete -> requests.add(new BootstrapDeleteRequest(pathDelete))); + // Lwm2m update or new + for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfigNew.security).values()) { + if (!security.bootstrapServer) { + // Security + Integer secureInstanceId = this.lwM2MBootstrapSessionClients.get(endpoint).getSecurityInstances().get(security.serverId); + if (secureInstanceId != null) { + pathsDelete.add("/" + SECURITY + "/" + secureInstanceId); + requestsWrite.put("/" + SECURITY + "/" + secureInstanceId, toWriteRequest(secureInstanceId, security, contentFormat)); + } else { + secureInstanceId = ++lwm2mSecurityInstanceIdMax; + if (bootstrapSecurityInstanceId.equals(secureInstanceId)) { + secureInstanceId = ++lwm2mSecurityInstanceIdMax; + } + requestsWrite.put("/" + SECURITY + "/" + secureInstanceId, toWriteRequest(secureInstanceId, security, contentFormat)); + } + Integer serverInstanceId = this.lwM2MBootstrapSessionClients.get(endpoint).getServerInstances().get(security.serverId); + if (serverInstanceId != null) { + pathsDelete.add("/" + SERVER + "/" + serverInstanceId); + } else { + serverInstanceId = ++lwm2mServerInstanceIdMax; + } + Integer finalServerInstanceId = serverInstanceId; + new TreeMap<>(bootstrapConfigNew.servers).values().stream() + .filter(server -> server.shortId == security.serverId) + .findFirst() + .ifPresent(server -> + requestsWrite.put( + "/" + SERVER + "/" + finalServerInstanceId, + toWriteRequest(finalServerInstanceId, server, contentFormat) + ) + ); + } } - // handle write - if (requestsWrite.size() > 0) { - requests.addAll(requestsWrite); + + /// handle acl + for (Map.Entry acl : bootstrapConfigNew.acls.entrySet()) { + requestsWrite.put("/" + ACCESS_CONTROL + "/" + acl.getKey(), toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); + } + /// handle delete + pathsDelete.forEach(pathDelete -> requests.add(new BootstrapDeleteRequest(pathDelete))); + + /// handle write + if (!requestsWrite.isEmpty()) { + requests.addAll(requestsWrite.values()); } return (requests); } - private void initSupportedObjectsDefault() { this.supportedObjects = new HashMap<>(); - this.supportedObjects.put(0, "1.1"); - this.supportedObjects.put(1, "1.1"); - this.supportedObjects.put(2, "1.0"); + this.supportedObjects.put(SECURITY, "1.1"); + this.supportedObjects.put(SERVER, "1.1"); + this.supportedObjects.put(ACCESS_CONTROL, "1.0"); + } + + private boolean validateLwm2mShortServerId(int id){ + return id >= PRIMARY_LWM2M_SERVER.getId() && id <= LWM2M_SERVER_MAX.getId(); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MConfigurationChecker.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MConfigurationChecker.java index 6bb8d5ac26..dec5170069 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MConfigurationChecker.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MConfigurationChecker.java @@ -21,6 +21,10 @@ import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException; import java.util.Map; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.NOT_USED_IDENTIFYING_LWM2M_SERVER_MIN; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.NOT_USED_IDENTIFYING_LWM2M_SERVER_MAX; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.isNotLwm2mServer; + public class LwM2MConfigurationChecker extends ConfigurationChecker { @Override @@ -74,15 +78,16 @@ public class LwM2MConfigurationChecker extends ConfigurationChecker { * This Resource MUST be set when the Bootstrap-Server Resource has false value. * Specific ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server (Section 6.3 of the LwM2M version 1.0 specification). */ - if (!security.bootstrapServer && (srvCfg.shortId < 1 && srvCfg.shortId > 65534 )) { - throw new InvalidConfigurationException("Specific ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server"); + if (!security.bootstrapServer && isNotLwm2mServer(srvCfg.shortId)) { + throw new InvalidConfigurationException("Specific ID:" + NOT_USED_IDENTIFYING_LWM2M_SERVER_MIN.getId() + " and ID:" + NOT_USED_IDENTIFYING_LWM2M_SERVER_MAX.getId() + " values MUST NOT be used for identifying the LwM2M Server"); } } } protected static BootstrapConfig.ServerSecurity getSecurityEntry(BootstrapConfig config, int shortId) { for (Map.Entry es : config.security.entrySet()) { - if (es.getValue().serverId == shortId) { + if ((es.getValue().serverId == null && shortId == 0) || + (es.getValue().serverId != null && es.getValue().serverId == shortId)) { return es.getValue(); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index d2b24fe9a8..5373c56320 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -43,7 +43,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { private int dtlsRetransmissionTimeout; @Getter - @Value("${transport.lwm2m.dtls.connection_id_length:}") + @Value("${transport.lwm2m.dtls.connection_id_length:8}") private Integer dtlsCidLength; @Getter diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java index 65916971ae..234ef11d0b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java @@ -38,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @@ -180,8 +181,16 @@ public class LwM2mTransportServerHelper { case BOOLEAN: kvProto.setType(BOOLEAN_V).setBoolV((Boolean) value).build(); break; - case STRING: case TIME: + if (value instanceof Date) { + kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV(((Date) value).getTime()); + } else if (value instanceof Integer || value instanceof Long) { + kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV((long) (value)); + } else { + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(value.toString()); + } + break; + case STRING: case OPAQUE: case OBJLNK: kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV((String) value); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 67d2838e1d..dea3b901e1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -66,7 +66,9 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.BOOTSTRAP_TRIGGER_PARAMS_ID; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LWM2M_OBJECT_VERSION_DEFAULT; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.REGISTRATION_TRIGGER_PARAMS_ID; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.equalsResourceTypeGetSimpleName; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId; @@ -342,6 +344,10 @@ public class LwM2mClient { public String isValidObjectVersion(String path) { LwM2mPath pathIds = getLwM2mPathFromString(path); + if (pathIds.isResource() && (pathIds.toString().equals(REGISTRATION_TRIGGER_PARAMS_ID ) || + pathIds.toString().equals(BOOTSTRAP_TRIGGER_PARAMS_ID))) { + return ""; + } LwM2m.Version verSupportedObject = this.getSupportedObjectVersion(pathIds.getObjectId()); if (verSupportedObject == null) { return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MObserveCompositeCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MObserveCompositeCallback.java index c4aa473a39..5db88ce306 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MObserveCompositeCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/composite/TbLwM2MObserveCompositeCallback.java @@ -23,6 +23,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MUplinkTarge import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import java.util.concurrent.CountDownLatch; + @Slf4j public class TbLwM2MObserveCompositeCallback extends TbLwM2MUplinkTargetedCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index f906e1e9bf..cb922e5905 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -94,6 +94,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.model.LwM2MModelConfig; import org.thingsboard.server.transport.lwm2m.server.model.LwM2MModelConfigService; @@ -502,14 +504,30 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl private void sendReadRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { try { Set targetIds = new HashSet<>(profile.getObserveAttr().getAttribute()); + Boolean initAttrTelAsObsStrategy = profile.getObserveAttr().getInitAttrTelAsObsStrategy(); targetIds.addAll(profile.getObserveAttr().getTelemetry()); targetIds = diffSets(profile.getObserveAttr().getObserve(), targetIds); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); - - CountDownLatch latch = new CountDownLatch(targetIds.size()); - targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId, - new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)))); - latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); + if (!targetIds.isEmpty()) { + TelemetryObserveStrategy observeStrategy = profile.getObserveAttr().getObserveStrategy(); + long timeoutMs = config.getTimeout(); + if (initAttrTelAsObsStrategy && observeStrategy != SINGLE) { + switch (observeStrategy) { + case COMPOSITE_ALL -> { + sendReadCompositeRequest(lwM2MClient, targetIds.toArray(new String[0])); + } + case COMPOSITE_BY_OBJECT -> { + Map versionedObjectIds = groupByObjectIdVersionedIds(targetIds); + versionedObjectIds.forEach((k, v) -> sendReadCompositeRequest(lwM2MClient, v)); + } + } + } else { + CountDownLatch latch = new CountDownLatch(targetIds.size()); + targetIds.forEach(versionedId -> sendReadSingleRequest(lwM2MClient, versionedId, + new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)))); + latch.await(timeoutMs, TimeUnit.MILLISECONDS); + } + } } catch (InterruptedException e) { log.error("[{}] Failed to await Read requests!", lwM2MClient.getEndpoint(), e); } catch (Exception e) { @@ -536,17 +554,11 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl if (!completed) log.trace("[{}] Timeout occurred during SINGLE observe init", lwM2MClient.getEndpoint()); } case COMPOSITE_ALL -> { - CountDownLatch latch = new CountDownLatch(targetIds.size()); sendObserveCompositeRequest(lwM2MClient, targetIds.toArray(new String[0])); - boolean completed = latch.await(timeoutMs, TimeUnit.MILLISECONDS); - if (!completed) log.trace("[{}] Timeout occurred during COMPOSITE_ALL observe init", lwM2MClient.getEndpoint()); } case COMPOSITE_BY_OBJECT -> { Map versionedObjectIds = groupByObjectIdVersionedIds(targetIds); - CountDownLatch latch = new CountDownLatch(versionedObjectIds.size()); versionedObjectIds.forEach((k, v) -> sendObserveCompositeRequest(lwM2MClient, v)); - boolean completed = latch.await(timeoutMs, TimeUnit.MILLISECONDS); - if (!completed) log.trace("[{}] Timeout occurred during COMPOSITE_BY_OBJECT observe init", lwM2MClient.getEndpoint()); } } } @@ -569,23 +581,22 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl } } - private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId) { - sendReadRequest(lwM2MClient, versionedId, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)); - } - - private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { + private void sendReadSingleRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, callback); } - private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId) { - sendObserveRequest(lwM2MClient, versionedId, new TbLwM2MObserveCallback(this, logService, lwM2MClient, versionedId)); + private void sendReadCompositeRequest(LwM2mClient lwM2MClient, String[] versionedIds) { + TbLwM2MReadCompositeRequest request = TbLwM2MReadCompositeRequest.builder().versionedIds(versionedIds).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); + var mainCallback = new TbLwM2MReadCompositeCallback(this, logService, lwM2MClient, versionedIds); + defaultLwM2MDownlinkMsgHandler.sendReadCompositeRequest(lwM2MClient, request, mainCallback ); } private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, callback); } + private void sendObserveCompositeRequest(LwM2mClient lwM2MClient, String[] versionedIds) { TbLwM2MObserveCompositeRequest request = TbLwM2MObserveCompositeRequest.builder().versionedIds(versionedIds).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); @@ -829,7 +840,8 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); request.getObjectInstances().forEach(instance -> instance.getResources().forEach((resId, lwM2mResource) ->{ - this.updateResourcesValue(updateResource, lwM2mResource, versionId + "/" + resId, Mode.REPLACE, 0); + String path = versionId.endsWith("/") ? versionId + resId : versionId + "/" + resId; + this.updateResourcesValue(updateResource, lwM2mResource, path, Mode.REPLACE, 0); }) ); clientContext.update(lwM2MClient); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java index 8eb1e3e8cd..cfa21a3bec 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java @@ -81,7 +81,8 @@ public class LwM2MTransportUtil { public static final String LOG_LWM2M_INFO = "info"; public static final String LOG_LWM2M_ERROR = "error"; public static final String LOG_LWM2M_WARN = "warn"; - public static final int BOOTSTRAP_DEFAULT_SHORT_ID_0 = 0; + public static final String REGISTRATION_TRIGGER_PARAMS_ID = "/1/0/8"; + public static final String BOOTSTRAP_TRIGGER_PARAMS_ID = "/1/0/9";; public static LwM2mOtaConvert convertOtaUpdateValueToString(String pathIdVer, Object value, ResourceModel.Type currentType) { String path = fromVersionedIdToObjectId(pathIdVer); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index b9e11db3e0..43867564ae 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -33,6 +33,7 @@ import java.util.Date; import static org.eclipse.leshan.core.model.ResourceModel.Type.NONE; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; +import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; @Slf4j public class LwM2mValueConverterImpl implements LwM2mValueConverter { @@ -58,7 +59,7 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { currentType = OPAQUE; } - if (currentType == expectedType || currentType == NONE) { + if (currentType == expectedType || currentType == NONE || currentType == TIME) { /** expected type */ return value; } @@ -135,7 +136,7 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { **/ } catch (IllegalArgumentException e) { log.debug("Unable to convert string to date", e); - throw new CodecException("Unable to convert string (%s) to date for resource %s", value, + throw new CodecException("Unable to convert string (%s) to %s for resource %s", value, TIME.name(), resourcePath); } default: @@ -149,7 +150,7 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { case FLOAT: return String.valueOf(value); case TIME: - String DATE_FORMAT = "MMM d, yyyy HH:mm a"; + String DATE_FORMAT = "yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]"; Long timeValue; try { timeValue = ((Date) value).getTime(); diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 502f3b6e93..5521d83c13 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 3d45327242..b1c85d22e3 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index bb504dc389..eb6ddc4c16 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index ff35ed08f9..26c15a8c9f 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 - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 113a6d3e97..96e393dc57 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/common/util/src/main/java/org/thingsboard/common/util/ExpressionFunctionsUtil.java b/common/util/src/main/java/org/thingsboard/common/util/ExpressionUtils.java similarity index 87% rename from common/util/src/main/java/org/thingsboard/common/util/ExpressionFunctionsUtil.java rename to common/util/src/main/java/org/thingsboard/common/util/ExpressionUtils.java index 07ffdaa2a1..529c017990 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/ExpressionFunctionsUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/ExpressionUtils.java @@ -15,13 +15,16 @@ */ package org.thingsboard.common.util; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; import net.objecthunter.exp4j.function.Function; import net.objecthunter.exp4j.function.Functions; import java.util.ArrayList; import java.util.List; +import java.util.Set; -public class ExpressionFunctionsUtil { +public class ExpressionUtils { public static final List userDefinedFunctions = new ArrayList<>(); @@ -75,4 +78,13 @@ public class ExpressionFunctionsUtil { userDefinedFunctions.add(Functions.getBuiltinFunction("signum")); } + public static Expression createExpression(String expression, Set variables) { + return new ExpressionBuilder(expression) + .functions(userDefinedFunctions) + .implicitMultiplication(true) + .operator() + .variables(variables) + .build(); + } + } 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 9913210c1a..2cf758885f 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 @@ -115,10 +115,15 @@ public class JacksonUtil { @Contract("null, _ -> null") // so that IDE doesn't show NPE warning when input is not null public static T fromString(String string, Class clazz) { + return fromString(string, clazz, "The given string value cannot be transformed to Json object: " + string); + } + + @Contract("null, _, _ -> null") // so that IDE doesn't show NPE warning when input is not null + public static T fromString(String string, Class clazz, String errorMsg) { try { return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; } catch (IOException e) { - throw new IllegalArgumentException("The given string value cannot be transformed to Json object: " + string, e); + throw new IllegalArgumentException(errorMsg, e); } } diff --git a/common/util/src/main/java/org/thingsboard/common/util/KvUtil.java b/common/util/src/main/java/org/thingsboard/common/util/KvUtil.java index 1baff75a79..bcf028abf7 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/KvUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/KvUtil.java @@ -61,6 +61,37 @@ public class KvUtil { } } + public static Long getLongValue(KvEntry entry) { + switch (entry.getDataType()) { + case LONG -> { + return entry.getLongValue().orElse(null); + } + case DOUBLE -> { + return entry.getDoubleValue().map(Double::longValue).orElse(null); + } + case BOOLEAN -> { + return entry.getBooleanValue().map(b -> b ? 1L : 0L).orElse(null); + } + case STRING -> { + try { + return Long.parseLong(entry.getStrValue().orElse("")); + } catch (RuntimeException e) { + return null; + } + } + case JSON -> { + try { + return Long.parseLong(entry.getJsonValue().orElse("")); + } catch (RuntimeException e) { + return null; + } + } + default -> { + return null; + } + } + } + public static Boolean getBoolValue(KvEntry entry) { switch (entry.getDataType()) { case LONG: diff --git a/common/util/src/main/java/org/thingsboard/common/util/NumberUtils.java b/common/util/src/main/java/org/thingsboard/common/util/NumberUtils.java new file mode 100644 index 0000000000..6681557c87 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/NumberUtils.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.math.BigDecimal; +import java.math.RoundingMode; + +public class NumberUtils { + + public static boolean isNaN(double value) { + return Double.isNaN(value); + } + + public static double toFixed(double value, int precision) { + 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(); + } + + public static int toInt(double value) { + return BigDecimal.valueOf(value).setScale(0, RoundingMode.HALF_UP).intValue(); + } + + public static Object roundResult(double value, Integer precision) { + if (precision == null) { + return value; + } + if (precision.equals(0)) { + return toInt(value); + } + return toFixed(value, precision); + } + +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/geo/CirclePerimeterDefinition.java b/common/util/src/main/java/org/thingsboard/common/util/geo/CirclePerimeterDefinition.java new file mode 100644 index 0000000000..15b91f5a2e --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/geo/CirclePerimeterDefinition.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import lombok.Data; + +@Data +public class CirclePerimeterDefinition implements PerimeterDefinition { + + private final Double latitude; + private final Double longitude; + private final Double radius; + + @Override + public PerimeterType getType() { + return PerimeterType.CIRCLE; + } + + @Override + public boolean checkMatches(Coordinates entityCoordinates) { + Coordinates perimeterCoordinates = new Coordinates(latitude, longitude); + return radius > GeoUtil.distance(entityCoordinates, perimeterCoordinates, RangeUnit.METER); + } + +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinition.java b/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinition.java new file mode 100644 index 0000000000..218596b543 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinition.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.Serializable; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonDeserialize(using = PerimeterDefinitionDeserializer.class) +@JsonSerialize(using = PerimeterDefinitionSerializer.class) +public interface PerimeterDefinition extends Serializable { + + @JsonIgnore + PerimeterType getType(); + + @JsonIgnore + boolean checkMatches(Coordinates entityCoordinates); +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinitionDeserializer.java b/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinitionDeserializer.java new file mode 100644 index 0000000000..1c22d6a007 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinitionDeserializer.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; + +public class PerimeterDefinitionDeserializer extends JsonDeserializer { + + @Override + public PerimeterDefinition deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + ObjectCodec codec = p.getCodec(); + JsonNode node = codec.readTree(p); + + if (node.isObject()) { + if (!node.has("latitude") || !node.has("longitude") || !node.has("radius")) { + throw JsonMappingException.from(p, "CirclePerimeterDefinition missing required fields. Received: " + node); + } + double latitude = node.get("latitude").asDouble(); + double longitude = node.get("longitude").asDouble(); + double radius = node.get("radius").asDouble(); + return new CirclePerimeterDefinition(latitude, longitude, radius); + } + if (node.isArray()) { + ObjectMapper mapper = (ObjectMapper) p.getCodec(); + String polygonStrDefinition = mapper.writeValueAsString(node); + return new PolygonPerimeterDefinition(polygonStrDefinition); + } + throw JsonMappingException.from(p, "Unknown JSON format for PerimeterDefinition. Expected OBJECT (Circle) or ARRAY (Polygon), but found: " + node.getNodeType()); + } + +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinitionSerializer.java b/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinitionSerializer.java new file mode 100644 index 0000000000..76b86ee2a4 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/geo/PerimeterDefinitionSerializer.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.thingsboard.server.common.data.StringUtils; + +import java.io.IOException; + +public class PerimeterDefinitionSerializer extends JsonSerializer { + + @Override + public void serialize(PerimeterDefinition value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value instanceof CirclePerimeterDefinition c) { + gen.writeStartObject(); + gen.writeNumberField("latitude", c.getLatitude()); + gen.writeNumberField("longitude", c.getLongitude()); + gen.writeNumberField("radius", c.getRadius()); + gen.writeEndObject(); + return; + } + if (value instanceof PolygonPerimeterDefinition p) { + String raw = p.getPolygonDefinition(); + if (StringUtils.isBlank(raw)) { + throw new IOException("Failed to serialize PolygonPerimeterDefinition with blank: " + value); + } + ObjectMapper mapper = (ObjectMapper) gen.getCodec(); + gen.writeTree(mapper.readTree(raw)); + return; + } + throw new IOException("Failed to serialize PerimeterDefinition from value: " + value); + } +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/geo/PolygonPerimeterDefinition.java b/common/util/src/main/java/org/thingsboard/common/util/geo/PolygonPerimeterDefinition.java new file mode 100644 index 0000000000..b0720ae04a --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/geo/PolygonPerimeterDefinition.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import lombok.Data; + +@Data +public class PolygonPerimeterDefinition implements PerimeterDefinition { + + private final String polygonDefinition; + + @Override + public PerimeterType getType() { + return PerimeterType.POLYGON; + } + + @Override + public boolean checkMatches(Coordinates entityCoordinates) { + return GeoUtil.contains(polygonDefinition, entityCoordinates); + } + +} diff --git a/common/util/src/test/java/org/thingsboard/common/util/NumberUtilsTest.java b/common/util/src/test/java/org/thingsboard/common/util/NumberUtilsTest.java new file mode 100644 index 0000000000..1a1bf326e6 --- /dev/null +++ b/common/util/src/test/java/org/thingsboard/common/util/NumberUtilsTest.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 static org.assertj.core.api.Assertions.assertThat; + +public class NumberUtilsTest { + + private final Float floatVal = 29.29824f; + private final double doubleVal = 1729.1729; + + @Test + public void isNaN() { + assertThat(NumberUtils.isNaN(doubleVal)).isFalse(); + assertThat(NumberUtils.isNaN(Double.NaN)).isTrue(); + } + + @Test + public void toFixedFloat() { + float actualF = NumberUtils.toFixed(floatVal, 3); + assertThat(Float.compare(floatVal, actualF)).isEqualTo(1); + assertThat(Float.compare(29.298f, actualF)).isEqualTo(0); + } + + @Test + public void toFixedDouble() { + double actualD = NumberUtils.toFixed(doubleVal, 3); + assertThat(Double.compare(doubleVal, actualD)).isEqualTo(-1); + assertThat(Double.compare(1729.173, actualD)).isEqualTo(0); + } + + @Test + public void toInt() { + assertThat(NumberUtils.toInt(doubleVal)).isEqualTo(1729); + assertThat(NumberUtils.toInt(12.8)).isEqualTo(13); + assertThat(NumberUtils.toInt(28.0)).isEqualTo(28); + } + + @Test + public void roundResult() { + assertThat(NumberUtils.roundResult(doubleVal, null)).isEqualTo(1729.1729); + assertThat(NumberUtils.roundResult(doubleVal, 0)).isEqualTo(1729); + assertThat(NumberUtils.roundResult(doubleVal, 2)).isEqualTo(1729.17); + } + +} diff --git a/common/util/src/test/java/org/thingsboard/common/util/geo/PerimeterDefinitionDeserializerTest.java b/common/util/src/test/java/org/thingsboard/common/util/geo/PerimeterDefinitionDeserializerTest.java new file mode 100644 index 0000000000..df5d90336f --- /dev/null +++ b/common/util/src/test/java/org/thingsboard/common/util/geo/PerimeterDefinitionDeserializerTest.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import com.fasterxml.jackson.databind.JsonMappingException; +import org.junit.jupiter.api.Test; +import org.thingsboard.common.util.JacksonUtil; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class PerimeterDefinitionDeserializerTest { + + @Test + void shouldDeserializeCircle() { + String json = """ + {"latitude":50.45,"longitude":30.52,"radius":100.0}"""; + + PerimeterDefinition def = JacksonUtil.fromString(json, PerimeterDefinition.class); + + assertThat(def).isNotNull().isInstanceOf(CirclePerimeterDefinition.class); + + CirclePerimeterDefinition circle = (CirclePerimeterDefinition) def; + assertThat(circle.getLatitude()).isEqualTo(50.45); + assertThat(circle.getLongitude()).isEqualTo(30.52); + assertThat(circle.getRadius()).isEqualTo(100.0); + } + + @Test + void shouldDeserializePolygon() { + String json = "[[50.45,30.52],[50.46,30.53],[50.44,30.54]]"; + + PerimeterDefinition def = JacksonUtil.fromString(json, PerimeterDefinition.class); + + assertThat(def).isInstanceOf(PolygonPerimeterDefinition.class); + PolygonPerimeterDefinition poly = (PolygonPerimeterDefinition) def; + assertThat(poly.getPolygonDefinition()).isEqualTo(json); + } + + @Test + void shouldThrowWhenCircleFieldIsMissing() { + // Missing "radius" + String badJson = """ + { + "latitude": 48.8566, + "longitude": 2.3522 + } + """; + + String customError = "Custom error context"; + + assertThatThrownBy(() -> JacksonUtil.fromString(badJson, PerimeterDefinition.class, customError)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(customError) + .hasCauseInstanceOf(JsonMappingException.class) + .rootCause() + .hasMessageContaining("CirclePerimeterDefinition missing required fields"); + } + + @Test + void shouldThrowWhenJsonIsGarbage() { + String garbageJson = "\"NotAnObjectOrArray\""; + String customError = "Garbage check"; + + assertThatThrownBy(() -> JacksonUtil.fromString(garbageJson, PerimeterDefinition.class, customError)) + .isInstanceOf(IllegalArgumentException.class) + .hasCauseInstanceOf(JsonMappingException.class) + .rootCause() + .hasMessageContaining("Unknown JSON format"); + } + + @Test + void shouldReturnNullWhenInputIsNull() { + //noinspection ConstantConditions + PerimeterDefinition result = JacksonUtil.fromString(null, PerimeterDefinition.class, "Error"); + assertThat(result).isNull(); + } + +} diff --git a/common/util/src/test/java/org/thingsboard/common/util/geo/PerimeterDefinitionSerializerTest.java b/common/util/src/test/java/org/thingsboard/common/util/geo/PerimeterDefinitionSerializerTest.java new file mode 100644 index 0000000000..bed15db25b --- /dev/null +++ b/common/util/src/test/java/org/thingsboard/common/util/geo/PerimeterDefinitionSerializerTest.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.geo; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.Test; +import org.thingsboard.common.util.JacksonUtil; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PerimeterDefinitionSerializerTest { + + @Test + void shouldSerializeCircle() { + PerimeterDefinition circle = new CirclePerimeterDefinition(50.45, 30.52, 120.0); + + String json = JacksonUtil.writeValueAsString(circle); + + JsonNode actual = JacksonUtil.toJsonNode(json); + assertThat(actual.get("latitude").asDouble()).isEqualTo(50.45); + assertThat(actual.get("longitude").asDouble()).isEqualTo(30.52); + assertThat(actual.get("radius").asDouble()).isEqualTo(120.0); + } + + @Test + void shouldSerializePolygon() throws Exception { + String rawArray = "[[50.45,30.52],[50.46,30.53],[50.44,30.54]]"; + PerimeterDefinition polygon = new PolygonPerimeterDefinition(rawArray); + + String json = JacksonUtil.writeValueAsString(polygon); + + JsonNode actual = JacksonUtil.toJsonNode(json); + JsonNode expected = JacksonUtil.toJsonNode(rawArray); + assertThat(actual).isEqualTo(expected); + assertThat(actual.isArray()).isTrue(); + assertThat(actual.size()).isEqualTo(3); + } + +} diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index bb2e107224..f41fe2b4c0 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT common org.thingsboard.common diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java similarity index 100% rename from application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java rename to common/version-control/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java diff --git a/application/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java similarity index 100% rename from application/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java rename to common/version-control/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java diff --git a/dao/pom.xml b/dao/pom.xml index ca551a92e1..0b1874919b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard dao diff --git a/dao/src/main/java/org/thingsboard/server/dao/Dao.java b/dao/src/main/java/org/thingsboard/server/dao/Dao.java index 893688a7d1..08561d8b68 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/Dao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/Dao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edqs.fields.EntityFields; import org.thingsboard.server.common.data.id.TenantId; @@ -32,6 +33,10 @@ public interface Dao { ListenableFuture findByIdAsync(TenantId tenantId, UUID id); + default List findEntityInfosByNamePrefix(TenantId tenantId, String name) { + throw new UnsupportedOperationException(); + } + boolean existsById(TenantId tenantId, UUID id); ListenableFuture existsByIdAsync(TenantId tenantId, UUID id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/ai/AiModelServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/ai/AiModelServiceImpl.java index 31fa250a2a..35cbbfe71a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ai/AiModelServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ai/AiModelServiceImpl.java @@ -29,12 +29,15 @@ 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.CachedVersionedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.model.sql.AiModelEntity; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.sql.JpaExecutorService; import java.util.Optional; import java.util.Set; +import java.util.UUID; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -64,11 +67,23 @@ class AiModelServiceImpl extends CachedVersionedEntityService findAiModelByTenantIdAndId(tenantId, modelId))); } + @Override + public Optional findAiModelByTenantIdAndName(TenantId tenantId, String name) { + return Optional.ofNullable(aiModelDao.findByTenantIdAndName(tenantId.getId(), name)); + } + @Override @Transactional public boolean deleteByTenantIdAndId(TenantId tenantId, AiModelId modelId) { - return deleteByTenantIdAndIdInternal(tenantId, modelId); + return deleteByTenantIdAndIdInternal(tenantId, modelId.getId()); } @Override @@ -130,14 +150,21 @@ class AiModelServiceImpl extends CachedVersionedEntityService, ExportableEntityDao< List findTenantAssetProfileNames(UUID tenantId, boolean activeOnly); + List findAssetProfilesByTenantIdAndIds(UUID tenantId, List assetProfileIds); + } 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 49b21976f4..829a60621b 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 @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.CachedVersionedEntityService; 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.exception.DataValidationException; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -51,6 +51,7 @@ import java.util.Optional; import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; @Service("AssetProfileDaoService") @@ -345,6 +346,12 @@ public class AssetProfileServiceImpl extends CachedVersionedEntityService findAssetProfilesByIds(TenantId tenantId, List assetProfileIds) { + log.trace("Executing findAssetProfilesByIds, tenantId [{}], assetProfileIds [{}]", tenantId, assetProfileIds); + return assetProfileDao.findAssetProfilesByTenantIdAndIds(tenantId.getId(), toUUIDs(assetProfileIds)); + } + private final PaginatedRemover tenantAssetProfilesRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index a4e9226a0f..b4ff58120d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -26,6 +26,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.ProfileEntityIdInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.Asset; @@ -51,7 +53,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.sql.JpaExecutorService; @@ -147,14 +149,24 @@ public class BaseAssetService extends AbstractCachedEntityService saveAsset(asset, true, nameConflictStrategy)); + } + @Override public Asset saveAsset(Asset asset, boolean doValidate) { + return saveEntity(asset, () -> saveAsset(asset, doValidate, NameConflictStrategy.DEFAULT)); + } + + private Asset saveAsset(Asset asset, boolean doValidate, NameConflictStrategy nameConflictStrategy) { log.trace("Executing saveAsset [{}]", asset); - Asset oldAsset = null; + Asset oldAsset = (asset.getId() != null) ? assetDao.findById(asset.getTenantId(), asset.getId().getId()) : null; + if (nameConflictStrategy.policy() == NameConflictPolicy.UNIQUIFY && (oldAsset == null || !oldAsset.getName().equals(asset.getName()))) { + uniquifyEntityName(asset, oldAsset, asset::setName, EntityType.ASSET, nameConflictStrategy); + } if (doValidate) { - oldAsset = assetValidator.validate(asset, Asset::getTenantId); - } else if (asset.getId() != null) { - oldAsset = findAssetById(asset.getTenantId(), asset.getId()); + assetValidator.validate(asset, Asset::getTenantId); } AssetCacheEvictEvent evictEvent = new AssetCacheEvictEvent(asset.getTenantId(), asset.getName(), oldAsset != null ? oldAsset.getName() : null); Asset savedAsset; diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java index d2033d4535..2241b20e14 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java @@ -53,7 +53,7 @@ public interface AttributesDao { List findAllKeysByEntityIds(TenantId tenantId, List entityIds); - List findAllKeysByEntityIdsAndAttributeType(TenantId tenantId, List entityIds, String attributeType); + List findAllKeysByEntityIdsAndScope(TenantId tenantId, List entityIds, AttributeScope scope); List> removeAllByEntityId(TenantId tenantId, EntityId entityId); 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 a6a052e247..117c47b61c 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 @@ -28,7 +28,6 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ObjectType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edqs.AttributeKv; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; @@ -93,11 +92,11 @@ public class BaseAttributesService implements AttributesService { } @Override - public List findAllKeysByEntityIds(TenantId tenantId, List entityIds, String scope) { - if (StringUtils.isEmpty(scope)) { + public List findAllKeysByEntityIds(TenantId tenantId, List entityIds, AttributeScope scope) { + if (scope == null) { return attributesDao.findAllKeysByEntityIds(tenantId, entityIds); } else { - return attributesDao.findAllKeysByEntityIdsAndAttributeType(tenantId, entityIds, scope); + return attributesDao.findAllKeysByEntityIdsAndScope(tenantId, entityIds, scope); } } 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 cc36c1f76e..c150eabfa2 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 @@ -212,11 +212,11 @@ public class CachedAttributesService implements AttributesService { } @Override - public List findAllKeysByEntityIds(TenantId tenantId, List entityIds, String scope) { - if (StringUtils.isEmpty(scope)) { + public List findAllKeysByEntityIds(TenantId tenantId, List entityIds, AttributeScope scope) { + if (scope == null) { return attributesDao.findAllKeysByEntityIds(tenantId, entityIds); } else { - return attributesDao.findAllKeysByEntityIdsAndAttributeType(tenantId, entityIds, scope); + return attributesDao.findAllKeysByEntityIdsAndScope(tenantId, entityIds, scope); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java b/dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java index 76b8158074..00c5ec372e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java @@ -19,25 +19,32 @@ import com.google.common.util.concurrent.FluentFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; +import org.thingsboard.server.common.data.cf.CalculatedFieldFilter; +import org.thingsboard.server.common.data.cf.CalculatedFieldInfo; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; 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.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityService; 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.validator.CalculatedFieldDataValidator; +import java.util.EnumSet; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -52,15 +59,13 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements public static final String INCORRECT_CALCULATED_FIELD_ID = "Incorrect calculatedFieldId "; public static final String INCORRECT_ENTITY_ID = "Incorrect entityId "; + private final EntityService entityService; private final CalculatedFieldDao calculatedFieldDao; - private final CalculatedFieldLinkDao calculatedFieldLinkDao; - private final DataValidator calculatedFieldDataValidator; - private final DataValidator calculatedFieldLinkDataValidator; + private final CalculatedFieldDataValidator calculatedFieldDataValidator; @Override public CalculatedField save(CalculatedField calculatedField) { - CalculatedField oldCalculatedField = calculatedFieldDataValidator.validate(calculatedField, CalculatedField::getTenantId); - return doSave(calculatedField, oldCalculatedField); + return save(calculatedField, true); } @Override @@ -81,14 +86,19 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements log.trace("Executing save calculated field, [{}]", calculatedField); updateDebugSettings(tenantId, calculatedField, System.currentTimeMillis()); CalculatedField savedCalculatedField = calculatedFieldDao.save(tenantId, calculatedField); - createOrUpdateCalculatedFieldLink(tenantId, savedCalculatedField); - eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedCalculatedField.getTenantId()).entityId(savedCalculatedField.getId()) - .entity(savedCalculatedField).oldEntity(oldCalculatedField).created(calculatedField.getId() == null).build()); + eventPublisher.publishEvent(SaveEntityEvent.builder() + .tenantId(savedCalculatedField.getTenantId()) + .entityId(savedCalculatedField.getId()) + .entity(savedCalculatedField) + .oldEntity(oldCalculatedField) + .created(calculatedField.getId() == null) + .build()); return savedCalculatedField; } catch (Exception e) { checkConstraintViolation(e, - "calculated_field_unq_key", "Calculated Field with such name is already in exists!", - "calculated_field_external_id_unq_key", "Calculated Field with such external id already exists!"); + "calculated_field_unq_key", calculatedField.getType() == CalculatedFieldType.ALARM ? + "Alarm rule with such type already exists" : "Calculated field with such name and type already exists", + "calculated_field_external_id_unq_key", "Calculated field with such external id already exists"); throw e; } } @@ -102,10 +112,10 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements } @Override - public CalculatedField findByEntityIdAndName(EntityId entityId, String name) { - log.trace("Executing findByEntityIdAndName [{}], calculatedFieldName[{}]", entityId, name); + public CalculatedField findByEntityIdAndTypeAndName(EntityId entityId, CalculatedFieldType type, String name) { + log.trace("Executing findByEntityIdAndTypeAndName entityId [{}], type [{}], name [{}]", entityId, type, name); validateId(entityId.getId(), id -> INCORRECT_ENTITY_ID + id); - return calculatedFieldDao.findByEntityIdAndName(entityId, name); + return calculatedFieldDao.findByEntityIdAndTypeAndName(entityId, type, name); } @Override @@ -138,11 +148,36 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements } @Override - public PageData findAllCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) { + public PageData findCalculatedFieldsByTenantIdAndFilter(TenantId tenantId, CalculatedFieldFilter filter, PageLink pageLink) { + PageData calculatedFields = calculatedFieldDao.findByTenantIdAndFilter(tenantId, filter, pageLink); + Set entityIds = calculatedFields.getData().stream() + .map(CalculatedField::getEntityId) + .collect(Collectors.toSet()); + Map entityInfos = entityService.fetchEntityInfos(tenantId, null, entityIds); + return calculatedFields.mapData(calculatedField -> { + EntityInfo entityInfo = entityInfos.get(calculatedField.getEntityId()); + return new CalculatedFieldInfo(calculatedField, entityInfo.getName()); + }); + } + + @Override + public PageData findCalculatedFieldNamesByTenantIdAndType(TenantId tenantId, CalculatedFieldType type, PageLink pageLink) { + return calculatedFieldDao.findNamesByTenantIdAndType(tenantId, type, pageLink); + } + + @Override + public PageData findCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId, CalculatedFieldType type, PageLink pageLink) { log.trace("Executing findAllByEntityId, entityId [{}], pageLink [{}]", entityId, pageLink); validateId(entityId.getId(), id -> INCORRECT_ENTITY_ID + id); validatePageLink(pageLink); - return calculatedFieldDao.findAllByEntityId(tenantId, entityId, pageLink); + Set types; + if (type == null) { + types = EnumSet.allOf(CalculatedFieldType.class); + types.remove(CalculatedFieldType.ALARM); + } else { + types = Set.of(type); + } + return calculatedFieldDao.findByEntityIdAndTypes(tenantId, entityId, types, pageLink); } @Override @@ -180,48 +215,6 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements return calculatedFields.size(); } - @Override - public CalculatedFieldLink saveCalculatedFieldLink(TenantId tenantId, CalculatedFieldLink calculatedFieldLink) { - calculatedFieldLinkDataValidator.validate(calculatedFieldLink, CalculatedFieldLink::getTenantId); - log.trace("Executing save calculated field link, [{}]", calculatedFieldLink); - return calculatedFieldLinkDao.save(tenantId, calculatedFieldLink); - } - - @Override - public CalculatedFieldLink findCalculatedFieldLinkById(TenantId tenantId, CalculatedFieldLinkId calculatedFieldLinkId) { - log.trace("Executing findCalculatedFieldLinkById, tenantId [{}], calculatedFieldLinkId [{}]", tenantId, calculatedFieldLinkId); - validateId(tenantId, id -> INCORRECT_TENANT_ID + id); - validateId(calculatedFieldLinkId, id -> "Incorrect calculatedFieldLinkId " + id); - return calculatedFieldLinkDao.findById(tenantId, calculatedFieldLinkId.getId()); - } - - @Override - public List findAllCalculatedFieldLinksById(TenantId tenantId, CalculatedFieldId calculatedFieldId) { - log.trace("Executing findAllCalculatedFieldLinksById, calculatedFieldId [{}]", calculatedFieldId); - return calculatedFieldLinkDao.findCalculatedFieldLinksByCalculatedFieldId(tenantId, calculatedFieldId); - } - - @Override - public List findAllCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId) { - log.trace("Executing findAllCalculatedFieldLinksByEntityId, entityId [{}]", entityId); - return calculatedFieldLinkDao.findCalculatedFieldLinksByEntityId(tenantId, entityId); - } - - @Override - public PageData findAllCalculatedFieldLinksByTenantId(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findAllCalculatedFieldLinksByTenantId, tenantId[{}] pageLink [{}]", tenantId, pageLink); - validateId(tenantId, id -> INCORRECT_TENANT_ID + id); - validatePageLink(pageLink); - return calculatedFieldLinkDao.findAllByTenantId(tenantId, pageLink); - } - - @Override - public PageData findAllCalculatedFieldLinks(PageLink pageLink) { - log.trace("Executing findAllCalculatedFieldLinks, pageLink [{}]", pageLink); - validatePageLink(pageLink); - return calculatedFieldLinkDao.findAll(pageLink); - } - @Override public boolean referencedInAnyCalculatedField(TenantId tenantId, EntityId referencedEntityId) { return calculatedFieldDao.findAllByTenantId(tenantId).stream() @@ -247,9 +240,4 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements return EntityType.CALCULATED_FIELD; } - private void createOrUpdateCalculatedFieldLink(TenantId tenantId, CalculatedField calculatedField) { - List links = calculatedField.getConfiguration().buildCalculatedFieldLinks(tenantId, calculatedField.getEntityId(), calculatedField.getId()); - links.forEach(link -> saveCalculatedFieldLink(tenantId, link)); - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldDao.java b/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldDao.java index 633b77407b..8da1697c1f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldDao.java @@ -16,6 +16,8 @@ package org.thingsboard.server.dao.cf; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldFilter; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -24,6 +26,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import java.util.List; +import java.util.Set; public interface CalculatedFieldDao extends Dao { @@ -35,16 +38,20 @@ public interface CalculatedFieldDao extends Dao { List findAll(); - CalculatedField findByEntityIdAndName(EntityId entityId, String name); + CalculatedField findByEntityIdAndTypeAndName(EntityId entityId, CalculatedFieldType type, String name); PageData findAll(PageLink pageLink); PageData findAllByTenantId(TenantId tenantId, PageLink pageLink); - PageData findAllByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink); + PageData findByEntityIdAndTypes(TenantId tenantId, EntityId entityId, Set types, PageLink pageLink); List removeAllByEntityId(TenantId tenantId, EntityId entityId); - long countCFByEntityId(TenantId tenantId, EntityId entityId); + long countByEntityIdAndTypeNot(TenantId tenantId, EntityId entityId, CalculatedFieldType type); + + PageData findByTenantIdAndFilter(TenantId tenantId, CalculatedFieldFilter filter, PageLink pageLink); + + PageData findNamesByTenantIdAndType(TenantId tenantId, CalculatedFieldType type, PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldLinkDao.java b/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldLinkDao.java deleted file mode 100644 index 1d705e2d75..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldLinkDao.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; - -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.CalculatedFieldId; -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.dao.Dao; - -import java.util.List; - -public interface CalculatedFieldLinkDao extends Dao { - - List findCalculatedFieldLinksByCalculatedFieldId(TenantId tenantId, CalculatedFieldId calculatedFieldId); - - List findCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId); - - List findCalculatedFieldLinksByTenantId(TenantId tenantId); - - List findAll(); - - PageData findAll(PageLink pageLink); - - PageData findAllByTenantId(TenantId tenantId, PageLink pageLink); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java index ccf145be21..5e9292af5f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.page.PageLink; 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 org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.Validator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index 551a9139b7..2505121448 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -24,6 +24,7 @@ import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -78,4 +79,6 @@ public interface CustomerDao extends Dao, TenantEntityDao, E */ PageData findCustomersWithTheSameTitle(PageLink pageLink); + List findCustomersByTenantIdAndIds(UUID tenantId, List customerIds); + } 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 0d06a51c97..f872a823e1 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 @@ -29,6 +29,8 @@ import org.thingsboard.server.cache.customer.CustomerCacheEvictEvent; import org.thingsboard.server.cache.customer.CustomerCacheKey; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -43,7 +45,7 @@ import org.thingsboard.server.dao.entity.AbstractCachedEntityService; 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.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -55,6 +57,7 @@ import org.thingsboard.server.dao.user.UserService; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -141,19 +144,29 @@ public class CustomerServiceImpl extends AbstractCachedEntityService saveCustomer(customer, true, nameConflictStrategy)); } private Customer saveCustomer(Customer customer, boolean doValidate) { + return saveCustomer(customer, doValidate, NameConflictStrategy.DEFAULT); + } + + private Customer saveCustomer(Customer customer, boolean doValidate, NameConflictStrategy nameConflictStrategy) { log.trace("Executing saveCustomer [{}]", customer); - String oldCustomerTitle = null; + Customer oldCustomer = (customer.getId() != null) ? customerDao.findById(customer.getTenantId(), customer.getId().getId()) : null; + if (nameConflictStrategy.policy() == NameConflictPolicy.UNIQUIFY && (oldCustomer == null || !oldCustomer.getTitle().equals(customer.getTitle()))) { + uniquifyEntityName(customer, oldCustomer, customer::setTitle, EntityType.CUSTOMER, nameConflictStrategy); + } if (doValidate) { - Customer oldCustomer = customerValidator.validate(customer, Customer::getTenantId); - if (oldCustomer != null) { - oldCustomerTitle = oldCustomer.getTitle(); - } + customerValidator.validate(customer, Customer::getTenantId); } - var evictEvent = new CustomerCacheEvictEvent(customer.getTenantId(), customer.getTitle(), oldCustomerTitle); + var evictEvent = new CustomerCacheEvictEvent(customer.getTenantId(), customer.getTitle(), oldCustomer != null ? oldCustomer.getTitle() : null); try { Customer savedCustomer = customerDao.saveAndFlush(customer.getTenantId(), customer); if (!savedCustomer.isPublic()) { @@ -163,8 +176,13 @@ public class CustomerServiceImpl extends AbstractCachedEntityService findCustomersByTenantIdAndIds(TenantId tenantId, List customerIds) { + log.trace("Executing findCustomersByTenantIdAndIds, tenantId [{}], customerIds [{}]", tenantId, customerIds); + return customerDao.findCustomersByTenantIdAndIds(tenantId.getId(), customerIds.stream().map(CustomerId::getId).collect(Collectors.toList())); + } + @Override public void deleteByTenantId(TenantId tenantId) { deleteCustomersByTenantId(tenantId); 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 578f7fd19d..b47e87ae4d 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 @@ -22,6 +22,7 @@ import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ImageContainerDao; import org.thingsboard.server.dao.ResourceContainerDao; +import java.util.List; import java.util.UUID; /** @@ -81,4 +82,6 @@ public interface DashboardInfoDao extends Dao, ImageContainerDao< String findTitleById(UUID tenantId, UUID dashboardId); + List findDashboardsByIds(UUID tenantId, List dashboardIds); + } 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 de0168514d..acbc9d724d 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 @@ -50,7 +50,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.service.DataValidator; @@ -63,6 +63,7 @@ import java.util.Map; import java.util.Optional; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; @Service("DashboardDaoService") @@ -160,6 +161,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public Dashboard saveDashboard(Dashboard dashboard, boolean doValidate) { + return saveEntity(dashboard, () -> doSaveDashboard(dashboard, doValidate)); + } + + private Dashboard doSaveDashboard(Dashboard dashboard, boolean doValidate) { log.trace("Executing saveDashboard [{}]", dashboard); if (doValidate) { dashboardValidator.validate(dashboard, DashboardInfo::getTenantId); @@ -409,6 +414,12 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return dashboardDao.findAllIds(pageLink); } + @Override + public List findDashboardInfoByIds(TenantId tenantId, List dashboardIds) { + log.trace("Executing findDashboardInfoByIds, dashboardIds [{}]", dashboardIds); + return dashboardInfoDao.findDashboardsByIds(tenantId.getId(), toUUIDs(dashboardIds)); + } + private final PaginatedRemover tenantDashboardsRemover = new PaginatedRemover<>() { @Override 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 index 89cb538c28..70f5f48272 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -85,7 +85,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private String mqttsPemCertFile; @Value("${device.connectivity.coaps.pem_cert_file:}") private String coapsPemCertFile; - @Value("${device.connectivity.gateway.image_version:3.7-stable}") + @Value("${device.connectivity.gateway.image_version:3.8-stable}") private String gatewayImageVersion; @Override 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 37fc1be448..899df92a77 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 @@ -43,9 +43,9 @@ 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.validator.DeviceCredentialsDataValidator; +import org.thingsboard.server.exception.DataValidationException; import java.util.Objects; 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 045ddd1002..567abf7e79 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 @@ -53,4 +53,6 @@ public interface DeviceProfileDao extends Dao, ExportableEntityDa List findTenantDeviceProfileNames(UUID tenantId, boolean activeOnly); + List findDeviceProfilesByTenantIdAndIds(UUID tenantId, List deviceProfileIds); + } 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 6bb341df03..cfaf76f05c 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 @@ -46,11 +46,11 @@ import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.dao.entity.CachedVersionedEntityService; 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.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.validator.DeviceProfileDataValidator; +import org.thingsboard.server.exception.DataValidationException; import java.io.ByteArrayInputStream; import java.security.cert.Certificate; @@ -66,6 +66,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -391,6 +392,12 @@ public class DeviceProfileServiceImpl extends CachedVersionedEntityService findDeviceProfilesByIds(TenantId tenantId, List deviceProfileIds) { + log.trace("Executing findDeviceProfilesByIds, tenantId [{}], deviceProfileIds [{}]", tenantId, deviceProfileIds); + return deviceProfileDao.findDeviceProfilesByTenantIdAndIds(tenantId.getId(), toUUIDs(deviceProfileIds)); + } + private final PaginatedRemover tenantDeviceProfilesRemover = new PaginatedRemover<>() { 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 3f58d01722..b83f7b3ef7 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 @@ -40,6 +40,8 @@ import org.thingsboard.server.common.data.DeviceTransportType; 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.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.ProfileEntityIdInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; @@ -78,7 +80,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.validator.DeviceDataValidator; @@ -167,6 +169,12 @@ public class DeviceServiceImpl extends CachedVersionedEntityService doSaveWithCredentials(device, deviceCredentials, nameConflictStrategy)); + } + + private Device doSaveWithCredentials(Device device, DeviceCredentials deviceCredentials, NameConflictStrategy nameConflictStrategy) { + Device savedDevice = doSaveDeviceWithoutCredentials(device, true, nameConflictStrategy); deviceCredentials.setDeviceId(savedDevice.getId()); if (device.getId() == null) { deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); @@ -198,7 +216,15 @@ public class DeviceServiceImpl extends CachedVersionedEntityService saveWithoutCredentials(device, accessToken, doValidate, nameConflictStrategy)); + } + + private Device saveWithoutCredentials(Device device, String accessToken, boolean doValidate, NameConflictStrategy nameConflictStrategy) { + Device savedDevice = doSaveDeviceWithoutCredentials(device, doValidate, nameConflictStrategy); if (device.getId() == null) { DeviceCredentials deviceCredentials = new DeviceCredentials(); deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId())); @@ -209,13 +235,14 @@ public class DeviceServiceImpl extends CachedVersionedEntityService doSaveEdge(edge)); + } + + private Edge doSaveEdge(Edge edge) { log.trace("Executing saveEdge [{}]", edge); Edge oldEdge = edgeValidator.validate(edge, Edge::getTenantId); EdgeCacheEvictEvent evictEvent = new EdgeCacheEvictEvent(edge.getTenantId(), edge.getName(), oldEdge != null ? oldEdge.getName() : null); diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/PostgresEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/PostgresEdgeEventService.java index 4e3e0bd942..646d7374e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/PostgresEdgeEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/PostgresEdgeEventService.java @@ -68,7 +68,8 @@ public class PostgresEdgeEventService extends BaseEdgeEventService { Futures.addCallback(saveFuture, new FutureCallback<>() { @Override public void onSuccess(Void result) { - statsCounterService.ifPresent(statsCounterService -> statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_ADDED, edgeEvent.getTenantId(), edgeEvent.getEdgeId(), 1)); + statsCounterService.ifPresent(statsCounterService -> + statsCounterService.recordEvent(EdgeStatsKey.DOWNLINK_MSGS_ADDED, edgeEvent.getTenantId(), edgeEvent.getEdgeId(), 1)); eventPublisher.publishEvent(SaveEntityEvent.builder() .tenantId(edgeEvent.getTenantId()) .entityId(edgeEvent.getEdgeId()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/stats/EdgeStatsCounterService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/stats/EdgeStatsCounterService.java index 118978dbe0..e85c78646f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/stats/EdgeStatsCounterService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/stats/EdgeStatsCounterService.java @@ -24,13 +24,13 @@ import org.thingsboard.server.common.data.id.TenantId; import java.util.concurrent.ConcurrentHashMap; -@ConditionalOnProperty(prefix = "edges.stats", name = "enabled", havingValue = "true", matchIfMissing = false) +@ConditionalOnProperty(prefix = "edges.stats", name = "enabled", havingValue = "true") @Service @Slf4j @Getter public class EdgeStatsCounterService { - private final ConcurrentHashMap counterByEdge = new ConcurrentHashMap<>(); + private final ConcurrentHashMap msgCountersByEdge = new ConcurrentHashMap<>(); public void recordEvent(EdgeStatsKey type, TenantId tenantId, EdgeId edgeId, long value) { MsgCounters counters = getOrCreateCounters(tenantId, edgeId); @@ -39,19 +39,16 @@ public class EdgeStatsCounterService { case DOWNLINK_MSGS_PUSHED -> counters.getMsgsPushed().addAndGet(value); case DOWNLINK_MSGS_PERMANENTLY_FAILED -> counters.getMsgsPermanentlyFailed().addAndGet(value); case DOWNLINK_MSGS_TMP_FAILED -> counters.getMsgsTmpFailed().addAndGet(value); + case DOWNLINK_MSGS_LAG -> counters.getMsgsLag().set(value); } } - public void setDownlinkMsgsLag(TenantId tenantId, EdgeId edgeId, long value) { - getOrCreateCounters(tenantId, edgeId).getMsgsLag().set(value); + public MsgCounters getOrCreateCounters(TenantId tenantId, EdgeId edgeId) { + return msgCountersByEdge.computeIfAbsent(edgeId, id -> new MsgCounters(tenantId)); } public void clear(EdgeId edgeId) { - counterByEdge.remove(edgeId); - } - - public MsgCounters getOrCreateCounters(TenantId tenantId, EdgeId edgeId) { - return counterByEdge.computeIfAbsent(edgeId, id -> new MsgCounters(tenantId)); + msgCountersByEdge.remove(edgeId); } } 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 45453d6f7f..95575f26f8 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 @@ -21,21 +21,30 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; +import org.springframework.util.ConcurrentReferenceHashMap; import org.thingsboard.common.util.DebugModeUtil; +import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.HasDebugSettings; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.EdgeId; 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.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.housekeeper.CleanUpService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -44,7 +53,15 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentMap; +import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.thingsboard.server.common.data.UniquifyStrategy.RANDOM; @Slf4j public abstract class AbstractEntityService { @@ -52,6 +69,8 @@ public abstract class AbstractEntityService { public static final String INCORRECT_EDGE_ID = "Incorrect edgeId "; public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; + private final ConcurrentMap entityCreationLocks = new ConcurrentReferenceHashMap<>(16); + @Autowired protected ApplicationEventPublisher eventPublisher; @@ -81,11 +100,28 @@ public abstract class AbstractEntityService { @Autowired @Lazy - private TbTenantProfileCache tbTenantProfileCache; + protected TbTenantProfileCache tbTenantProfileCache; + + @Autowired + protected EntityDaoRegistry entityDaoRegistry; @Value("${debug.settings.default_duration:15}") private int defaultDebugDurationMinutes; + protected E saveEntity(E entity, Supplier saveFunction) { + if (entity.getId() == null) { + ReentrantLock lock = entityCreationLocks.computeIfAbsent(entity.getTenantId(), id -> new ReentrantLock()); + lock.lock(); + try { + return saveFunction.get(); + } finally { + lock.unlock(); + } + } else { + return saveFunction.get(); + } + } + protected void createRelation(TenantId tenantId, EntityRelation relation) { log.debug("Creating relation: {}", relation); relationService.saveRelation(tenantId, relation); @@ -155,4 +191,33 @@ public abstract class AbstractEntityService { private long getMaxDebugAllUntil(TenantId tenantId, long now) { return now + TimeUnit.MINUTES.toMillis(DebugModeUtil.getMaxDebugAllDuration(tbTenantProfileCache.get(tenantId).getDefaultProfileConfiguration().getMaxDebugModeDurationMinutes(), defaultDebugDurationMinutes)); } + + protected & HasTenantId & HasName> void uniquifyEntityName(E entity, E oldEntity, Consumer setName, EntityType entityType, NameConflictStrategy strategy) { + Dao dao = entityDaoRegistry.getDao(entityType); + List existingEntities = dao.findEntityInfosByNamePrefix(entity.getTenantId(), entity.getName()); + Set existingNames = existingEntities.stream() + .filter(e -> (oldEntity == null || !e.getId().equals(oldEntity.getId()))) + .map(EntityInfo::getName) + .collect(Collectors.toSet()); + + if (existingNames.contains(entity.getName())) { + String uniqueName = generateUniqueName(entity.getName(), existingNames, strategy); + setName.accept(uniqueName); + } + } + + private String generateUniqueName(String baseName, Set existingNames, NameConflictStrategy strategy) { + String newName; + int index = 1; + String separator = strategy.separator(); + boolean isRandom = strategy.uniquifyStrategy() == RANDOM; + + do { + String suffix = isRandom ? StringUtils.randomAlphanumeric(6) : String.valueOf(index++); + newName = baseName + separator + suffix; + } while (existingNames.contains(newName)); + + return newName; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java index de8374d211..0516294d79 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java @@ -43,9 +43,6 @@ public class DefaultEntityServiceRegistry implements EntityServiceRegistry { if (EntityType.RULE_CHAIN.equals(entityType)) { entityDaoServicesMap.put(EntityType.RULE_NODE, entityDaoService); } - if (EntityType.CALCULATED_FIELD.equals(entityType)) { - entityDaoServicesMap.put(EntityType.CALCULATED_FIELD_LINK, entityDaoService); - } }); log.debug("Initialized EntityServiceRegistry total [{}] entries", entityDaoServicesMap.size()); } 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 69f41104de..5a43cb8a05 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 @@ -169,6 +169,8 @@ public interface EntityViewDao extends Dao, ExportableEntityDao findEntityViewsByTenantIdAndIds(UUID tenantId, List entityViewIds); + /** * Find entity views by tenantId, edgeId, type and page link. * 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 5fc2d25811..7867801107 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 @@ -30,6 +30,8 @@ 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.EntityViewInfo; +import org.thingsboard.server.common.data.NameConflictPolicy; +import org.thingsboard.server.common.data.NameConflictStrategy; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; @@ -49,7 +51,7 @@ import org.thingsboard.server.dao.entity.CachedVersionedEntityService; 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.exception.DataValidationException; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.validator.EntityViewDataValidator; import org.thingsboard.server.dao.sql.JpaExecutorService; @@ -62,6 +64,7 @@ import java.util.Optional; import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -109,14 +112,24 @@ public class EntityViewServiceImpl extends CachedVersionedEntityService findEntityViewsByTenantIdAndIds(TenantId tenantId, List entityViewIds) { + log.trace("Executing findEntityViewsByTenantIdAndIds, tenantId [{}], entityViewIds [{}]", tenantId, entityViewIds); + return entityViewDao.findEntityViewsByTenantIdAndIds(tenantId.getId(), toUUIDs(entityViewIds)); + } + @Override public PageData findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + diff --git a/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java b/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java index 77ca4a0da2..0d2549e67f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java +++ b/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java @@ -15,8 +15,12 @@ */ package org.thingsboard.server.dao.exception; +import org.thingsboard.server.exception.DataValidationException; + public class DeviceCredentialsValidationException extends DataValidationException { + public DeviceCredentialsValidationException(String message) { super(message); } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index 9594e2624e..0edc757be1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -36,9 +36,6 @@ import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; -/** - * Created by ashvayka on 13.07.17. - */ @Data @MappedSuperclass public abstract class BaseSqlEntity implements BaseEntity { 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 de1a04e81e..9f9e4a67e9 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 @@ -26,8 +26,7 @@ import java.util.UUID; public class ModelConstants { - private ModelConstants() { - } + private ModelConstants() {} public static final UUID NULL_UUID = Uuids.startOf(0); public static final TenantId SYSTEM_TENANT = TenantId.fromUUID(ModelConstants.NULL_UUID); @@ -258,6 +257,7 @@ public class ModelConstants { public static final String ALARM_ORIGINATOR_NAME_PROPERTY = "originator_name"; public static final String ALARM_ORIGINATOR_LABEL_PROPERTY = "originator_label"; public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type"; + public static final String ALARM_ORIGINATOR_DISPLAY_NAME_PROPERTY = "originator_display_name"; public static final String ALARM_SEVERITY_PROPERTY = "severity"; public static final String ALARM_ASSIGNEE_ID_PROPERTY = "assignee_id"; public static final String ALARM_ASSIGNEE_FIRST_NAME_PROPERTY = "assignee_first_name"; @@ -731,15 +731,6 @@ public class ModelConstants { public static final String CALCULATED_FIELD_CONFIGURATION = "configuration"; public static final String CALCULATED_FIELD_VERSION = "version"; - /** - * Calculated field links constants. - */ - public static final String CALCULATED_FIELD_LINK_TABLE_NAME = "calculated_field_link"; - public static final String CALCULATED_FIELD_LINK_TENANT_ID_COLUMN = TENANT_ID_COLUMN; - public static final String CALCULATED_FIELD_LINK_ENTITY_TYPE = ENTITY_TYPE_COLUMN; - public static final String CALCULATED_FIELD_LINK_ENTITY_ID = ENTITY_ID_COLUMN; - public static final String CALCULATED_FIELD_LINK_CALCULATED_FIELD_ID = "calculated_field_id"; - /** * Tasks constants. */ @@ -760,6 +751,17 @@ public class ModelConstants { public static final String AI_MODEL_NAME_COLUMN_NAME = NAME_PROPERTY; public static final String AI_MODEL_CONFIGURATION_COLUMN_NAME = "configuration"; + /** + * Api Key constants. + */ + public static final String API_KEY_TABLE_NAME = "api_key"; + public static final String API_KEY_TENANT_ID_COLUMN_NAME = TENANT_ID_COLUMN; + public static final String API_KEY_USER_ID_COLUMN_NAME = USER_ID_PROPERTY; + public static final String API_KEY_VALUE_COLUMN_NAME = "value"; + public static final String API_KEY_EXPIRATION_TIME_COLUMN_NAME = "expiration_time"; + public static final String API_KEY_ENABLED_COLUMN_NAME = "enabled"; + public static final String API_KEY_DESCRIPTION_COLUMN_NAME = "description"; + protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; protected static final String[] COUNT_AGGREGATION_COLUMNS = new String[]{count(LONG_VALUE_COLUMN), count(DOUBLE_VALUE_COLUMN), count(BOOLEAN_VALUE_COLUMN), count(STRING_VALUE_COLUMN), count(JSON_VALUE_COLUMN), max(TS_COLUMN)}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractApiKeyInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractApiKeyInfoEntity.java new file mode 100644 index 0000000000..6fe6ce625f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractApiKeyInfoEntity.java @@ -0,0 +1,81 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.ApiKeyId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.model.BaseSqlEntity; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_DESCRIPTION_COLUMN_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_ENABLED_COLUMN_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_EXPIRATION_TIME_COLUMN_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_TENANT_ID_COLUMN_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_USER_ID_COLUMN_NAME; + +@Data +@EqualsAndHashCode(callSuper = true) +@MappedSuperclass +public abstract class AbstractApiKeyInfoEntity extends BaseSqlEntity implements BaseEntity { + + @Column(name = API_KEY_TENANT_ID_COLUMN_NAME) + private UUID tenantId; + + @Column(name = API_KEY_USER_ID_COLUMN_NAME) + private UUID userId; + + @Column(name = API_KEY_EXPIRATION_TIME_COLUMN_NAME) + private long expirationTime; + + @Column(name = API_KEY_ENABLED_COLUMN_NAME) + private boolean enabled; + + @Column(name = API_KEY_DESCRIPTION_COLUMN_NAME) + private String description; + + public AbstractApiKeyInfoEntity() { + super(); + } + + public AbstractApiKeyInfoEntity(ApiKeyInfo apiKeyInfo) { + super(apiKeyInfo); + this.tenantId = apiKeyInfo.getTenantId().getId(); + this.userId = apiKeyInfo.getUserId().getId(); + this.expirationTime = apiKeyInfo.getExpirationTime(); + this.description = apiKeyInfo.getDescription(); + this.enabled = apiKeyInfo.isEnabled(); + } + + protected ApiKeyInfo toApiKeyInfo() { + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(new ApiKeyId(getUuid())); + apiKeyInfo.setCreatedTime(createdTime); + apiKeyInfo.setTenantId(TenantId.fromUUID(tenantId)); + apiKeyInfo.setUserId(new UserId(userId)); + apiKeyInfo.setEnabled(enabled); + apiKeyInfo.setExpirationTime(expirationTime); + apiKeyInfo.setDescription(description); + return apiKeyInfo; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java index d0b1dfaae7..9641054070 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java @@ -40,10 +40,6 @@ import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; -/** - * Created by Victor Basanets on 8/30/2017. - */ - @Data @EqualsAndHashCode(callSuper = true) @MappedSuperclass diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiKeyEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiKeyEntity.java new file mode 100644 index 0000000000..47a12e9a71 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiKeyEntity.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.pat.ApiKey; + +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_VALUE_COLUMN_NAME; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = API_KEY_TABLE_NAME) +public class ApiKeyEntity extends AbstractApiKeyInfoEntity { + + @Column(name = API_KEY_VALUE_COLUMN_NAME) + private String value; + + public ApiKeyEntity() { + super(); + } + + public ApiKeyEntity(ApiKey apiKey) { + super(apiKey); + this.value = apiKey.getValue(); + } + + @Override + public ApiKey toData() { + return new ApiKey(super.toApiKeyInfo(), value); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiKeyInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiKeyInfoEntity.java new file mode 100644 index 0000000000..8889d0d2f1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiKeyInfoEntity.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; + +import static org.thingsboard.server.dao.model.ModelConstants.API_KEY_TABLE_NAME; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = API_KEY_TABLE_NAME) +public class ApiKeyInfoEntity extends AbstractApiKeyInfoEntity { + + public ApiKeyInfoEntity() { + super(); + } + + public ApiKeyInfoEntity(ApiKey apiKey) { + super(apiKey); + } + + @Override + public ApiKeyInfo toData() { + return super.toApiKeyInfo(); + } + +} 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 aba85d75a2..93c7ee4e8f 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 @@ -33,9 +33,6 @@ import org.thingsboard.server.dao.model.ModelConstants; import java.util.UUID; -/** - * Created by Valerii Sosliuk on 4/21/2017. - */ @Data @EqualsAndHashCode(callSuper = true) @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldLinkEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldLinkEntity.java deleted file mode 100644 index 80459962d8..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldLinkEntity.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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 jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; -import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.model.BaseEntity; -import org.thingsboard.server.dao.model.BaseSqlEntity; - -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_LINK_CALCULATED_FIELD_ID; -import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_LINK_ENTITY_ID; -import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_LINK_ENTITY_TYPE; -import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_LINK_TABLE_NAME; -import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_LINK_TENANT_ID_COLUMN; - -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@Table(name = CALCULATED_FIELD_LINK_TABLE_NAME) -public class CalculatedFieldLinkEntity extends BaseSqlEntity implements BaseEntity { - - @Column(name = CALCULATED_FIELD_LINK_TENANT_ID_COLUMN) - private UUID tenantId; - - @Column(name = CALCULATED_FIELD_LINK_ENTITY_TYPE) - private String entityType; - - @Column(name = CALCULATED_FIELD_LINK_ENTITY_ID) - private UUID entityId; - - @Column(name = CALCULATED_FIELD_LINK_CALCULATED_FIELD_ID) - private UUID calculatedFieldId; - - public CalculatedFieldLinkEntity() { - super(); - } - - public CalculatedFieldLinkEntity(CalculatedFieldLink calculatedFieldLink) { - super(calculatedFieldLink); - this.tenantId = calculatedFieldLink.getTenantId().getId(); - this.entityType = calculatedFieldLink.getEntityId().getEntityType().name(); - this.entityId = calculatedFieldLink.getEntityId().getId(); - this.calculatedFieldId = calculatedFieldLink.getCalculatedFieldId().getId(); - } - - @Override - public CalculatedFieldLink toData() { - CalculatedFieldLink calculatedFieldLink = new CalculatedFieldLink(new CalculatedFieldLinkId(id)); - calculatedFieldLink.setCreatedTime(createdTime); - calculatedFieldLink.setTenantId(TenantId.fromUUID(tenantId)); - calculatedFieldLink.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); - calculatedFieldLink.setCalculatedFieldId(new CalculatedFieldId(calculatedFieldId)); - return calculatedFieldLink; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleOauth2ClientEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleOauth2ClientEntity.java index 0519130ab4..5e850542a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleOauth2ClientEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleOauth2ClientEntity.java @@ -32,7 +32,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_BUNDLE_ import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_BUNDLE_OAUTH2_CLIENT_MOBILE_APP_BUNDLE_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_BUNDLE_OAUTH2_CLIENT_TABLE_NAME; - @Data @Entity @Table(name = MOBILE_APP_BUNDLE_OAUTH2_CLIENT_TABLE_NAME) @@ -63,4 +62,5 @@ public final class MobileAppBundleOauth2ClientEntity implements ToData findRecipientsForNotificationTargetConfig(TenantId tenantId, PlatformUsersNotificationTargetConfig targetConfig, PageLink pageLink) { UsersFilter usersFilter = targetConfig.getUsersFilter(); - switch (usersFilter.getType()) { - case USER_LIST: { - List users = ((UserListFilter) usersFilter).getUsersIds().stream() - .limit(pageLink.getPageSize()) - .map(UserId::new).map(userId -> userService.findUserById(tenantId, userId)) - .filter(Objects::nonNull).collect(Collectors.toList()); - return new PageData<>(users, 1, users.size(), false); - } - case CUSTOMER_USERS: { - if (tenantId.equals(TenantId.SYS_TENANT_ID)) { - throw new IllegalArgumentException("Customer users target is not supported for system administrator"); - } - CustomerUsersFilter filter = (CustomerUsersFilter) usersFilter; - return userService.findCustomerUsers(tenantId, new CustomerId(filter.getCustomerId()), pageLink); - } - case TENANT_ADMINISTRATORS: { - TenantAdministratorsFilter filter = (TenantAdministratorsFilter) usersFilter; - if (!tenantId.equals(TenantId.SYS_TENANT_ID)) { - return userService.findTenantAdmins(tenantId, pageLink); - } else { - if (isNotEmpty(filter.getTenantsIds())) { - return userService.findTenantAdminsByTenantsIds(filter.getTenantsIds().stream() - .map(TenantId::fromUUID).collect(Collectors.toList()), pageLink); - } else if (isNotEmpty(filter.getTenantProfilesIds())) { - return userService.findTenantAdminsByTenantProfilesIds(filter.getTenantProfilesIds().stream() - .map(TenantProfileId::new).collect(Collectors.toList()), pageLink); - } else { - return userService.findAllTenantAdmins(pageLink); - } - } - } - case SYSTEM_ADMINISTRATORS: - return userService.findSysAdmins(pageLink); - case ALL_USERS: { - if (!tenantId.equals(TenantId.SYS_TENANT_ID)) { - return userService.findUsersByTenantId(tenantId, pageLink); - } else { - return userService.findAllUsers(pageLink); - } - } - default: - throw new IllegalArgumentException("Recipient type not supported"); - } + return userService.findUsersByFilter(tenantId, usersFilter, pageLink); } @Override 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 92a36f8def..b46540aa55 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 @@ -52,12 +52,15 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.RateL import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.TaskProcessingFailureNotificationRuleTriggerConfig; +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.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.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -102,7 +105,40 @@ 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 entitiesLimitIncreaseRequest = DefaultNotification.builder() + .name("Entities limit increase request") + .type(NotificationType.ENTITIES_LIMIT_INCREASE_REQUEST) + .subject("${entityType} limit increase request") + .text("${userEmail} has reached the maximum number of ${entityType:lowerCase}s allowed and is requesting an increase to the ${entityType:lowerCase} limit.") + .button("${increaseLimitActionLabel}").link("${increaseLimitLink}") + .emailTemplate(DefaultEmailTemplate.builder() + .subject("${entityType} limit increase request") + .body(""" + + + + + + +
+ + + + + + + + + + + + +
${entityType} limit increase request
${userEmail} has reached the maximum number of ${entityType:lowerCase}s allowed and is requesting an increase to the ${entityType:lowerCase} limit.
+ ${increaseLimitActionLabel} +
+
""") + .build()) + .build(); public static final DefaultNotification apiFeatureWarningForSysadmin = DefaultNotification.builder() .name("API feature warning notification for sysadmin") .type(NotificationType.API_USAGE_LIMIT) @@ -415,6 +451,8 @@ public class DefaultNotifications { private final String button; private final String link; + private final DefaultEmailTemplate emailTemplate; + private final DefaultRule rule; public NotificationTemplate toTemplate() { @@ -448,9 +486,17 @@ public class DefaultNotifications { } webTemplate.setAdditionalConfig(additionalConfig); webTemplate.setEnabled(true); - templateConfig.setDeliveryMethodsTemplates(Map.of( - NotificationDeliveryMethod.WEB, webTemplate - )); + + Map deliveryMethodsTemplates = new HashMap<>(); + deliveryMethodsTemplates.put(NotificationDeliveryMethod.WEB, webTemplate); + if (this.emailTemplate != null) { + EmailDeliveryMethodNotificationTemplate emailTemplate = new EmailDeliveryMethodNotificationTemplate(); + emailTemplate.setSubject(this.emailTemplate.getSubject()); + emailTemplate.setBody(this.emailTemplate.getBody()); + emailTemplate.setEnabled(true); + deliveryMethodsTemplates.put(NotificationDeliveryMethod.EMAIL, emailTemplate); + } + templateConfig.setDeliveryMethodsTemplates(deliveryMethodsTemplates); template.setConfiguration(templateConfig); return template; } @@ -482,6 +528,13 @@ public class DefaultNotifications { } + @Data + @Builder(toBuilder = true) + public static class DefaultEmailTemplate { + private final String subject; + private final String body; + } + @Data @Builder(toBuilder = true) public static class DefaultRule { diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java index d4753d045d..86708e9e76 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import java.util.List; 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 95515bd15e..484b3c49b3 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 @@ -41,7 +41,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyCacheKey.java new file mode 100644 index 0000000000..ea733cdef3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyCacheKey.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.io.Serializable; + +import static java.util.Objects.requireNonNull; + +record ApiKeyCacheKey(String value) implements Serializable { + + ApiKeyCacheKey { + requireNonNull(value); + } + + static ApiKeyCacheKey of(String value) { + return new ApiKeyCacheKey(value); + } + + @NonNull + @Override + public String toString() { + return /* cache name */ "_" + value; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyCaffeineCache.java new file mode 100644 index 0000000000..7ffc48ee86 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyCaffeineCache.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +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.pat.ApiKey; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("ApiKeyCache") +public class ApiKeyCaffeineCache extends CaffeineTbTransactionalCache { + + public ApiKeyCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.API_KEYS_CACHE); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyDao.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyDao.java new file mode 100644 index 0000000000..d71d6502cf --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyDao.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.dao.Dao; + +import java.util.Set; + +public interface ApiKeyDao extends Dao { + + ApiKey findByValue(String value); + + Set deleteByTenantId(TenantId tenantId); + + Set deleteByUserId(TenantId tenantId, UserId userId); + + int deleteAllByExpirationTimeBefore(long ts); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyEvictEvent.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyEvictEvent.java new file mode 100644 index 0000000000..681e89e122 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyEvictEvent.java @@ -0,0 +1,18 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +public record ApiKeyEvictEvent(String value) {} diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyInfoDao.java new file mode 100644 index 0000000000..8b7e1fd2ef --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyInfoDao.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +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.pat.ApiKeyInfo; +import org.thingsboard.server.dao.Dao; + +public interface ApiKeyInfoDao extends Dao { + + PageData findByUserId(TenantId tenantId, UserId userId, PageLink pageLink); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyRedisCache.java new file mode 100644 index 0000000000..e79ddb69f2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyRedisCache.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +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.TbJsonRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.pat.ApiKey; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("ApiKeyCache") +public class ApiKeyRedisCache extends RedisTbTransactionalCache { + + public ApiKeyRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { + super(CacheConstants.API_KEYS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(ApiKey.class)); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java new file mode 100644 index 0000000000..17a3e2bda3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java @@ -0,0 +1,163 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +import com.google.common.util.concurrent.FluentFuture; +import lombok.RequiredArgsConstructor; +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.springframework.transaction.event.TransactionalEventListener; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.ApiKeyId; +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.UserId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; +import org.thingsboard.server.dao.service.validator.ApiKeyDataValidator; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.user.UserServiceImpl.INCORRECT_TENANT_ID; +import static org.thingsboard.server.dao.user.UserServiceImpl.INCORRECT_USER_ID; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ApiKeyServiceImpl extends AbstractCachedEntityService implements ApiKeyService { + + private static final String INCORRECT_API_KEY_ID = "Incorrect ApiKeyId "; + private static final int MAX_API_KEY_VALUE_LENGTH = 255; + + private final ApiKeyDao apiKeyDao; + private final ApiKeyInfoDao apiKeyInfoDao; + @Lazy + private final ApiKeyDataValidator apiKeyValidator; + + @Value("${security.api_key.value_prefix:}") + private String prefix; + + @Value("${security.api_key.value_bytes_size:64}") + private int valueBytesSize; + + @Override + @TransactionalEventListener + public void handleEvictEvent(ApiKeyEvictEvent event) { + cache.evict(ApiKeyCacheKey.of(event.value())); + } + + @Override + public ApiKey saveApiKey(TenantId tenantId, ApiKeyInfo apiKeyInfo) { + log.trace("Executing saveApiKey [{}]", apiKeyInfo); + try { + var apiKey = new ApiKey(apiKeyInfo); + var old = apiKeyValidator.validate(apiKey, ApiKeyInfo::getTenantId); + if (old == null) { + String value = generateApiKeySecret(); + apiKey.setValue(value); + } else { + apiKey.setValue(old.getValue()); + } + var savedApiKey = apiKeyDao.save(tenantId, apiKey); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entityId(savedApiKey.getId()).entity(savedApiKey).created(apiKey.getId() == null).build()); + if (old != null && old.isEnabled() != apiKey.isEnabled()) { + publishEvictEvent(new ApiKeyEvictEvent(apiKey.getValue())); + } + return savedApiKey; + } catch (Exception e) { + checkConstraintViolation(e, "api_key_value_unq_key", "API Key with such value already exists!"); + throw e; + } + } + + @Override + public ApiKey findApiKeyById(TenantId tenantId, ApiKeyId apiKeyId) { + log.trace("Executing findApiKeyById [{}] [{}]", tenantId, apiKeyId); + validateId(apiKeyId, id -> INCORRECT_API_KEY_ID + id); + return apiKeyDao.findById(tenantId, apiKeyId.getId()); + } + + @Override + public PageData findApiKeysByUserId(TenantId tenantId, UserId userId, PageLink pageLink) { + log.trace("Executing findApiKeysByUserId [{}][{}]", tenantId, userId); + validateId(userId, id -> INCORRECT_USER_ID + id); + return apiKeyInfoDao.findByUserId(tenantId, userId, pageLink); + } + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findApiKeyById(tenantId, new ApiKeyId(entityId.getId()))); + } + + @Override + public FluentFuture>> findEntityAsync(TenantId tenantId, EntityId entityId) { + return FluentFuture.from(apiKeyDao.findByIdAsync(tenantId, entityId.getId())) + .transform(Optional::ofNullable, directExecutor()); + } + + @Override + public void deleteApiKey(TenantId tenantId, ApiKey apiKey, boolean force) { + UUID apiKeyId = apiKey.getUuidId(); + validateId(apiKeyId, id -> INCORRECT_API_KEY_ID + id); + apiKeyDao.removeById(tenantId, apiKeyId); + publishEvictEvent(new ApiKeyEvictEvent(apiKey.getValue())); + } + + @Override + public void deleteByTenantId(TenantId tenantId) { + log.trace("Executing deleteApiKeysByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Set values = apiKeyDao.deleteByTenantId(tenantId); + values.forEach(value -> publishEvictEvent(new ApiKeyEvictEvent(value))); + } + + @Override + public void deleteByUserId(TenantId tenantId, UserId userId) { + log.trace("Executing deleteApiKeysByUserId, tenantId [{}]", tenantId); + validateId(userId, id -> INCORRECT_USER_ID + id); + Set values = apiKeyDao.deleteByUserId(tenantId, userId); + values.forEach(value -> publishEvictEvent(new ApiKeyEvictEvent(value))); + } + + @Override + public ApiKey findApiKeyByValue(String value) { + log.trace("Executing findApiKeyByValue [{}]", value); + var cacheKey = ApiKeyCacheKey.of(value); + return cache.getAndPutInTransaction(cacheKey, () -> apiKeyDao.findByValue(value), true); + } + + private String generateApiKeySecret() { + return prefix + StringUtils.generateSafeToken(Math.min(valueBytesSize, MAX_API_KEY_VALUE_LENGTH)); + } + + @Override + public EntityType getEntityType() { + return EntityType.API_KEY; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java index b75cfec1ed..0d9d25ff10 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.dao.entity.AbstractEntityService; 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.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index d55969b6b4..6f404e1aca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -19,7 +19,6 @@ import com.google.common.base.Function; 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; import com.google.common.util.concurrent.SettableFuture; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -32,29 +31,36 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.util.CollectionUtils; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.StringUtils; 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.id.UUIDBased; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; +import org.thingsboard.server.common.data.relation.EntityRelationPathQuery; 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.RelationPathLevel; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.eventsourcing.RelationActionEvent; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.sql.JpaExecutorService; import org.thingsboard.server.dao.sql.relation.JpaRelationQueryExecutorService; +import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import org.thingsboard.server.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -65,15 +71,15 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.function.Predicate; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validatePositiveNumber; -/** - * Created by ashvayka on 28.04.17. - */ -@Service @Slf4j -public class BaseRelationService implements RelationService { +@Service +class BaseRelationService implements RelationService { private final RelationDao relationDao; private final EntityService entityService; @@ -81,7 +87,9 @@ public class BaseRelationService implements RelationService { private final ApplicationEventPublisher eventPublisher; private final JpaExecutorService executor; private final JpaRelationQueryExecutorService relationsExecutor; - protected ScheduledExecutorService timeoutExecutorService; + private final ApiLimitService apiLimitService; + + private ScheduledExecutorService timeoutExecutorService; @Value("${sql.relations.query_timeout:20}") private Integer relationQueryTimeout; @@ -89,13 +97,14 @@ public class BaseRelationService implements RelationService { public BaseRelationService(RelationDao relationDao, @Lazy EntityService entityService, TbTransactionalCache cache, ApplicationEventPublisher eventPublisher, JpaExecutorService executor, - JpaRelationQueryExecutorService relationsExecutor) { + JpaRelationQueryExecutorService relationsExecutor, ApiLimitService apiLimitService) { this.relationDao = relationDao; this.entityService = entityService; this.cache = cache; this.eventPublisher = eventPublisher; this.executor = executor; this.relationsExecutor = relationsExecutor; + this.apiLimitService = apiLimitService; } @PostConstruct @@ -179,7 +188,11 @@ public class BaseRelationService implements RelationService { @Override public ListenableFuture saveRelationAsync(TenantId tenantId, EntityRelation relation) { log.trace("Executing saveRelationAsync [{}]", relation); - validate(relation); + try { + validate(relation); + } catch (DataValidationException e) { + return Futures.immediateFailedFuture(e); + } var future = relationDao.saveRelationAsync(tenantId, relation); return Futures.transform(future, savedRelation -> { if (savedRelation != null) { @@ -187,7 +200,7 @@ public class BaseRelationService implements RelationService { eventPublisher.publishEvent(new RelationActionEvent(tenantId, savedRelation, ActionType.RELATION_ADD_OR_UPDATE)); } return savedRelation != null; - }, MoreExecutors.directExecutor()); + }, directExecutor()); } @Override @@ -205,7 +218,11 @@ public class BaseRelationService implements RelationService { @Override public ListenableFuture deleteRelationAsync(TenantId tenantId, EntityRelation relation) { log.trace("Executing deleteRelationAsync [{}]", relation); - validate(relation); + try { + validate(relation); + } catch (DataValidationException e) { + return Futures.immediateFailedFuture(e); + } var future = relationDao.deleteRelationAsync(tenantId, relation); return Futures.transform(future, deletedRelation -> { if (deletedRelation != null) { @@ -213,7 +230,7 @@ public class BaseRelationService implements RelationService { eventPublisher.publishEvent(new RelationActionEvent(tenantId, deletedRelation, ActionType.RELATION_DELETED)); } return deletedRelation != null; - }, MoreExecutors.directExecutor()); + }, directExecutor()); } @Override @@ -239,7 +256,7 @@ public class BaseRelationService implements RelationService { eventPublisher.publishEvent(new RelationActionEvent(tenantId, deletedEvent, ActionType.RELATION_DELETED)); } return deletedEvent != null; - }, MoreExecutors.directExecutor()); + }, directExecutor()); } @Transactional @@ -316,17 +333,10 @@ public class BaseRelationService implements RelationService { log.trace("Executing findInfoByFrom [{}][{}]", from, typeGroup); validate(from); validateTypeGroup(typeGroup); - ListenableFuture> relations = executor.submit(() -> relationDao.findAllByFrom(tenantId, from, typeGroup)); - return Futures.transformAsync(relations, - relations1 -> { - List> futures = new ArrayList<>(); - relations1.forEach(relation -> - futures.add(fetchRelationInfoAsync(tenantId, relation, - EntityRelation::getTo, - EntityRelationInfo::setToName)) - ); - return Futures.successfulAsList(futures); - }, MoreExecutors.directExecutor()); + return Futures.transform(executor.submit(() -> relationDao.findAllByFrom(tenantId, from, typeGroup)), + relations -> relations.stream() + .map(relation -> fetchRelationInfo(tenantId, relation, EntityRelation::getTo, EntityRelationInfo::setToName)) + .toList(), directExecutor()); } @Override @@ -372,26 +382,10 @@ public class BaseRelationService implements RelationService { log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup); validate(to); validateTypeGroup(typeGroup); - ListenableFuture> relations = findByToAsync(tenantId, to, typeGroup); - return Futures.transformAsync(relations, - relations1 -> { - List> futures = new ArrayList<>(); - relations1.forEach(relation -> - futures.add(fetchRelationInfoAsync(tenantId, relation, - EntityRelation::getFrom, - EntityRelationInfo::setFromName)) - ); - return Futures.successfulAsList(futures); - }, MoreExecutors.directExecutor()); - } - - private ListenableFuture fetchRelationInfoAsync(TenantId tenantId, EntityRelation relation, - Function entityIdGetter, - BiConsumer entityNameSetter) { - EntityRelationInfo relationInfo = new EntityRelationInfo(relation); - entityNameSetter.accept(relationInfo, - entityService.fetchEntityName(tenantId, entityIdGetter.apply(relation)).orElse("N/A")); - return Futures.immediateFuture(relationInfo); + return Futures.transform(findByToAsync(tenantId, to, typeGroup), + relations -> relations.stream() + .map(relation -> fetchRelationInfo(tenantId, relation, EntityRelation::getFrom, EntityRelationInfo::setFromName)) + .toList(), directExecutor()); } @Override @@ -443,7 +437,7 @@ public class BaseRelationService implements RelationService { } } return relations; - }, MoreExecutors.directExecutor()); + }, directExecutor()); } catch (Exception e) { log.warn("Failed to query relations: [{}]", query, e); throw new RuntimeException(e); @@ -453,24 +447,31 @@ public class BaseRelationService implements RelationService { @Override public ListenableFuture> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query) { log.trace("Executing findInfoByQuery [{}]", query); - ListenableFuture> relations = findByQuery(tenantId, query); + EntitySearchDirection direction = query.getParameters().getDirection(); - return Futures.transformAsync(relations, - relations1 -> { - List> futures = new ArrayList<>(); - relations1.forEach(relation -> - futures.add(fetchRelationInfoAsync(tenantId, relation, - relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(), - (EntityRelationInfo relationInfo, String entityName) -> { - if (direction == EntitySearchDirection.FROM) { - relationInfo.setToName(entityName); - } else { - relationInfo.setFromName(entityName); - } - })) - ); - return Futures.successfulAsList(futures); - }, MoreExecutors.directExecutor()); + + Function entityIdGetter = relation -> direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); + + BiConsumer entityNameSetter = (EntityRelationInfo relationInfo, String entityName) -> { + if (direction == EntitySearchDirection.FROM) { + relationInfo.setToName(entityName); + } else { + relationInfo.setFromName(entityName); + } + }; + + return Futures.transform(findByQuery(tenantId, query), + relations -> relations.stream() + .map(relation -> fetchRelationInfo(tenantId, relation, entityIdGetter, entityNameSetter)) + .toList(), directExecutor()); + } + + private EntityRelationInfo fetchRelationInfo(TenantId tenantId, EntityRelation relation, + Function entityIdGetter, + BiConsumer entityNameSetter) { + var relationInfo = new EntityRelationInfo(relation); + entityNameSetter.accept(relationInfo, entityService.fetchEntityName(tenantId, entityIdGetter.apply(relation)).orElse("N/A")); + return relationInfo; } @Override @@ -495,15 +496,90 @@ public class BaseRelationService implements RelationService { return relationDao.findRuleNodeToRuleChainRelations(ruleChainType, limit); } - protected void validate(EntityRelation relation) { + @Override + public ListenableFuture> findByRelationPathQueryAsync(TenantId tenantId, EntityRelationPathQuery relationPathQuery) { + return findFilteredRelationsByPathQueryAsync(tenantId, relationPathQuery, null); + } + + @Override + public ListenableFuture> findFilteredRelationsByPathQueryAsync(TenantId tenantId, EntityRelationPathQuery relationPathQuery, Predicate relationFilter) { + log.trace("Executing findByRelationPathQuery, tenantId [{}], relationPathQuery {}", tenantId, relationPathQuery); + validateId(tenantId, id -> "Invalid tenant id: " + id); + validate(relationPathQuery); + int limit = (int) apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxRelatedEntitiesToReturnPerCfArgument); + validatePositiveNumber(limit, "Max related entities limit for relation path query must be positive!"); + if (relationPathQuery.levels().size() == 1) { + RelationPathLevel relationPathLevel = relationPathQuery.levels().get(0); + var relationsFuture = switch (relationPathLevel.direction()) { + case FROM -> findByFromAndTypeAsync(tenantId, relationPathQuery.rootEntityId(), relationPathLevel.relationType(), RelationTypeGroup.COMMON); + case TO -> findByToAndTypeAsync(tenantId, relationPathQuery.rootEntityId(), relationPathLevel.relationType(), RelationTypeGroup.COMMON); + }; + return Futures.transform(relationsFuture, entityRelations -> { + if (entityRelations == null || entityRelations.isEmpty()) { + return Collections.emptyList(); + } + List relations = relationFilter != null ? filterRelations(entityRelations, relationFilter) : entityRelations; + if (relations.size() > limit) { + List limitedRelations = new ArrayList<>(relations); + limitedRelations.sort(Comparator.comparing(r -> r.getFrom().getId())); + return limitedRelations.subList(0, limit); + } else { + return relations; + } + }, directExecutor()); + } + return executor.submit(() -> { + List entityRelations = relationDao.findByRelationPathQuery(tenantId, relationPathQuery, limit); + return relationFilter != null ? filterRelations(entityRelations, relationFilter) : entityRelations; + }); + } + + private List filterRelations(List entityRelations, Predicate relationFilter) { + return entityRelations.stream() + .filter(relationFilter) + .toList(); + } + + @Override + public List findByRelationPathQuery(TenantId tenantId, EntityRelationPathQuery relationPathQuery) { + log.trace("Executing findByRelationPathQuery, tenantId [{}], relationPathQuery {}", tenantId, relationPathQuery); + validateId(tenantId, id -> "Invalid tenant id: " + id); + validate(relationPathQuery); + int limit = (int) apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxRelatedEntitiesToReturnPerCfArgument); + if (relationPathQuery.levels().size() == 1) { + RelationPathLevel relationPathLevel = relationPathQuery.levels().get(0); + var relations = switch (relationPathLevel.direction()) { + case FROM -> findByFromAndType(tenantId, relationPathQuery.rootEntityId(), relationPathLevel.relationType(), RelationTypeGroup.COMMON); + case TO -> findByToAndType(tenantId, relationPathQuery.rootEntityId(), relationPathLevel.relationType(), RelationTypeGroup.COMMON); + }; + if (relations.size() > limit) { + List limitedRelations = new ArrayList<>(relations); + limitedRelations.sort(Comparator.comparing(r -> r.getFrom().getId())); + return limitedRelations.subList(0, limit); + } else { + return relations; + } + } + return relationDao.findByRelationPathQuery(tenantId, relationPathQuery, limit); + } + + private void validate(EntityRelationPathQuery relationPathQuery) { + validateId((UUIDBased) relationPathQuery.rootEntityId(), id -> "Invalid root entity id: " + id); + List levels = relationPathQuery.levels(); + if (CollectionUtils.isEmpty(levels)) { + throw new DataValidationException("Validation error: relation path levels should be specified!"); + } + levels.forEach(RelationPathLevel::validate); + } + + private static void validate(EntityRelation relation) { if (relation == null) { - throw new DataValidationException("Relation type should be specified!"); + throw new DataValidationException("Validation error: relation must not be null"); } ConstraintValidator.validateFields(relation); - validate(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); } - protected void validate(EntityId from, EntityId to, String type, RelationTypeGroup typeGroup) { + private static void validate(EntityId from, EntityId to, String type, RelationTypeGroup typeGroup) { validateType(type); validateTypeGroup(typeGroup); if (from == null) { @@ -514,25 +590,25 @@ public class BaseRelationService implements RelationService { } } - private void validateType(String type) { - if (StringUtils.isEmpty(type)) { + private static void validateType(String type) { + if (type == null) { throw new DataValidationException("Relation type should be specified!"); } } - private void validateTypeGroup(RelationTypeGroup typeGroup) { + private static void validateTypeGroup(RelationTypeGroup typeGroup) { if (typeGroup == null) { throw new DataValidationException("Relation type group should be specified!"); } } - protected void validate(EntityId entity) { + private static void validate(EntityId entity) { if (entity == null) { throw new DataValidationException("Entity should be specified!"); } } - private boolean matchFilters(List filters, EntityRelation relation, EntitySearchDirection direction) { + private static boolean matchFilters(List filters, EntityRelation relation, EntitySearchDirection direction) { for (RelationEntityTypeFilter filter : filters) { if (match(filter, relation, direction)) { return true; @@ -541,7 +617,7 @@ public class BaseRelationService implements RelationService { return false; } - private boolean match(RelationEntityTypeFilter filter, EntityRelation relation, EntitySearchDirection direction) { + private static boolean match(RelationEntityTypeFilter filter, EntityRelation relation, EntitySearchDirection direction) { if (StringUtils.isEmpty(filter.getRelationType()) || filter.getRelationType().equals(relation.getType())) { if (filter.getEntityTypes() == null || filter.getEntityTypes().isEmpty()) { return true; @@ -556,6 +632,7 @@ public class BaseRelationService implements RelationService { @RequiredArgsConstructor private static class RelationQueueCtx { + final SettableFuture> future = SettableFuture.create(); final Set result = ConcurrentHashMap.newKeySet(); final Queue tasks = new ConcurrentLinkedQueue<>(); @@ -569,12 +646,7 @@ public class BaseRelationService implements RelationService { } - @RequiredArgsConstructor - private static class RelationTask { - private final int currentLvl; - private final EntityId root; - private final List prevRelations; - } + private record RelationTask(int currentLvl, EntityId root, List prevRelations) {} private void processQueue(RelationQueueCtx ctx) { RelationTask task = ctx.tasks.poll(); @@ -648,4 +720,5 @@ 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 f8f2dbd9e8..e9cc33de50 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 @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; 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.EntityRelationPathQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -71,4 +72,6 @@ public interface RelationDao { List findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit); + List findByRelationPathQuery(TenantId tenantId, EntityRelationPathQuery relationPathQuery, int limit); + } 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 40b5a9778b..432329a621 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 @@ -58,7 +58,7 @@ import org.thingsboard.server.dao.dashboard.DashboardInfoDao; 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.exception.DataValidationException; import org.thingsboard.server.dao.rule.RuleChainDao; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; @@ -91,7 +91,9 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Primary public class BaseResourceService extends AbstractCachedEntityService implements ResourceService { - public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; + protected static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; + protected static final int MAX_ENTITIES_TO_FIND = 10; + protected final TbResourceDao resourceDao; protected final TbResourceInfoDao resourceInfoDao; protected final ResourceDataValidator resourceValidator; @@ -100,7 +102,6 @@ public class BaseResourceService extends AbstractCachedEntityService> resourceLinkContainerDaoMap = new HashMap<>(); private final Map> generalResourceContainerDaoMap = new HashMap<>(); - protected static final int MAX_ENTITIES_TO_FIND = 10; @PostConstruct public void init() { 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 c96ba0542f..6b90b6fac6 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 @@ -63,11 +63,11 @@ 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; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.validator.RuleChainDataValidator; +import org.thingsboard.server.exception.DataValidationException; import java.util.ArrayList; import java.util.Collection; @@ -85,6 +85,7 @@ import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.thingsboard.server.common.data.DataConstants.TENANT; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; 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; @@ -127,6 +128,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override @Transactional public RuleChain saveRuleChain(RuleChain ruleChain, boolean publishSaveEvent, boolean doValidate) { + return saveEntity(ruleChain, () -> doSaveRuleChain(ruleChain, publishSaveEvent, doValidate)); + } + + private RuleChain doSaveRuleChain(RuleChain ruleChain, boolean publishSaveEvent, boolean doValidate) { log.trace("Executing doSaveRuleChain [{}]", ruleChain); if (doValidate) { ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); @@ -869,6 +874,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } } + @Override + public List findRuleChainsByIds(TenantId tenantId, List ruleChainIds) { + log.trace("Executing findRuleChainsByIds, tenantId [{}], ruleChainIds [{}]", tenantId, ruleChainIds); + return ruleChainDao.findRuleChainsByTenantIdAndIds(tenantId.getId(), toUUIDs(ruleChainIds)); + } + @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleNodeStateService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleNodeStateService.java index 1981879e76..aaae87bd4e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleNodeStateService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleNodeStateService.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleNodeState; import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; @Service @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index ec6a4fb14f..b88ed2294b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -21,13 +21,13 @@ 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.RuleChainType; -import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.ResourceContainerDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.Collection; +import java.util.List; import java.util.UUID; /** @@ -83,4 +83,6 @@ public interface RuleChainDao extends Dao, TenantEntityDao Collection findByTenantIdAndTypeAndName(TenantId tenantId, RuleChainType type, String name); + List findRuleChainsByTenantIdAndIds(UUID tenantId, List ruleChainIds); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java index 5df895ac80..a9b9f4ec6a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.validation.NoNullChar; import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.RateLimit; import org.thingsboard.server.common.data.validation.ValidJsonSchema; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.util.Collection; import java.util.Set; 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 b71db2744c..088c24d810 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 @@ -16,6 +16,8 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.exception.DataValidationException; +import org.thingsboard.server.exception.EntitiesLimitExceededException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -27,8 +29,6 @@ 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; @@ -51,7 +51,8 @@ public abstract class DataValidator> { private static final String NAME = "name"; private static final String TOPIC = "topic"; - @Autowired @Lazy + @Autowired + @Lazy private ApiLimitService apiLimitService; // Returns old instance of the same object that is fetched during validation. @@ -93,7 +94,7 @@ public abstract class DataValidator> { } public void validateString(String exceptionPrefix, String name) { - if (StringUtils.isEmpty(name) || name.trim().length() == 0) { + if (StringUtils.isBlank(name)) { throw new DataValidationException(exceptionPrefix + " should be specified!"); } if (StringUtils.contains0x00(name)) { @@ -123,7 +124,8 @@ public abstract class DataValidator> { protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, EntityType entityType) { if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) { - throw new EntitiesLimitException(tenantId, entityType); + long limit = apiLimitService.getLimit(tenantId, profileConfiguration -> profileConfiguration.getEntitiesLimit(entityType)); + throw new EntitiesLimitExceededException(tenantId, entityType, limit); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java index d03fd3153f..5e90ceae53 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.service.DataValidator; 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 9c4b70ef79..f29fb3ea15 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 @@ -19,7 +19,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.settings.AdminSettingsService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AiModelDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AiModelDataValidator.java index 9b138fe279..b955bed043 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AiModelDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AiModelDataValidator.java @@ -20,7 +20,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.ai.AiModel; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ai.AiModelDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java index 006661536c..ea2060a6a4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java @@ -19,7 +19,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component 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 0fbd787d52..ed7a6d50d6 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 @@ -19,7 +19,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiKeyDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiKeyDataValidator.java new file mode 100644 index 0000000000..2420a54cdf --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiKeyDataValidator.java @@ -0,0 +1,77 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.exception.DataValidationException; +import org.thingsboard.server.dao.pat.ApiKeyDao; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; + +@Component +@RequiredArgsConstructor +public class ApiKeyDataValidator extends DataValidator { + + private final ApiKeyDao apiKeyDao; + private final TenantService tenantService; + private final UserService userService; + + @Override + protected void validateDataImpl(TenantId tenantId, ApiKey apiKey) { + if (apiKey.getId() != null) { + if (apiKey.getUuidId() == null) { + throw new DataValidationException("API Key UUID should be specified!"); + } + if (apiKey.getId().isNullUid()) { + throw new DataValidationException("API key UUID must not be the reserved null value!"); + } + } + + if (apiKey.getTenantId() == null || apiKey.getTenantId().getId() == null) { + throw new DataValidationException("API key should be assigned to tenant!"); + } + if (!TenantId.SYS_TENANT_ID.equals(apiKey.getTenantId()) && !tenantService.tenantExists(apiKey.getTenantId())) { + throw new DataValidationException("API key reference a non-existent tenant!"); + } + + if (apiKey.getUserId() == null || apiKey.getUserId().getId() == null) { + throw new DataValidationException("API key should be assigned to user!"); + } + if (userService.findUserById(apiKey.getTenantId(), apiKey.getUserId()) == null) { + throw new DataValidationException("API key reference a non-existent user!"); + } + } + + @Override + protected ApiKey validateUpdate(TenantId tenantId, ApiKey apiKey) { + ApiKey old = apiKeyDao.findById(tenantId, apiKey.getUuidId()); + if (old == null) { + throw new DataValidationException("Cannot update non-existent API key!"); + } + if (!old.getUserId().equals(apiKey.getUserId())) { + throw new DataValidationException("Cannot update API key user id!"); + } + if (old.getExpirationTime() != apiKey.getExpirationTime()) { + throw new DataValidationException("Cannot update API key expiration time!"); + } + return old; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java index ac7cdca282..c62ff4ad6b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java @@ -21,7 +21,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 9eccc17cc6..b4a75455fb 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 @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; 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.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 292741f85c..27674823cc 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 @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java index 258475825a..781c7e6654 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component 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 c008d60898..a5e11e5f07 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 @@ -23,9 +23,9 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.dao.device.DeviceProfileDao; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.exception.DataValidationException; import java.util.Objects; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidator.java index c387414571..82f90a6286 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidator.java @@ -18,14 +18,23 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.cf.CalculatedFieldDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + @Component public class CalculatedFieldDataValidator extends DataValidator { @@ -36,46 +45,110 @@ public class CalculatedFieldDataValidator extends DataValidator private ApiLimitService apiLimitService; @Override - protected void validateCreate(TenantId tenantId, CalculatedField calculatedField) { - validateNumberOfCFsPerEntity(tenantId, calculatedField.getEntityId()); + protected void validateDataImpl(TenantId tenantId, CalculatedField calculatedField) { validateNumberOfArgumentsPerCF(tenantId, calculatedField); - validateArgumentNames(calculatedField); + validateCalculatedFieldConfiguration(calculatedField); + validateSchedulingConfiguration(tenantId, calculatedField); + validateRelationQuerySourceArguments(tenantId, calculatedField); + validateRelatedAggregationConfiguration(tenantId, calculatedField); + validateEntityAggregationConfiguration(tenantId, calculatedField); } @Override - protected CalculatedField validateUpdate(TenantId tenantId, CalculatedField calculatedField) { - CalculatedField old = calculatedFieldDao.findById(calculatedField.getTenantId(), calculatedField.getId().getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing calculated field!"); + protected void validateCreate(TenantId tenantId, CalculatedField calculatedField) { + if (calculatedField.getType() == CalculatedFieldType.ALARM) { + return; } - validateNumberOfArgumentsPerCF(tenantId, calculatedField); - validateArgumentNames(calculatedField); - return old; - } - - private void validateNumberOfCFsPerEntity(TenantId tenantId, EntityId entityId) { long maxCFsPerEntity = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxCalculatedFieldsPerEntity); if (maxCFsPerEntity <= 0) { return; } - if (calculatedFieldDao.countCFByEntityId(tenantId, entityId) >= maxCFsPerEntity) { + if (calculatedFieldDao.countByEntityIdAndTypeNot(tenantId, calculatedField.getEntityId(), CalculatedFieldType.ALARM) >= maxCFsPerEntity) { throw new DataValidationException("Calculated fields per entity limit reached!"); } } + @Override + protected CalculatedField validateUpdate(TenantId tenantId, CalculatedField calculatedField) { + CalculatedField old = calculatedFieldDao.findById(calculatedField.getTenantId(), calculatedField.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing calculated field!"); + } + return old; + } + private void validateNumberOfArgumentsPerCF(TenantId tenantId, CalculatedField calculatedField) { + if (!(calculatedField instanceof ArgumentsBasedCalculatedFieldConfiguration argumentsBasedCfg)) { + return; + } long maxArgumentsPerCF = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxArgumentsPerCF); if (maxArgumentsPerCF <= 0) { return; } - if (calculatedField.getConfiguration().getArguments().size() > maxArgumentsPerCF) { + if (argumentsBasedCfg.getArguments().size() > maxArgumentsPerCF) { throw new DataValidationException("Calculated field arguments limit reached!"); } } - private void validateArgumentNames(CalculatedField calculatedField) { - if (calculatedField.getConfiguration().getArguments().containsKey("ctx")) { - throw new DataValidationException("Argument name 'ctx' is reserved and cannot be used."); + private void validateCalculatedFieldConfiguration(CalculatedField calculatedField) { + wrapAsDataValidation(calculatedField.getConfiguration()::validate); + } + + private void validateSchedulingConfiguration(TenantId tenantId, CalculatedField calculatedField) { + if (!(calculatedField.getConfiguration() instanceof ScheduledUpdateSupportedCalculatedFieldConfiguration scheduledUpdateCfg) + || !scheduledUpdateCfg.isScheduledUpdateEnabled()) { + return; + } + long minAllowedScheduledUpdateInterval = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMinAllowedScheduledUpdateIntervalInSecForCF); + wrapAsDataValidation(() -> scheduledUpdateCfg.validate(minAllowedScheduledUpdateInterval)); + } + + private void validateRelationQuerySourceArguments(TenantId tenantId, CalculatedField calculatedField) { + if (!(calculatedField.getConfiguration() instanceof ArgumentsBasedCalculatedFieldConfiguration argumentsBasedCfg)) { + return; + } + Map relationQueryBasedArguments = argumentsBasedCfg.getArguments().entrySet() + .stream() + .filter(entry -> entry.getValue().hasRelationQuerySource()) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> (RelationPathQueryDynamicSourceConfiguration) entry.getValue().getRefDynamicSourceConfiguration())); + if (relationQueryBasedArguments.isEmpty()) { + return; + } + int maxRelationLevel = (int) apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxRelationLevelPerCfArgument); + relationQueryBasedArguments.forEach((argumentName, relationQueryDynamicSourceConfiguration) -> + wrapAsDataValidation(() -> relationQueryDynamicSourceConfiguration.validateMaxRelationLevel(argumentName, maxRelationLevel))); + } + + private void validateRelatedAggregationConfiguration(TenantId tenantId, CalculatedField calculatedField) { + if (!(calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration aggConfiguration)) { + return; + } + long minDeduplicationInterval = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMinAllowedDeduplicationIntervalInSecForCF); + if (aggConfiguration.getDeduplicationIntervalInSec() < minDeduplicationInterval) { + throw new IllegalArgumentException("Deduplication interval is less than configured " + + "minimum allowed interval in tenant profile: " + minDeduplicationInterval); + } + } + + private void validateEntityAggregationConfiguration(TenantId tenantId, CalculatedField calculatedField) { + if (!(calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration aggConfiguration)) { + return; + } + long minAggregationIntervalInSec = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMinAllowedAggregationIntervalInSecForCF); + if (minAggregationIntervalInSec <= 0) { + return; + } + if (aggConfiguration.getInterval().getCurrentIntervalDurationMillis() < TimeUnit.SECONDS.toMillis(minAggregationIntervalInSec)) { + throw new IllegalArgumentException("Aggregation interval duration is less than configured " + + "minimum allowed aggregation interval in tenant profile: " + minAggregationIntervalInSec + " sec."); + } + } + + private static void wrapAsDataValidation(Runnable validation) { + try { + validation.run(); + } catch (IllegalArgumentException e) { + throw new DataValidationException(e.getMessage(), e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidator.java deleted file mode 100644 index aae76d0909..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidator.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.cf.CalculatedFieldLinkDao; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.service.DataValidator; - -@Component -public class CalculatedFieldLinkDataValidator extends DataValidator { - - @Autowired - private CalculatedFieldLinkDao calculatedFieldLinkDao; - - @Override - protected CalculatedFieldLink validateUpdate(TenantId tenantId, CalculatedFieldLink calculatedFieldLink) { - CalculatedFieldLink old = calculatedFieldLinkDao.findById(calculatedFieldLink.getTenantId(), calculatedFieldLink.getId().getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing calculated field link!"); - } - return old; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java index c15655c36c..36be32d0f5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java @@ -19,7 +19,7 @@ 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.oauth2.OAuth2ClientRegistrationTemplate; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component 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 825224cde3..0ae9eb1645 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 @@ -19,7 +19,7 @@ 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.plugin.ComponentDescriptor; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java index 05ea4b390e..50ff63d686 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.customer.CustomerServiceImpl; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 3236b75b2c..12c4c62f94 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 @@ -20,7 +20,7 @@ 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.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 d604a8438f..9d47ade7de 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 @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; 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.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TenantService; import java.util.Optional; 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 6402ec0d1f..74fa56dc87 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 @@ -52,11 +52,11 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.exception.DataValidationException; import java.io.FileInputStream; import java.security.KeyStore; @@ -69,6 +69,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.LWM2M_SERVER_MAX; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.PRIMARY_LWM2M_SERVER; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.isNotLwm2mServer; + @Slf4j @Component public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator { @@ -182,6 +186,8 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator 65535) { - throw new DeviceCredentialsValidationException("Bootstrap Server ShortServerId must be in range [0 - 65535]!"); - } - } else { - if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) { - throw new DeviceCredentialsValidationException("LwM2M Server ShortServerId must be in range [1 - 65534]!"); + if (serverConfig.isBootstrapServerIs()) { + if (serverConfig.getShortServerId() != null) { + if (serverConfig.getShortServerId() == 0) { + serverConfig.setShortServerId(null); + } else { + throw new DeviceCredentialsValidationException("Bootstrap Server ShortServerId must be null!"); } } } else { - String serverName = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; - throw new DeviceCredentialsValidationException(serverName + " ShortServerId must not be null!"); + if (serverConfig.getShortServerId() != null) { + if (isNotLwm2mServer(serverConfig.getShortServerId())) { + throw new DeviceCredentialsValidationException("LwM2M Server ShortServerId must be in range [" + PRIMARY_LWM2M_SERVER.getId() + " - " + LWM2M_SERVER_MAX.getId() + "]!"); + } + } else { + throw new DeviceCredentialsValidationException("LwM2M Server ShortServerId must not be null!"); + } } String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java index f1ebb64759..e972175ee6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java index f91f37644f..359e124bc9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component 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 88c03bbe5c..ca29854f46 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 @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.CustomerId; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java index 466eaf96f6..e1c3fd6bd5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java @@ -19,7 +19,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.event.Event; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppBundleDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppBundleDataValidator.java index 568739ccbd..1bff2b9891 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppBundleDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppBundleDataValidator.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.mobile.MobileAppDao; import org.thingsboard.server.dao.service.DataValidator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java index e64d35afe9..bc445e7cc9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.app.MobileAppStatus; import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2ClientDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2ClientDataValidator.java index 5cff024d00..d7984109ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2ClientDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2ClientDataValidator.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java index 55a4d963d4..5eec0d670e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageDao; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QrCodeSettingsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QrCodeSettingsDataValidator.java index 557a9c3b47..2b8e3eb113 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QrCodeSettingsDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QrCodeSettingsDataValidator.java @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.app.MobileAppStatus; import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings; import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.mobile.MobileAppDao; import org.thingsboard.server.dao.service.DataValidator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java index 3de9d666a9..519ab54721 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java @@ -19,7 +19,7 @@ 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.queue.QueueStats; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Component diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index 8776d87027..afe6730f00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java @@ -24,7 +24,7 @@ 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.common.data.queue.SubmitStrategyType; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.queue.QueueDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 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 5bcd4c0613..1ee469a9c3 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 @@ -17,14 +17,16 @@ package org.thingsboard.server.dao.service.validator; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; 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.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.resource.TbResourceDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -54,8 +56,8 @@ public class ResourceDataValidator extends DataValidator { @Override protected TbResource validateUpdate(TenantId tenantId, TbResource resource) { - if (resource.getData() != null && !resource.getResourceType().isUpdatable() && - tenantId != null && !tenantId.isSysTenantId()) { + if ((resource.getData() != null && !resource.getResourceType().isUpdatable() && tenantId != null && !tenantId.isSysTenantId()) + || resource.getResourceType().equals(ResourceType.LWM2M_MODEL)) { throw new DataValidationException("This type of resource can't be updated"); } return resource; @@ -81,7 +83,7 @@ public class ResourceDataValidator extends DataValidator { if (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); } - if (StringUtils.containsAny(resource.getFileName(), "/", "\\")) { + if (Strings.CS.containsAny(resource.getFileName(), "/", "\\")) { throw new DataValidationException("File name contains forbidden symbols"); } if (StringUtils.isEmpty(resource.getResourceKey())) { @@ -104,4 +106,5 @@ public class ResourceDataValidator extends DataValidator { validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, dataSize, TB_RESOURCE); } } + } 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 ecc0c6cc83..5f8a6db3cb 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 @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; 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.exception.DataValidationException; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.service.DataValidator; 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 4950e6af0f..7f6087963b 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 @@ -20,7 +20,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantDao; 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 94c0330920..b0337cb345 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 @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantProfileDao; import org.thingsboard.server.dao.tenant.TenantProfileService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java index a8e0ad05f9..0f3470fa34 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.UserCredentials; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.user.UserCredentialsDao; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java index 79cf13f441..64038fffd4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.customer.CustomerDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 d7cb4fe98b..2cfebfbdb5 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 @@ -20,7 +20,7 @@ import org.springframework.stereotype.Component; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 ecbab2e83f..775c2c1464 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 @@ -19,7 +19,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; 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 9d93b5f83c..f5fbcf2e23 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 @@ -87,4 +87,10 @@ public interface AssetProfileRepository extends JpaRepository :id ORDER BY a.id") List findNextBatch(@Param("id") UUID id, Limit limit); + @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 a.id IN :assetProfileIds") + List findAssetProfileInfosByTenantIdAndIdIn(@Param("tenantId") UUID tenantId, + @Param("assetProfileIds") List assetProfileIds); + } 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 42d9a85c1c..10243dd959 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 @@ -21,6 +21,7 @@ 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.edqs.fields.AssetFields; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.ExportableEntityRepository; @@ -100,6 +101,10 @@ public interface AssetRepository extends JpaRepository, Expor AssetEntity findByTenantIdAndName(UUID tenantId, String name); + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'ASSET', a.name) " + + "FROM AssetEntity a WHERE a.tenantId = :tenantId AND a.name LIKE CONCAT(:prefix, '%')") + List findEntityInfosByNamePrefix(UUID tenantId, String prefix); + @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + "AND a.type = :type " + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true " + 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 afd1fed0ce..e37608d136 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 @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +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.ProfileEntityIdInfo; @@ -267,6 +268,12 @@ public class JpaAssetDao extends JpaAbstractDao implements A return nativeAssetRepository.findProfileEntityIdInfosByTenantId(tenantId, DaoUtil.toPageable(pageLink)); } + @Override + public List findEntityInfosByNamePrefix(TenantId tenantId, String name) { + log.debug("Find asset entity infos by name [{}]", name); + return assetRepository.findEntityInfosByNamePrefix(tenantId.getId(), name); + } + @Override public Long countByTenantId(TenantId tenantId) { return assetRepository.countByTenantId(tenantId.getId()); 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 819c578789..7561802e0a 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 @@ -105,6 +105,11 @@ public class JpaAssetProfileDao extends JpaAbstractDao findAssetProfilesByTenantIdAndIds(UUID tenantId, List assetProfileIds) { + return assetProfileRepository.findAssetProfileInfosByTenantIdAndIdIn(tenantId, assetProfileIds); + } + @Override public AssetProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { return DaoUtil.getData(assetProfileRepository.findByTenantIdAndExternalId(tenantId, externalId)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 913ac69c13..d8799bbc71 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -177,10 +177,12 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl } @Override - public List findAllKeysByEntityIdsAndAttributeType(TenantId tenantId, List entityIds, String attributeType) { + public List findAllKeysByEntityIdsAndScope(TenantId tenantId, List entityIds, AttributeScope scope) { return attributeKvRepository - .findAllKeysByEntityIdsAndAttributeType(entityIds.stream().map(EntityId::getId).collect(Collectors.toList()), AttributeScope.valueOf(attributeType).getId()) - .stream().map(id -> keyDictionaryDao.getKey(id)).collect(Collectors.toList()); + .findAllKeysByEntityIdsAndAttributeType(entityIds.stream().map(EntityId::getId).toList(), scope.getId()) + .stream() + .map(keyDictionaryDao::getKey) + .toList(); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldRepository.java index 9b3767115b..b7b94c5b8c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldRepository.java @@ -23,13 +23,14 @@ import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.dao.model.sql.CalculatedFieldEntity; import java.util.List; +import java.util.Set; import java.util.UUID; public interface CalculatedFieldRepository extends JpaRepository { boolean existsByTenantIdAndEntityId(UUID tenantId, UUID entityId); - CalculatedFieldEntity findByEntityIdAndName(UUID entityId, String name); + CalculatedFieldEntity findByEntityIdAndTypeAndName(UUID entityId, String type, String name); List findCalculatedFieldIdsByTenantIdAndEntityId(UUID tenantId, UUID entityId); @@ -38,14 +39,28 @@ public interface CalculatedFieldRepository extends JpaRepository findAllByTenantId(UUID tenantId, Pageable pageable); @Query("SELECT cf FROM CalculatedFieldEntity cf WHERE cf.tenantId = :tenantId " + - "AND cf.entityId = :entityId " + - "AND (:textSearch IS NULL OR ilike(cf.name, CONCAT('%', :textSearch, '%')) = true)") - Page findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId, String textSearch, Pageable pageable); + "AND cf.entityId = :entityId AND cf.type IN :types " + + "AND (:textSearch IS NULL OR ilike(cf.name, CONCAT('%', :textSearch, '%')) = true)") + Page findByTenantIdAndEntityIdAndTypes(UUID tenantId, UUID entityId, List types, String textSearch, Pageable pageable); + + @Query("SELECT cf FROM CalculatedFieldEntity cf WHERE cf.tenantId = :tenantId " + + "AND cf.type IN :types " + + "AND cf.entityType IN :entityTypes " + + "AND (:entityIds IS NULL OR cf.entityId IN :entityIds) " + + "AND (:names IS NULL OR cf.name IN :names) " + + "AND (:textSearch IS NULL OR ilike(cf.name, CONCAT('%', :textSearch, '%')) = true)") + Page findByTenantIdAndFilter(UUID tenantId, List types, List entityTypes, + Set entityIds, Set names, String textSearch, Pageable pageable); + + @Query("SELECT DISTINCT cf.name FROM CalculatedFieldEntity cf " + + "WHERE cf.tenantId = :tenantId AND cf.type = :type AND " + + "(:textSearch IS NULL OR ilike(cf.name, CONCAT('%', :textSearch, '%')) = true)") + Page findNamesByTenantIdAndType(UUID tenantId, String type, String textSearch, Pageable pageable); List findAllByTenantId(UUID tenantId); List removeAllByTenantIdAndEntityId(UUID tenantId, UUID entityId); - long countByTenantIdAndEntityId(UUID tenantId, UUID entityId); + long countByTenantIdAndEntityIdAndTypeNot(UUID tenantId, UUID entityId, String type); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java index 9734af7d7a..f6113155ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java @@ -25,12 +25,10 @@ import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -49,9 +47,6 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF private final String CF_COUNT_QUERY = "SELECT count(id) FROM calculated_field;"; private final String CF_QUERY = "SELECT * FROM calculated_field ORDER BY created_time ASC LIMIT %s OFFSET %s"; - private final String CFL_COUNT_QUERY = "SELECT count(id) FROM calculated_field_link;"; - private final String CFL_QUERY = "SELECT * FROM calculated_field_link ORDER BY created_time ASC LIMIT %s OFFSET %s"; - private final NamedParameterJdbcTemplate jdbcTemplate; private final TransactionTemplate transactionTemplate; @@ -103,37 +98,4 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF }); } - @Override - public PageData findCalculatedFieldLinks(Pageable pageable) { - return transactionTemplate.execute(status -> { - long startTs = System.currentTimeMillis(); - int totalElements = jdbcTemplate.queryForObject(CFL_COUNT_QUERY, Collections.emptyMap(), Integer.class); - log.debug("Count query took {} ms", System.currentTimeMillis() - startTs); - startTs = System.currentTimeMillis(); - List> rows = jdbcTemplate.queryForList(String.format(CFL_QUERY, pageable.getPageSize(), pageable.getOffset()), Collections.emptyMap()); - log.debug("Main query took {} ms", System.currentTimeMillis() - startTs); - int totalPages = pageable.getPageSize() > 0 ? (int) Math.ceil((float) totalElements / pageable.getPageSize()) : 1; - boolean hasNext = pageable.getPageSize() > 0 && totalElements > pageable.getOffset() + rows.size(); - var data = rows.stream().map(row -> { - - UUID id = (UUID) row.get("id"); - long createdTime = (long) row.get("created_time"); - UUID tenantId = (UUID) row.get("tenant_id"); - EntityType entityType = EntityType.valueOf((String) row.get("entity_type")); - UUID entityId = (UUID) row.get("entity_id"); - UUID calculatedFieldId = (UUID) row.get("calculated_field_id"); - - CalculatedFieldLink calculatedFieldLink = new CalculatedFieldLink(); - calculatedFieldLink.setId(new CalculatedFieldLinkId(id)); - calculatedFieldLink.setCreatedTime(createdTime); - calculatedFieldLink.setTenantId(new TenantId(tenantId)); - calculatedFieldLink.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); - calculatedFieldLink.setCalculatedFieldId(new CalculatedFieldId(calculatedFieldId)); - - return calculatedFieldLink; - }).collect(Collectors.toList()); - return new PageData<>(data, totalPages, totalElements, hasNext); - }); - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldDao.java index 81293690fb..a374531a47 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldDao.java @@ -15,13 +15,17 @@ */ package org.thingsboard.server.dao.sql.cf; +import com.google.common.base.Strings; import jakarta.transaction.Transactional; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; 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.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldFilter; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -34,6 +38,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; +import java.util.Set; import java.util.UUID; @Slf4j @@ -66,8 +71,8 @@ public class JpaCalculatedFieldDao extends JpaAbstractDao findAllByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) { - log.debug("Try to find calculated fields by entityId[{}] and pageLink [{}]", entityId, pageLink); - return DaoUtil.toPageData(calculatedFieldRepository.findAllByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); + public PageData findByEntityIdAndTypes(TenantId tenantId, EntityId entityId, Set types, PageLink pageLink) { + log.debug("Try to find calculated fields by entityId [{}] and type [{}] and pageLink [{}]", entityId, types, pageLink); + return DaoUtil.toPageData(calculatedFieldRepository.findByTenantIdAndEntityIdAndTypes(tenantId.getId(), entityId.getId(), + types.stream().map(Enum::name).toList(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override @@ -95,8 +101,24 @@ public class JpaCalculatedFieldDao extends JpaAbstractDao findByTenantIdAndFilter(TenantId tenantId, CalculatedFieldFilter filter, PageLink pageLink) { + return DaoUtil.toPageData(calculatedFieldRepository.findByTenantIdAndFilter(tenantId.getId(), + filter.getTypes().stream().map(Enum::name).toList(), + filter.getEntityTypes().stream().map(Enum::name).toList(), + CollectionUtils.isNotEmpty(filter.getEntityIds()) ? filter.getEntityIds() : null, + CollectionUtils.isNotEmpty(filter.getNames()) ? filter.getNames() : null, + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findNamesByTenantIdAndType(TenantId tenantId, CalculatedFieldType type, PageLink pageLink) { + return DaoUtil.pageToPageData(calculatedFieldRepository.findNamesByTenantIdAndType(tenantId.getId(), type.name(), + Strings.emptyToNull(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, false))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldLinkDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldLinkDao.java deleted file mode 100644 index cda10a8730..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldLinkDao.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf; - -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -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.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.CalculatedFieldId; -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.dao.DaoUtil; -import org.thingsboard.server.dao.cf.CalculatedFieldLinkDao; -import org.thingsboard.server.dao.model.sql.CalculatedFieldLinkEntity; -import org.thingsboard.server.dao.sql.JpaAbstractDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.List; -import java.util.UUID; - -@Slf4j -@Component -@AllArgsConstructor -@SqlDao -public class JpaCalculatedFieldLinkDao extends JpaAbstractDao implements CalculatedFieldLinkDao { - - private final CalculatedFieldLinkRepository calculatedFieldLinkRepository; - private final NativeCalculatedFieldRepository nativeCalculatedFieldRepository; - - @Override - public List findCalculatedFieldLinksByCalculatedFieldId(TenantId tenantId, CalculatedFieldId calculatedFieldId) { - return DaoUtil.convertDataList(calculatedFieldLinkRepository.findAllByTenantIdAndCalculatedFieldId(tenantId.getId(), calculatedFieldId.getId())); - } - - @Override - public List findCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId) { - return DaoUtil.convertDataList(calculatedFieldLinkRepository.findAllByTenantIdAndEntityId(tenantId.getId(), entityId.getId())); - } - - @Override - public List findCalculatedFieldLinksByTenantId(TenantId tenantId) { - return DaoUtil.convertDataList(calculatedFieldLinkRepository.findAllByTenantId(tenantId.getId())); - } - - @Override - public List findAll() { - return DaoUtil.convertDataList(calculatedFieldLinkRepository.findAll()); - } - - @Override - public PageData findAll(PageLink pageLink) { - log.debug("Try to find calculated field links by pageLink [{}]", pageLink); - return nativeCalculatedFieldRepository.findCalculatedFieldLinks(DaoUtil.toPageable(pageLink)); - } - - @Override - public PageData findAllByTenantId(TenantId tenantId, PageLink pageLink) { - log.debug("Try to find calculated field links by tenantId [{}], pageLink [{}]", tenantId, pageLink); - return DaoUtil.toPageData(calculatedFieldLinkRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); - } - - @Override - protected Class getEntityClass() { - return CalculatedFieldLinkEntity.class; - } - - @Override - protected JpaRepository getRepository() { - return calculatedFieldLinkRepository; - } - - @Override - public EntityType getEntityType() { - return EntityType.CALCULATED_FIELD_LINK; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/NativeCalculatedFieldRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/NativeCalculatedFieldRepository.java index 64593b7a84..59bf1fa6ef 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/NativeCalculatedFieldRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/NativeCalculatedFieldRepository.java @@ -17,13 +17,10 @@ package org.thingsboard.server.dao.sql.cf; import org.springframework.data.domain.Pageable; import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; import org.thingsboard.server.common.data.page.PageData; public interface NativeCalculatedFieldRepository { PageData findCalculatedFields(Pageable pageable); - PageData findCalculatedFieldLinks(Pageable pageable); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index 5ad61bd856..13fe86123f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -21,6 +21,7 @@ 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.edqs.fields.CustomerFields; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.CustomerEntity; @@ -41,6 +42,10 @@ public interface CustomerRepository extends JpaRepository, CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title); + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'CUSTOMER', a.title) " + + "FROM CustomerEntity a WHERE a.tenantId = :tenantId AND a.title LIKE CONCAT(:prefix, '%')") + List findEntityInfosByNamePrefix(UUID tenantId, String prefix); + @Query(value = "SELECT * FROM customer c WHERE c.tenant_id = :tenantId " + "AND c.is_public IS TRUE ORDER BY c.id ASC LIMIT 1", nativeQuery = true) CustomerEntity findPublicCustomerByTenantId(@Param("tenantId") UUID tenantId); @@ -62,4 +67,6 @@ public interface CustomerRepository extends JpaRepository, "c.title, c.version, c.additionalInfo, c.country, c.state, c.city, c.address, c.address2, c.zip, c.phone, c.email) " + "FROM CustomerEntity c WHERE c.id > :id ORDER BY c.id") List findNextBatch(@Param("id") UUID id, Limit limit); + + List findCustomersByTenantIdAndIdIn(UUID tenantId, List customerIds); } 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 075ea4ae33..3d9df8d6ec 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 @@ -20,6 +20,7 @@ import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edqs.fields.CustomerFields; import org.thingsboard.server.common.data.id.CustomerId; @@ -107,6 +108,11 @@ public class JpaCustomerDao extends JpaAbstractDao imp ); } + @Override + public List findCustomersByTenantIdAndIds(UUID tenantId, List customerIds) { + return DaoUtil.convertDataList(customerRepository.findCustomersByTenantIdAndIdIn(tenantId, customerIds)); + } + @Override public PageData findAllByTenantId(TenantId tenantId, PageLink pageLink) { return findByTenantId(tenantId.getId(), pageLink); @@ -117,6 +123,11 @@ public class JpaCustomerDao extends JpaAbstractDao imp return customerRepository.findNextBatch(id, Limit.of(batchSize)); } + @Override + public List findEntityInfosByNamePrefix(TenantId tenantId, String name) { + return customerRepository.findEntityInfosByNamePrefix(tenantId.getId(), name); + } + @Override public EntityType getEntityType() { return EntityType.CUSTOMER; 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 2ba46780c9..f67ad1e056 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 @@ -20,6 +20,7 @@ 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.DashboardInfo; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; @@ -99,4 +100,6 @@ public interface DashboardInfoRepository extends JpaRepository findDashboardInfosByResourceLink(@Param("link") String link, Pageable pageable); + List findByIdIn(List dashboardIds); + } 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 ea60d959ed..59e82a0b81 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.dashboard; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -126,6 +127,11 @@ public class JpaDashboardInfoDao extends JpaAbstractDao findDashboardsByIds(UUID tenantId, List dashboardIds) { + return DaoUtil.convertDataList(dashboardInfoRepository.findByIdIn(dashboardIds)); + } + @Override public List findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { return DaoUtil.convertDataList(dashboardInfoRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, limit)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeAssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeAssetRepository.java index e7ba0c206c..4775d5d35b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeAssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeAssetRepository.java @@ -23,6 +23,8 @@ import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.common.data.ProfileEntityIdInfo; 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.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -41,13 +43,13 @@ public class DefaultNativeAssetRepository extends AbstractNativeRepository imple @Override public PageData findProfileEntityIdInfos(Pageable pageable) { - String PROFILE_ASSET_ID_INFO_QUERY = "SELECT tenant_id as tenantId, asset_profile_id as profileId, id as id FROM asset ORDER BY created_time ASC LIMIT %s OFFSET %s"; + String PROFILE_ASSET_ID_INFO_QUERY = "SELECT tenant_id as tenantId, customer_id as customerId, asset_profile_id as profileId, id as id FROM asset ORDER BY created_time ASC LIMIT %s OFFSET %s"; return find(COUNT_QUERY, PROFILE_ASSET_ID_INFO_QUERY, pageable, DefaultNativeAssetRepository::toInfo); } @Override public PageData findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) { - String PROFILE_ASSET_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, asset_profile_id as profileId, id as id FROM asset WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId); + String PROFILE_ASSET_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, asset_profile_id as profileId, id as id FROM asset WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId); String COUNT_QUERY_BY_TENANT = String.format("SELECT count(id) FROM asset WHERE tenant_id = '%s';", tenantId); return find(COUNT_QUERY_BY_TENANT, PROFILE_ASSET_ID_INFO_QUERY, pageable, DefaultNativeAssetRepository::toInfo); } @@ -56,8 +58,10 @@ public class DefaultNativeAssetRepository extends AbstractNativeRepository imple var tenantIdObj = row.get("tenantId"); UUID tenantId = tenantIdObj != null ? (UUID) tenantIdObj : TenantId.SYS_TENANT_ID.getId(); AssetId id = new AssetId((UUID) row.get("id")); + CustomerId customerId = new CustomerId((UUID) row.get("customerId")); + EntityId ownerId = !customerId.isNullUid() ? customerId : TenantId.fromUUID(tenantId); AssetProfileId profileId = new AssetProfileId((UUID) row.get("profileId")); - return ProfileEntityIdInfo.create(tenantId, profileId, id); + return ProfileEntityIdInfo.create(tenantId, ownerId, profileId, id); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java index c4ba36ce2e..2a15aee984 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java @@ -22,8 +22,10 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.ProfileEntityIdInfo; +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.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -53,13 +55,13 @@ public class DefaultNativeDeviceRepository extends AbstractNativeRepository impl @Override public PageData findProfileEntityIdInfos(Pageable pageable) { - String PROFILE_DEVICE_ID_INFO_QUERY = "SELECT tenant_id as tenantId, device_profile_id as profileId, id as id FROM device ORDER BY created_time ASC LIMIT %s OFFSET %s"; + String PROFILE_DEVICE_ID_INFO_QUERY = "SELECT tenant_id as tenantId, customer_id as customerId, device_profile_id as profileId, id as id FROM device ORDER BY created_time ASC LIMIT %s OFFSET %s"; return find(COUNT_QUERY, PROFILE_DEVICE_ID_INFO_QUERY, pageable, DefaultNativeDeviceRepository::toInfo); } @Override public PageData findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) { - String PROFILE_DEVICE_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, device_profile_id as profileId, id as id FROM device WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId); + String PROFILE_DEVICE_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, device_profile_id as profileId, id as id FROM device WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId); String COUNT_QUERY_BY_TENANT = String.format("SELECT count(id) FROM device WHERE tenant_id = '%s';", tenantId); return find(COUNT_QUERY_BY_TENANT, PROFILE_DEVICE_ID_INFO_QUERY, pageable, DefaultNativeDeviceRepository::toInfo); } @@ -68,8 +70,10 @@ public class DefaultNativeDeviceRepository extends AbstractNativeRepository impl var tenantIdObj = row.get("tenantId"); UUID tenantId = tenantIdObj != null ? (UUID) tenantIdObj : TenantId.SYS_TENANT_ID.getId(); DeviceId id = new DeviceId((UUID) row.get("id")); + CustomerId customerId = new CustomerId((UUID) row.get("customerId")); + EntityId ownerId = !customerId.isNullUid() ? customerId : TenantId.fromUUID(tenantId); DeviceProfileId profileId = new DeviceProfileId((UUID) row.get("profileId")); - return ProfileEntityIdInfo.create(tenantId, profileId, id); + return ProfileEntityIdInfo.create(tenantId, ownerId, profileId, id); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java index 7fb4d5296a..a07d6b732f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java @@ -59,6 +59,11 @@ public interface DeviceProfileRepository extends JpaRepository findDeviceProfileInfosByTenantIdAndIdIn(@Param("tenantId") UUID tenantId, @Param("deviceProfileIds") List deviceProfileIds); + @Query("SELECT d FROM DeviceProfileEntity d " + "WHERE d.tenantId = :tenantId AND d.isDefault = true") DeviceProfileEntity findByDefaultTrueAndTenantId(@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 662dbd83bf..147a030147 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 @@ -22,6 +22,7 @@ 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.DeviceTransportType; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.edqs.fields.DeviceFields; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.DeviceEntity; @@ -151,6 +152,10 @@ public interface DeviceRepository extends JpaRepository, Exp DeviceEntity findByTenantIdAndName(UUID tenantId, String name); + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'DEVICE', a.name) " + + "FROM DeviceEntity a WHERE a.tenantId = :tenantId AND a.name LIKE CONCAT(:prefix, '%')") + List findEntityInfosByNamePrefix(UUID tenantId, String prefix); + List findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List deviceIds); List findDevicesByTenantIdAndIdIn(UUID tenantId, List deviceIds); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 982763c31d..e3721c6584 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceInfoFilter; import org.thingsboard.server.common.data.DeviceTransportType; +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.ProfileEntityIdInfo; @@ -114,6 +115,11 @@ public class JpaDeviceDao extends JpaAbstractDao implement DaoUtil.toPageable(pageLink))); } + @Override + public List findEntityInfosByNamePrefix(TenantId tenantId, String name) { + return deviceRepository.findEntityInfosByNamePrefix(tenantId.getId(), name); + } + @Override public ListenableFuture> findDevicesByTenantIdAndIdsAsync(UUID tenantId, List deviceIds) { return service.submit(() -> DaoUtil.convertDataList(deviceRepository.findDevicesByTenantIdAndIdIn(tenantId, deviceIds))); 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 ced6b8b01a..d07f60aa1e 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 @@ -123,6 +123,11 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findDeviceProfilesByTenantIdAndIds(UUID tenantId, List deviceProfileIds) { + return deviceProfileRepository.findDeviceProfileInfosByTenantIdAndIdIn(tenantId, deviceProfileIds); + } + @Override public DeviceProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndExternalId(tenantId, externalId)); 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 97c8aff01c..fb78aef774 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 @@ -21,6 +21,7 @@ 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.edqs.fields.EntityViewFields; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.EntityViewEntity; @@ -115,10 +116,16 @@ public interface EntityViewRepository extends JpaRepository findEntityInfosByNamePrefix(UUID tenantId, String prefix); + List findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId); boolean existsByTenantIdAndEntityId(UUID tenantId, UUID entityId); + List findEntityViewsByTenantIdAndIdIn(UUID tenantId, List entityViewIds); + @Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId") List findTenantEntityViewTypes(@Param("tenantId") UUID tenantId); 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 26894b2190..3d6fd3f8fe 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 @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +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.EntityView; @@ -184,6 +185,11 @@ public class JpaEntityViewDao extends JpaAbstractDao findEntityViewsByTenantIdAndIds(UUID tenantId, List entityViewIds) { + return DaoUtil.convertDataList(entityViewRepository.findEntityViewsByTenantIdAndIdIn(tenantId, entityViewIds)); + } + @Override public PageData findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink) { log.debug("Try to find entity views by tenantId [{}], edgeId [{}], type [{}] and pageLink [{}]", tenantId, edgeId, type, pageLink); @@ -227,6 +233,11 @@ public class JpaEntityViewDao extends JpaAbstractDao findEntityInfosByNamePrefix(TenantId tenantId, String name) { + return entityViewRepository.findEntityInfosByNamePrefix(tenantId.getId(), name); + } + @Override public EntityType getEntityType() { return EntityType.ENTITY_VIEW; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldLinkRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/ApiKeyInfoRepository.java similarity index 50% rename from dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldLinkRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/pat/ApiKeyInfoRepository.java index f4ee630dcc..30ad09db0a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldLinkRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/ApiKeyInfoRepository.java @@ -13,24 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.cf; +package org.thingsboard.server.dao.sql.pat; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import org.thingsboard.server.dao.model.sql.CalculatedFieldLinkEntity; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.model.sql.ApiKeyInfoEntity; -import java.util.List; import java.util.UUID; -public interface CalculatedFieldLinkRepository extends JpaRepository { +public interface ApiKeyInfoRepository extends JpaRepository { - List findAllByTenantIdAndCalculatedFieldId(UUID tenantId, UUID calculatedFieldId); - - List findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId); - - List findAllByTenantId(UUID tenantId); - - Page findAllByTenantId(UUID tenantId, Pageable pageable); + @Query("SELECT ak FROM ApiKeyInfoEntity ak WHERE ak.tenantId = :tenantId AND ak.userId = :userId AND " + + "(:searchText is NULL OR ilike(ak.description, concat('%', :searchText, '%')) = true)") + Page findByUserId(@Param("tenantId") UUID tenantId, + @Param("userId") UUID userId, + @Param("searchText") String searchText, + Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/pat/ApiKeyRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/ApiKeyRepository.java new file mode 100644 index 0000000000..e3e560725c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/ApiKeyRepository.java @@ -0,0 +1,58 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +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.dao.model.sql.ApiKeyEntity; + +import java.util.Set; +import java.util.UUID; + +public interface ApiKeyRepository extends JpaRepository { + + ApiKeyEntity findByValue(String value); + + @Transactional + @Modifying + @Query(value = """ + DELETE FROM api_key + WHERE tenant_id = :tenantId + RETURNING value + """, nativeQuery = true + ) + Set deleteByTenantId(@Param("tenantId") UUID tenantId); + + @Transactional + @Modifying + @Query(value = """ + DELETE FROM api_key + WHERE tenant_id = :tenantId AND user_id = :userId + RETURNING value + """, nativeQuery = true + ) + Set deleteByUserId(@Param("tenantId") UUID tenantId, + @Param("userId") UUID userId); + + @Transactional + @Modifying + @Query("DELETE FROM ApiKeyEntity ak WHERE ak.expirationTime > 0 AND ak.expirationTime < :ts") + int deleteAllByExpirationTimeBefore(@Param("ts") long ts); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/pat/JpaApiKeyDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/JpaApiKeyDao.java new file mode 100644 index 0000000000..61ca7bd142 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/JpaApiKeyDao.java @@ -0,0 +1,78 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +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.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.ApiKeyEntity; +import org.thingsboard.server.dao.pat.ApiKeyDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.Set; +import java.util.UUID; + +@Slf4j +@SqlDao +@Component +public class JpaApiKeyDao extends JpaAbstractDao implements ApiKeyDao { + + @Autowired + private ApiKeyRepository apiKeyRepository; + + @Override + public ApiKey findByValue(String value) { + return DaoUtil.getData(apiKeyRepository.findByValue(value)); + } + + @Override + public Set deleteByTenantId(TenantId tenantId) { + return apiKeyRepository.deleteByTenantId(tenantId.getId()); + } + + @Override + public Set deleteByUserId(TenantId tenantId, UserId userId) { + return apiKeyRepository.deleteByUserId(tenantId.getId(), userId.getId()); + } + + @Override + public int deleteAllByExpirationTimeBefore(long ts) { + return apiKeyRepository.deleteAllByExpirationTimeBefore(ts); + } + + @Override + protected Class getEntityClass() { + return ApiKeyEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return apiKeyRepository; + } + + @Override + public EntityType getEntityType() { + return EntityType.API_KEY; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/pat/JpaApiKeyInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/JpaApiKeyInfoDao.java new file mode 100644 index 0000000000..0ce3d0d1bc --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/pat/JpaApiKeyInfoDao.java @@ -0,0 +1,58 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pat; + +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.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.pat.ApiKeyInfo; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.ApiKeyInfoEntity; +import org.thingsboard.server.dao.pat.ApiKeyInfoDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; + +@Slf4j +@SqlDao +@Component +public class JpaApiKeyInfoDao extends JpaAbstractDao implements ApiKeyInfoDao { + + @Autowired + private ApiKeyInfoRepository apiKeyInfoRepository; + + @Override + public PageData findByUserId(TenantId tenantId, UserId userId, PageLink pageLink) { + return DaoUtil.toPageData(apiKeyInfoRepository.findByUserId(tenantId.getId(), userId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); + } + + @Override + protected Class getEntityClass() { + return ApiKeyInfoEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return apiKeyInfoRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java index 3bbfcc2158..ced8de8e66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java @@ -117,10 +117,13 @@ public class AlarmDataAdapter { String originatorName = originatorNameObj != null ? originatorNameObj.toString() : null; Object originatorLabelObj = row.get(ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY); String originatorLabel = originatorLabelObj != null ? originatorLabelObj.toString() : null; + Object originatorDisplayNameObj = row.get(ModelConstants.ALARM_ORIGINATOR_DISPLAY_NAME_PROPERTY); + String originatorDisplayName = originatorDisplayNameObj != null ? originatorDisplayNameObj.toString() : null; AlarmData alarmData = new AlarmData(alarm, entityId); alarmData.setOriginatorName(originatorName); alarmData.setOriginatorLabel(originatorLabel); + alarmData.setOriginatorDisplayName(originatorDisplayName); if (alarm.getAssigneeId() != null) { alarmData.setAssignee(new AlarmAssignee(alarm.getAssigneeId(), assigneeFirstName, assigneeLastName, assigneeEmail)); } 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 673c94c7ec..9d5ae1889b 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 @@ -83,6 +83,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { alarmFieldColumnMap.put(ASSIGNEE_FIRST_NAME_KEY, ModelConstants.ALARM_ASSIGNEE_FIRST_NAME_PROPERTY); alarmFieldColumnMap.put(ASSIGNEE_LAST_NAME_KEY, ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY); alarmFieldColumnMap.put(ASSIGNEE_EMAIL_KEY, ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY); + alarmFieldColumnMap.put("originatorDisplayName", ModelConstants.ALARM_ORIGINATOR_DISPLAY_NAME_PROPERTY); } private static final String FIELDS_SELECTION = "select a.id as id," + @@ -106,6 +107,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " a.type as type, " + " a.originator_name as originator_name, " + " a.originator_label as originator_label, " + + " coalesce(a.originator_label, a.originator_name) as originator_display_name, " + " a.assignee_first_name as assignee_first_name, " + " a.assignee_last_name as assignee_last_name, " + " a.assignee_email as assignee_email, " + @@ -319,7 +321,11 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { SqlQueryContext ctx = new SqlQueryContext(new QueryContext(tenantId, null, EntityType.ALARM)); if (query.isSearchPropagatedAlarms()) { - ctx.append("select count(distinct(a.id)) from alarm_info a "); + if (query.getEntityFilter() == null) { + ctx.append("select count(distinct(a.id)) from alarm_info a "); + } else { + ctx.append("select count(a.id) from alarm_info a "); + } ctx.append(JOIN_ENTITY_ALARMS); if (orderedEntityIds != null) { if (orderedEntityIds.isEmpty()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java index c259d48372..b9a134e713 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java @@ -65,6 +65,7 @@ public class EntityKeyMapping { public static final String NAME = "name"; public static final String TYPE = "type"; public static final String LABEL = "label"; + public static final String DISPLAY_NAME = "displayName"; public static final String FIRST_NAME = "firstName"; public static final String LAST_NAME = "lastName"; public static final String EMAIL = "email"; @@ -83,6 +84,8 @@ public class EntityKeyMapping { public static final String SERVICE_ID = "serviceId"; public static final String OWNER_NAME = "ownerName"; public static final String OWNER_TYPE = "ownerType"; + public static final String LABELED_ENTITY_DISPLAY_NAME_SELECT_QUERY = "COALESCE(NULLIF(TRIM(e." + LABEL + "), ''), e." + NAME + ")"; + public static final String USER_DISPLAY_NAME_SELECT_QUERY = "COALESCE(NULLIF(TRIM(CONCAT_WS(' ', e.first_name, e.last_name)), ''), e.email)"; public static final String OWNER_NAME_SELECT_QUERY = "case when e.customer_id = '" + NULL_UUID + "' " + "then (select title from tenant where id = e.tenant_id) " + "else (select title from customer where id = e.customer_id) end"; @@ -94,6 +97,16 @@ public class EntityKeyMapping { OWNER_NAME, OWNER_NAME_SELECT_QUERY, OWNER_TYPE, OWNER_TYPE_SELECT_QUERY ); + public static final Map labeledPropertiesFunctions = Map.of( + OWNER_NAME, OWNER_NAME_SELECT_QUERY, + OWNER_TYPE, OWNER_TYPE_SELECT_QUERY, + DISPLAY_NAME, LABELED_ENTITY_DISPLAY_NAME_SELECT_QUERY + ); + public static final Map userPropertiesFunctions = Map.of( + OWNER_NAME, OWNER_NAME_SELECT_QUERY, + OWNER_TYPE, OWNER_TYPE_SELECT_QUERY, + DISPLAY_NAME, USER_DISPLAY_NAME_SELECT_QUERY + ); public static final Map queueStatsPropertiesFunctions = Map.of(NAME, QUEUE_STATS_NAME_QUERY); public static final List typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO); @@ -153,20 +166,24 @@ public class EntityKeyMapping { Map contactBasedAliases = new HashMap<>(); contactBasedAliases.put(NAME, TITLE); contactBasedAliases.put(LABEL, TITLE); + contactBasedAliases.put(DISPLAY_NAME, TITLE); aliases.put(EntityType.TENANT, contactBasedAliases); aliases.put(EntityType.CUSTOMER, contactBasedAliases); aliases.put(EntityType.DASHBOARD, contactBasedAliases); + Map deviceAndAssetAliases = new HashMap<>(); + deviceAndAssetAliases.put(TITLE, NAME); + aliases.put(EntityType.DEVICE, deviceAndAssetAliases); + aliases.put(EntityType.ASSET, deviceAndAssetAliases); Map commonEntityAliases = new HashMap<>(); commonEntityAliases.put(TITLE, NAME); - aliases.put(EntityType.DEVICE, commonEntityAliases); - aliases.put(EntityType.ASSET, commonEntityAliases); + commonEntityAliases.put(DISPLAY_NAME, NAME); aliases.put(EntityType.ENTITY_VIEW, commonEntityAliases); aliases.put(EntityType.WIDGETS_BUNDLE, commonEntityAliases); - propertiesFunctions.put(EntityType.DEVICE, ownerPropertiesFunctions); - propertiesFunctions.put(EntityType.ASSET, ownerPropertiesFunctions); + propertiesFunctions.put(EntityType.DEVICE, labeledPropertiesFunctions); + propertiesFunctions.put(EntityType.ASSET, labeledPropertiesFunctions); propertiesFunctions.put(EntityType.ENTITY_VIEW, ownerPropertiesFunctions); - propertiesFunctions.put(EntityType.USER, ownerPropertiesFunctions); + propertiesFunctions.put(EntityType.USER, userPropertiesFunctions); propertiesFunctions.put(EntityType.DASHBOARD, ownerPropertiesFunctions); propertiesFunctions.put(EntityType.QUEUE_STATS, queueStatsPropertiesFunctions); 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 18d773a6ca..4648f53e19 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 @@ -25,6 +25,9 @@ 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.EntityRelationPathQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.DaoUtil; @@ -43,6 +46,7 @@ import java.util.stream.Collectors; import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_GROUP_PROPERTY; @@ -288,4 +292,106 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple return DaoUtil.convertDataList(relationRepository.findRuleNodeToRuleChainRelations(ruleChainType, PageRequest.of(0, limit))); } + @Override + public List findByRelationPathQuery(TenantId tenantId, EntityRelationPathQuery query, int limit) { + List levels = query.levels(); + if (levels == null || levels.isEmpty()) { + return List.of(); + } + if (limit <= 0) { + return List.of(); + } + String sql = buildRelationPathSql(query); + Object[] params = buildRelationPathParams(query, limit); + + log.trace("[{}] relation path query: {}", tenantId, sql); + + return jdbcTemplate.queryForList(sql, params).stream() + .map(row -> { + var entityRelation = new EntityRelation(); + var fromId = (UUID) row.get(RELATION_FROM_ID_PROPERTY); + var fromType = (String) row.get(RELATION_FROM_TYPE_PROPERTY); + var toId = (UUID) row.get(RELATION_TO_ID_PROPERTY); + var toType = (String) row.get(RELATION_TO_TYPE_PROPERTY); + var grp = (String) row.get(RELATION_TYPE_GROUP_PROPERTY); + var type = (String) row.get(RELATION_TYPE_PROPERTY); + var version = (Long) row.get(VERSION_COLUMN); + + entityRelation.setFrom(EntityIdFactory.getByTypeAndUuid(fromType, fromId)); + entityRelation.setTo(EntityIdFactory.getByTypeAndUuid(toType, toId)); + entityRelation.setType(type); + entityRelation.setTypeGroup(RelationTypeGroup.valueOf(grp)); + entityRelation.setVersion(version); + return entityRelation; + }) + .collect(Collectors.toList()); + } + + private Object[] buildRelationPathParams(EntityRelationPathQuery query, int limit) { + final List params = new ArrayList<>(); + // seed + params.add(query.rootEntityId().getId()); + params.add(query.rootEntityId().getEntityType().name()); + + // levels + for (var lvl : query.levels()) { + params.add(lvl.relationType()); + } + + // limit + params.add(limit); + + return params.toArray(); + } + + private static String buildRelationPathSql(EntityRelationPathQuery query) { + List levels = query.levels(); + StringBuilder sb = new StringBuilder(); + + sb.append("WITH seed AS (\n") + .append(" SELECT ?::uuid AS id, ?::varchar AS type\n") + .append(")"); + + String prev = "seed"; + for (int i = 0; i < levels.size() - 1; i++) { + RelationPathLevel lvl = levels.get(i); + boolean down = lvl.direction() == EntitySearchDirection.FROM; + + String cur = "lvl" + (i + 1); + String joinCond = down + ? "r.from_id = p.id AND r.from_type = p.type" + : "r.to_id = p.id AND r.to_type = p.type"; + String selectNext = down + ? "r.to_id AS id, r.to_type AS type" + : "r.from_id AS id, r.from_type AS type"; + + sb.append(",\n").append(cur).append(" AS (\n") + .append(" SELECT ").append(selectNext).append("\n") + .append(" FROM ").append(RELATION_TABLE_NAME).append(" r\n") + .append(" JOIN ").append(prev).append(" p ON ").append(joinCond).append("\n") + .append(" WHERE r.relation_type_group = '").append(RelationTypeGroup.COMMON).append("'\n") + .append(" AND r.relation_type = ?\n") + .append(")"); + prev = cur; + } + + RelationPathLevel last = levels.get(levels.size() - 1); + boolean lastDown = last.direction() == EntitySearchDirection.FROM; + String prevForLast = (levels.size() == 1) ? "seed" : prev; + String lastJoin = lastDown + ? "r.from_id = p.id AND r.from_type = p.type" + : "r.to_id = p.id AND r.to_type = p.type"; + + sb.append("\n") + .append("SELECT r.from_id, r.from_type, r.to_id, r.to_type,\n") + .append(" r.relation_type_group, r.relation_type, r.version\n") + .append("FROM ").append(RELATION_TABLE_NAME).append(" r\n") + .append("JOIN ").append(prevForLast).append(" p ON ").append(lastJoin).append("\n") + .append("WHERE r.relation_type_group = '").append(RelationTypeGroup.COMMON).append("'\n") + .append(" AND r.relation_type = ?\n") + .append("LIMIT ?"); + + return sb.toString(); + } + } 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 691711da08..0e821a41b2 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 @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.relation; -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.JpaSpecificationExecutor; @@ -96,4 +95,5 @@ public interface RelationRepository @Param("toId") UUID toId, @Param("toType") String toType, @Param("batchSize") int batchSize); + } 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 cba859e767..a72b053a98 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 @@ -112,6 +112,11 @@ public class JpaRuleChainDao extends JpaAbstractDao return DaoUtil.convertDataList(ruleChainRepository.findByTenantIdAndTypeAndName(tenantId.getId(), type, name)); } + @Override + public List findRuleChainsByTenantIdAndIds(UUID tenantId, List ruleChainIds) { + return DaoUtil.convertDataList(ruleChainRepository.findRuleChainsByTenantIdAndIdIn(tenantId, ruleChainIds)); + } + @Override public Long countByTenantId(TenantId tenantId) { return ruleChainRepository.countByTenantId(tenantId.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 3ace8a585d..66e2bfd7b0 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 @@ -90,4 +90,7 @@ public interface RuleChainRepository extends JpaRepository :id ORDER BY r.id") List findNextBatch(@Param("id") UUID id, Limit limit); + + List findRuleChainsByTenantIdAndIdIn(UUID tenantId, List ruleChainIds); + } 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 a41d719b49..4a10efdcec 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 @@ -98,6 +98,11 @@ public class JpaTenantDao extends JpaAbstractDao implement return DaoUtil.getData(tenantRepository.findFirstByTitle(name)); } + @Override + public List findTenantsByIds(UUID tenantId, List tenantIds) { + return DaoUtil.convertDataList(tenantRepository.findTenantsByIdIn(tenantIds)); + } + @Override public List findNextBatch(UUID id, int batchSize) { return tenantRepository.findNextBatch(id, Limit.of(batchSize)); 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 d38a5fd513..3b0eb13a93 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 @@ -59,4 +59,6 @@ public interface TenantRepository extends JpaRepository { TenantEntity findFirstByTitle(String name); + List findTenantsByIdIn(List tenantIds); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index e9dbcce733..696e098636 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -21,6 +21,7 @@ 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.User; +import org.thingsboard.server.common.data.UserAuthDetails; import org.thingsboard.server.common.data.edqs.fields.UserFields; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -28,6 +29,7 @@ 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.security.Authority; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.UserEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; @@ -136,11 +138,22 @@ public class JpaUserDao extends JpaAbstractDao implements User DaoUtil.toPageable(pageLink))); } + @Override + public UserAuthDetails findUserAuthDetailsByUserId(UUID tenantId, UUID userId) { + TbPair result = userRepository.findUserAuthDetailsByUserId(userId); + return result != null ? new UserAuthDetails(result.getFirst().toData(), result.getSecond()) : null; + } + @Override public int countTenantAdmins(UUID tenantId) { return userRepository.countByTenantIdAndAuthority(tenantId, Authority.TENANT_ADMIN); } + @Override + public List findUsersByTenantIdAndIds(UUID tenantId, List userIds) { + return DaoUtil.convertDataList(userRepository.findUsersByTenantIdAndIdIn(tenantId, userIds)); + } + @Override public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 1d9417fbfa..b63c42aa8c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -23,15 +23,13 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.edqs.fields.UserFields; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.model.sql.UserEntity; import java.util.Collection; import java.util.List; import java.util.UUID; -/** - * @author Valerii Sosliuk - */ public interface UserRepository extends JpaRepository { UserEntity findByEmail(String email); @@ -80,4 +78,11 @@ public interface UserRepository extends JpaRepository { List findNextBatch(@Param("id") UUID id, Limit limit); int countByTenantIdAndAuthority(UUID tenantId, Authority authority); + + @Query("SELECT new org.thingsboard.server.common.data.util.TbPair(u, uc.enabled) " + + "FROM UserEntity u JOIN UserCredentialsEntity uc ON u.id = uc.userId WHERE u.id = :userId ") + TbPair findUserAuthDetailsByUserId(@Param("userId") UUID userId); + + List findUsersByTenantIdAndIdIn(UUID tenantId, List userIds); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java index 7ffdd38092..e1c6341203 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java @@ -108,6 +108,11 @@ public class JpaWidgetsBundleDao extends JpaAbstractDao findSystemOrTenantWidgetBundlesByIds(UUID tenantId, List widgetsBundleIds) { + return DaoUtil.convertDataList(widgetsBundleRepository.findSystemOrTenantWidgetsBundlesByIdIn(tenantId, TenantId.NULL_UUID, widgetsBundleIds)); + } + private PageData findTenantWidgetsBundlesByTenantIds(List tenantIds, WidgetsBundleFilter widgetsBundleFilter, PageLink pageLink) { if (widgetsBundleFilter.isFullSearch()) { return DaoUtil.toPageData( 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 4a4d062006..1830c7fefc 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 @@ -142,4 +142,11 @@ public interface WidgetsBundleRepository extends JpaRepository :id ORDER BY w.id") List findNextBatch(@Param("id") UUID id, Limit limit); + + @Query("SELECT wb FROM WidgetsBundleEntity wb WHERE " + + "wb.id IN (:widgetsBundleIds) AND (wb.tenantId = :tenantId OR wb.tenantId = :systemTenantId)") + List findSystemOrTenantWidgetsBundlesByIdIn(@Param("tenantId") UUID tenantId, + @Param("systemTenantId") UUID systemTenantId, + @Param("widgetsBundleIds") List widgetsBundleIds); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/CachedRedisSqlTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/CachedRedisSqlTimeseriesLatestDao.java index a97799a032..eabbdfed80 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/CachedRedisSqlTimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/CachedRedisSqlTimeseriesLatestDao.java @@ -167,5 +167,9 @@ public class CachedRedisSqlTimeseriesLatestDao extends BaseAbstractSqlTimeseries return sqlDao.findAllKeysByEntityIds(tenantId, entityIds); } + @Override + public ListenableFuture> findAllKeysByEntityIdsAsync(TenantId tenantId, List entityIds) { + return sqlDao.findAllKeysByEntityIdsAsync(tenantId, entityIds); + } } 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 38962b45b1..d807794ddc 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 @@ -24,7 +24,6 @@ import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.domain.Page; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; @@ -38,8 +37,6 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQueryResult; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.dictionary.KeyDictionaryDao; @@ -64,7 +61,6 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.function.Function; -import java.util.stream.Collectors; @Slf4j @Component @@ -185,9 +181,13 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme @Override public List findAllKeysByEntityIds(TenantId tenantId, List entityIds) { - return tsKvLatestRepository.findAllKeysByEntityIds(entityIds.stream().map(EntityId::getId).collect(Collectors.toList())); + return tsKvLatestRepository.findAllKeysByEntityIds(entityIds.stream().map(EntityId::getId).toList()); } + @Override + public ListenableFuture> findAllKeysByEntityIdsAsync(TenantId tenantId, List entityIds) { + return service.submit(() -> findAllKeysByEntityIds(tenantId, entityIds)); + } private ListenableFuture getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query, Long version) { ListenableFuture> future = findNewLatestEntryFuture(tenantId, entityId, query); @@ -211,7 +211,7 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme ReadTsKvQueryResult::getData, MoreExecutors.directExecutor()); } - protected TsKvEntry doFindLatestSync(EntityId entityId, String key) { + protected TsKvEntry doFindLatestSync(EntityId entityId, String key) { TsKvLatestCompositeKey compositeKey = new TsKvLatestCompositeKey( entityId.getId(), diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java index b1840e75a8..87958eb0cc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java @@ -42,4 +42,6 @@ public interface TenantDao extends Dao { Tenant findTenantByName(TenantId tenantId, String name); + List findTenantsByIds(UUID tenantId, List tenantIds); + } 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 db823fd22a..7be47f4a0e 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 @@ -35,10 +35,10 @@ 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; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.exception.DataValidationException; import java.util.ArrayList; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 3674804493..392c422792 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -53,7 +53,9 @@ import java.util.Optional; import java.util.function.Consumer; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateIds; @Service("TenantDaoService") @Slf4j @@ -77,6 +79,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService findTenantsByIds(TenantId callerId, List tenantIds) { + log.trace("Executing findTenantsByIds, callerId [{}], tenantIds [{}]", callerId, tenantIds); + return tenantDao.findTenantsByIds(callerId.getId(), toUUIDs(tenantIds)); + } + @Override public boolean tenantExists(TenantId tenantId) { return existsTenantCache.getAndPutInTransaction(tenantId, () -> tenantDao.existsById(tenantId, tenantId.getId()), false); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index 2b14ffdae3..747197470c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -156,6 +156,11 @@ public class BaseTimeseriesService implements TimeseriesService { return timeseriesLatestDao.findAllKeysByEntityIds(tenantId, entityIds); } + @Override + public ListenableFuture> findAllKeysByEntityIdsAsync(TenantId tenantId, List entityIds) { + return timeseriesLatestDao.findAllKeysByEntityIdsAsync(tenantId, entityIds); + } + @Override public void cleanup(long systemTtl) { timeseriesDao.cleanup(systemTtl); @@ -300,13 +305,13 @@ public class BaseTimeseriesService implements TimeseriesService { long interval = query.getInterval(); if (interval < 1) { throw new IncorrectParameterException("Invalid TsKvQuery: 'interval' must be greater than 0, but got " + interval + - ". Please check your query parameters and ensure 'endTs' is greater than 'startTs' or increase 'interval'."); + ". Please check your query parameters and ensure 'endTs' is greater than 'startTs' or increase 'interval'."); } long step = Math.max(interval, 1000); long intervalCounts = (query.getEndTs() - query.getStartTs()) / step; if (intervalCounts > maxTsIntervals || intervalCounts < 0) { throw new IncorrectParameterException("Incorrect TsKvQuery. Number of intervals is to high - " + intervalCounts + ". " + - "Please increase 'interval' parameter for your query or reduce the time range of the query."); + "Please increase 'interval' parameter for your query or reduce the time range of the query."); } } } 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 cc9c2919f2..6bcc88c2df 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 @@ -36,17 +36,13 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQueryResult; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.nosql.TbResultSet; import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao; import org.thingsboard.server.dao.util.NoSqlTsLatestDao; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Optional; import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal; @@ -103,6 +99,10 @@ public class CassandraBaseTimeseriesLatestDao extends AbstractCassandraBaseTimes return Collections.emptyList(); } + @Override + public ListenableFuture> findAllKeysByEntityIdsAsync(TenantId tenantId, List entityIds) { + return Futures.immediateFuture(Collections.emptyList()); + } @Override public ListenableFuture saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) { 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 6685b862f1..26e784b760 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 @@ -22,12 +22,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import java.util.List; -import java.util.Map; import java.util.Optional; public interface TimeseriesLatestDao { @@ -54,4 +50,6 @@ public interface TimeseriesLatestDao { List findAllKeysByEntityIds(TenantId tenantId, List entityIds); + ListenableFuture> findAllKeysByEntityIdsAsync(TenantId tenantId, List entityIds); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index da0df65ef5..fe23c9cc0b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.user; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserAuthDetails; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; @@ -102,4 +103,9 @@ public interface UserDao extends Dao, TenantEntityDao { PageData findByAuthorityAndTenantProfilesIds(Authority authority, List tenantProfilesIds, PageLink pageLink); int countTenantAdmins(UUID tenantId); + + UserAuthDetails findUserAuthDetailsByUserId(UUID tenantId, UUID userId); + + List findUsersByTenantIdAndIds(UUID tenantId, List userIds); + } 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 a5fb56e7eb..886c9e46c3 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 @@ -35,6 +35,7 @@ import org.thingsboard.server.cache.user.UserCacheKey; 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.UserAuthDetails; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -45,6 +46,11 @@ import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.mobile.MobileSessionInfo; import org.thingsboard.server.common.data.mobile.UserMobileSessionInfo; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.SystemLevelUsersFilter; +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.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; @@ -59,10 +65,12 @@ 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.pat.ApiKeyService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.settings.SecuritySettingsService; import org.thingsboard.server.dao.sql.JpaExecutorService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import java.util.ArrayList; import java.util.Collections; @@ -72,9 +80,12 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.thingsboard.server.common.data.StringUtils.generateSafeToken; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -86,8 +97,9 @@ public class UserServiceImpl extends AbstractCachedEntityService userValidator; private final DataValidator userCredentialsValidator; private final ApplicationEventPublisher eventPublisher; @@ -161,8 +175,23 @@ public class UserServiceImpl extends AbstractCachedEntityService doSaveUser(tenantId, user, doValidate)); + } + + private User doSaveUser(TenantId tenantId, User user, boolean doValidate) { log.trace("Executing saveUser [{}]", user); - User oldUser = userValidator.validate(user, User::getTenantId); + User oldUser = null; + if (doValidate) { + oldUser = userValidator.validate(user, User::getTenantId); + } else if (user.getId() != null) { + oldUser = findUserById(user.getTenantId(), user.getId()); + } if (!userLoginCaseSensitive) { user.setEmail(user.getEmail().toLowerCase()); } @@ -217,8 +246,15 @@ public class UserServiceImpl extends AbstractCachedEntityService tenantId); + if (doValidate) { + userCredentialsValidator.validate(userCredentials, data -> tenantId); + } UserCredentials result = userCredentialsDao.save(tenantId, userCredentials); eventPublisher.publishEvent(ActionEntityEvent.builder() .tenantId(tenantId) @@ -296,7 +332,7 @@ public class UserServiceImpl extends AbstractCachedEntityService INCORRECT_USER_CREDENTIALS_ID + id); + userCredentialsDao.removeById(tenantId, userCredentialsId.getId()); + } + @Override @Transactional public void deleteUser(TenantId tenantId, User user) { @@ -334,6 +379,7 @@ public class UserServiceImpl extends AbstractCachedEntityService INCORRECT_USER_ID + id); userCredentialsDao.removeByUserId(tenantId, userId); userAuthSettingsDao.removeByUserId(userId); + apiKeyService.deleteByUserId(tenantId, userId); publishEvictEvent(new UserCacheEvictEvent(user.getTenantId(), user.getEmail(), null)); userSettingsDao.removeByUserId(tenantId, userId); userDao.removeById(tenantId, userId.getId()); @@ -492,6 +538,19 @@ public class UserServiceImpl extends AbstractCachedEntityService INCORRECT_USER_ID + id); + return userDao.findUserAuthDetailsByUserId(tenantId.getId(), userId.getId()); + } + + @Override + public List findUsersByTenantIdAndIds(TenantId tenantId, List userIds) { + log.trace("Executing findUsersByTenantIdAndIds, tenantId [{}], userIds [{}]", tenantId, userIds); + return userDao.findUsersByTenantIdAndIds(tenantId.getId(), toUUIDs(userIds)); + } + private Optional findMobileSessionInfo(TenantId tenantId, UserId userId) { return Optional.ofNullable(userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.MOBILE)) .map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileSessionInfo.class)); @@ -503,6 +562,80 @@ public class UserServiceImpl extends AbstractCachedEntityService findUsersByFilter(TenantId tenantId, UsersFilter filter, PageLink pageLink) { + switch (filter.getType()) { + case USER_LIST -> { + List users = ((UserListFilter) filter).getUsersIds().stream() + .limit(pageLink.getPageSize()) + .map(UserId::new).map(userId -> findUserById(tenantId, userId)) + .filter(Objects::nonNull).collect(Collectors.toList()); + return new PageData<>(users, 1, users.size(), false); + } + case CUSTOMER_USERS -> { + if (tenantId.equals(TenantId.SYS_TENANT_ID)) { + throw new IllegalArgumentException("Customer users target is not supported for system administrator"); + } + CustomerUsersFilter customerUsersFilter = (CustomerUsersFilter) filter; + return findCustomerUsers(tenantId, new CustomerId(customerUsersFilter.getCustomerId()), pageLink); + } + case TENANT_ADMINISTRATORS -> { + TenantAdministratorsFilter tenantAdministratorsFilter = (TenantAdministratorsFilter) filter; + if (!tenantId.equals(TenantId.SYS_TENANT_ID)) { + return findTenantAdmins(tenantId, pageLink); + } else { + if (isNotEmpty(tenantAdministratorsFilter.getTenantsIds())) { + return findTenantAdminsByTenantsIds(tenantAdministratorsFilter.getTenantsIds().stream() + .map(TenantId::fromUUID).collect(Collectors.toList()), pageLink); + } else if (isNotEmpty(tenantAdministratorsFilter.getTenantProfilesIds())) { + return findTenantAdminsByTenantProfilesIds(tenantAdministratorsFilter.getTenantProfilesIds().stream() + .map(TenantProfileId::new).collect(Collectors.toList()), pageLink); + } else { + return findAllTenantAdmins(pageLink); + } + } + } + case SYSTEM_ADMINISTRATORS -> { + return findSysAdmins(pageLink); + } + case ALL_USERS -> { + if (!tenantId.equals(TenantId.SYS_TENANT_ID)) { + return findUsersByTenantId(tenantId, pageLink); + } else { + return findAllUsers(pageLink); + } + } + default -> throw new IllegalArgumentException("Recipient type not supported"); + } + } + + @Override + public boolean matchesFilter(TenantId tenantId, SystemLevelUsersFilter filter, User user) { + switch (filter.getType()) { + case TENANT_ADMINISTRATORS -> { + if (user.isSystemAdmin() || user.isCustomerUser()) { + return false; + } + TenantAdministratorsFilter tenantAdministratorsFilter = (TenantAdministratorsFilter) filter; + if (isNotEmpty(tenantAdministratorsFilter.getTenantsIds())) { + return tenantAdministratorsFilter.getTenantsIds().contains(user.getTenantId().getId()); + } else if (isNotEmpty(tenantAdministratorsFilter.getTenantProfilesIds())) { + return tenantAdministratorsFilter.getTenantProfilesIds().contains(tenantProfileCache.get(user.getTenantId()).getUuidId()); + } else { + return user.getAuthority() == Authority.TENANT_ADMIN; + } + } + case SYSTEM_ADMINISTRATORS -> { + return user.getAuthority() == Authority.SYS_ADMIN; + } + case ALL_USERS -> { + return true; + } + default -> throw new IllegalArgumentException("Recipient type not supported"); + } + + } + private void updatePasswordHistory(UserCredentials userCredentials) { JsonNode additionalInfo = userCredentials.getAdditionalInfo(); if (!(additionalInfo instanceof ObjectNode)) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java index 893cfce237..89064c2a8b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey; import org.thingsboard.server.common.data.settings.UserSettingsType; import org.thingsboard.server.dao.entity.AbstractCachedService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; import java.util.Iterator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index 5b50d062a2..d1d31c4745 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -117,24 +117,26 @@ public class DeviceConnectivityUtil { dockerComposeBuilder.append("\n"); dockerComposeBuilder.append(" # Environment variables\n"); dockerComposeBuilder.append(" environment:\n"); - dockerComposeBuilder.append(" - host=").append(isLocalhost(host) ? HOST_DOCKER_INTERNAL : host).append("\n"); - dockerComposeBuilder.append(" - port=1883\n"); + dockerComposeBuilder.append(" - TB_GW_HOST=").append(isLocalhost(host) ? HOST_DOCKER_INTERNAL : host).append("\n"); + dockerComposeBuilder.append(" - TB_GW_PORT=1883\n"); switch (deviceCredentials.getCredentialsType()) { case ACCESS_TOKEN: - dockerComposeBuilder.append(" - accessToken=").append(deviceCredentials.getCredentialsId()).append("\n"); + dockerComposeBuilder.append(" - TB_GW_SECURITY_TYPE=accessToken\n"); + dockerComposeBuilder.append(" - TB_GW_ACCESS_TOKEN=").append(deviceCredentials.getCredentialsId()).append("\n"); break; case MQTT_BASIC: + dockerComposeBuilder.append(" - TB_GW_SECURITY_TYPE=usernamePassword\n"); BasicMqttCredentials credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), BasicMqttCredentials.class); if (credentials != null) { if (StringUtils.isNotEmpty(credentials.getClientId())) { - dockerComposeBuilder.append(" - clientId=").append(credentials.getClientId()).append("\n"); + dockerComposeBuilder.append(" - TB_GW_CLIENT_ID=").append(credentials.getClientId()).append("\n"); } if (StringUtils.isNotEmpty(credentials.getUserName())) { - dockerComposeBuilder.append(" - username=").append(credentials.getUserName()).append("\n"); + dockerComposeBuilder.append(" - TB_GW_USERNAME=").append(credentials.getUserName()).append("\n"); } if (StringUtils.isNotEmpty(credentials.getPassword())) { - dockerComposeBuilder.append(" - password=").append(credentials.getPassword()).append("\n"); + dockerComposeBuilder.append(" - TB_GW_PASSWORD=").append(credentials.getPassword()).append("\n"); } } break; 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 c923ae208b..afe0518208 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 @@ -19,13 +19,21 @@ 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.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.NoXssValidator; +import org.thingsboard.server.exception.DataValidationException; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; public class KvUtils { @@ -74,4 +82,33 @@ public class KvUtils { } } } + + public static List toTsKvEntryList(Map> tsKvMap) { + List tsKvEntryList = new ArrayList<>(); + for (Map.Entry> tsKvEntry : tsKvMap.entrySet()) { + for (KvEntry kvEntry : tsKvEntry.getValue()) { + tsKvEntryList.add(new BasicTsKvEntry(tsKvEntry.getKey(), kvEntry)); + } + } + return tsKvEntryList; + } + + public static 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()); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/TimeUtils.java b/dao/src/main/java/org/thingsboard/server/dao/util/TimeUtils.java index c163558ee4..888d0da866 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/TimeUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/TimeUtils.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.dao.util; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.kv.IntervalType; import java.time.Instant; @@ -24,6 +26,7 @@ import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.WeekFields; +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class TimeUtils { public static long calculateIntervalEnd(long startTs, IntervalType intervalType, ZoneId tzId) { @@ -42,4 +45,8 @@ public class TimeUtils { } } + public static ZonedDateTime toZonedDateTime(long ts, ZoneId zoneId) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(ts), zoneId); + } + } 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 937e8117fc..4d7f30cace 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 @@ -25,6 +25,7 @@ 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; /** @@ -87,5 +88,7 @@ public interface WidgetsBundleDao extends Dao, ExportableEntityDa PageData findAllWidgetsBundles(PageLink pageLink); + List findSystemOrTenantWidgetBundlesByIds(UUID tenantId, List widgetsBundleIds); + } 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 14e04c6090..d950c3eafa 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 @@ -48,6 +48,7 @@ import java.util.Optional; import java.util.stream.Stream; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.entity.AbstractEntityService.checkConstraintViolation; @Service("WidgetsBundleDaoService") @@ -252,6 +253,12 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { }); } + @Override + public List findSystemOrTenantWidgetsBundlesByIds(TenantId tenantId, List widgetsBundleIds) { + log.trace("Executing findSystemOrTenantWidgetsBundlesByIds, tenantId [{}], widgetsBundleIds [{}]", tenantId, widgetsBundleIds); + return widgetsBundleDao.findSystemOrTenantWidgetBundlesByIds(tenantId.getId(), toUUIDs(widgetsBundleIds)); + } + private WidgetTypeDetails updateSystemWidget(JsonNode widgetTypeJson) { WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); WidgetType existingWidget = widgetTypeService.findWidgetTypeByTenantIdAndFqn(TenantId.SYS_TENANT_ID, widgetTypeDetails.getFqn()); diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index cf52cb944f..43e64bab95 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -20,7 +20,7 @@ CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); --- Drop index by 'status' column and replace with new indexes that has only active alarms; +-- Drop index by 'status' column and replace with new indexes that have only active alarms; CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type_active ON alarm USING btree (originator_id, type) WHERE cleared = false; @@ -108,8 +108,6 @@ CREATE INDEX IF NOT EXISTS idx_notification_delivery_method_recipient_id_unread 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); CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id); @@ -117,3 +115,5 @@ CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tena CREATE INDEX IF NOT EXISTS idx_job_tenant_id ON job(tenant_id); CREATE INDEX IF NOT EXISTS idx_ai_model_tenant_id ON ai_model(tenant_id); + +CREATE INDEX IF NOT EXISTS idx_api_key_user_id ON api_key(user_id); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index b051b187dc..3d9c9f17e6 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -709,6 +709,18 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) ); +CREATE TABLE IF NOT EXISTS api_key ( + id uuid NOT NULL CONSTRAINT api_key_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid, + user_id uuid, + value varchar(512), + enabled boolean NOT NULL DEFAULT TRUE, + expiration_time bigint DEFAULT 0, + description varchar(255), + CONSTRAINT api_key_value_unq_key UNIQUE (value) +); + CREATE TABLE IF NOT EXISTS resource ( id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -923,17 +935,7 @@ CREATE TABLE IF NOT EXISTS calculated_field ( configuration varchar(1000000), version BIGINT DEFAULT 1, debug_settings varchar(1024), - CONSTRAINT calculated_field_unq_key UNIQUE (entity_id, name) -); - -CREATE TABLE IF NOT EXISTS calculated_field_link ( - id uuid NOT NULL CONSTRAINT calculated_field_link_pkey PRIMARY KEY, - created_time bigint NOT NULL, - tenant_id uuid NOT NULL, - entity_type VARCHAR(32), - entity_id uuid NOT NULL, - calculated_field_id uuid NOT NULL, - CONSTRAINT fk_calculated_field_id FOREIGN KEY (calculated_field_id) REFERENCES calculated_field(id) ON DELETE CASCADE + CONSTRAINT calculated_field_unq_key UNIQUE (entity_id, type, name) ); CREATE TABLE IF NOT EXISTS cf_debug_event ( 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 d5baec09d5..9a467071ef 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 @@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; 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.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; @@ -185,8 +186,13 @@ public abstract class AbstractServiceTest { } public Tenant createTenant() { + return createTenant(null); + } + + public Tenant createTenant(TenantProfileId tenantProfileId) { Tenant tenant = new Tenant(); tenant.setTitle("My tenant " + UUID.randomUUID()); + tenant.setTenantProfileId(tenantProfileId); Tenant savedTenant = tenantService.saveTenant(tenant); assertNotNull(savedTenant); return savedTenant; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AdminSettingsServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AdminSettingsServiceTest.java index 2c5851eb82..8423599c90 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AdminSettingsServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AdminSettingsServiceTest.java @@ -25,7 +25,7 @@ import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.settings.AdminSettingsService; import static org.assertj.core.api.Assertions.assertThatThrownBy; 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 e1093e4f46..c951c58060 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 @@ -211,7 +211,7 @@ public class AlarmServiceTest extends AbstractServiceTest { Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); - alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null); + alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null, true); created = alarmService.findAlarmInfoById(tenantId, created.getId()); alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() @@ -319,7 +319,7 @@ public class AlarmServiceTest extends AbstractServiceTest { Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); - alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null); + alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null, true); created = alarmService.findAlarmInfoById(tenantId, created.getId()); alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() @@ -622,7 +622,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .severity(AlarmSeverity.MAJOR) .startTs(System.currentTimeMillis()).build()); AlarmInfo alarm1 = result.getAlarm(); - alarmService.clearAlarm(tenantId, alarm1.getId(), System.currentTimeMillis(), null); + alarmService.clearAlarm(tenantId, alarm1.getId(), System.currentTimeMillis(), null, true); result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) @@ -632,7 +632,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .startTs(System.currentTimeMillis()).build()); AlarmInfo alarm2 = result.getAlarm(); alarmService.acknowledgeAlarm(tenantId, alarm2.getId(), System.currentTimeMillis()); - alarmService.clearAlarm(tenantId, alarm2.getId(), System.currentTimeMillis(), null); + alarmService.clearAlarm(tenantId, alarm2.getId(), System.currentTimeMillis(), null, true); result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) @@ -852,7 +852,7 @@ public class AlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(1, alarmsCount); - alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null); + alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null, true); created = alarmService.findAlarmInfoById(tenantId, created.getId()); alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery); @@ -974,13 +974,16 @@ public class AlarmServiceTest extends AbstractServiceTest { alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery, List.of(parentId)); Assert.assertEquals(1, alarmsCount); + alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery, List.of(childId, parentId)); + Assert.assertEquals(2, alarmsCount); + created = alarmService.acknowledgeAlarm(tenantId, created.getId(), System.currentTimeMillis()).getAlarm(); countQuery.setStatusList(List.of(AlarmSearchStatus.UNACK)); alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery, List.of(childId)); Assert.assertEquals(0, alarmsCount); - alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null); + alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null, true); countQuery.setStatusList(List.of(AlarmSearchStatus.CLEARED)); alarmsCount = alarmService.countAlarmsByQuery(tenantId, null, countQuery, List.of(childId)); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ApiKeyServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ApiKeyServiceTest.java new file mode 100644 index 0000000000..f345c34c0d --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ApiKeyServiceTest.java @@ -0,0 +1,220 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.User; +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.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.exception.DataValidationException; +import org.thingsboard.server.dao.pat.ApiKeyService; +import org.thingsboard.server.dao.user.UserService; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DaoSqlTest +public class ApiKeyServiceTest extends AbstractServiceTest { + + private static final String TEST_API_KEY_DESCRIPTION = "Test API Key Description"; + + @Autowired + ApiKeyService apiKeyService; + + @Autowired + UserService userService; + + private UserId userId; + + @Before + public void before() { + User tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(tenantId); + tenantAdmin.setEmail("tenant@thingsboard.org"); + User user = userService.saveUser(TenantId.SYS_TENANT_ID, tenantAdmin); + userId = user.getId(); + } + + @After + public void after() { + apiKeyService.deleteByTenantId(tenantId); + User user = userService.findUserById(tenantId, userId); + userService.deleteUser(tenantId, user); + } + + @Test + public void testSaveApiKey() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(TEST_API_KEY_DESCRIPTION); + ApiKey savedApiKey = apiKeyService.saveApiKey(tenantId, apiKeyInfo); + + Assert.assertNotNull(savedApiKey); + Assert.assertNotNull(savedApiKey.getId()); + Assert.assertEquals(tenantId, savedApiKey.getTenantId()); + Assert.assertEquals(TEST_API_KEY_DESCRIPTION, savedApiKey.getDescription()); + Assert.assertTrue(savedApiKey.isEnabled()); + Assert.assertNotNull(savedApiKey.getValue()); + } + + @Test + public void testSaveApiKeyWithTooLongDescription() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(StringUtils.randomAlphabetic(300)); + + assertThatThrownBy(() -> apiKeyService.saveApiKey(tenantId, apiKeyInfo)) + .isInstanceOf(DataValidationException.class) + .hasMessageContaining("description length must be equal or less than 255"); + } + + @Test + public void testUpdateDescriptionApiKey() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(TEST_API_KEY_DESCRIPTION); + ApiKey savedApiKey = apiKeyService.saveApiKey(tenantId, apiKeyInfo); + + String newDescription = "Updated API Key Description"; + savedApiKey.setDescription(newDescription); + ApiKey updatedApiKey = apiKeyService.saveApiKey(tenantId, savedApiKey); + + Assert.assertNotNull(updatedApiKey); + Assert.assertEquals(savedApiKey.getId(), updatedApiKey.getId()); + Assert.assertEquals(newDescription, updatedApiKey.getDescription()); + Assert.assertEquals(savedApiKey.getValue(), updatedApiKey.getValue()); + } + + @Test + public void testDisableApiKey() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(TEST_API_KEY_DESCRIPTION); + ApiKey savedApiKey = apiKeyService.saveApiKey(tenantId, apiKeyInfo); + + savedApiKey.setEnabled(false); + ApiKey disabledApiKey = apiKeyService.saveApiKey(tenantId, savedApiKey); + + Assert.assertNotNull(disabledApiKey); + Assert.assertEquals(savedApiKey.getId(), disabledApiKey.getId()); + Assert.assertFalse(disabledApiKey.isEnabled()); + } + + @Test + public void testFindApiKeyById() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(TEST_API_KEY_DESCRIPTION); + ApiKey savedApiKey = apiKeyService.saveApiKey(tenantId, apiKeyInfo); + + ApiKey foundApiKey = apiKeyService.findApiKeyById(tenantId, savedApiKey.getId()); + + Assert.assertNotNull(foundApiKey); + Assert.assertEquals(savedApiKey.getId(), foundApiKey.getId()); + Assert.assertEquals(savedApiKey.getDescription(), foundApiKey.getDescription()); + Assert.assertEquals(savedApiKey.isEnabled(), foundApiKey.isEnabled()); + Assert.assertEquals(savedApiKey.getValue(), foundApiKey.getValue()); + } + + @Test + public void testFindApiKeyByHash() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(TEST_API_KEY_DESCRIPTION); + ApiKey savedApiKey = apiKeyService.saveApiKey(tenantId, apiKeyInfo); + + ApiKey foundApiKey = apiKeyService.findApiKeyByValue(savedApiKey.getValue()); + + Assert.assertNotNull(foundApiKey); + Assert.assertEquals(savedApiKey.getId(), foundApiKey.getId()); + Assert.assertEquals(savedApiKey.getDescription(), foundApiKey.getDescription()); + Assert.assertEquals(savedApiKey.isEnabled(), foundApiKey.isEnabled()); + Assert.assertEquals(savedApiKey.getValue(), foundApiKey.getValue()); + } + + @Test + public void testFindApiKeysByUserId() { + int size = 3; + for (int i = 0; i < size; i++) { + ApiKeyInfo apiKeyInfo = createApiKeyInfo("API Key " + i); + apiKeyService.saveApiKey(tenantId, apiKeyInfo); + } + + PageLink pageLink = new PageLink(10); + PageData pageData = apiKeyService.findApiKeysByUserId(tenantId, userId, pageLink); + + Assert.assertNotNull(pageData); + Assert.assertEquals(size, pageData.getData().size()); + Assert.assertEquals(size, pageData.getTotalElements()); + } + + @Test + public void testDeleteApiKey() { + ApiKeyInfo apiKeyInfo = createApiKeyInfo(TEST_API_KEY_DESCRIPTION); + ApiKey savedApiKey = apiKeyService.saveApiKey(tenantId, apiKeyInfo); + + apiKeyService.deleteApiKey(tenantId, savedApiKey, false); + + ApiKey foundApiKey = apiKeyService.findApiKeyById(tenantId, savedApiKey.getId()); + Assert.assertNull(foundApiKey); + } + + @Test + public void testDeleteByTenantId() { + for (int i = 0; i < 3; i++) { + ApiKeyInfo apiKeyInfo = createApiKeyInfo("API Key " + i); + apiKeyService.saveApiKey(tenantId, apiKeyInfo); + } + + apiKeyService.deleteByTenantId(tenantId); + + PageLink pageLink = new PageLink(10); + PageData pageData = apiKeyService.findApiKeysByUserId(tenantId, userId, pageLink); + + Assert.assertNotNull(pageData); + Assert.assertEquals(0, pageData.getData().size()); + Assert.assertEquals(0, pageData.getTotalElements()); + } + + @Test + public void testDeleteByUserId() { + int size = 3; + for (int i = 0; i < size; i++) { + ApiKeyInfo apiKeyInfo = createApiKeyInfo("API Key " + i); + apiKeyService.saveApiKey(tenantId, apiKeyInfo); + } + + PageData pageData = apiKeyService.findApiKeysByUserId(tenantId, userId, new PageLink(10)); + Assert.assertNotNull(pageData); + Assert.assertEquals(size, pageData.getData().size()); + Assert.assertEquals(size, pageData.getTotalElements()); + + apiKeyService.deleteByUserId(tenantId, userId); + + pageData = apiKeyService.findApiKeysByUserId(tenantId, userId, new PageLink(10)); + Assert.assertNotNull(pageData); + Assert.assertEquals(0, pageData.getData().size()); + Assert.assertEquals(0, pageData.getTotalElements()); + } + + private ApiKeyInfo createApiKeyInfo(String description) { + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); + apiKeyInfo.setTenantId(tenantId); + apiKeyInfo.setUserId(userId); + apiKeyInfo.setDescription(description); + apiKeyInfo.setEnabled(true); + return apiKeyInfo; + } + +} 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 46afabfc80..7335cc14a9 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 @@ -32,7 +32,7 @@ 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.asset.AssetService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; 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 ce01e94d37..204b200482 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 @@ -16,17 +16,25 @@ package org.thingsboard.server.dao.service; import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.AfterClass; import org.junit.Assert; +import org.junit.BeforeClass; 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.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import org.thingsboard.common.util.ThingsBoardThreadFactory; 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.TenantProfile; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetProfile; @@ -34,29 +42,34 @@ import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; 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.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; 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.cf.CalculatedFieldService; import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.tenant.TenantProfileService; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; +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.dao.model.ModelConstants.NULL_UUID; @@ -72,13 +85,28 @@ public class AssetServiceTest extends AbstractServiceTest { @Autowired RelationService relationService; @Autowired + TenantProfileService tenantProfileService; + @Autowired private AssetProfileService assetProfileService; @Autowired private CalculatedFieldService calculatedFieldService; @Autowired private PlatformTransactionManager platformTransactionManager; + private static ListeningExecutorService executor; + private IdComparator idComparator = new IdComparator<>(); + private TenantId anotherTenantId; + + @BeforeClass + public static void before() { + executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10, ThingsBoardThreadFactory.forName("AssetServiceTestScope"))); + } + + @AfterClass + public static void after() { + executor.shutdownNow(); + } @Test public void testSaveAsset() { @@ -105,6 +133,39 @@ public class AssetServiceTest extends AbstractServiceTest { assetService.deleteAsset(tenantId, savedAsset.getId()); } + @Test + public void testAssetLimitOnTenantProfileLevel() throws InterruptedException { + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName("Test profile"); + tenantProfile.setDescription("Test"); + TenantProfileData profileData = new TenantProfileData(); + profileData.setConfiguration(DefaultTenantProfileConfiguration.builder().maxAssets(5l).build()); + tenantProfile.setProfileData(profileData); + tenantProfile.setDefault(false); + tenantProfile.setIsolatedTbRuleEngine(false); + + tenantProfile = tenantProfileService.saveTenantProfile(anotherTenantId, tenantProfile); + anotherTenantId = createTenant(tenantProfile.getId()).getId(); + + for (int i = 0; i < 20; i++) { + executor.submit(() -> { + Asset asset = new Asset(); + asset.setTenantId(anotherTenantId); + asset.setName(RandomStringUtils.randomAlphabetic(10)); + asset.setType("default"); + assetService.saveAsset(asset); + }); + } + + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + long countByTenantId = assetService.countByTenantId(anotherTenantId); + return countByTenantId == 5; + }); + + Thread.sleep(2000); + assertThat(assetService.countByTenantId(anotherTenantId)).isEqualTo(5); + } + @Test public void testShouldNotPutInCacheRolledbackAssetProfile() { AssetProfile assetProfile = new AssetProfile(); @@ -894,9 +955,8 @@ public class AssetServiceTest extends AbstractServiceTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java index 060be564c4..ba914c6d35 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java @@ -15,34 +15,40 @@ */ package org.thingsboard.server.dao.service; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; -import org.thingsboard.server.common.data.id.CalculatedFieldId; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.exception.DataValidationException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; -import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; @DaoSqlTest public class CalculatedFieldServiceTest extends AbstractServiceTest { @@ -51,18 +57,8 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { private CalculatedFieldService calculatedFieldService; @Autowired private DeviceService deviceService; - - private ListeningExecutorService executor; - - @Before - public void before() { - executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); - } - - @After - public void after() { - executor.shutdownNow(); - } + @Autowired + private TbTenantProfileCache tbTenantProfileCache; @Test public void testSaveCalculatedField() { @@ -90,6 +86,157 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { calculatedFieldService.deleteCalculatedField(tenantId, savedCalculatedField.getId()); } + @Test + public void testSaveGeofencingCalculatedField_shouldThrowWhenScheduledIntervalLessThanMinAllowedIntervalInTenantProfile() { + // Arrange a device + Device device = createTestDevice(); + + // Build a valid Geofencing configuration + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + + // Coordinates: TS_LATEST, no dynamic source + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + cfg.setEntityCoordinates(entityCoordinates); + + // Zone-group argument (ATTRIBUTE) — make it DYNAMIC so scheduling is enabled + ZoneGroupConfiguration zoneGroupConfiguration = new ZoneGroupConfiguration("allowed", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var dynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + dynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE))); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(dynamicSourceConfiguration); + cfg.setZoneGroups(Map.of("allowed", zoneGroupConfiguration)); + + AttributesOutput out = new AttributesOutput(); + out.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(out); + + // Get tenant profile min. + int min = tbTenantProfileCache.get(tenantId) + .getDefaultProfileConfiguration() + .getMinAllowedScheduledUpdateIntervalInSecForCF(); + int valueFromConfig = min - 10; + + // Enable scheduling with an interval below tenant min + cfg.setScheduledUpdateEnabled(true); + cfg.setScheduledUpdateInterval(valueFromConfig); + + // Create & save Calculated Field + CalculatedField cf = new CalculatedField(); + cf.setTenantId(tenantId); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("GF min allowed scheduled update interval test"); + cf.setConfigurationVersion(0); + cf.setConfiguration(cfg); + + assertThatThrownBy(() -> calculatedFieldService.save(cf)) + .isInstanceOf(DataValidationException.class) + .hasCauseInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Scheduled update interval is less than configured " + + "minimum allowed interval in tenant profile: "); + } + + @Test + public void testSaveGeofencingCalculatedField_shouldThrowWhenRelationLevelGreaterThanMaxAllowedRelationLevelInTenantProfile() { + // Arrange a device + Device device = createTestDevice(); + + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + + // Coordinates: TS_LATEST, no dynamic source + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + cfg.setEntityCoordinates(entityCoordinates); + + int maxRelationLevel = tbTenantProfileCache.get(tenantId) + .getDefaultProfileConfiguration() + .getMaxRelationLevelPerCfArgument(); + + // Zone-group argument (ATTRIBUTE) + ZoneGroupConfiguration zoneGroupConfiguration = new ZoneGroupConfiguration("allowed", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var dynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + + List levels = new ArrayList<>(); + for (int i = 0; i < maxRelationLevel + 1; i++) { + levels.add(mock(RelationPathLevel.class)); + } + + dynamicSourceConfiguration.setLevels(levels); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(dynamicSourceConfiguration); + cfg.setZoneGroups(Map.of("allowed", zoneGroupConfiguration)); + + AttributesOutput out = new AttributesOutput(); + out.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(out); + + // Create & save Calculated Field + CalculatedField cf = new CalculatedField(); + cf.setTenantId(tenantId); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("GF max relation level test"); + cf.setConfigurationVersion(0); + cf.setConfiguration(cfg); + + assertThatThrownBy(() -> calculatedFieldService.save(cf)) + .isInstanceOf(DataValidationException.class) + .hasCauseInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Max relation level is greater than configured maximum allowed relation level in tenant profile"); + } + + @Test + public void testSaveGeofencingCalculatedField_shouldSaveWithoutDataValidationExceptionOnScheduledUpdateInterval() { + // Arrange a device + Device device = createTestDevice(); + + // Build a valid Geofencing configuration + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + + // Coordinates: TS_LATEST, no dynamic source + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + cfg.setEntityCoordinates(entityCoordinates); + + // Zone-group argument (ATTRIBUTE) — make it DYNAMIC so scheduling is enabled + ZoneGroupConfiguration zoneGroupConfiguration = new ZoneGroupConfiguration("allowed", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var dynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + dynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE))); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(dynamicSourceConfiguration); + cfg.setZoneGroups(Map.of("allowed", zoneGroupConfiguration)); + + AttributesOutput out = new AttributesOutput(); + out.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(out); + + // Get tenant profile min. + int min = tbTenantProfileCache.get(tenantId) + .getDefaultProfileConfiguration() + .getMinAllowedScheduledUpdateIntervalInSecForCF(); + int valueFromConfig = min + 100; + + // Enable scheduling with an interval greater than tenant min + cfg.setScheduledUpdateEnabled(true); + cfg.setScheduledUpdateInterval(valueFromConfig); + + // Create & save Calculated Field + CalculatedField cf = new CalculatedField(); + cf.setTenantId(tenantId); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("GF no validation error test"); + cf.setConfigurationVersion(0); + cf.setConfiguration(cfg); + + CalculatedField saved = calculatedFieldService.save(cf); + + assertThat(saved).isNotNull(); + assertThat(saved.getConfiguration()).isInstanceOf(GeofencingCalculatedFieldConfiguration.class); + + var geofencingCalculatedFieldConfiguration = (GeofencingCalculatedFieldConfiguration) saved.getConfiguration(); + + int savedInterval = geofencingCalculatedFieldConfiguration.getScheduledUpdateInterval(); + assertThat(savedInterval).isEqualTo(valueFromConfig); + + calculatedFieldService.deleteCalculatedField(tenantId, saved.getId()); + } + @Test public void testSaveCalculatedFieldWithExistingName() { Device device = createTestDevice(); @@ -98,7 +245,7 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { assertThatThrownBy(() -> calculatedFieldService.save(calculatedField)) .isInstanceOf(DataValidationException.class) - .hasMessage("Calculated Field with such name is already in exists!"); + .hasMessage("Calculated field with such name and type already exists"); } @Test @@ -149,9 +296,8 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ConstraintValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ConstraintValidatorTest.java index 0609d4a57f..668400423d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/ConstraintValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ConstraintValidatorTest.java @@ -19,7 +19,7 @@ import org.junit.Assert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.thingsboard.server.common.data.kv.StringDataEntry; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; class ConstraintValidatorTest { @@ -47,4 +47,4 @@ class ConstraintValidatorTest { Assertions.assertTrue(MIN_IN_MS > end - start); } -} \ No newline at end of file +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/CustomerServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/CustomerServiceTest.java index a587646337..c9eadd2ed5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/CustomerServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/CustomerServiceTest.java @@ -35,17 +35,16 @@ import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; 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.AssetService; import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.util.ArrayList; import java.util.List; @@ -389,9 +388,8 @@ public class CustomerServiceTest extends AbstractServiceTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DashboardServiceTest.java index 478df46244..9f586503e4 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DashboardServiceTest.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.edge.EdgeService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.io.IOException; import java.util.ArrayList; 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 a4f102f4fd..899932fc6e 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 @@ -20,7 +20,7 @@ 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.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.spy; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceTest.java index 5f1c502011..bc932f3aef 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceTest.java @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; @DaoSqlTest public class DeviceCredentialsServiceTest extends AbstractServiceTest { 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 54c471fd1f..25cd63ec63 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 @@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import java.nio.ByteBuffer; 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 94745c804e..b9fd609521 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 @@ -16,9 +16,13 @@ package org.thingsboard.server.dao.service; import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; @@ -27,6 +31,8 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; @@ -43,10 +49,9 @@ import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; @@ -63,7 +68,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.service.validator.DeviceCredentialsDataValidator; @@ -75,7 +80,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; @@ -108,6 +116,17 @@ public class DeviceServiceTest extends AbstractServiceTest { private IdComparator idComparator = new IdComparator<>(); private TenantId anotherTenantId; + private static ListeningExecutorService executor; + + @BeforeClass + public static void beforeClass() { + executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10, ThingsBoardThreadFactory.forName("DeviceServiceTestScope"))); + } + + @AfterClass + public static void afterClass() { + executor.shutdownNow(); + } @Before public void before() { @@ -140,6 +159,31 @@ public class DeviceServiceTest extends AbstractServiceTest { deleteDevice(tenantId, device); } + @Test + public void testDeviceLimitOnTenantProfileLevel() throws InterruptedException { + TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId); + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxDevices(5l).build()); + tenantProfileService.saveTenantProfile(tenantId, defaultTenantProfile); + + for (int i = 0; i < 50; i++) { + executor.submit(() -> { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName(StringUtils.randomAlphabetic(10)); + device.setType("default"); + deviceService.saveDevice(device); + }); + } + + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + long countByTenantId = deviceService.countByTenantId(tenantId); + return countByTenantId == 5; + }); + + Thread.sleep(2000); + assertThat(deviceService.countByTenantId(tenantId)).isEqualTo(5); + } + @Test public void testSaveDevicesWithMaxDeviceOutOfLimit() { TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId); @@ -1248,9 +1292,8 @@ public class DeviceServiceTest extends AbstractServiceTest { config.setExpression("T - (100 - H) / 5"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("output"); - output.setType(OutputType.TIME_SERIES); config.setOutput(output); 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 8a2ee49ae3..d58c1c8080 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 @@ -40,7 +40,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.edge.EdgeService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceRegistryTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceRegistryTest.java index 525656bf59..a065939851 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceRegistryTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceRegistryTest.java @@ -20,7 +20,6 @@ import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.entity.EntityDaoService; import org.thingsboard.server.dao.entity.EntityServiceRegistry; import org.thingsboard.server.dao.rule.RuleChainService; @@ -45,8 +44,4 @@ public class EntityServiceRegistryTest extends AbstractServiceTest { Assert.assertTrue(entityServiceRegistry.getServiceByEntityType(EntityType.RULE_NODE) instanceof RuleChainService); } - @Test - public void givenCalculatedFieldLinkEntityType_whenGetServiceByEntityTypeCalled_thenReturnedCalculatedFieldService() { - Assert.assertTrue(entityServiceRegistry.getServiceByEntityType(EntityType.CALCULATED_FIELD_LINK) instanceof CalculatedFieldService); - } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ConfigTemplateServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ConfigTemplateServiceTest.java index 34f74f292b..4b8c7c0937 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ConfigTemplateServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ConfigTemplateServiceTest.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import java.util.Arrays; 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 a38499c82c..002a5a7845 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 @@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/QueueServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/QueueServiceTest.java index 764d1f8b3e..a2d54e7d37 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/QueueServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/QueueServiceTest.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.tenant.TenantProfileService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java index cf5fc67ef1..f9564df710 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java @@ -27,7 +27,7 @@ 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.queue.QueueStats; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.queue.QueueStatsService; import static org.assertj.core.api.Assertions.assertThat; 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 e7f6ba74c7..55fe46c06c 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 @@ -28,13 +28,16 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationPathQuery; 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.RelationPathLevel; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import java.util.ArrayList; import java.util.Collections; @@ -42,12 +45,17 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutionException; +import static org.assertj.core.api.Assertions.assertThat; + @DaoSqlTest public class RelationServiceTest extends AbstractServiceTest { @Autowired RelationService relationService; + @Autowired + private TbTenantProfileCache tbTenantProfileCache; + @Before public void before() { } @@ -348,14 +356,14 @@ public class RelationServiceTest extends AbstractServiceTest { query.setFilters(Collections.singletonList(new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); List relations = relationService.findByQuery(SYSTEM_TENANT_ID, query).get(); Assert.assertEquals(expected.size(), relations.size()); - for(EntityRelation r : expected){ + for (EntityRelation r : expected) { Assert.assertTrue(relations.contains(r)); } //Test from cache relations = relationService.findByQuery(SYSTEM_TENANT_ID, query).get(); Assert.assertEquals(expected.size(), relations.size()); - for(EntityRelation r : expected){ + for (EntityRelation r : expected) { Assert.assertTrue(relations.contains(r)); } } @@ -623,6 +631,114 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertTrue(relations.contains(relationF)); } + @Test + public void testFindByPathQueryWithoutExceedingLimit() throws Exception { + /* + A + └──[firstLevel, TO]→ B + └──[secondLevel, TO]→ C + ├──[thirdLevel, FROM]→ D1 + ├──[thirdLevel, FROM]→ D2 + ├──[thirdLevel, FROM]→ ... + └──[thirdLevel, FROM]→ D{N - 1}, where N is the limit + */ + AssetId assetA = new AssetId(Uuids.timeBased()); + AssetId assetB = new AssetId(Uuids.timeBased()); + AssetId assetC = new AssetId(Uuids.timeBased()); + + // create first and second level + saveRelation(new EntityRelation(assetB, assetA, "firstLevel")); + saveRelation(new EntityRelation(assetC, assetB, "secondLevel")); + + int limit = tbTenantProfileCache.get(tenantId) + .getDefaultProfileConfiguration() + .getMaxRelatedEntitiesToReturnPerCfArgument(); + + int totalCreated = limit - 1; + + List allThirdLevelRelations = new ArrayList<>(); + for (int i = 0; i < totalCreated; i++) { + AssetId leaf = new AssetId(Uuids.timeBased()); + allThirdLevelRelations.add(saveRelation(new EntityRelation(assetC, leaf, "thirdLevel"))); + } + + EntityRelationPathQuery query = new EntityRelationPathQuery(assetA, List.of( + new RelationPathLevel(EntitySearchDirection.TO, "firstLevel"), + new RelationPathLevel(EntitySearchDirection.TO, "secondLevel"), + new RelationPathLevel(EntitySearchDirection.FROM, "thirdLevel") + )); + + // call a method that applies the default limit internally + List result = relationService.findByRelationPathQueryAsync(tenantId, query).get(); + + // verify that limit has been applied + assertThat(result).hasSize(totalCreated); + + // verify all returned are valid third-level relations under C + assertThat(result) + .allSatisfy(rel -> { + assertThat(rel.getType()).isEqualTo("thirdLevel"); + assertThat(rel.getFrom()).isEqualTo(assetC); + }); + + // verify the returned subset is part of all created relations + assertThat(result).isEqualTo(allThirdLevelRelations); + } + + @Test + public void testFindByPathQueryWithExceedingLimit() throws Exception { + /* + A + └──[firstLevel, TO]→ B + └──[secondLevel, TO]→ C + ├──[thirdLevel, FROM]→ D1 + ├──[thirdLevel, FROM]→ D2 + ├──[thirdLevel, FROM]→ ... + └──[thirdLevel, FROM]→ D{N + 20}, where N is the limit + */ + AssetId assetA = new AssetId(Uuids.timeBased()); + AssetId assetB = new AssetId(Uuids.timeBased()); + AssetId assetC = new AssetId(Uuids.timeBased()); + + // create first and second level + saveRelation(new EntityRelation(assetB, assetA, "firstLevel")); + saveRelation(new EntityRelation(assetC, assetB, "secondLevel")); + + int limit = tbTenantProfileCache.get(tenantId) + .getDefaultProfileConfiguration() + .getMaxRelatedEntitiesToReturnPerCfArgument(); + + int totalCreated = limit + 20; + + List allThirdLevelRelations = new ArrayList<>(); + for (int i = 0; i < totalCreated; i++) { + AssetId leaf = new AssetId(Uuids.timeBased()); + allThirdLevelRelations.add(saveRelation(new EntityRelation(assetC, leaf, "thirdLevel"))); + } + + EntityRelationPathQuery query = new EntityRelationPathQuery(assetA, List.of( + new RelationPathLevel(EntitySearchDirection.TO, "firstLevel"), + new RelationPathLevel(EntitySearchDirection.TO, "secondLevel"), + new RelationPathLevel(EntitySearchDirection.FROM, "thirdLevel") + )); + + // call a method that applies the default limit internally + List result = relationService.findByRelationPathQueryAsync(tenantId, query).get(); + + // verify that limit has been applied + assertThat(result).hasSize(limit); + + // verify all returned are valid third-level relations under C + assertThat(result) + .allSatisfy(rel -> { + assertThat(rel.getType()).isEqualTo("thirdLevel"); + assertThat(rel.getFrom()).isEqualTo(assetC); + }); + + // verify the returned subset is part of all created relations + assertThat(result).isSubsetOf(allThirdLevelRelations); + } + @Test public void testFindByQueryLargeHierarchyFetchAllWithUnlimLvl() throws Exception { AssetId rootAsset = 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 ca415ea6dc..3bfb2d4c81 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 @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.edge.EdgeService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; 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 49661c747f..d3436fa9d1 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 @@ -38,7 +38,7 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; import org.thingsboard.server.common.util.ProtoUtils; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.gen.transport.TransportProtos; 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 e9f5b4d16f..1873fa9ea9 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 @@ -62,7 +62,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; 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.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; 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 a3ed1e1ceb..030d6c3bfd 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 @@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.user.UserService; import java.util.ArrayList; 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 c1052865cc..dee5aee6a6 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 @@ -27,7 +27,7 @@ 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; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; 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 3d2ad93538..e7b16a2cb2 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 @@ -25,7 +25,7 @@ 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.common.data.widget.WidgetsBundleFilter; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.widget.WidgetsBundleService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/attributes/BaseAttributesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/attributes/BaseAttributesServiceTest.java index 57dee96462..d3aa0fde0f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/attributes/BaseAttributesServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/attributes/BaseAttributesServiceTest.java @@ -223,7 +223,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest { saveAttribute(tenantId, deviceId, AttributeScope.SERVER_SCOPE, "key2", "123"); Awaitility.await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { - List keys = attributesService.findAllKeysByEntityIds(tenantId, List.of(deviceId), AttributeScope.SERVER_SCOPE.name()); + List keys = attributesService.findAllKeysByEntityIds(tenantId, List.of(deviceId), AttributeScope.SERVER_SCOPE); assertThat(keys).containsOnly("key1", "key2"); }); } 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 index 540d25578a..eb46a87018 100644 --- 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 @@ -27,7 +27,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TenantService; import java.util.UUID; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidatorTest.java index 221782b23c..3558f19cb3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidatorTest.java @@ -17,14 +17,14 @@ 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.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.cf.CalculatedFieldDao; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.usagerecord.DefaultApiLimitService; import java.util.UUID; @@ -38,11 +38,11 @@ public class CalculatedFieldDataValidatorTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("7b5229e9-166e-41a9-a257-3b1dafad1b04")); private final CalculatedFieldId CALCULATED_FIELD_ID = new CalculatedFieldId(UUID.fromString("060fbe45-fbb2-4549-abf3-f72a6be3cb9f")); - @MockBean + @MockitoBean private CalculatedFieldDao calculatedFieldDao; - @MockBean + @MockitoBean private DefaultApiLimitService apiLimitService; - @SpyBean + @MockitoSpyBean private CalculatedFieldDataValidator validator; @Test diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidatorTest.java deleted file mode 100644 index 41d16a0670..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidatorTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright © 2016-2026 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF 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.cf.CalculatedFieldLink; -import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.cf.CalculatedFieldLinkDao; -import org.thingsboard.server.dao.exception.DataValidationException; - -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.BDDMockito.given; - -@SpringBootTest(classes = CalculatedFieldLinkDataValidator.class) -public class CalculatedFieldLinkDataValidatorTest { - - private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("2ba09d99-6143-43dc-b645-381fc0c43ebe")); - private final CalculatedFieldLinkId CALCULATED_FIELD_LINK_ID = new CalculatedFieldLinkId(UUID.fromString("a5609ef4-cb42-43ce-9b23-e090a4878d1c")); - - @MockBean - private CalculatedFieldLinkDao calculatedFieldLinkDao; - @SpyBean - private CalculatedFieldLinkDataValidator validator; - - @Test - public void testUpdateNonExistingCalculatedField() { - CalculatedFieldLink calculatedFieldLink = new CalculatedFieldLink(CALCULATED_FIELD_LINK_ID); - calculatedFieldLink.setCalculatedFieldId(new CalculatedFieldId(UUID.fromString("136477af-fd07-4498-b9c9-54fe50e82992"))); - - given(calculatedFieldLinkDao.findById(TENANT_ID, CALCULATED_FIELD_LINK_ID.getId())).willReturn(null); - - assertThatThrownBy(() -> validator.validateUpdate(TENANT_ID, calculatedFieldLink)) - .isInstanceOf(DataValidationException.class) - .hasMessage("Can't update non existing calculated field link!"); - } - -} 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 index 260114abd7..f0aacc91bd 100644 --- 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 @@ -27,7 +27,7 @@ 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.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TenantService; import java.util.UUID; 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 index f3f172a5ce..34523c1829 100644 --- 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 @@ -48,6 +48,9 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.verify; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.LWM2M_SERVER_MAX; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.NOT_USED_IDENTIFYING_LWM2M_SERVER_MAX; +import static org.thingsboard.server.common.data.device.credentials.lwm2m.Lwm2mServerIdentifier.PRIMARY_LWM2M_SERVER; @SpringBootTest(classes = DeviceProfileDataValidator.class) class DeviceProfileDataValidatorTest { @@ -74,8 +77,8 @@ class DeviceProfileDataValidatorTest { " \"clientOnlyObserveAfterConnect\": 1\n" + " }"; - private static final String msgErrorLwm2mRange = "LwM2M Server ShortServerId must be in range [1 - 65534]!"; - private static final String msgErrorBsRange = "Bootstrap Server ShortServerId must be in range [0 - 65535]!"; + private static final String msgErrorLwm2mRange = "LwM2M Server ShortServerId must be in range [" + PRIMARY_LWM2M_SERVER.getId() + " - " + LWM2M_SERVER_MAX.getId() + "]!"; + private static final String msgErrorBsRange = "Bootstrap Server ShortServerId must be null!"; private static final String msgErrorNotNull = " Server ShortServerId must not be null!"; private static final String host = "localhost"; private static final String hostBs = "localhost"; @@ -124,7 +127,7 @@ class DeviceProfileDataValidatorTest { @Test void testValidateDeviceProfile_Lwm2mBootstrap_ShortServerId_Ok() { Integer shortServerId = 123; - Integer shortServerIdBs = 0; + Integer shortServerIdBs = null; DeviceProfile deviceProfile = getDeviceProfile(shortServerId, shortServerIdBs); validator.validateDataImpl(tenantId, deviceProfile); @@ -132,8 +135,13 @@ class DeviceProfileDataValidatorTest { } @Test - void testValidateDeviceProfile_Lwm2mShortServerId_Ok_BootstrapShortServerId_null_Error() { - verifyValidationError(123, null, "Bootstrap" + msgErrorNotNull); + void testValidateDeviceProfile_Lwm2mShortServerId_Ok_BootstrapShortServerId_validate_0_to_null_Ok() { + Integer shortServerId = 123; + Integer shortServerIdBs = 0; + DeviceProfile deviceProfile = getDeviceProfile(shortServerId, shortServerIdBs); + + validator.validateDataImpl(tenantId, deviceProfile); + verify(validator).validateString("Device profile name", deviceProfile.getName()); } @Test @@ -153,7 +161,7 @@ class DeviceProfileDataValidatorTest { @Test void testValidateDeviceProfile_Lwm2mShortServerId_More_65534_Error_BootstrapShortServerId_Ok() { - verifyValidationError(65535, 111, msgErrorLwm2mRange); + verifyValidationError(NOT_USED_IDENTIFYING_LWM2M_SERVER_MAX.getId(), 111, msgErrorLwm2mRange); } @Test diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 60605e1710..821317b6b4 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -115,6 +115,9 @@ cache.specs.trendzSettings.maxSize=10000 cache.specs.aiModel.timeToLiveInMinutes=1440 cache.specs.aiModel.maxSize=10000 +cache.specs.apiKeys.timeToLiveInMinutes=1440 +cache.specs.apiKeys.maxSize=10000 + redis.connection.host=localhost redis.connection.port=6379 redis.connection.db=0 diff --git a/edqs/pom.xml b/edqs/pom.xml index d243802042..57b843bfb9 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 6d761b20d5..23c857204c 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard diff --git a/monitoring/src/main/conf/logback.xml b/monitoring/src/main/conf/logback.xml index bbc8e7ad95..50536a7110 100644 --- a/monitoring/src/main/conf/logback.xml +++ b/monitoring/src/main/conf/logback.xml @@ -19,20 +19,6 @@ - - ${pkg.logFolder}/${pkg.name}.log - - ${pkg.logFolder}/${pkg.name}.%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 @@ -40,11 +26,11 @@ - - + + + - diff --git a/monitoring/src/main/conf/tb-monitoring.conf b/monitoring/src/main/conf/tb-monitoring.conf index 653a25bb54..b4042f1144 100644 --- a/monitoring/src/main/conf/tb-monitoring.conf +++ b/monitoring/src/main/conf/tb-monitoring.conf @@ -14,9 +14,10 @@ # limitations under the License. # -export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=/var/log/tb-monitoring/gc.log:time,uptime,level,tags:filecount=10,filesize=10M" +export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=/var/log/tb-monitoring/gc.log:time,uptime,level,tags:filecount=3,filesize=10M" export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError" -export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" -export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10" +export JAVA_OPTS="$JAVA_OPTS -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" +export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10" +export JAVA_OPTS="$JAVA_OPTS -XX:+ExitOnOutOfMemoryError" export LOG_FILENAME=tb-monitoring.out export LOADER_PATH=/usr/share/tb-monitoring/conf diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/ThingsboardMonitoringApplication.java b/monitoring/src/main/java/org/thingsboard/monitoring/ThingsboardMonitoringApplication.java index 836d7de2d8..9218b78bea 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/ThingsboardMonitoringApplication.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/ThingsboardMonitoringApplication.java @@ -15,36 +15,43 @@ */ package org.thingsboard.monitoring; +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.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; +import org.springframework.context.event.ContextClosedEvent; import org.springframework.scheduling.annotation.EnableScheduling; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.monitoring.data.notification.InfoNotification; +import org.thingsboard.monitoring.notification.NotificationService; import org.thingsboard.monitoring.service.BaseMonitoringService; import org.thingsboard.monitoring.service.MonitoringEntityService; +import jakarta.annotation.PreDestroy; import java.util.List; import java.util.Map; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @SpringBootApplication @EnableScheduling @Slf4j +@RequiredArgsConstructor public class ThingsboardMonitoringApplication { - @Autowired - private List> monitoringServices; - @Autowired - private MonitoringEntityService entityService; + private final List> monitoringServices; + private final MonitoringEntityService entityService; + private final NotificationService notificationService; @Value("${monitoring.monitoring_rate_ms}") private int monitoringRateMs; + ScheduledExecutorService scheduler = ThingsBoardExecutors.newSingleThreadScheduledExecutor("monitoring"); + public static void main(String[] args) { new SpringApplicationBuilder(ThingsboardMonitoringApplication.class) .properties(Map.of("spring.config.name", "tb-monitoring")) @@ -56,12 +63,33 @@ public class ThingsboardMonitoringApplication { entityService.checkEntities(); monitoringServices.forEach(BaseMonitoringService::init); - ScheduledExecutorService scheduler = ThingsBoardExecutors.newSingleThreadScheduledExecutor("monitoring-executor"); - scheduler.scheduleWithFixedDelay(() -> { - monitoringServices.forEach(monitoringService -> { - monitoringService.runChecks(); - }); - }, 0, monitoringRateMs, TimeUnit.MILLISECONDS); + for (int i = 0; i < monitoringServices.size(); i++) { + int initialDelay = (monitoringRateMs / monitoringServices.size()) * i; + BaseMonitoringService service = monitoringServices.get(i); + log.info("Scheduling initialDelay {}, fixedDelay {} for monitoring '{}' ", initialDelay, monitoringRateMs, service.getClass().getSimpleName()); + scheduler.scheduleWithFixedDelay(service::runChecks, initialDelay, monitoringRateMs, TimeUnit.MILLISECONDS); + } + + String publicDashboardUrl = entityService.getDashboardPublicLink(); + notificationService.sendNotification(new InfoNotification(":rocket: <"+publicDashboardUrl+"|Monitoring> started")); + } + + @EventListener(ContextClosedEvent.class) + public void onShutdown(ContextClosedEvent event) { + log.info("Shutting down monitoring service"); + try { + var futures = notificationService.sendNotification(new InfoNotification(":warning: Monitoring is shutting down")); + for (Future future : futures) { + future.get(5, TimeUnit.SECONDS); + } + } catch (Exception e) { + log.warn("Failed to send shutdown notification", e); + } + } + + @PreDestroy + public void shutdownScheduler() { + scheduler.shutdown(); } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/InfoNotification.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/InfoNotification.java new file mode 100644 index 0000000000..b2b73414de --- /dev/null +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/InfoNotification.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.monitoring.data.notification; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class InfoNotification implements Notification { + private final String message; + @Override + public String getText() { + return message; + } +} diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/notification/NotificationService.java b/monitoring/src/main/java/org/thingsboard/monitoring/notification/NotificationService.java index 1d18ab9063..e813b3205d 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/notification/NotificationService.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/notification/NotificationService.java @@ -22,10 +22,13 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.monitoring.data.notification.Notification; import org.thingsboard.monitoring.notification.channels.NotificationChannel; +import jakarta.annotation.PreDestroy; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; @Component @RequiredArgsConstructor @@ -38,22 +41,38 @@ public class NotificationService { @Value("${monitoring.notifications.message_prefix}") private String messagePrefix; - public void sendNotification(Notification notification) { + public List> sendNotification(Notification notification) { String message; if (StringUtils.isEmpty(messagePrefix)) { message = notification.getText(); } else { - message = messagePrefix + System.lineSeparator() + notification.getText(); + message = messagePrefix + " " + notification.getText(); } - notificationChannels.forEach(notificationChannel -> { + return notificationChannels.stream().map(notificationChannel -> notificationExecutor.submit(() -> { try { notificationChannel.sendNotification(message); } catch (Exception e) { log.error("Failed to send notification to {}", notificationChannel.getClass().getSimpleName(), e); } - }); - }); + }) + ).toList(); } + @PreDestroy + public void shutdownExecutor() { + try { + notificationExecutor.shutdown(); + if (!notificationExecutor.awaitTermination(10, TimeUnit.SECONDS)) { + var dropped = notificationExecutor.shutdownNow(); + log.warn("Notification executor did not terminate in time. Forced shutdown; {} task(s) will not be executed.", dropped.size()); + } + } catch (InterruptedException e) { + var dropped = notificationExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + log.warn("Interrupted during notification executor shutdown. Forced shutdown; {} task(s) will not be executed.", dropped.size()); + } catch (Exception e) { + log.warn("Unexpected error while shutting down notification executor", e); + } + } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringEntityService.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringEntityService.java index 57fd7d280d..d2c275945a 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringEntityService.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringEntityService.java @@ -30,20 +30,25 @@ import org.thingsboard.monitoring.config.transport.TransportMonitoringTarget; import org.thingsboard.monitoring.config.transport.TransportType; import org.thingsboard.monitoring.util.ResourceUtils; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DashboardInfo; 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.ShortCustomerInfo; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.cf.CalculatedField; +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.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecBootstrapClientCredential; @@ -57,7 +62,6 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTra import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.kv.KvEntry; -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.rule.RuleChainType; @@ -66,6 +70,8 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static org.thingsboard.monitoring.service.BaseHealthChecker.TEST_CF_TELEMETRY_KEY; @@ -76,11 +82,16 @@ import static org.thingsboard.monitoring.service.BaseHealthChecker.TEST_TELEMETR @RequiredArgsConstructor public class MonitoringEntityService { + private static final String DASHBOARD_TITLE = "[Monitoring] Cloud monitoring"; + private static final String DASHBOARD_RESOURCE_PATH = "dashboard_cloud_monitoring.json"; + private final TbClient tbClient; @Value("${monitoring.calculated_fields.enabled:true}") private boolean calculatedFieldsMonitoringEnabled; + DashboardId dashboardId = null; + public void checkEntities() { RuleChain ruleChain = tbClient.getRuleChains(RuleChainType.CORE, new PageLink(10)).getData().stream() .filter(RuleChain::isRoot) @@ -95,27 +106,34 @@ public class MonitoringEntityService { int currentVersion = Integer.parseInt(attributes.getOrDefault("version", "0")); int newVersion = ruleChainDescriptor.get("version").asInt(); if (currentVersion == newVersion) { - log.info("Not updating rule chain, version is the same ({})", currentVersion); - return; + log.debug("Not updating rule chain, version is the same ({})", currentVersion); } else { log.info("Updating rule chain '{}' from version {} to {}", ruleChain.getName(), currentVersion, newVersion); + + String metadataJson = RegexUtils.replace(ruleChainDescriptor.get("metadata").toString(), + "\\$\\{MONITORING:(.+?)}", matchResult -> { + String key = matchResult.group(1); + String value = attributes.get(key); + if (value == null) { + throw new IllegalArgumentException("No attribute found for key " + key); + } + log.info("Using {}: {}", key, value); + return value; + }); + RuleChainMetaData metaData = JacksonUtil.fromString(metadataJson, RuleChainMetaData.class); + metaData.setRuleChainId(ruleChainId); + tbClient.saveRuleChainMetaData(metaData); + tbClient.saveEntityAttributesV2(ruleChainId, DataConstants.SERVER_SCOPE, JacksonUtil.newObjectNode() + .put("version", newVersion)); } - String metadataJson = RegexUtils.replace(ruleChainDescriptor.get("metadata").toString(), - "\\$\\{MONITORING:(.+?)}", matchResult -> { - String key = matchResult.group(1); - String value = attributes.get(key); - if (value == null) { - throw new IllegalArgumentException("No attribute found for key " + key); - } - log.info("Using {}: {}", key, value); - return value; - }); - RuleChainMetaData metaData = JacksonUtil.fromString(metadataJson, RuleChainMetaData.class); - metaData.setRuleChainId(ruleChainId); - tbClient.saveRuleChainMetaData(metaData); - tbClient.saveEntityAttributesV2(ruleChainId, DataConstants.SERVER_SCOPE, JacksonUtil.newObjectNode() - .put("version", newVersion)); + Asset asset = getOrCreateMonitoringAsset(); + Dashboard dashboard = getOrCreateMonitoringDashboard(); + + tbClient.assignAssetToPublicCustomer(asset.getId()); + tbClient.assignDashboardToPublicCustomer(dashboard.getId()); + + this.dashboardId = Optional.ofNullable(dashboard).map(Dashboard::getId).orElse(null); } public Asset getOrCreateMonitoringAsset() { @@ -241,12 +259,77 @@ public class MonitoringEntityService { TEST_TELEMETRY_KEY, testDataArgument )); configuration.setExpression("return { \"" + TEST_CF_TELEMETRY_KEY + "\": " + TEST_TELEMETRY_KEY + " + \"-cf\" };"); - Output output = new Output(); - output.setType(OutputType.TIME_SERIES); - configuration.setOutput(output); + configuration.setOutput(new TimeSeriesOutput()); calculatedField.setConfiguration(configuration); calculatedField.setDebugMode(true); tbClient.saveCalculatedField(calculatedField); } + public String getDashboardPublicLink() { + String link = ""; + try { + Optional infoOpt = tbClient.getDashboardInfoById(dashboardId); + if (infoOpt.isPresent()) { + String publicCustomerId = null; + Set customers = infoOpt.get().getAssignedCustomers(); + if (customers != null) { + publicCustomerId = customers.stream() + .filter(ShortCustomerInfo::isPublic) + .map(c -> c.getCustomerId().getId().toString()) + .findFirst().orElse(null); + } + if (publicCustomerId != null) { + link = buildPublicDashboardLink(dashboardId, publicCustomerId); + log.info("Public Monitoring dashboard link: {}", link); + } else { + log.warn("Dashboard is not assigned to public customer. Public link can't be generated."); + } + } + } catch (Exception e) { + log.error("Failed to get a public link to Monitoring dashboard ", e); + } + return link; + } + + private Dashboard getOrCreateMonitoringDashboard() { + Dashboard existing = findDashboardByTitle(DASHBOARD_TITLE).orElse(null); + if (existing != null) { + log.debug("Found Monitoring dashboard '{}' with id {}", existing.getTitle(), existing.getId()); + return existing; + } + + Dashboard dashboardFromResource = ResourceUtils.getResource(DASHBOARD_RESOURCE_PATH, Dashboard.class); + dashboardFromResource.setTitle(DASHBOARD_TITLE); + //Optional.ofNullable(existing).map(Dashboard::getId).ifPresent(dashboardFromResource::setId); + Dashboard saved = tbClient.saveDashboard(dashboardFromResource); + log.info("Created Monitoring dashboard '{}' with id {}", saved.getTitle(), saved.getId()); + return saved; + } + + private Optional findDashboardByTitle(String title) { + // Use text search first and then filter by exact title + PageData page = tbClient.getTenantDashboards(new PageLink(10, 0, title)); + return page.getData().stream() + .filter(info -> title.equals(info.getTitle())) + .findFirst() + .flatMap(info -> tbClient.getDashboardById(info.getId())); + } + + private String buildPublicDashboardLink(DashboardId dashboardId, String publicCustomerId) { + String base = getBaseUrl(); + return String.format("%s/dashboard/%s?publicId=%s", base, dashboardId.getId().toString(), publicCustomerId); + } + + private String getBaseUrl() { + // TbClient.baseURL contains the root url, without trailing slash + try { + var baseUrlField = tbClient.getClass().getSuperclass().getDeclaredField("baseURL"); + baseUrlField.setAccessible(true); + return (String) baseUrlField.get(tbClient); + } catch (Exception e) { + log.warn("Unable to access baseURL from RestClient. Falling back to http://localhost:8080"); + return "http://localhost:8080"; + } + } + } diff --git a/monitoring/src/main/resources/dashboard_cloud_monitoring.json b/monitoring/src/main/resources/dashboard_cloud_monitoring.json new file mode 100644 index 0000000000..8be9677c61 --- /dev/null +++ b/monitoring/src/main/resources/dashboard_cloud_monitoring.json @@ -0,0 +1,580 @@ +{ + "title": "Cloud - Monitoring", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "7db7f580-2aac-d7ee-20f6-3d9315e7003f": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "6451461f-d748-9528-e3cd-64078c7fae05", + "filterId": null, + "dataKeys": [ + { + "name": "arrivalLatency", + "type": "timeseries", + "label": "Arrival latency", + "color": "#ffc107", + "settings": {}, + "_hash": 0.7424467450112592, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "processingTime", + "type": "timeseries", + "label": "Processing time", + "color": "#607d8b", + "settings": {}, + "_hash": 0.6065128737901203, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "mqttTransportWsUpdateLatency", + "type": "timeseries", + "label": "MQTT - overall", + "color": "#f44336", + "settings": {}, + "_hash": 0.19638031054010696, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "coapTransportWsUpdateLatency", + "type": "timeseries", + "label": "CoAP - overall", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.8167481665043521, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "httpTransportWsUpdateLatency", + "type": "timeseries", + "label": "HTTP - overall", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.45369210935861803, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "lwm2mTransportWsUpdateLatency", + "type": "timeseries", + "label": "LwM2M - overall", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.3477627917073993, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "coapIntegrationWsUpdateLatency", + "type": "timeseries", + "label": "CoAP integration - overall", + "color": "#e91e63", + "settings": {}, + "_hash": 0.8332881819050183, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "wsSubscribeLatency", + "type": "timeseries", + "label": "WS subscribe latency", + "color": "#03a9f4", + "settings": {}, + "_hash": 0.7843180730206556, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "wsConnectLatency", + "type": "timeseries", + "label": "WS connect latency", + "color": "#4caf50", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 1, + "fillLines": false, + "showPoints": false, + "tooltipValueFormatter": "", + "showSeparateAxis": false, + "axisTitle": "", + "axisMin": null, + "axisMax": null, + "axisPosition": "left", + "axisTickSize": null, + "axisTickDecimals": null, + "axisTicksFormatter": "", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.23140687573220564, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "logInLatency", + "type": "timeseries", + "label": "Log in latency", + "color": "#2196f3", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 1, + "fillLines": false, + "showPoints": false, + "tooltipValueFormatter": "", + "showSeparateAxis": false, + "axisTitle": "", + "axisMin": null, + "axisMax": null, + "axisPosition": "left", + "axisTickSize": null, + "axisTickDecimals": null, + "axisTicksFormatter": "", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.6130919996800452, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "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, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [], + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": true, + "showAvg": true, + "showTotal": false, + "showLatest": true + } + }, + "title": "General latencies", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": true, + "displayTimewindow": true + }, + "row": 0, + "col": 0, + "id": "7db7f580-2aac-d7ee-20f6-3d9315e7003f", + "typeFullFqn": "system.charts.basic_timeseries" + }, + "e0a2df43-2f9a-efcf-80f7-bf51b0a174e1": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "6451461f-d748-9528-e3cd-64078c7fae05", + "filterId": null, + "dataKeys": [ + { + "name": "mqttTransportRequestLatency", + "type": "timeseries", + "label": "MQTT transport request latency", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8576659620523571, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "httpTransportRequestLatency", + "type": "timeseries", + "label": "HTTP transport request latency", + "color": "#4caf50", + "settings": {}, + "_hash": 0.9749033105491403, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "coapTransportRequestLatency", + "type": "timeseries", + "label": "CoAP transport request latency", + "color": "#f44336", + "settings": {}, + "_hash": 0.6977575200148037, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "coapIntegrationRequestLatency", + "type": "timeseries", + "label": "CoAP integration request latency", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.18839685494200875, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "lwm2mTransportRequestLatency", + "type": "timeseries", + "label": "LwM2M transport request latency", + "color": "#ffc107", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 1, + "fillLines": false, + "showPoints": false, + "tooltipValueFormatter": "", + "showSeparateAxis": false, + "axisTitle": "", + "axisMin": null, + "axisMax": null, + "axisPosition": "left", + "axisTickSize": null, + "axisTickDecimals": null, + "axisTicksFormatter": "", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.85549409179514, + "aggregationType": null, + "units": "ms", + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "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, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": true, + "showAvg": true, + "showTotal": false, + "showLatest": true + } + }, + "title": "Transport latencies", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": true, + "displayTimewindow": true + }, + "row": 0, + "col": 0, + "id": "e0a2df43-2f9a-efcf-80f7-bf51b0a174e1", + "typeFullFqn": "system.charts.basic_timeseries" + } + }, + "states": { + "default": { + "name": "Cloud - Monitoring", + "root": true, + "layouts": { + "main": { + "widgets": { + "7db7f580-2aac-d7ee-20f6-3d9315e7003f": { + "sizeX": 12, + "sizeY": 11, + "row": 0, + "col": 0 + }, + "e0a2df43-2f9a-efcf-80f7-bf51b0a174e1": { + "sizeX": 12, + "sizeY": 11, + "row": 0, + "col": 12 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "outerMargin": true, + "layoutType": "default" + } + } + } + } + }, + "entityAliases": { + "6451461f-d748-9528-e3cd-64078c7fae05": { + "id": "6451461f-d748-9528-e3cd-64078c7fae05", + "alias": "Monitoring stats asset", + "filter": { + "type": "entityName", + "resolveMultiple": true, + "entityType": "ASSET", + "entityNameFilter": "[Monitoring] Latencies" + } + } + }, + "filters": {}, + "timewindow": { + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 120000, + "timewindowMs": 18000000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, + "history": { + "historyType": 0, + "interval": 600000, + "timewindowMs": 43200000, + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false + }, + "aggregation": { + "type": "AVG", + "limit": 2500 + }, + "timezone": null + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": true, + "showEntitiesSelect": true, + "showDashboardTimewindow": true, + "showDashboardExport": true, + "toolbarAlwaysOpen": true + } + }, + "name": "Cloud - Monitoring", + "resources": null +} \ No newline at end of file diff --git a/monitoring/src/main/resources/logback.xml b/monitoring/src/main/resources/logback.xml index e2b90894d1..50536a7110 100644 --- a/monitoring/src/main/resources/logback.xml +++ b/monitoring/src/main/resources/logback.xml @@ -17,7 +17,7 @@ --> - + @@ -34,5 +34,4 @@ - diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index a6a5408258..6f59dd3a3d 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java index f95978d5a5..ebf90bb7f0 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java @@ -26,9 +26,12 @@ import org.apache.http.ssl.SSLContexts; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Listeners; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; @@ -39,6 +42,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import java.net.URI; import java.util.Map; import java.util.Random; +import java.util.function.Consumer; @Slf4j @@ -186,4 +190,12 @@ public abstract class AbstractContainerTest { return testRestClient.postDeviceProfile(deviceProfile); } + protected void updateDefaultTenantProfile(Consumer updater) { + EntityInfo defaultTenantProfileInfo = testRestClient.getDefaultTenantProfileInfo(); + TenantProfile oldTenantProfile = testRestClient.getTenantProfileById(defaultTenantProfileInfo.getId().getId().toString()); + TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile); + updater.accept(tenantProfile); + testRestClient.postTenantProfile(tenantProfile); + } + } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java index 7b2cfc68d9..91c4927b3c 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java @@ -16,6 +16,7 @@ package org.thingsboard.server.msa; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.restassured.RestAssured; import io.restassured.common.mapper.TypeRef; @@ -33,10 +34,12 @@ 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.DeviceProfile; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EventInfo; 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.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.Asset; @@ -231,9 +234,9 @@ public class TestRestClient { .statusCode(anyOf(is(HTTP_OK), is(HTTP_NOT_FOUND))); } - public ValidatableResponse postTelemetryAttribute(EntityId entityId, String scope, JsonNode attribute) { + public ValidatableResponse postTelemetryAttribute(EntityId entityId, AttributeScope scope, JsonNode attribute) { return given().spec(requestSpec).body(attribute) - .post("/api/plugins/telemetry/{entityType}/{entityId}/attributes/{scope}", entityId.getEntityType(), entityId.getId(), scope) + .post("/api/plugins/telemetry/{entityType}/{entityId}/attributes/{scope}", entityId.getEntityType(), entityId.getId(), scope.name()) .then() .statusCode(HTTP_OK); } @@ -256,13 +259,40 @@ public class TestRestClient { .as(JsonNode.class); } - public JsonNode getAttributes(EntityId entityId, AttributeScope scope, String keys) { + public ArrayNode getAttributes(EntityId entityId, AttributeScope scope, String keys) { return given().spec(requestSpec) .get("/api/plugins/telemetry/{entityType}/{entityId}/values/attributes/{scope}?keys={keys}", entityId.getEntityType(), entityId.getId(), scope, keys) .then() .statusCode(HTTP_OK) .extract() - .as(JsonNode.class); + .as(ArrayNode.class); + } + + + public ValidatableResponse deleteEntityAttributes(EntityId entityId, AttributeScope scope, String keys) { + Map pathParams = new HashMap<>(); + pathParams.put("entityId", entityId.getId().toString()); + pathParams.put("entityType", entityId.getEntityType().name()); + pathParams.put("scope", scope.name()); + return given().spec(requestSpec) + .pathParams(pathParams) + .queryParam("keys", keys) + .delete("/api/plugins/telemetry/{entityType}/{entityId}/{scope}") + .then() + .statusCode(HTTP_OK); + } + + public ValidatableResponse deleteEntityTimeseries(EntityId entityId, String keys, boolean deleteAllDataForKeys) { + Map pathParams = new HashMap<>(); + pathParams.put("entityType", entityId.getEntityType().name()); + pathParams.put("entityId", entityId.getId().toString()); + return given().spec(requestSpec) + .pathParams(pathParams) + .queryParam("keys", keys) + .queryParam("deleteAllDataForKeys", Boolean.toString(deleteAllDataForKeys)) + .delete("/api/plugins/telemetry/{entityType}/{entityId}/timeseries/delete") + .then() + .statusCode(HTTP_OK); } public JsonNode getLatestTelemetry(EntityId entityId) { @@ -367,6 +397,33 @@ public class TestRestClient { }); } + public EntityRelation postEntityRelation(EntityRelation entityRelation) { + return given().spec(requestSpec) + .body(entityRelation) + .post("/api/v2/relation") + .then() + .statusCode(HTTP_OK) + .extract() + .as(EntityRelation.class); + } + + + public EntityRelation deleteEntityRelation(EntityId fromId, String relationType, EntityId toId) { + Map queryParams = new HashMap<>(); + queryParams.put("fromId", fromId.getId().toString()); + queryParams.put("fromType", fromId.getEntityType().name()); + queryParams.put("relationType", relationType); + queryParams.put("toId", toId.getId().toString()); + queryParams.put("toType", toId.getEntityType().name()); + return given().spec(requestSpec) + .queryParams(queryParams) + .delete("/api/v2/relation") + .then() + .statusCode(HTTP_OK) + .extract() + .as(EntityRelation.class); + } + public JsonNode postServerSideRpc(DeviceId deviceId, JsonNode serverRpcPayload) { return given().spec(requestSpec) .body(serverRpcPayload) @@ -428,7 +485,7 @@ public class TestRestClient { .as(DeviceProfile.class); } - public void deleteDeviseProfile(DeviceProfileId deviceProfileId) { + public void deleteDeviceProfile(DeviceProfileId deviceProfileId) { given().spec(requestSpec) .delete("/api/deviceProfile/{deviceProfileId}", deviceProfileId.getId()) .then() @@ -761,4 +818,33 @@ public class TestRestClient { .then() .statusCode(HTTP_OK); } + + public TenantProfile postTenantProfile(TenantProfile tenantProfile) { + return given().spec(requestSpec).body(tenantProfile) + .post("/api/tenantProfile") + .then() + .statusCode(HTTP_OK) + .extract() + .as(TenantProfile.class); + } + + public EntityInfo getDefaultTenantProfileInfo() { + return given().spec(requestSpec) + .get("/api/tenantProfileInfo/default") + .then() + .statusCode(HTTP_OK) + .extract() + .as(EntityInfo.class); + } + + public TenantProfile getTenantProfileById(String tenantProfileId) { + return given().spec(requestSpec) + .pathParams("tenantProfileId", tenantProfileId) + .get("/api/tenantProfile/{tenantProfileId}") + .then() + .statusCode(HTTP_OK) + .extract() + .as(TenantProfile.class); + } + } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java index 30a70d5288..4c5b228d3c 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java @@ -16,43 +16,68 @@ package org.thingsboard.server.msa.cf; import com.fasterxml.jackson.databind.JsonNode; -import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.apache.commons.lang3.RandomStringUtils; import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AttributeScope; -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.asset.Asset; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldEventType; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; -import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.AttributesOutput; +import org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.TimeSeriesOutput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunction; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggFunctionInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggKeyInput; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; +import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; +import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; +import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId; 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.page.SortOrder; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.msa.AbstractContainerTest; import org.thingsboard.server.msa.ui.utils.EntityPrototypes; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.thingsboard.server.common.data.AttributeScope.SERVER_SCOPE; +import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultAssetProfile; import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultDeviceProfile; import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultTenantAdmin; @@ -62,20 +87,22 @@ public class CalculatedFieldTest extends AbstractContainerTest { public final int TIMEOUT = 60; public final int POLL_INTERVAL = 1; - private final String deviceToken = "zmzURIVRsq3lvnTP2XBE"; + private final String exampleScript = """ + var avgTemperature = temperature.mean(); // Get average temperature + var temperatureK = (avgTemperature - 32) * (5 / 9) + 273.15; // Convert Fahrenheit to Kelvin + + // Estimate air pressure based on altitude + var pressure = 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588); + + // Air density formula + var airDensity = pressure / (287.05 * temperatureK); + + return { + "airDensity": toFixed(airDensity, 2) + } + """; - private final String exampleScript = "var avgTemperature = temperature.mean(); // Get average temperature\n" + - " var temperatureK = (avgTemperature - 32) * (5 / 9) + 273.15; // Convert Fahrenheit to Kelvin\n" + - "\n" + - " // Estimate air pressure based on altitude\n" + - " var pressure = 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588);\n" + - "\n" + - " // Air density formula\n" + - " var airDensity = pressure / (287.05 * temperatureK);\n" + - "\n" + - " return {\n" + - " \"airDensity\": toFixed(airDensity, 2)\n" + - " };"; + private final String deviceToken = "zmzURIVRsq3lvnTP2XBE"; private TenantId tenantId; private UserId tenantAdminId; @@ -88,28 +115,38 @@ public class CalculatedFieldTest extends AbstractContainerTest { public void beforeClass() { testRestClient.login("sysadmin@thingsboard.org", "sysadmin"); + updateDefaultTenantProfile(tenantProfile -> { + TenantProfileData profileData = tenantProfile.getProfileData(); + DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) profileData.getConfiguration(); + profileConfiguration.setMinAllowedDeduplicationIntervalInSecForCF(1); + profileConfiguration.setMinAllowedScheduledUpdateIntervalInSecForCF(1); + tenantProfile.setProfileData(profileData); + }); + tenantId = testRestClient.postTenant(EntityPrototypes.defaultTenantPrototype("Tenant")).getId(); tenantAdminId = testRestClient.createUserAndLogin(defaultTenantAdmin(tenantId, "tenantAdmin@thingsboard.org"), "tenant"); + } - deviceProfileId = testRestClient.postDeviceProfile(defaultDeviceProfile("Device Profile 1")).getId(); - device = testRestClient.postDevice(deviceToken, createDevice("Device 1", deviceProfileId)); + @BeforeMethod + public void beforeMethod() { + testRestClient.getAndSetUserToken(tenantAdminId); - assetProfileId = testRestClient.postAssetProfile(defaultAssetProfile("Asset Profile 1")).getId(); - asset = testRestClient.postAsset(createAsset("Asset 1", assetProfileId)); + deviceProfileId = testRestClient.postDeviceProfile(defaultDeviceProfile("Device Profile")).getId(); + device = testRestClient.postDevice(deviceToken, createDevice("Device", deviceProfileId)); - testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); - testRestClient.postTelemetryAttribute(device.getId(), DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"deviceTemperature\":40}")); + assetProfileId = testRestClient.postAssetProfile(defaultAssetProfile("Asset Profile")).getId(); + asset = testRestClient.postAsset(createAsset("Asset", assetProfileId)); + } - testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperatureInF\":72.32}")); - testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperatureInF\":72.86}")); - testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperatureInF\":73.58}")); + @AfterMethod + public void tearDown() { + testRestClient.getAndSetUserToken(tenantAdminId); - testRestClient.postTelemetryAttribute(asset.getId(), DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"altitude\":1035}")); - } + testRestClient.deleteDeviceIfExists(device.getId()); + testRestClient.deleteDeviceProfile(deviceProfileId); - @BeforeMethod - public void beforeMethod() { - testRestClient.login("sysadmin@thingsboard.org", "sysadmin"); + testRestClient.deleteAsset(asset.getId()); + testRestClient.deleteAssetProfile(assetProfileId); } @AfterClass @@ -121,12 +158,10 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testPerformInitialCalculationForSimpleType() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); - - CalculatedField savedCalculatedField = createSimpleCalculatedField(); - testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); + + CalculatedField savedCalculatedField = createSimpleCalculatedField(device.getId()); await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -142,13 +177,25 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testChangeConfigArgument() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); + testRestClient.postTelemetryAttribute(device.getId(), SERVER_SCOPE, JacksonUtil.toJsonNode("{\"deviceTemperature\":40}")); + + CalculatedField savedCalculatedField = createSimpleCalculatedField(device.getId()); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device.getId()); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("77.0"); + }); - CalculatedField savedCalculatedField = createSimpleCalculatedField(); + assertThat(savedCalculatedField.getConfiguration() instanceof SimpleCalculatedFieldConfiguration).isTrue(); - Argument savedArgument = savedCalculatedField.getConfiguration().getArguments().get("T"); - savedArgument.setRefEntityKey(new ReferencedEntityKey("deviceTemperature", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE)); + Argument savedArgument = ((SimpleCalculatedFieldConfiguration) savedCalculatedField.getConfiguration()).getArguments().get("T"); + savedArgument.setRefEntityKey(new ReferencedEntityKey("deviceTemperature", ArgumentType.ATTRIBUTE, SERVER_SCOPE)); testRestClient.postCalculatedField(savedCalculatedField); await().alias("update CF argument -> perform calculation with new argument").atMost(TIMEOUT, TimeUnit.SECONDS) @@ -165,21 +212,31 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testChangeConfigOutput() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); + + CalculatedField savedCalculatedField = createSimpleCalculatedField(device.getId()); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device.getId()); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("77.0"); + }); - CalculatedField savedCalculatedField = createSimpleCalculatedField(); + AttributesOutput output = new AttributesOutput(); + output.setScope(SERVER_SCOPE); + output.setName("temperatureF"); + ((SimpleCalculatedFieldConfiguration) savedCalculatedField.getConfiguration()).setOutput(output); - Output savedOutput = savedCalculatedField.getConfiguration().getOutput(); - savedOutput.setType(OutputType.ATTRIBUTES); - savedOutput.setScope(AttributeScope.SERVER_SCOPE); - savedOutput.setName("temperatureF"); testRestClient.postCalculatedField(savedCalculatedField); await().alias("update CF output -> perform calculation with updated output").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { - JsonNode temperatureF = testRestClient.getAttributes(device.getId(), AttributeScope.SERVER_SCOPE, "temperatureF"); + ArrayNode temperatureF = testRestClient.getAttributes(device.getId(), SERVER_SCOPE, "temperatureF"); assertThat(temperatureF).isNotNull(); assertThat(temperatureF.get(0)).isNotNull(); assertThat(temperatureF.get(0).get("value").asText()).isEqualTo("77.0"); @@ -190,13 +247,23 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testChangeConfigExpression() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); + + CalculatedField savedCalculatedField = createSimpleCalculatedField(device.getId()); + assertThat(savedCalculatedField.getConfiguration() instanceof SimpleCalculatedFieldConfiguration).isTrue(); - CalculatedField savedCalculatedField = createSimpleCalculatedField(); + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device.getId()); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("77.0"); + }); savedCalculatedField.setName("F to C"); - savedCalculatedField.getConfiguration().setExpression("(T - 32) / 1.8"); + ((SimpleCalculatedFieldConfiguration) savedCalculatedField.getConfiguration()).setExpression("(T - 32) / 1.8"); testRestClient.postCalculatedField(savedCalculatedField); await().alias("update CF expression -> perform calculation with new expression").atMost(TIMEOUT, TimeUnit.SECONDS) @@ -213,10 +280,19 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testTelemetryUpdated() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); + + CalculatedField savedCalculatedField = createSimpleCalculatedField(device.getId()); - CalculatedField savedCalculatedField = createSimpleCalculatedField(); + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device.getId()); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("77.0"); + }); testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":30}")); @@ -234,8 +310,8 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testEntityIdIsProfile() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); CalculatedField savedCalculatedField = createSimpleCalculatedField(deviceProfileId); @@ -253,46 +329,61 @@ public class CalculatedFieldTest extends AbstractContainerTest { @Test public void testEntityAddedAndDeleted() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + waitInitialTelemetry(device.getId(), "temperature"); CalculatedField savedCalculatedField = createSimpleCalculatedField(deviceProfileId); - String newDeviceToken = "mmmXRIVRsq9lbnTP2XBE"; - Device newDevice = testRestClient.postDevice(newDeviceToken, createDevice("Device 2", deviceProfileId)); + await().alias("create CF -> perform initial calculation for device by profile").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device.getId()); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("77.0"); + }); + + String deviceToken2 = "beeUmKVqsq3lvnovt6BE"; + Device device2 = testRestClient.postDevice(deviceToken2, createDevice("Device 2", deviceProfileId)); await().alias("create device by profile -> perform initial calculation for new device by profile").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // used default value since telemetry is not present - JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(newDevice.getId()); + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device2.getId()); assertThat(fahrenheitTemp).isNotNull(); assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("53.6"); }); DeviceProfile newDeviceProfile = testRestClient.postDeviceProfile(defaultDeviceProfile("Test Profile")); - newDevice.setDeviceProfileId(newDeviceProfile.getId()); - testRestClient.postDevice(newDeviceToken, newDevice); + device2.setDeviceProfileId(newDeviceProfile.getId()); + testRestClient.postDevice(deviceToken2, device2); - testRestClient.postTelemetry(newDeviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + testRestClient.postTelemetry(deviceToken2, JacksonUtil.toJsonNode("{\"temperature\":25}")); await().alias("update telemetry -> no updates").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { - JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(newDevice.getId()); + JsonNode fahrenheitTemp = testRestClient.getLatestTelemetry(device2.getId()); assertThat(fahrenheitTemp).isNotNull(); assertThat(fahrenheitTemp.get("fahrenheitTemp")).isNotNull(); assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("53.6"); }); testRestClient.deleteCalculatedFieldIfExists(savedCalculatedField.getId()); + testRestClient.deleteDeviceIfExists(device2.getId()); + testRestClient.deleteDeviceProfileIfExists(newDeviceProfile); } @Test public void testEntityIdIsProfileAndRefEntityIsCommon() { - // login tenant admin - testRestClient.getAndSetUserToken(tenantAdminId); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperatureInF\":72.32}")); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperatureInF\":72.86}")); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperatureInF\":73.58}")); + waitInitialTelemetry(device.getId(), "temperatureInF"); + + testRestClient.postTelemetryAttribute(asset.getId(), SERVER_SCOPE, JacksonUtil.toJsonNode("{\"altitude\":1035}")); CalculatedField savedCalculatedField = createScriptCalculatedField(deviceProfileId, asset.getId()); @@ -305,7 +396,7 @@ public class CalculatedFieldTest extends AbstractContainerTest { assertThat(airDensity.get("airDensity").get(0).get("value").asText()).isEqualTo("1.05"); }); - testRestClient.postTelemetryAttribute(asset.getId(), DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"altitude\":1531}")); + testRestClient.postTelemetryAttribute(asset.getId(), SERVER_SCOPE, JacksonUtil.toJsonNode("{\"altitude\":1531}")); await().alias("create CF -> update telemetry for common entity").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -319,15 +410,520 @@ public class CalculatedFieldTest extends AbstractContainerTest { testRestClient.deleteCalculatedFieldIfExists(savedCalculatedField.getId()); } - private CalculatedField createSimpleCalculatedField() { - return createSimpleCalculatedField(device.getId()); + @Test + public void testPerformSerialsOfCalculationsForGeofencingType() { + // Device and initial coords (inside Allowed, outside Restricted) + String deviceToken = "geoDeviceTokenA"; + Device device = testRestClient.postDevice(deviceToken, createDevice("GF Device", deviceProfileId)); + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"latitude\":50.4730,\"longitude\":30.5050}")); + + // Create zones + Asset allowed = testRestClient.postAsset(createAsset("Allowed Zone", null)); + testRestClient.postTelemetryAttribute(allowed.getId(), SERVER_SCOPE, + JacksonUtil.toJsonNode("{\"zone\":[[50.472000,30.504000],[50.472000,30.506000],[50.474000,30.506000],[50.474000,30.504000]]}")); + + Asset restricted = testRestClient.postAsset(createAsset("Restricted Zone", null)); + testRestClient.postTelemetryAttribute(restricted.getId(), SERVER_SCOPE, + JacksonUtil.toJsonNode("{\"zone\":[[50.475000,30.510000],[50.475000,30.512000],[50.477000,30.512000],[50.477000,30.510000]]}")); + + // Relations FROM device + testRestClient.postEntityRelation(new EntityRelation(device.getId(), allowed.getId(), "AllowedZone")); + testRestClient.postEntityRelation(new EntityRelation(device.getId(), restricted.getId(), "RestrictedZone")); + + // Build CF: GEOFENCING -> attributes output + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.GEOFENCING); + cf.setName("Geofencing CF"); + cf.setDebugSettings(DebugSettings.off()); + + GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); + + EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); + cfg.setEntityCoordinates(entityCoordinates); + + // Dynamic groups via relations + ZoneGroupConfiguration allowedZoneGroupConfiguration = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var allowedDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "AllowedZone"))); + allowedZoneGroupConfiguration.setRefDynamicSourceConfiguration(allowedDynamicSourceConfiguration); + + ZoneGroupConfiguration restrictedZoneGroupConfiguration = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); + var restrictedDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + restrictedDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "RestrictedZone"))); + restrictedZoneGroupConfiguration.setRefDynamicSourceConfiguration(restrictedDynamicSourceConfiguration); + + cfg.setZoneGroups(Map.of("allowedZones", allowedZoneGroupConfiguration, "restrictedZones", restrictedZoneGroupConfiguration)); + + AttributesOutput out = new AttributesOutput(); + out.setScope(SERVER_SCOPE); + cfg.setOutput(out); + cf.setConfiguration(cfg); + + CalculatedField saved = testRestClient.postCalculatedField(cf); + + // Initial ENTERED/INSIDE and OUTSIDE + await().alias("initial geofencing evaluation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = testRestClient.getAttributes(device.getId(), SERVER_SCOPE, + "allowedZonesEvent,allowedZonesStatus,restrictedZonesEvent,restrictedZonesStatus"); + assertThat(attrs).isNotNull().hasSize(2); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesStatus", "INSIDE") + .containsEntry("restrictedZonesStatus", "OUTSIDE"); + }); + + // Move device into Restricted zone -> expect LEFT/ENTERED and statuses flipped + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"latitude\":50.4760,\"longitude\":30.5110}")); + + await().alias("transition after movement").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs = testRestClient.getAttributes(device.getId(), SERVER_SCOPE, + "allowedZonesEvent,allowedZonesStatus,restrictedZonesEvent,restrictedZonesStatus"); + assertThat(attrs).isNotNull().hasSize(4); + Map m = kv(attrs); + assertThat(m).containsEntry("allowedZonesEvent", "LEFT") + .containsEntry("allowedZonesStatus", "OUTSIDE") + .containsEntry("restrictedZonesEvent", "ENTERED") + .containsEntry("restrictedZonesStatus", "INSIDE"); + }); + + testRestClient.deleteCalculatedFieldIfExists(saved.getId()); + testRestClient.deleteDeviceIfExists(device.getId()); + testRestClient.deleteAsset(allowed.getId()); + testRestClient.deleteAsset(restricted.getId()); + } + + @Test + public void testPropagationCalculatedField_withExpression() { + // --- Arrange entities --- + String deviceToken = "propagationDeviceTokenA"; + Device device = testRestClient.postDevice(deviceToken, createDevice("Propagation Device With Expression", deviceProfileId)); + Asset asset1 = testRestClient.postAsset(createAsset("Propagated Asset 1", null)); + Asset asset2 = testRestClient.postAsset(createAsset("Propagated Asset 2", null)); + + // Create relations FROM assets TO device + EntityRelation rel1 = new EntityRelation(asset1.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + EntityRelation rel2 = new EntityRelation(asset2.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel1); + testRestClient.postEntityRelation(rel2); + + // Telemetry on device + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":12.5}")); + + // --- Build CF: PROPAGATION with expression --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.PROPAGATION); + cf.setName("Propagation CF (expr)"); + cf.setConfigurationVersion(1); + + PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + cfg.setApplyExpressionToResolvedArguments(true); + + Argument arg = new Argument(); + arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + cfg.setArguments(Map.of("t", arg)); + + cfg.setExpression("{\"testResult\": t * 2}"); + + AttributesOutput output = new AttributesOutput(); + output.setScope(AttributeScope.SERVER_SCOPE); + cfg.setOutput(output); + + cf.setConfiguration(cfg); + + CalculatedField saved = testRestClient.postCalculatedField(cf); + + // --- Assert propagated calculation (expression applied) --- + await().alias("propagation expr mode evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs1 = testRestClient.getAttributes(asset1.getId(), SERVER_SCOPE, "testResult"); + assertThat(attrs1).isNotNull().hasSize(1); + Map m1 = intKv(attrs1); + assertThat(m1).containsEntry("testResult", 25); + + ArrayNode attrs2 = testRestClient.getAttributes(asset2.getId(), SERVER_SCOPE, "testResult"); + assertThat(attrs2).isNotNull().hasSize(1); + Map m2 = intKv(attrs2); + assertThat(m2).containsEntry("testResult", 25); + }); + + testRestClient.deleteEntityRelation(asset1.getId(), EntityRelation.CONTAINS_TYPE, device.getId()); + testRestClient.deleteEntityAttributes(asset1.getId(), SERVER_SCOPE, "testResult"); + + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25}")); + + // --- Assert propagated calculation (expression applied with new temperature argument and one relation removed) --- + await().alias("propagation expr mode evaluation after temperature update") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ArrayNode attrs1 = testRestClient.getAttributes(asset1.getId(), SERVER_SCOPE, "testResult"); + assertThat(attrs1).isNullOrEmpty(); + + ArrayNode attrs2 = testRestClient.getAttributes(asset2.getId(), SERVER_SCOPE, "testResult"); + assertThat(attrs2).isNotNull().hasSize(1); + Map m2 = intKv(attrs2); + assertThat(m2).containsEntry("testResult", 50); + }); + + testRestClient.deleteCalculatedFieldIfExists(saved.getId()); + testRestClient.deleteDeviceIfExists(device.getId()); + testRestClient.deleteAsset(asset1.getId()); + testRestClient.deleteAsset(asset2.getId()); + } + + @Test + public void testPropagationCalculatedField_withoutExpression() { + // --- Arrange entities --- + String deviceToken = "propagationDeviceTokenB"; + Device device = testRestClient.postDevice(deviceToken, createDevice("Propagation Device Without Expression", deviceProfileId)); + Asset asset1 = testRestClient.postAsset(createAsset("Propagated Asset 3", null)); + Asset asset2 = testRestClient.postAsset(createAsset("Propagated Asset 4", null)); + + // Create relations FROM assets TO device + EntityRelation rel1 = new EntityRelation(asset1.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + EntityRelation rel2 = new EntityRelation(asset2.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel1); + testRestClient.postEntityRelation(rel2); + + // Telemetry on device + long ts = System.currentTimeMillis() - 300000L; + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":12.5}}", ts))); + + // --- Build CF: PROPAGATION without expression --- + CalculatedField cf = new CalculatedField(); + cf.setEntityId(device.getId()); + cf.setType(CalculatedFieldType.PROPAGATION); + cf.setName("Propagation CF (args-only)"); + cf.setConfigurationVersion(1); + + PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + cfg.setApplyExpressionToResolvedArguments(false); // arguments-only mode + + Argument arg = new Argument(); + arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + cfg.setArguments(Map.of("temperatureComputed", arg)); + + cfg.setOutput(new TimeSeriesOutput()); + + cf.setConfiguration(cfg); + + CalculatedField saved = testRestClient.postCalculatedField(cf); + + // --- Assert propagated calculation (arguments-only mode) --- + await().alias("propagation args-only evaluation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode temperature1 = testRestClient.getLatestTelemetry(asset1.getId()); + assertThat(temperature1).isNotNull(); + assertThat(temperature1.get("temperatureComputed")).isNotNull(); + assertThat(temperature1.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(temperature1.get("temperatureComputed").get(0).get("value").asText()).isEqualTo("12.5"); + + JsonNode temperature2 = testRestClient.getLatestTelemetry(asset2.getId()); + assertThat(temperature2).isNotNull(); + assertThat(temperature2.get("temperatureComputed")).isNotNull(); + assertThat(temperature2.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(temperature2.get("temperatureComputed").get(0).get("value").asText()).isEqualTo("12.5"); + }); + + testRestClient.deleteEntityRelation(asset1.getId(), EntityRelation.CONTAINS_TYPE, device.getId()); + testRestClient.deleteEntityTimeseries(asset1.getId(), "temperatureComputed", true); + + // Update telemetry on device + long newTs = System.currentTimeMillis() - 300000L; + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":25}}", newTs))); + + // --- Assert propagated calculation (arguments-only mode after update) --- + await().alias("propagation args-only evaluation after temperature update") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode temperature1 = testRestClient.getLatestTelemetry(asset1.getId()); + assertThat(temperature1).isNullOrEmpty(); + + JsonNode temperature2 = testRestClient.getLatestTelemetry(asset2.getId()); + assertThat(temperature2).isNotNull(); + assertThat(temperature2.get("temperatureComputed")).isNotNull(); + assertThat(temperature2.get("temperatureComputed").get(0).get("ts").asText()).isEqualTo(Long.toString(newTs)); + assertThat(temperature2.get("temperatureComputed").get(0).get("value").asInt()).isEqualTo(25); + }); + + testRestClient.deleteCalculatedFieldIfExists(saved.getId()); + testRestClient.deleteDeviceIfExists(device.getId()); + testRestClient.deleteAsset(asset1.getId()); + testRestClient.deleteAsset(asset2.getId()); + } + + @Test + public void testRelatedEntitiesAggregationCalculatedField() { + // --- Create entities --- + String device_1_1_token = "000000011"; + Device device_1_1 = testRestClient.postDevice(device_1_1_token, createDevice("Device 1-1", deviceProfileId)); + String device_1_2_token = "000000012"; + Device device_1_2 = testRestClient.postDevice(device_1_2_token, createDevice("Device 1-2", deviceProfileId)); + + // Create relations FROM asset TO devices + EntityRelation rel_1_1 = new EntityRelation(asset.getId(), device_1_1.getId(), EntityRelation.CONTAINS_TYPE); + EntityRelation rel_1_2 = new EntityRelation(asset.getId(), device_1_2.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel_1_1); + testRestClient.postEntityRelation(rel_1_2); + + // Post telemetry + testRestClient.postTelemetry(device_1_1_token, JacksonUtil.toJsonNode("{\"occupied\":true}")); + testRestClient.postTelemetry(device_1_2_token, JacksonUtil.toJsonNode("{\"occupied\":false}")); + + // --- Create CF: Related entities aggregation --- + CalculatedField calculatedField = createOccupancyCF(assetProfileId); + + // --- Assert aggregation --- + await().alias("create cf -> check aggregation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode occupancy = testRestClient.getLatestTelemetry(asset.getId()); + assertThat(occupancy).isNotNull(); + + assertThat(occupancy.get("freeSpaces")).isNotNull(); + assertThat(occupancy.get("freeSpaces").get(0).get("value").asText()).isEqualTo("1"); + + assertThat(occupancy.get("occupiedSpaces")).isNotNull(); + assertThat(occupancy.get("occupiedSpaces").get(0).get("value").asText()).isEqualTo("1"); + + assertThat(occupancy.get("totalSpaces")).isNotNull(); + assertThat(occupancy.get("totalSpaces").get(0).get("value").asText()).isEqualTo("2"); + }); + + // Post telemetry + testRestClient.postTelemetry(device_1_2_token, JacksonUtil.toJsonNode("{\"occupied\":true}")); + + // --- Assert aggregation --- + await().alias("update telemetry -> check aggregation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode occupancy = testRestClient.getLatestTelemetry(asset.getId()); + assertThat(occupancy).isNotNull(); + + assertThat(occupancy.get("freeSpaces")).isNotNull(); + assertThat(occupancy.get("freeSpaces").get(0).get("value").asText()).isEqualTo("0"); + + assertThat(occupancy.get("occupiedSpaces")).isNotNull(); + assertThat(occupancy.get("occupiedSpaces").get(0).get("value").asText()).isEqualTo("2"); + + assertThat(occupancy.get("totalSpaces")).isNotNull(); + assertThat(occupancy.get("totalSpaces").get(0).get("value").asText()).isEqualTo("2"); + }); + + // Add entity to profile + Asset asset2 = testRestClient.postAsset(createAsset("Asset 2", assetProfileId)); + String device_2_1_token = "000000021"; + Device device_2_1 = testRestClient.postDevice(device_2_1_token, createDevice("Device 2-1", deviceProfileId)); + String device_2_2_token = "000000022"; + Device device_2_2 = testRestClient.postDevice(device_2_2_token, createDevice("Device 2-2", deviceProfileId)); + + // Post telemetry + testRestClient.postTelemetry(device_2_1_token, JacksonUtil.toJsonNode("{\"occupied\":true}")); + testRestClient.postTelemetry(device_2_2_token, JacksonUtil.toJsonNode("{\"occupied\":false}")); + + // --- Assert aggregation --- + await().alias("add entity to profile cf -> no aggregated values since no relations") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode occupancy = testRestClient.getLatestTelemetry(asset2.getId()); + assertThat(occupancy).isNullOrEmpty(); + }); + + // Create relations FROM asset TO devices + EntityRelation rel_2_1 = new EntityRelation(asset2.getId(), device_2_1.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel_2_1); + EntityRelation rel_2_2 = new EntityRelation(asset2.getId(), device_2_2.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel_2_2); + + // --- Assert aggregation --- + await().alias("create relation -> check aggregation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode occupancy = testRestClient.getLatestTelemetry(asset2.getId()); + assertThat(occupancy).isNotNull(); + + assertThat(occupancy.get("freeSpaces")).isNotNull(); + assertThat(occupancy.get("freeSpaces").get(0).get("value").asText()).isEqualTo("1"); + + assertThat(occupancy.get("occupiedSpaces")).isNotNull(); + assertThat(occupancy.get("occupiedSpaces").get(0).get("value").asText()).isEqualTo("1"); + + assertThat(occupancy.get("totalSpaces")).isNotNull(); + assertThat(occupancy.get("totalSpaces").get(0).get("value").asText()).isEqualTo("2"); + }); + + testRestClient.deleteEntityRelation(asset2.getId(), EntityRelation.CONTAINS_TYPE, device_2_2.getId()); + + // --- Assert aggregation --- + await().alias("delete relation -> check aggregation") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode occupancy = testRestClient.getLatestTelemetry(asset2.getId()); + assertThat(occupancy).isNotNull(); + + assertThat(occupancy.get("freeSpaces")).isNotNull(); + assertThat(occupancy.get("freeSpaces").get(0).get("value").asText()).isEqualTo("0"); + + assertThat(occupancy.get("occupiedSpaces")).isNotNull(); + assertThat(occupancy.get("occupiedSpaces").get(0).get("value").asText()).isEqualTo("1"); + + assertThat(occupancy.get("totalSpaces")).isNotNull(); + assertThat(occupancy.get("totalSpaces").get(0).get("value").asText()).isEqualTo("1"); + }); + + testRestClient.deleteCalculatedFieldIfExists(calculatedField.getId()); + testRestClient.deleteDeviceIfExists(device_1_1.getId()); + testRestClient.deleteDeviceIfExists(device_1_2.getId()); + testRestClient.deleteDeviceIfExists(device_2_1.getId()); + testRestClient.deleteDeviceIfExists(device_2_2.getId()); + testRestClient.deleteAsset(asset2.getId()); + } + + @Test + public void testDebugEvents() { + // --- Arrange entities --- + String deviceToken = "12345678901"; + Device device = testRestClient.postDevice(deviceToken, createDevice("Propagation Device", deviceProfileId)); + Asset asset1 = testRestClient.postAsset(createAsset("Propagated Asset 1", null)); + + // Create relations FROM asset 1 TO device + EntityRelation rel1 = new EntityRelation(asset1.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel1); + + // --- Build CF: PROPAGATION --- + CalculatedField saved = createPropagationCF(device.getId()); + + // Create relations FROM asset 2 TO device + Asset asset2 = testRestClient.postAsset(createAsset("Propagated Asset 2", null)); + EntityRelation rel2 = new EntityRelation(asset2.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); + testRestClient.postEntityRelation(rel2); + + // Telemetry on device + testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25.1}")); + + // Delete relation between asset 1 and device + testRestClient.deleteEntityRelation(asset1.getId(), EntityRelation.CONTAINS_TYPE, device.getId()); + + // --- Assert propagated calculation (arguments-only mode) --- + await().alias("check debug events") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + List eventTypes = testRestClient.getEvents(saved.getId(), EventType.DEBUG_CALCULATED_FIELD, tenantId, new TimePageLink(4, 0, null, SortOrder.BY_CREATED_TIME_DESC)).getData().stream() + .map(e -> e.getBody().get("msgType").asText()) + .toList(); + + assertThat(eventTypes).as("Check sequence of debug events") + .containsSequence( + CalculatedFieldEventType.RELATION_DELETED.name(), + TbMsgType.POST_TELEMETRY_REQUEST.name(), + CalculatedFieldEventType.RELATION_ADD_OR_UPDATE.name(), + CalculatedFieldEventType.INITIALIZED.name() + ); + }); + + testRestClient.deleteCalculatedFieldIfExists(saved.getId()); + testRestClient.deleteDeviceIfExists(device.getId()); + testRestClient.deleteAsset(asset1.getId()); + testRestClient.deleteAsset(asset2.getId()); + } + + private CalculatedField createPropagationCF(EntityId entityId) { + CalculatedField cf = new CalculatedField(); + cf.setEntityId(entityId); + cf.setType(CalculatedFieldType.PROPAGATION); + cf.setName("Propagation CF (args-only)"); + cf.setConfigurationVersion(1); + + PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); + cfg.setApplyExpressionToResolvedArguments(false); // arguments-only mode + + Argument arg = new Argument(); + arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null)); + cfg.setArguments(Map.of("deviceTemperature", arg)); + + cfg.setOutput(new TimeSeriesOutput()); + + cf.setConfiguration(cfg); + + cf.setDebugSettings(DebugSettings.all()); + + return testRestClient.postCalculatedField(cf); + } + + private CalculatedField createOccupancyCF(EntityId entityId) { + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setName("Occupancy"); + calculatedField.setEntityId(entityId); + calculatedField.setType(CalculatedFieldType.RELATED_ENTITIES_AGGREGATION); + + RelatedEntitiesAggregationCalculatedFieldConfiguration configuration = new RelatedEntitiesAggregationCalculatedFieldConfiguration(); + + configuration.setRelation(new RelationPathLevel(EntitySearchDirection.FROM, "Contains")); + + Map arguments = new HashMap<>(); + Argument argument = new Argument(); + argument.setRefEntityKey(new ReferencedEntityKey("occupied", ArgumentType.TS_LATEST, null)); + argument.setDefaultValue("false"); + arguments.put("oc", argument); + configuration.setArguments(arguments); + + configuration.setDeduplicationIntervalInSec(5); + configuration.setScheduledUpdateInterval(10); + + Map aggMetrics = new HashMap<>(); + + AggMetric freeSpaces = new AggMetric(); + freeSpaces.setFunction(AggFunction.COUNT); + freeSpaces.setFilter("return oc == false;"); + freeSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("freeSpaces", freeSpaces); + + AggMetric occupiedSpaces = new AggMetric(); + occupiedSpaces.setFunction(AggFunction.COUNT); + occupiedSpaces.setFilter("return oc == true;"); + occupiedSpaces.setInput(new AggKeyInput("oc")); + aggMetrics.put("occupiedSpaces", occupiedSpaces); + + AggMetric totalSpaces = new AggMetric(); + totalSpaces.setFunction(AggFunction.COUNT); + totalSpaces.setInput(new AggFunctionInput("return 1;")); + aggMetrics.put("totalSpaces", totalSpaces); + configuration.setMetrics(aggMetrics); + + TimeSeriesOutput output = new TimeSeriesOutput(); + output.setDecimalsByDefault(0); + configuration.setOutput(output); + + calculatedField.setConfiguration(configuration); + calculatedField.setDebugSettings(DebugSettings.all()); + + return testRestClient.postCalculatedField(calculatedField); } private CalculatedField createSimpleCalculatedField(EntityId entityId) { CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(entityId); calculatedField.setType(CalculatedFieldType.SIMPLE); - calculatedField.setName("C to F" + RandomStringUtils.randomAlphabetic(5)); + calculatedField.setName("C to F" + RandomStringUtils.insecure().nextAlphabetic(5)); calculatedField.setDebugSettings(DebugSettings.all()); SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); @@ -335,14 +931,13 @@ public class CalculatedFieldTest extends AbstractContainerTest { Argument argument = new Argument(); ReferencedEntityKey refEntityKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null); argument.setRefEntityKey(refEntityKey); - argument.setDefaultValue("12"); // not used because real telemetry value in db is present + argument.setDefaultValue("12"); config.setArguments(Map.of("T", argument)); config.setExpression("(T * 9/5) + 32"); - Output output = new Output(); + TimeSeriesOutput output = new TimeSeriesOutput(); output.setName("fahrenheitTemp"); - output.setType(OutputType.TIME_SERIES); output.setDecimalsByDefault(2); config.setOutput(output); @@ -351,22 +946,18 @@ public class CalculatedFieldTest extends AbstractContainerTest { return testRestClient.postCalculatedField(calculatedField); } - private CalculatedField createScriptCalculatedField() { - return createScriptCalculatedField(device.getId(), asset.getId()); - } - private CalculatedField createScriptCalculatedField(EntityId entityId, EntityId refEntityId) { CalculatedField calculatedField = new CalculatedField(); calculatedField.setEntityId(entityId); calculatedField.setType(CalculatedFieldType.SCRIPT); - calculatedField.setName("Air density" + RandomStringUtils.randomAlphabetic(5)); + calculatedField.setName("Air density" + RandomStringUtils.insecure().nextAlphabetic(5)); calculatedField.setDebugSettings(DebugSettings.all()); ScriptCalculatedFieldConfiguration config = new ScriptCalculatedFieldConfiguration(); Argument argument1 = new Argument(); argument1.setRefEntityId(refEntityId); - ReferencedEntityKey refEntityKey1 = new ReferencedEntityKey("altitude", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE); + ReferencedEntityKey refEntityKey1 = new ReferencedEntityKey("altitude", ArgumentType.ATTRIBUTE, SERVER_SCOPE); argument1.setRefEntityKey(refEntityKey1); Argument argument2 = new Argument(); ReferencedEntityKey refEntityKey2 = new ReferencedEntityKey("temperatureInF", ArgumentType.TS_ROLLING, null); @@ -378,15 +969,23 @@ public class CalculatedFieldTest extends AbstractContainerTest { config.setExpression(exampleScript); - Output output = new Output(); - output.setType(OutputType.TIME_SERIES); - config.setOutput(output); + config.setOutput(new TimeSeriesOutput()); calculatedField.setConfiguration(config); return testRestClient.postCalculatedField(calculatedField); } + private void waitInitialTelemetry(EntityId entityId, String key) { + await().alias("wait initial telemetry").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + JsonNode telemetry = testRestClient.getLatestTelemetry(entityId); + assertThat(telemetry).isNotNull(); + assertThat(telemetry.get(key)).isNotNull(); + }); + } + private Device createDevice(String name, DeviceProfileId deviceProfileId) { Device device = new Device(); device.setName(name); @@ -406,4 +1005,20 @@ public class CalculatedFieldTest extends AbstractContainerTest { return asset; } + private static Map kv(ArrayNode attrs) { + Map m = new HashMap<>(); + for (JsonNode n : attrs) { + m.put(n.get("key").asText(), n.get("value").asText()); + } + return m; + } + + private static Map intKv(ArrayNode attrs) { + Map m = new HashMap<>(); + for (JsonNode n : attrs) { + m.put(n.get("key").asText(), n.get("value").asInt()); + } + return m; + } + } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java index 73960af516..6e08c41f40 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java @@ -71,7 +71,7 @@ public class CoapClientTest extends AbstractCoapClientTest{ assertThat(provisionResponse2.get("status").asText()).isEqualTo("FAILURE"); // update provision attribute to non-valid value - testRestClient.postTelemetryAttribute(device.getId(), AttributeScope.SERVER_SCOPE.name(), JacksonUtil.valueToTree(Map.of("provisionState", "non-valid"))); + testRestClient.postTelemetryAttribute(device.getId(), AttributeScope.SERVER_SCOPE, JacksonUtil.valueToTree(Map.of("provisionState", "non-valid"))); JsonNode provisionResponse3 = JacksonUtil.fromBytes(createCoapClientAndPublish(device.getName())); assertThat(provisionResponse3.get("status").asText()).isEqualTo("FAILURE"); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java index 519373229d..1f60b96edb 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java @@ -35,8 +35,7 @@ import java.util.Arrays; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; -import static org.thingsboard.server.common.data.DataConstants.DEVICE; -import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE; +import static org.thingsboard.server.common.data.AttributeScope.SHARED_SCOPE; import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype; @DisableUIListeners diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java new file mode 100644 index 0000000000..c9ebfad3e2 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java @@ -0,0 +1,465 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.msa.connectivity; + +import com.google.gson.JsonObject; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; +import org.apache.hc.client5.http.ssl.HostnameVerificationPolicy; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.core5.ssl.SSLContexts; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; +import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rest.client.RestClient; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +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.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.domain.DomainInfo; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.NotificationTemplateId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.mobile.app.MobileApp; +import org.thingsboard.server.common.data.mobile.app.MobileAppStatus; +import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; +import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo; +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.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; +import org.thingsboard.server.common.data.notification.NotificationRequestPreview; +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.SlackNotificationDeliveryMethodConfig; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +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.UserListFilter; +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.HasSubject; +import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate; +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.SmsDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.oauth2.PlatformType; +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.common.data.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; +import org.thingsboard.server.common.data.query.AvailableEntityKeys; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityDataSortOrder; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.EntityTypeFilter; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.msa.AbstractContainerTest; +import org.thingsboard.server.msa.TestProperties; + +import javax.net.ssl.SSLContext; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.notification.NotificationDeliveryMethod.EMAIL; +import static org.thingsboard.server.common.data.notification.NotificationDeliveryMethod.MICROSOFT_TEAMS; +import static org.thingsboard.server.common.data.notification.NotificationDeliveryMethod.WEB; +import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype; +import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultTenantAdmin; + +public class JavaRestClientTest extends AbstractContainerTest { + + public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test"; + public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL; + private RestClient restClient; + private Tenant tenant; + private User user; + + @BeforeClass + public void beforeClass() throws Exception { + SSLContext ssl = SSLContexts.custom() + .loadTrustMaterial((chain, authType) -> true) + .build(); + + var tls = new DefaultClientTlsStrategy( + ssl, + HostnameVerificationPolicy.CLIENT, + NoopHostnameVerifier.INSTANCE + ); + + HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() + .setTlsSocketStrategy(tls) + .build(); + + CloseableHttpClient httpClient = HttpClients.custom() + .setConnectionManager(cm) + .build(); + + RestTemplate rt = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient)); + restClient = new RestClient(rt, TestProperties.getBaseUrl()); + } + + @BeforeMethod + public void setUp() throws Exception { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + + // create tenant and tenant admin + tenant = new Tenant(); + tenant.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.randomAlphabetic(5)); + tenant = restClient.saveTenant(tenant); + + String email = RandomStringUtils.randomAlphabetic(5) + "@gmail.com"; + user = restClient.saveUser(defaultTenantAdmin(tenant.getId(), email), false); + restClient.activateUser(user.getId(), "password123", false); + restClient.login(email, "password123"); + } + + @AfterMethod + public void tearDown() { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + if (tenant != null) { + restClient.deleteTenant(tenant.getId()); + } + } + + @Test + public void testGetAlarmsV2() { + Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5))); + assertThat(device).isNotNull(); + + String type = "High temp" + RandomStringUtils.randomAlphabetic(5); + Alarm alarm = Alarm.builder() + .originator(device.getId()) + .severity(AlarmSeverity.CRITICAL) + .type(type) + .build(); + restClient.saveAlarm(alarm); + + // get /api/v2/alarm + PageData alarmsV2 = restClient.getAlarmsV2(device.getId(), null, null, List.of(type), null, new TimePageLink(10, 0)); + assertThat(alarmsV2.getData()).hasSize(1); + + PageData activeAlarms = restClient.getAlarmsV2(device.getId(), List.of(AlarmSearchStatus.ACTIVE), null, List.of(type), null, new TimePageLink(10, 0)); + assertThat(activeAlarms.getData()).hasSize(1); + + PageData cleared = restClient.getAlarmsV2(device.getId(), List.of(AlarmSearchStatus.CLEARED), null, List.of(type), null, new TimePageLink(10, 0)); + assertThat(cleared.getData()).hasSize(0); + + PageData activeAndClearedAlarms = restClient.getAlarmsV2(device.getId(), List.of(AlarmSearchStatus.CLEARED, AlarmSearchStatus.ACTIVE), null, null, null, new TimePageLink(10, 0)); + assertThat(activeAndClearedAlarms.getData()).hasSize(1); + + // get /api/v2/alarms + PageData allAlarmsV2 = restClient.getAllAlarmsV2(List.of(AlarmSearchStatus.ACTIVE), null, List.of(type), null, new TimePageLink(10, 0)); + assertThat(allAlarmsV2.getData()).hasSize(1); + + PageData allClearedAlarmsV2 = restClient.getAllAlarmsV2(List.of(AlarmSearchStatus.CLEARED), null, List.of(type), null, new TimePageLink(10, 0)); + assertThat(allClearedAlarmsV2.getData()).hasSize(0); + + // get /api/alarms + PageData allAlarms = restClient.getAllAlarms(AlarmSearchStatus.ACTIVE, null, new TimePageLink(10, 0), null); + assertThat(allAlarms.getData()).hasSize(1); + + PageData allClearedAlarms = restClient.getAllAlarms(AlarmSearchStatus.CLEARED, null, new TimePageLink(10, 0), null); + assertThat(allClearedAlarms.getData()).hasSize(0); + } + + @Test + public void testTimeSeriesByReadTsKvQueries() { + Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5))); + assertThat(device).isNotNull(); + + DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get(); + for (int i = 0; i < 3; i++) { + JsonObject values = new JsonObject(); + values.addProperty("temperature", i + 25); + testRestClient.postTelemetry(deviceCredentials.getCredentialsId(), JacksonUtil.toJsonNode(createPayload().toString())); + } + + restClient.saveEntityTelemetry(device.getId(), "ts", JacksonUtil.toJsonNode("{\"temperature\": 25, \"humidity\": 60}")); + restClient.saveEntityTelemetry(device.getId(), "ts", JacksonUtil.toJsonNode("{\"temperature\": 27, \"humidity\": 59}")); + restClient.saveEntityTelemetry(device.getId(), "ts", JacksonUtil.toJsonNode("{\"temperature\": 33, \"humidity\": 62}")); + + EntityTypeFilter filter = new EntityTypeFilter(); + filter.setEntityType(EntityType.DEVICE); + var pageLink = new EntityDataPageLink(20, 0, null, new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.DESC), false); + + var entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime")); + + EntityDataQuery entityDataQuery = new EntityDataQuery(filter, pageLink, entityFields, null, null); + AvailableEntityKeys availableEntityKeys = restClient.findAvailableEntityKeysByQuery(entityDataQuery, true, true, null); + assertThat(availableEntityKeys).isNotNull(); + assertThat(availableEntityKeys.timeseries()).contains("temperature", "humidity"); + } + + @Test + public void testFindNotifications() { + NotificationTarget notificationTarget = createNotificationTarget(user.getId()); + String notificationText1 = "Notification 1"; + NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText1, new NotificationDeliveryMethod[]{WEB}); + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationTemplate.getId()); + + String notificationText2 = "Notification 2"; + NotificationTemplate notificationTemplate2 = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText2, new NotificationDeliveryMethod[]{WEB}); + NotificationRequest notificationRequest2 = submitNotificationRequest(notificationTarget.getId(), notificationTemplate2.getId()); + + PageData initialRequests = restClient.getNotificationRequests(new PageLink(30)); + assertThat(initialRequests.getTotalElements()).isGreaterThanOrEqualTo(2); + + NotificationRequestInfo notificationRequestInfo = restClient.getNotificationRequestById(notificationRequest.getId()).get(); + assertThat(notificationRequestInfo.getName()).isEqualTo(notificationRequest.getName()); + assertThat(notificationRequestInfo.getTemplateName()).isEqualTo(notificationTemplate.getName()); + + NotificationRequestPreview requestPreview = restClient.getNotificationRequestPreview(notificationRequest, 10); + assertThat(requestPreview.getTotalRecipientsCount()).isEqualTo(1); + assertThat(requestPreview.getRecipientsPreview()).isEqualTo(List.of(user.getEmail())); + + PageData notifications = restClient.getNotifications(false, WEB, new PageLink(30)); + assertThat(notifications.getTotalElements()).isEqualTo(2); + + Integer unreadCount = restClient.getUnreadNotificationsCount(WEB); + assertThat(unreadCount).isEqualTo(2); + + restClient.markNotificationAsRead(notifications.getData().get(0).getId()); + + Integer unreadCountAfterRead = restClient.getUnreadNotificationsCount(WEB); + assertThat(unreadCountAfterRead).isEqualTo(1); + + restClient.markAllNotificationsAsRead(WEB); + + Integer unreadCountAfterAllRead = restClient.getUnreadNotificationsCount(WEB); + assertThat(unreadCountAfterAllRead).isEqualTo(0); + + restClient.deleteNotification(notifications.getData().get(0).getId()); + notifications = restClient.getNotifications(false, WEB, new PageLink(30)); + assertThat(notifications.getTotalElements()).isEqualTo(1); + + restClient.deleteNotificationRequest(notificationRequest.getId()); + PageData requestsAfterUpdate = restClient.getNotificationRequests(new PageLink(30)); + assertThat(requestsAfterUpdate.getTotalElements()).isEqualTo(initialRequests.getTotalElements() - 1); + + List availableDeliveryMethods = restClient.getAvailableDeliveryMethods(); + assertThat(availableDeliveryMethods).contains(WEB, EMAIL, MICROSOFT_TEAMS); + } + + @Test + public void testSaveNotificationSettings() { + NotificationSettings settings = new NotificationSettings(); + SlackNotificationDeliveryMethodConfig slackConfig = new SlackNotificationDeliveryMethodConfig(); + String slackToken = "xoxb-123123123"; + slackConfig.setBotToken(slackToken); + settings.setDeliveryMethodsConfigs(Map.of( + NotificationDeliveryMethod.SLACK, slackConfig + )); + + restClient.saveNotificationSettings(settings); + + NotificationSettings savedSettings = restClient.getNotificationSettings().get(); + assertThat(savedSettings.getDeliveryMethodsConfigs()).hasSize(1); + assertThat(savedSettings.getDeliveryMethodsConfigs().get(slackConfig.getMethod())).isEqualTo(slackConfig); + + // save user notification settings + var entityActionNotificationPref = new UserNotificationSettings.NotificationPref(); + entityActionNotificationPref.setEnabled(true); + entityActionNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.WEB, true, + NotificationDeliveryMethod.SMS, false, + NotificationDeliveryMethod.EMAIL, false + )); + + UserNotificationSettings userNotificationSettings = new UserNotificationSettings(Map.of( + NotificationType.ENTITY_ACTION, entityActionNotificationPref + )); + UserNotificationSettings saved = restClient.saveUserNotificationSettings(userNotificationSettings); + UserNotificationSettings retrieved = restClient.getUserNotificationSettings().get(); + assertThat(retrieved).isEqualTo(saved); + } + + @Test + public void testSaveDomain() { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + + Domain domain = new Domain(); + String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); + domain.setName(prefix + ".test.com"); + Domain savedDomain = restClient.saveDomain(domain); + assertThat(savedDomain.getName()).isEqualTo(domain.getName()); + + PageData domainInfos = restClient.getTenantDomainInfos(new PageLink(10, 0, prefix)); + assertThat(domainInfos.getData()).hasSize(1); + } + + @Test + public void testSaveMobileApp() { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + + MobileApp mobileApp = new MobileApp(); + String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); + mobileApp.setPkgName(prefix + "test.app.apple"); + mobileApp.setPlatformType(PlatformType.ANDROID); + mobileApp.setAppSecret(RandomStringUtils.randomAlphabetic(20)); + mobileApp.setStatus(MobileAppStatus.DRAFT); + + MobileApp savedMobileApp = restClient.saveMobileApp(mobileApp); + assertThat(savedMobileApp.getName()).isEqualTo(mobileApp.getName()); + + PageData retrieved = restClient.getTenantMobileApps(new PageLink(10, 0, prefix)); + assertThat(retrieved.getData()).hasSize(1); + + MobileAppBundle mobileAppBundle = new MobileAppBundle(); + String bundlePrefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); + mobileAppBundle.setTitle(bundlePrefix + "Test Bundle"); + mobileAppBundle.setAndroidAppId(savedMobileApp.getId()); + + MobileAppBundle savedMobileAppBundle = restClient.saveMobileBundle(mobileAppBundle); + PageData bundleInfos = restClient.getTenantMobileBundleInfos(new PageLink(10, 0, bundlePrefix)); + assertThat(bundleInfos.getData()).hasSize(1); + } + + private NotificationTarget createNotificationTarget(UserId... usersIds) { + UserListFilter filter = new UserListFilter(); + filter.setUsersIds(Arrays.stream(usersIds).map(UUIDBased::getId).toList()); + + NotificationTarget notificationTarget = new NotificationTarget(); + notificationTarget.setName(filter + RandomStringUtils.randomNumeric(5)); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + targetConfig.setUsersFilter(filter); + notificationTarget.setConfiguration(targetConfig); + return restClient.saveNotificationTarget(notificationTarget); + } + + private NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, + String text, NotificationDeliveryMethod... deliveryMethods) { + NotificationTemplate notificationTemplate = new NotificationTemplate(); + notificationTemplate.setName("Notification template: " + RandomStringUtils.randomAlphabetic(5)); + notificationTemplate.setNotificationType(notificationType); + NotificationTemplateConfig config = new NotificationTemplateConfig(); + config.setDeliveryMethodsTemplates(new HashMap<>()); + for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { + DeliveryMethodNotificationTemplate deliveryMethodNotificationTemplate; + switch (deliveryMethod) { + case WEB: { + deliveryMethodNotificationTemplate = new WebDeliveryMethodNotificationTemplate(); + break; + } + case EMAIL: { + deliveryMethodNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); + break; + } + case SMS: { + deliveryMethodNotificationTemplate = new SmsDeliveryMethodNotificationTemplate(); + break; + } + case MOBILE_APP: + deliveryMethodNotificationTemplate = new MobileAppDeliveryMethodNotificationTemplate(); + break; + default: + throw new IllegalArgumentException("Unsupported delivery method " + deliveryMethod); + } + deliveryMethodNotificationTemplate.setEnabled(true); + deliveryMethodNotificationTemplate.setBody(text); + if (deliveryMethodNotificationTemplate instanceof HasSubject) { + ((HasSubject) deliveryMethodNotificationTemplate).setSubject(subject); + } + config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate); + } + notificationTemplate.setConfiguration(config); + return restClient.saveNotificationTemplate(notificationTemplate); + } + + private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, NotificationTemplateId notificationTemplateId) { + NotificationRequestConfig config = new NotificationRequestConfig(); + config.setSendingDelayInSec(0); + NotificationRequest notificationRequest = NotificationRequest.builder() + .targets(Stream.of(targetId).map(UUIDBased::getId).collect(Collectors.toList())) + .templateId(notificationTemplateId) + .additionalConfig(config) + .build(); + return restClient.saveNotificationRequest(notificationRequest); + } + + @Test + public void testApiKeyOperations() { + // Create an API key + ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); + apiKeyInfo.setDescription("Test API Key " + RandomStringUtils.randomAlphabetic(5)); + apiKeyInfo.setEnabled(true); + apiKeyInfo.setUserId(user.getId()); + apiKeyInfo.setExpirationTime(0); + + ApiKey savedApiKey = restClient.saveApiKey(apiKeyInfo); + assertThat(savedApiKey).isNotNull(); + assertThat(savedApiKey.getId()).isNotNull(); + assertThat(savedApiKey.getDescription()).isEqualTo(apiKeyInfo.getDescription()); + assertThat(savedApiKey.isEnabled()).isTrue(); + assertThat(savedApiKey.getUserId()).isEqualTo(user.getId()); + assertThat(savedApiKey.getTenantId()).isEqualTo(tenant.getId()); + assertThat(savedApiKey.getValue()).isNotNull(); + + // Get user API keys + PageData apiKeys = restClient.getUserApiKeys(user.getId(), new PageLink(10)); + assertThat(apiKeys).isNotNull(); + assertThat(apiKeys.getData()).hasSize(1); + assertThat(apiKeys.getData().get(0).getId()).isEqualTo(savedApiKey.getId()); + + // Update API key description + String updatedDescription = "Updated description " + RandomStringUtils.randomAlphabetic(5); + ApiKeyInfo updatedApiKeyInfo = restClient.updateApiKeyDescription(savedApiKey.getId(), updatedDescription); + assertThat(updatedApiKeyInfo).isNotNull(); + assertThat(updatedApiKeyInfo.getDescription()).isEqualTo(updatedDescription); + + // Disable the API key + ApiKeyInfo disabledApiKey = restClient.enableApiKey(savedApiKey.getId(), false); + assertThat(disabledApiKey).isNotNull(); + assertThat(disabledApiKey.isEnabled()).isFalse(); + + // Enable the API key + ApiKeyInfo enabledApiKey = restClient.enableApiKey(savedApiKey.getId(), true); + assertThat(enabledApiKey).isNotNull(); + assertThat(enabledApiKey.isEnabled()).isTrue(); + + // Delete the API key + restClient.deleteApiKey(savedApiKey.getId()); + + // Verify the API key is deleted + PageData apiKeysAfterDelete = restClient.getUserApiKeys(user.getId(), new PageLink(10)); + assertThat(apiKeysAfterDelete.getData()).isEmpty(); + } + +} 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 8a3596e682..5c5914105b 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 @@ -81,7 +81,7 @@ import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; -import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE; +import static org.thingsboard.server.common.data.AttributeScope.SHARED_SCOPE; import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype; @DisableUIListeners @@ -577,7 +577,7 @@ public class MqttClientTest extends AbstractContainerTest { Awaitility .await() .alias("Check device disconnect.") - .atMost(TIMEOUT*timeoutMultiplier, TimeUnit.SECONDS) + .atMost(TIMEOUT * timeoutMultiplier, TimeUnit.SECONDS) .until(() -> !returnCodeByteValue.isEmpty()); assertThat(returnCodeByteValueSecondClient).isEmpty(); 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 26235cf663..bb464ee52e 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 @@ -64,7 +64,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; -import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE; +import static org.thingsboard.server.common.data.AttributeScope.SHARED_SCOPE; import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultGatewayPrototype; @DisableUIListeners diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java index 2416617554..562b872ee7 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java @@ -303,7 +303,7 @@ abstract public class AbstractDriverBaseTest extends AbstractContainerTest { public void deleteDeviceProfileByTitle(String deviceProfileTitle) { DeviceProfile deviceProfile = getDeviceProfileByName(deviceProfileTitle); if (deviceProfile != null) { - testRestClient.deleteDeviseProfile(deviceProfile.getId()); + testRestClient.deleteDeviceProfile(deviceProfile.getId()); } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java index bce76debc1..9937975414 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java @@ -52,7 +52,7 @@ public class CreateDeviceProfileImportTest extends AbstractDriverBaseTest { @AfterMethod public void delete() { if (name != null) { - testRestClient.deleteDeviseProfile(getDeviceProfileByName(name).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(name).getId()); name = null; } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java index 7f307aa48c..7da444e7fa 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java @@ -51,7 +51,7 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest { @AfterMethod public void delete() { if (name != null) { - testRestClient.deleteDeviseProfile(getDeviceProfileByName(name).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(name).getId()); name = null; } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java index dacfc51e8c..d34b69ba78 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java @@ -51,7 +51,7 @@ public class DeviceProfileEditMenuTest extends AbstractDriverBaseTest { @AfterMethod public void delete() { if (name != null) { - testRestClient.deleteDeviseProfile(getDeviceProfileByName(name).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(name).getId()); name = null; } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java index 6123a6b9f7..eced71c898 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java @@ -46,7 +46,7 @@ public class SearchDeviceProfileTest extends AbstractDriverBaseTest { @AfterMethod public void delete() { if (name != null) { - testRestClient.deleteDeviseProfile(getDeviceProfileByName(name).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(name).getId()); name = null; } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java index cc8c92a32b..acc1323462 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java @@ -45,7 +45,7 @@ public class SortByNameTest extends AbstractDriverBaseTest { @AfterMethod public void delete() { if (name != null) { - testRestClient.deleteDeviseProfile(getDeviceProfileByName(name).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(name).getId()); name = null; } } @@ -83,9 +83,9 @@ public class SortByNameTest extends AbstractDriverBaseTest { profilesPage.setProfileName(2); String thirdDeviceProfile = profilesPage.getProfileName(); - testRestClient.deleteDeviseProfile(getDeviceProfileByName(deviceProfile).getId()); - testRestClient.deleteDeviseProfile(getDeviceProfileByName(deviceProfileNumber).getId()); - testRestClient.deleteDeviseProfile(getDeviceProfileByName(deviceProfileSymbol).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(deviceProfile).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(deviceProfileNumber).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(deviceProfileSymbol).getId()); Assert.assertEquals(firstDeviceProfile, deviceProfileSymbol); Assert.assertEquals(secondDeviceProfile, deviceProfileNumber); @@ -126,9 +126,9 @@ public class SortByNameTest extends AbstractDriverBaseTest { profilesPage.setProfileName(lastIndex - 2); String thirdDeviceProfile = profilesPage.getProfileName(); - testRestClient.deleteDeviseProfile(getDeviceProfileByName(deviceProfile).getId()); - testRestClient.deleteDeviseProfile(getDeviceProfileByName(deviceProfileNumber).getId()); - testRestClient.deleteDeviseProfile(getDeviceProfileByName(deviceProfileSymbol).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(deviceProfile).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(deviceProfileNumber).getId()); + testRestClient.deleteDeviceProfile(getDeviceProfileByName(deviceProfileSymbol).getId()); Assert.assertEquals(firstDeviceProfile, deviceProfileSymbol); Assert.assertEquals(secondDeviceProfile, deviceProfileNumber); diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index 0a704cca86..2082f134b0 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 3c77172f4e..7d4d858f22 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "4.2.1.2", + "version": "4.3.0.1", "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 4c37896679..31ea60943d 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 4851e403a4..188f1f8bb2 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 495cf42c57..c03701ca93 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard msa @@ -32,7 +32,7 @@ ${basedir}/.. thingsboard - thingsboard/openjdk17:bookworm-slim + thingsboard/openjdk25:trixie-slim true true 1.4.13 @@ -133,7 +133,7 @@ - 4.2.1-latest + 4.3.0-latest false diff --git a/msa/tb-node/docker/Dockerfile b/msa/tb-node/docker/Dockerfile index 2180cf6870..0f1738ecb1 100644 --- a/msa/tb-node/docker/Dockerfile +++ b/msa/tb-node/docker/Dockerfile @@ -16,6 +16,21 @@ FROM ${docker.base.image} +# For SVG renderer Debian Trixie slim fonts should be installed explicitly +RUN echo "Previously installed font libraries:" \ + && (dpkg -l | grep -E 'font|dejavu|harfbuzz' || echo " (none)") \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + libharfbuzz0b fontconfig fonts-dejavu-core \ + && rm -rf /var/lib/apt/lists/* \ + && echo "Current fonts libraries:" \ + && (dpkg -l | grep -E 'font|dejavu|harfbuzz') \ + && echo "Font config list:" \ + && fc-list && test -n "$(fc-list)" \ + && (fc-list | grep -i dejavu) \ + && echo "Harfbuzz library test with ldconfig:" \ + && (ldconfig -p | grep harfbuzz) + COPY logback.xml start-tb-node.sh ${pkg.name}.deb /tmp/ RUN chmod a+x /tmp/*.sh \ diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index b471697fc5..d6f08dd6f7 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 1ee197c64b..f9449f97ce 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index bb3eb47250..9809bc2b1e 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index d51ac40e10..9ec5f75cd0 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 3446069ad5..a43ef587b9 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index a4656b3212..75e8da0d74 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index c2abe3ac96..60aa6e123f 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index acb13f8f76..fb28fbba16 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 3d7c74be5b..285761d35a 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 2d44000da8..1fbe33abaf 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 44aade2d73..13d040b836 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "4.2.1.2", + "version": "4.3.0.1", "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 5107038cda..0a84e52620 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 08d2c173bc..c7c077ef38 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard netty-mqtt - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 352122f057..3b7bd7dbba 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 4db2c93d97..86e2613dcd 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-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 f82b815d65..bd1d7b05c3 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 @@ -19,6 +19,7 @@ import com.auth0.jwt.JWT; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.Strings; +import lombok.Getter; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.concurrent.LazyInitializer; @@ -38,10 +39,12 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.rest.client.utils.RestJsonConverter; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.ClaimRequest; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -74,6 +77,7 @@ import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.UsageInfo; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.UserEmailInfo; +import org.thingsboard.server.common.data.ai.AiModel; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentInfo; @@ -98,8 +102,10 @@ import org.thingsboard.server.common.data.edge.EdgeInfo; 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.AiModelId; import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CalculatedFieldId; @@ -113,6 +119,8 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.OAuth2ClientId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.OtaPackageId; @@ -132,6 +140,15 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo; +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.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.NotificationTarget; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo; @@ -140,6 +157,8 @@ import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.pat.ApiKey; +import org.thingsboard.server.common.data.pat.ApiKeyInfo; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -148,6 +167,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; 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.common.data.query.AvailableEntityKeys; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -203,16 +223,15 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.StringUtils.isEmpty; -/** - * @author Andrew Shvayka - */ public class RestClient implements Closeable { - private static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; + + private static final String TOKEN_HEADER_PARAM = "X-Authorization"; private static final long AVG_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(30); protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken="; private final LazyInitializer executor = LazyInitializer.builder() .setInitializer(() -> ThingsBoardExecutors.newWorkStealingPool(10, getClass())) .get(); + @Getter protected final RestTemplate restTemplate; protected final RestTemplate loginRestTemplate; protected final String baseURL; @@ -220,56 +239,70 @@ public class RestClient implements Closeable { private String username; private String password; private String mainToken; + @Getter private String refreshToken; private long mainTokenExpTs; private long refreshTokenExpTs; private long clientServerTimeDiff; + public enum AuthType {JWT, API_KEY} + public RestClient(String baseURL) { this(new RestTemplate(), baseURL); } public RestClient(RestTemplate restTemplate, String baseURL) { - this(restTemplate, baseURL, null); + this(restTemplate, baseURL, AuthType.JWT, null); } public RestClient(RestTemplate restTemplate, String baseURL, String accessToken) { + this(restTemplate, baseURL, AuthType.JWT, accessToken); + } + + public RestClient(RestTemplate restTemplate, String baseURL, AuthType authType, String token) { this.restTemplate = restTemplate; this.loginRestTemplate = new RestTemplate(restTemplate.getRequestFactory()); this.baseURL = baseURL; this.restTemplate.getInterceptors().add((request, bytes, execution) -> { HttpRequest wrapper = new HttpRequestWrapper(request); - if (accessToken == null) { - long calculatedTs = System.currentTimeMillis() + clientServerTimeDiff + AVG_REQUEST_TIMEOUT; - if (calculatedTs > mainTokenExpTs) { - synchronized (RestClient.this) { + switch (authType) { + case JWT -> { + if (token == null) { + long calculatedTs = System.currentTimeMillis() + clientServerTimeDiff + AVG_REQUEST_TIMEOUT; if (calculatedTs > mainTokenExpTs) { - if (calculatedTs < refreshTokenExpTs) { - refreshToken(); - } else { - doLogin(); + synchronized (RestClient.this) { + if (calculatedTs > mainTokenExpTs) { + if (calculatedTs < refreshTokenExpTs) { + refreshToken(); + } else { + doLogin(); + } + } } } + } else { + mainToken = token; } + wrapper.getHeaders().set(TOKEN_HEADER_PARAM, "Bearer " + mainToken); + } + case API_KEY -> { + wrapper.getHeaders().set(TOKEN_HEADER_PARAM, "ApiKey " + token); } - } else { - mainToken = accessToken; } - wrapper.getHeaders().set(JWT_TOKEN_HEADER_PARAM, "Bearer " + mainToken); return execution.execute(wrapper, bytes); }); } - public RestTemplate getRestTemplate() { - return restTemplate; + public static RestClient withApiKey(String baseURL, String token) { + return withApiKey(new RestTemplate(), baseURL, token); } - public String getToken() { - return mainToken; + public static RestClient withApiKey(RestTemplate rt, String baseURL, String token) { + return new RestClient(rt, baseURL, AuthType.API_KEY, token); } - public String getRefreshToken() { - return refreshToken; + public String getToken() { + return mainToken; } public void refreshToken() { @@ -509,6 +542,99 @@ public class RestClient implements Closeable { params).getBody(); } + public PageData getAllAlarms(AlarmSearchStatus searchStatus, AlarmStatus status, TimePageLink pageLink, Boolean fetchOriginator) { + String urlSecondPart = "/api/alarms?"; + Map params = new HashMap<>(); + if (fetchOriginator != null) { + params.put("fetchOriginator", String.valueOf(fetchOriginator)); + urlSecondPart += "&fetchOriginator={fetchOriginator}"; + } + if (searchStatus != null) { + params.put("searchStatus", searchStatus.name()); + urlSecondPart += "&searchStatus={searchStatus}"; + } + if (status != null) { + params.put("status", status.name()); + urlSecondPart += "&status={status}"; + } + + addTimePageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + urlSecondPart + "&" + getTimeUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public PageData getAlarmsV2(EntityId entityId, List statusList, List severityList, + List typeList, String assignedId, TimePageLink pageLink) { + String urlSecondPart = "/api/v2/alarm/{entityType}/{entityId}?"; + Map params = new HashMap<>(); + params.put("entityType", entityId.getEntityType().name()); + params.put("entityId", entityId.getId().toString()); + if (!CollectionUtils.isEmpty(statusList)) { + params.put("statusList", listEnumToString(statusList)); + urlSecondPart += "&statusList={statusList}"; + } + if (!CollectionUtils.isEmpty(severityList)) { + params.put("severityList", listEnumToString(severityList)); + urlSecondPart += "&severityList={severityList}"; + } + if (!CollectionUtils.isEmpty(typeList)) { + params.put("typeList", String.join(",", typeList)); + urlSecondPart += "&typeList={typeList}"; + } + if (assignedId != null) { + params.put("assignedId", assignedId); + urlSecondPart += "&assignedId={assignedId}"; + } + + addTimePageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + urlSecondPart + "&" + getTimeUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public PageData getAllAlarmsV2(List statusList, List severityList, + List typeList, String assignedId, TimePageLink pageLink) { + String urlSecondPart = "/api/v2/alarms?"; + Map params = new HashMap<>(); + if (!CollectionUtils.isEmpty(statusList)) { + params.put("statusList", listEnumToString(statusList)); + urlSecondPart += "&statusList={statusList}"; + } + if (!CollectionUtils.isEmpty(severityList)) { + params.put("severityList", listEnumToString(severityList)); + urlSecondPart += "&severityList={severityList}"; + } + if (!CollectionUtils.isEmpty(typeList)) { + params.put("typeList", String.join(",", typeList)); + urlSecondPart += "&typeList={typeList}"; + } + if (assignedId != null) { + params.put("assignedId", assignedId); + urlSecondPart += "&assignedId={assignedId}"; + } + + addTimePageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + urlSecondPart + "&" + getTimeUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + public Optional getHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus searchStatus, AlarmStatus status) { Map params = new HashMap<>(); params.put("entityType", entityId.getEntityType().name()); @@ -1710,6 +1836,17 @@ public class RestClient implements Closeable { }).getBody(); } + public AvailableEntityKeys findAvailableEntityKeysByQuery(EntityDataQuery query, boolean includeTimeseries, boolean includeAttributes, AttributeScope scope) { + var uri = UriComponentsBuilder.fromUriString(baseURL) + .path("/api/entitiesQuery/find/keys") + .queryParam("timeseries", includeTimeseries) + .queryParam("attributes", includeAttributes) + .queryParamIfPresent("scope", Optional.ofNullable(scope)) + .build() + .toUri(); + return restTemplate.exchange(uri, HttpMethod.POST, new HttpEntity<>(query), new ParameterizedTypeReference() {}).getBody(); + } + public PageData findAlarmDataByQuery(AlarmDataQuery query) { return restTemplate.exchange( baseURL + "/api/alarmsQuery/find", @@ -2158,13 +2295,16 @@ public class RestClient implements Closeable { restTemplate.delete(baseURL + "/api/oauth2/client/{id}", oAuth2ClientId.getId()); } - public PageData getTenantDomainInfos() { + public PageData getTenantDomainInfos(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); return restTemplate.exchange( - baseURL + "/api/domain/infos", + baseURL + "/api/domain/infos?" + getUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, + params).getBody(); } public Optional getDomainInfoById(DomainId domainId) { @@ -2192,13 +2332,16 @@ public class RestClient implements Closeable { restTemplate.postForLocation(baseURL + "/api/domain/{id}/oauth2Clients", oauth2ClientIds, domainId.getId()); } - public PageData getTenantMobileApps() { + public PageData getTenantMobileApps(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); return restTemplate.exchange( - baseURL + "/api/mobile/app", + baseURL + "/api/mobile/app?" + getUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, + params).getBody(); } public Optional getMobileAppById(MobileAppId mobileAppId) { @@ -2222,13 +2365,16 @@ public class RestClient implements Closeable { restTemplate.delete(baseURL + "/api/mobile/app/{id}", mobileAppId.getId()); } - public PageData getTenantMobileBundleInfos() { + public PageData getTenantMobileBundleInfos(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); return restTemplate.exchange( - baseURL + "/api/mobile/bundle/infos", + baseURL + "/api/mobile/bundle/infos?" + getUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, + params).getBody(); } public Optional getMobileBundleById(MobileAppBundleId mobileAppBundleId) { @@ -2846,6 +2992,17 @@ public class RestClient implements Closeable { }, params).getBody(); } + public PageData getUsersByQuery(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/users/info?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + public PageData getTenantAdmins(TenantId tenantId, PageLink pageLink) { Map params = new HashMap<>(); params.put("tenantId", tenantId.getId().toString()); @@ -2893,6 +3050,44 @@ public class RestClient implements Closeable { userCredentialsEnabled); } + public ApiKey saveApiKey(ApiKeyInfo apiKeyInfo) { + return restTemplate.postForEntity(baseURL + "/api/apiKey", apiKeyInfo, ApiKey.class).getBody(); + } + + public PageData getUserApiKeys(UserId userId, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("userId", userId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/apiKeys/{userId}?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() {}, params).getBody(); + } + + public ApiKeyInfo updateApiKeyDescription(ApiKeyId apiKeyId, String description) { + return restTemplate.exchange( + baseURL + "/api/apiKey/{id}/description", + HttpMethod.PUT, + new HttpEntity<>(description), + ApiKeyInfo.class, + apiKeyId.getId()).getBody(); + } + + public ApiKeyInfo enableApiKey(ApiKeyId apiKeyId, boolean enabled) { + return restTemplate.exchange( + baseURL + "/api/apiKey/{id}/enabled/{enabledValue}", + HttpMethod.PUT, + HttpEntity.EMPTY, + ApiKeyInfo.class, + apiKeyId.getId(), + enabled).getBody(); + } + + public void deleteApiKey(ApiKeyId apiKeyId) { + restTemplate.delete(baseURL + "/api/apiKey/{id}", apiKeyId.getId()); + } + public Optional getWidgetsBundleById(WidgetsBundleId widgetsBundleId) { try { ResponseEntity widgetsBundle = @@ -3006,7 +3201,7 @@ public class RestClient implements Closeable { addWidgetInfoFiltersToParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList, params); return restTemplate.exchange( baseURL + "/api/widgetTypes?" + getUrlParams(pageLink) + - getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), + getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -3094,7 +3289,7 @@ public class RestClient implements Closeable { addWidgetInfoFiltersToParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList, params); return restTemplate.exchange( baseURL + "/api/widgetTypesInfos?widgetsBundleId={widgetsBundleId}&" + getUrlParams(pageLink) + - getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), + getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -4144,6 +4339,182 @@ public class RestClient implements Closeable { } } + public PageData getNotifications(Boolean unreadOnly, NotificationDeliveryMethod deliveryMethod, PageLink pageLink) { + Map params = new HashMap<>(); + + StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(baseURL).append("/api/notifications?").append(getUrlParams(pageLink)); + addPageLinkToParam(params, pageLink); + + if (unreadOnly != null) { + urlBuilder.append("&unreadOnly={unreadOnly}"); + params.put("unreadOnly", unreadOnly.toString()); + } + if (deliveryMethod != null) { + urlBuilder.append("&deliveryMethod={deliveryMethod}"); + params.put("deliveryMethod", deliveryMethod.name()); + } + + return restTemplate.exchange(urlBuilder.toString(), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Integer getUnreadNotificationsCount(NotificationDeliveryMethod deliveryMethod) { + String uri = "/api/notifications/unread/count?"; + Map params = new HashMap<>(); + if (deliveryMethod != null) { + params.put("deliveryMethod", deliveryMethod.name()); + uri += "&deliveryMethod={deliveryMethod}"; + } + return restTemplate.exchange( + baseURL + uri, + HttpMethod.GET, + HttpEntity.EMPTY, + Integer.class, params).getBody(); + } + + public void markNotificationAsRead(NotificationId notificationId) { + restTemplate.exchange( + baseURL + "/api/notification/{id}/read", + HttpMethod.PUT, + HttpEntity.EMPTY, + Void.class, + notificationId.getId()); + } + + public void markAllNotificationsAsRead(NotificationDeliveryMethod deliveryMethod) { + String uri = "/api/notifications/read?"; + Map params = new HashMap<>(); + if (deliveryMethod != null) { + params.put("deliveryMethod", deliveryMethod.name()); + uri += "&deliveryMethod={deliveryMethod}"; + } + restTemplate.exchange( + baseURL + uri, + HttpMethod.PUT, + HttpEntity.EMPTY, + Void.class, + params); + } + + + public void deleteNotification(NotificationId notificationId) { + restTemplate.delete(baseURL + "/api/notification/{id}", notificationId.getId()); + } + + public NotificationRequest saveNotificationRequest(NotificationRequest notificationRequest) { + return restTemplate.postForEntity(baseURL + "/api/notification/request", notificationRequest, NotificationRequest.class).getBody(); + } + + public NotificationRequestPreview getNotificationRequestPreview(NotificationRequest notificationRequest, int recipientsPreviewSize) { + return restTemplate.postForEntity(baseURL + "/api/notification/request/preview?recipientsPreviewSize={recipientsPreviewSize}", notificationRequest, NotificationRequestPreview.class, recipientsPreviewSize).getBody(); + } + + public Optional getNotificationRequestById(NotificationRequestId notificationRequestId) { + try { + ResponseEntity notificationRequest = restTemplate.getForEntity(baseURL + "/api/notification/request/{id}", NotificationRequestInfo.class, notificationRequestId.getId()); + return Optional.ofNullable(notificationRequest.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getNotificationRequests(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/notification/requests?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public void deleteNotificationRequest(NotificationRequestId notificationRequestId) { + restTemplate.delete(baseURL + "/api/notification/request/{id}", notificationRequestId.getId()); + } + + public NotificationSettings saveNotificationSettings(NotificationSettings notificationSettings) { + return restTemplate.postForEntity(baseURL + "/api/notification/settings", notificationSettings, NotificationSettings.class).getBody(); + } + + public Optional getNotificationSettings() { + try { + ResponseEntity notificationSettings = restTemplate.getForEntity(baseURL + "/api/notification/settings", NotificationSettings.class); + return Optional.ofNullable(notificationSettings.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public List getAvailableDeliveryMethods() { + return restTemplate.exchange(URI.create( + baseURL + "/api/notification/deliveryMethods"), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + + public UserNotificationSettings saveUserNotificationSettings(UserNotificationSettings userNotificationSettings) { + return restTemplate.postForEntity(baseURL + "/api/notification/settings/user", userNotificationSettings, UserNotificationSettings.class).getBody(); + } + + public Optional getUserNotificationSettings() { + try { + ResponseEntity userNotificationSettings = restTemplate.getForEntity(baseURL + "/api/notification/settings/user", UserNotificationSettings.class); + return Optional.ofNullable(userNotificationSettings.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) { + return restTemplate.postForEntity(baseURL + "/api/notification/target", notificationTarget, NotificationTarget.class).getBody(); + } + + public NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) { + return restTemplate.postForEntity(baseURL + "/api/notification/template", notificationTemplate, NotificationTemplate.class).getBody(); + } + + public AiModel saveAiModel(AiModel aiModel) { + return restTemplate.postForEntity(baseURL + "/api/ai/model", aiModel, AiModel.class).getBody(); + } + + public Optional getAiModel(AiModelId aiModelId) { + try { + ResponseEntity response = restTemplate.getForEntity( + baseURL + "/api/ai/model/{aiModelId}", AiModel.class, aiModelId.getId()); + return Optional.ofNullable(response.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public void deleteAiModel(AiModelId aiModelId) { + restTemplate.delete(baseURL + "/api/ai/model/{aiModelId}", aiModelId.getId()); + } + + private String getTimeUrlParams(TimePageLink pageLink) { String urlParams = getUrlParams(pageLink); if (pageLink.getStartTime() != null) { diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 3081cd2f09..a03695f64f 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 99b6679d19..07719631be 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 - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT rule-engine org.thingsboard.rule-engine 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 1d49d00113..a49e28db4b 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 @@ -63,6 +63,8 @@ public interface RuleEngineAlarmService { AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details); + AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details, boolean pushEvent); + AlarmApiCallResult assignAlarm(TenantId tenantId, AlarmId alarmId, UserId assigneeId, long assignTs); AlarmApiCallResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long assignTs); 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 dc27f79ee2..d3014ab906 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 @@ -76,6 +76,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; @@ -375,6 +376,8 @@ public interface TbContext { JobManager getJobManager(); + ApiKeyService getApiKeyService(); + boolean isExternalNodeForceAck(); /** diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 1dc4aeaf79..97f2ad65b1 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 - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractCustomerActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractCustomerActionNode.java index 0f4dca8934..1c896199e3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractCustomerActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractCustomerActionNode.java @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.util.EnumSet; import java.util.NoSuchElementException; 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 054182479a..e1cc6b0894 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 @@ -35,7 +35,7 @@ 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.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.util.EnumSet; import java.util.NoSuchElementException; 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 571c8de122..a3c29f549d 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 @@ -16,19 +16,27 @@ package org.thingsboard.rule.engine.action; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; @Data @AllArgsConstructor +@NoArgsConstructor +@Builder public class TbAlarmResult { + boolean isCreated; boolean isUpdated; boolean isSeverityUpdated; boolean isCleared; Alarm alarm; + Long conditionRepeats; + Long conditionDuration; + public TbAlarmResult(boolean isCreated, boolean isUpdated, boolean isCleared, Alarm alarm) { this.isCreated = isCreated; this.isUpdated = isUpdated; @@ -38,11 +46,13 @@ public class TbAlarmResult { public static TbAlarmResult fromAlarmResult(AlarmApiCallResult result) { boolean isSeverityChanged = result.isSeverityChanged(); - return new TbAlarmResult( - result.isCreated(), - result.isModified() && !isSeverityChanged, - isSeverityChanged, - result.isCleared(), - result.getAlarm()); + return TbAlarmResult.builder() + .isCreated(result.isCreated()) + .isUpdated(result.isModified() && !isSeverityChanged) + .isSeverityUpdated(isSeverityChanged) + .isCleared(result.isCleared()) + .alarm(result.getAlarm()) + .build(); } + } 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 4366d79e4e..ed024a3b2d 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 @@ -16,7 +16,6 @@ package org.thingsboard.rule.engine.action; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; @@ -30,17 +29,21 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static com.google.common.util.concurrent.Futures.transform; +import static com.google.common.util.concurrent.Futures.transformAsync; + @RuleNode( type = ComponentType.ACTION, name = "clear alarm", relationTypes = {"Cleared", "False"}, configClazz = TbClearAlarmNodeConfiguration.class, nodeDescription = "Clear Alarm", - nodeDetails = - "Details - JS function that creates JSON object based on incoming message. This object will be added into Alarm.details field.\n" + - "Node output:\n" + - "If alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. " + - "Message payload can be accessed via msg property. For example 'temperature = ' + msg.temperature ;. " + - "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", + nodeDetails = """ + Details - JS function that creates JSON object based on incoming message. This object will be added into Alarm.details field. + Node output: + If alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. + Message payload can be accessed via msg property. For example 'temperature = ' + msg.temperature ;. + Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.""", configDirective = "tbActionNodeClearAlarmConfig", icon = "notifications_off", docUrl = "https://thingsboard.io/docs/user-guide/rule-engine-2-0/nodes/action/clear-alarm/" @@ -54,22 +57,26 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode processAlarm(TbContext ctx, TbMsg msg) { - String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg); - Alarm alarm; - if (msg.getOriginator().getEntityType().equals(EntityType.ALARM)) { - alarm = ctx.getAlarmService().findAlarmById(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId())); + String alarmType = TbNodeUtils.processPattern(config.getAlarmType(), msg); + + ListenableFuture alarmFuture; + if (msg.getOriginator().getEntityType() == EntityType.ALARM) { + alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId())); } else { - alarm = ctx.getAlarmService().findLatestActiveByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType); - } - if (alarm != null && !alarm.getStatus().isCleared()) { - return clearAlarm(ctx, msg, alarm); + alarmFuture = ctx.getAlarmService().findLatestActiveByOriginatorAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), alarmType); } - return Futures.immediateFuture(new TbAlarmResult(false, false, false, null)); + + return transformAsync(alarmFuture, alarm -> { + if (alarm != null && !alarm.getStatus().isCleared()) { + return clearAlarmAsync(ctx, msg, alarm); + } + return immediateFuture(new TbAlarmResult(false, false, false, null)); + }, ctx.getDbCallbackExecutor()); } - private ListenableFuture clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { + private ListenableFuture clearAlarmAsync(TbContext ctx, TbMsg msg, Alarm alarm) { ListenableFuture asyncDetails = buildAlarmDetails(msg, alarm.getDetails()); - return Futures.transform(asyncDetails, details -> { + return transform(asyncDetails, details -> { AlarmApiCallResult result = ctx.getAlarmService().clearAlarm(ctx.getTenantId(), alarm.getId(), System.currentTimeMillis(), details); if (result.isSuccessful()) { return new TbAlarmResult(false, false, result.isCleared(), result.getAlarm()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index 1649b6b842..8df155aa78 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -16,21 +16,16 @@ package org.thingsboard.rule.engine.action; import com.datastax.oss.driver.api.core.ConsistencyLevel; -import com.datastax.oss.driver.api.core.cql.AsyncResultSet; import com.datastax.oss.driver.api.core.cql.BoundStatement; import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.cql.Statement; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.base.Function; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; -import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; @@ -50,8 +45,6 @@ import org.thingsboard.server.dao.nosql.TbResultSetFuture; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import static org.thingsboard.common.util.DonAsynchron.withCallback; @@ -85,7 +78,6 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { private CassandraCluster cassandraCluster; private ConsistencyLevel defaultWriteLevel; private PreparedStatement saveStmt; - private ExecutorService readResultsProcessingExecutor; private Map fieldsMap; @Override @@ -98,31 +90,19 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { if (!isTableExists()) { throw new TbNodeException("Table '" + TABLE_PREFIX + config.getTableName() + "' does not exist in Cassandra cluster."); } - startExecutor(); saveStmt = getSaveStmt(); } @Override public void onMsg(TbContext ctx, TbMsg msg) { - withCallback(save(msg, ctx), aVoid -> ctx.tellSuccess(msg), e -> ctx.tellFailure(msg, e), ctx.getDbCallbackExecutor()); + withCallback(save(msg, ctx), success -> ctx.tellSuccess(msg), e -> ctx.tellFailure(msg, e), ctx.getDbCallbackExecutor()); } @Override public void destroy() { - stopExecutor(); saveStmt = null; } - private void startExecutor() { - readResultsProcessingExecutor = Executors.newCachedThreadPool(); - } - - private void stopExecutor() { - if (readResultsProcessingExecutor != null) { - readResultsProcessingExecutor.shutdownNow(); - } - } - private boolean isTableExists() { var keyspaceMdOpt = getSession().getMetadata().getKeyspace(cassandraCluster.getKeyspaceName()); return keyspaceMdOpt.map(keyspaceMetadata -> @@ -183,7 +163,7 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { return query.toString(); } - private ListenableFuture save(TbMsg msg, TbContext ctx) { + private TbResultSetFuture save(TbMsg msg, TbContext ctx) { JsonElement data = JsonParser.parseString(msg.getData()); if (!data.isJsonObject()) { throw new IllegalStateException("Invalid message structure, it is not a JSON Object: " + data); @@ -224,7 +204,7 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { if (config.getDefaultTtl() > 0) { stmtBuilder.setInt(i.get(), config.getDefaultTtl()); } - return getFuture(executeAsyncWrite(ctx, stmtBuilder.build()), rs -> null); + return executeAsyncWrite(ctx, stmtBuilder.build()); } } @@ -254,16 +234,6 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { } } - private ListenableFuture getFuture(TbResultSetFuture future, java.util.function.Function transformer) { - return Futures.transform(future, new Function() { - @Nullable - @Override - public T apply(@Nullable AsyncResultSet input) { - return transformer.apply(input); - } - }, readResultsProcessingExecutor); - } - @Override public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { boolean hasChanges = false; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java index 3295c338b1..4b2a4776fb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java @@ -24,12 +24,10 @@ import java.util.Map; @Data public class TbSaveToCustomCassandraTableNodeConfiguration implements NodeConfiguration { - private String tableName; private Map fieldsMapping; private int defaultTtl; - @Override public TbSaveToCustomCassandraTableNodeConfiguration defaultConfiguration() { TbSaveToCustomCassandraTableNodeConfiguration configuration = new TbSaveToCustomCassandraTableNodeConfiguration(); @@ -40,4 +38,5 @@ public class TbSaveToCustomCassandraTableNodeConfiguration implements NodeConfig configuration.setFieldsMapping(map); return configuration; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/ai/TbAiNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/ai/TbAiNode.java index bff72beb9b..7495276723 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/ai/TbAiNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/ai/TbAiNode.java @@ -54,7 +54,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.resource.TbResourceDataCache; import java.nio.charset.StandardCharsets; @@ -92,7 +92,6 @@ import static org.thingsboard.server.dao.service.ConstraintValidator.validateFie configClazz = TbAiNodeConfiguration.class, configDirective = "tbExternalNodeAiConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDkiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OSA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zOC42MzExIDE3LjA3OTVDNDAuMTcwNSAxNy4wNzk2IDQxLjY1MTggMTcuNjg3MiA0Mi43NDc4IDE4Ljc3NjNDNDMuODQ0OCAxOS44NjYzIDQ0LjQ2NTkgMjEuMzUwMSA0NC40NjU5IDIyLjkwMjlWMzUuNDY1MkM0NC40NjU5IDM2LjM1MDkgNDQuMzU2NyAzNy4wNzY5IDQ0LjA5NzMgMzcuNzUxN0M0My44NDE0IDM4LjQxNjcgNDMuNDY1MSAzOC45NjE0IDQzLjA0NDggMzkuNTAyOEM0Mi40NjY3IDQwLjI0NzIgNDEuNjU2MyA0MC42ODU5IDQwLjg5MTkgNDAuOTM4OEM0MC4xMjExIDQxLjE5MzcgMzkuMzE0MyA0MS4yODg1IDM4LjYzMTEgNDEuMjg4NUgzMS4wMjU5TDIzLjM4MTIgNDUuODQ2NEMyMy4wNDMxIDQ2LjA0NzggMjIuNjI0MSA0Ni4wNTA3IDIyLjI4MzkgNDUuODUyOUMyMS45NDM3IDQ1LjY1NDcgMjEuNzMzOCA0NS4yODU5IDIxLjczMzcgNDQuODg3MlY0MS4yODg1SDE5LjY2NjNDMTguMTI2OSA0MS4yODg0IDE2LjY0NTUgNDAuNjgwOSAxNS41NDk2IDM5LjU5MThDMTQuNDUyNyAzOC41MDE5IDEzLjgzMTUgMzcuMDE3OSAxMy44MzE1IDM1LjQ2NTJWMjIuOTAyOUMxMy44MzE1IDIyLjMyMDIgMTMuOTE4NSAyMS43NDY4IDE0LjA4NTggMjEuMjAwN0wxNi4yODg5IDIxLjgxMDFMMTcuMjA5OSAyNS4yNTAyQzE3Ljk0MTYgMjcuOTg0NSAyMS43NTYyIDI3Ljk4NDQgMjIuNDg4IDI1LjI1MDJMMjMuNDA3OSAyMS44MTAxTDI2Ljc5MTcgMjAuODc0OUMyOC41NzkxIDIwLjM4MDUgMjkuMTc3IDE4LjUwMjYgMjguNTg4OCAxNy4wNzk1SDM4LjYzMTFaTTIyLjU4NDIgMzEuNTM5NUMyMS45OCAzMS41Mzk3IDIxLjQ5MDEgMzIuMDM3NiAyMS40OTAxIDMyLjY1MTlDMjEuNDkwMiAzMy4yNjYgMjEuOTgwMSAzMy43NjQgMjIuNTg0MiAzMy43NjQySDM0LjYxOTFDMzUuMjIzMyAzMy43NjQyIDM1LjcxMzEgMzMuMjY2MSAzNS43MTMyIDMyLjY1MTlDMzUuNzEzMiAzMi4wMzc1IDM1LjIyMzQgMzEuNTM5NSAzNC42MTkxIDMxLjUzOTVIMjIuNTg0MlpNMjQuNzcyMyAyNC44NjU3QzI0LjE2ODIgMjQuODY1OCAyMy42NzgzIDI1LjM2MzggMjMuNjc4MyAyNS45NzhDMjMuNjc4NCAyNi41OTIyIDI0LjE2ODMgMjcuMDkwMiAyNC43NzIzIDI3LjA5MDNIMzcuOTAxNEMzOC41MDU1IDI3LjA5MDMgMzguOTk1MyAyNi41OTIyIDM4Ljk5NTQgMjUuOTc4QzM4Ljk5NTQgMjUuMzYzNyAzOC41MDU2IDI0Ljg2NTcgMzcuOTAxNCAyNC44NjU3SDI0Ljc3MjNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjc2Ii8+CjxwYXRoIGQ9Ik0xOC43ODkxIDExLjI5NzVDMTkuMDY5MSAxMC4xODA4IDIwLjYyOTkgMTAuMTgwOCAyMC45MDk5IDExLjI5NzVMMjEuOTE0MyAxNS4zMDM2QzIyLjAxMTYgMTUuNjkxOCAyMi4zMDY1IDE1Ljk5NzggMjIuNjg2NyAxNi4xMDNMMjYuMzYxMSAxNy4xMTg3QzI3LjQzNyAxNy40MTYyIDI3LjQzNyAxOC45Njc2IDI2LjM2MTEgMTkuMjY1MUwyMi42NzYxIDIwLjI4NEMyMi4zMDE4IDIwLjM4NzQgMjIuMDA4NyAyMC42ODQ1IDIxLjkwNjggMjEuMDY1TDIwLjkwNDYgMjQuODEyNUMyMC42MTE3IDI1LjkwNTggMTkuMDg2MSAyNS45MDU5IDE4Ljc5MzMgMjQuODEyNUwxNy43OTExIDIxLjA2NUMxNy42ODkzIDIwLjY4NDcgMTcuMzk3IDIwLjM4NzUgMTcuMDIyOSAyMC4yODRMMTMuMzM2OCAxOS4yNjUxQzEyLjI2MTQgMTguOTY3MyAxMi4yNjE1IDE3LjQxNjUgMTMuMzM2OCAxNy4xMTg3TDE3LjAxMTIgMTYuMTAzQzE3LjM5MTYgMTUuOTk3OCAxNy42ODc0IDE1LjY5MTkgMTcuNzg0NyAxNS4zMDM2TDE4Ljc4OTEgMTEuMjk3NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPHBhdGggZD0iTTEwLjAzNDMgNy4wMjQyNUMxMC4zMDY4IDUuODk0NDQgMTEuODg2OCA1Ljg5NDQ0IDEyLjE1OTQgNy4wMjQyNUwxMi42OTg5IDkuMjYyOThDMTIuNzkyNyA5LjY1MTc0IDEzLjA4NTEgOS45NTg4NyAxMy40NjQgMTAuMDY3OUwxNS41NzczIDEwLjY3NTFDMTYuNjM5MyAxMC45ODAzIDE2LjYzOTMgMTIuNTEwOSAxNS41NzczIDEyLjgxNjFMMTMuNDUzMyAxMy40MjY1QzEzLjA4MDIgMTMuNTMzOCAxMi43OTA4IDEzLjgzMzkgMTIuNjkyNSAxNC4yMTUxTDEyLjE1NTEgMTYuMzA0QzExLjg3IDE3LjQxMTYgMTAuMzIzNiAxNy40MTE2IDEwLjAzODUgMTYuMzA0TDkuNTAwMDMgMTQuMjE1MUM5LjQwMTczIDEzLjgzMzkgOS4xMTIzNSAxMy41MzM3IDguNzM5MyAxMy40MjY1TDYuNjE1MjQgMTIuODE2MUM1LjU1Mzc4IDEyLjUxMDYgNS41NTM2NCAxMC45ODA0IDYuNjE1MjQgMTAuNjc1MUw4LjcyODYyIDEwLjA2NzlDOS4xMDc2IDkuOTU4OTggOS4zOTk3OCA5LjY1MTg0IDkuNDkzNjIgOS4yNjI5OEwxMC4wMzQzIDcuMDI0MjVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjc2Ii8+CjxwYXRoIGQ9Ik0yNS45MDI4IDYuNzMzMTNDMjYuMTg3OCA1LjYyNTQxIDI3LjczNDMgNS42MjU0MSAyOC4wMTkzIDYuNzMzMTNMMjguMjAzMSA3LjQ0Njc5QzI4LjMwMyA3LjgzNDMxIDI4LjYwMDEgOC4xMzcwNSAyOC45ODA5IDguMjM5NzVMMjkuNTM0NCA4LjM4OTY1QzMwLjYxOTIgOC42ODIxMiAzMC42MTkzIDEwLjI0NjkgMjkuNTM0NCAxMC41MzkzTDI4Ljk2OTIgMTAuNjkxNEMyOC41OTQ0IDEwLjc5MjUgMjguMjk5OSAxMS4wODgzIDI4LjE5NTYgMTEuNDY4TDI4LjAxNTEgMTIuMTI4NUMyNy43MTc0IDEzLjIxMjggMjYuMjA0NyAxMy4yMTI4IDI1LjkwNyAxMi4xMjg1TDI1LjcyNTQgMTEuNDY4QzI1LjYyMTEgMTEuMDg4MiAyNS4zMjY4IDEwLjc5MjQgMjQuOTUxOCAxMC42OTE0TDI0LjM4NzcgMTAuNTM5M0MyMy4zMDI2IDEwLjI0NyAyMy4zMDI2IDguNjgxOTggMjQuMzg3NyA4LjM4OTY1TDI0Ljk0MDEgOC4yMzk3NUMyNS4zMjExIDguMTM3MDkgMjUuNjE5MSA3LjgzNDQ2IDI1LjcxOSA3LjQ0Njc5TDI1LjkwMjggNi43MzMxM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPC9zdmc+Cg==", - ruleChainTypes = RuleChainType.CORE, docUrl = "https://thingsboard.io/docs/user-guide/rule-engine-2-0/nodes/external/ai-request/" ) public final class TbAiNode extends TbAbstractExternalNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java index 13dfe74902..6ebf1a3d57 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java @@ -35,7 +35,7 @@ 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 org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; 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 be38d70e30..21b8ea1846 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 @@ -41,6 +41,9 @@ 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.ALARM_ACK; import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_CLEAR; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_CREATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_SEVERITY_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_UPDATED; 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; @@ -78,7 +81,7 @@ public abstract class AbstractTbMsgPushNode (active && eval(alarmRule.getCondition(), data)) ? + AlarmEvalResult.TRUE : AlarmEvalResult.FALSE; + case DURATION -> evalDuration(data, active); + case REPEATING -> evalRepeating(data, active); + }; } private boolean isActive(DataSnapshot data, long eventTs) { @@ -600,4 +596,5 @@ class AlarmRuleState { return null; } } + } 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 81897e4bc5..89cbf8b207 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 @@ -48,6 +48,7 @@ import java.util.function.BiFunction; @Data @Slf4j +@Deprecated class AlarmState { public static final String ERROR_MSG = "Failed to process alarm rule for Device [%s]: %s"; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DataSnapshot.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DataSnapshot.java index cf74563d0d..0e588fe6cb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DataSnapshot.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DataSnapshot.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +@Deprecated class DataSnapshot { private volatile boolean ready; 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 c6c03a0269..7977d1e150 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 @@ -80,6 +80,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE import static org.thingsboard.server.common.data.msg.TbMsgType.TIMESERIES_UPDATED; @Slf4j +@Deprecated class DeviceState { private final boolean persistState; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtx.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtx.java index 41908963b7..367a19ecaa 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtx.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtx.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.profile; +@Deprecated public interface DynamicPredicateValueCtx { EntityKeyValue getTenantValue(String key); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtxImpl.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtxImpl.java index 50f649baa6..2a960e75c8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtxImpl.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtxImpl.java @@ -29,6 +29,7 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; @Slf4j +@Deprecated public class DynamicPredicateValueCtxImpl implements DynamicPredicateValueCtx { private final TenantId tenantId; private CustomerId customerId; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/EntityKeyValue.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/EntityKeyValue.java index 724d96d87d..c1aab735ab 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/EntityKeyValue.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/EntityKeyValue.java @@ -20,6 +20,7 @@ import lombok.Getter; import org.thingsboard.server.common.data.kv.DataType; @EqualsAndHashCode +@Deprecated class EntityKeyValue { @Getter diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java index 7430f8f0d8..3e3978518c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java @@ -45,6 +45,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +@Deprecated class ProfileState { private DeviceProfile deviceProfile; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/SnapshotUpdate.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/SnapshotUpdate.java index 462f539381..d9847ec110 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/SnapshotUpdate.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/SnapshotUpdate.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; import java.util.Set; +@Deprecated class SnapshotUpdate { @Getter 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 b56a4c314b..e9d0ace4a8 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 @@ -51,17 +51,18 @@ import java.util.concurrent.TimeUnit; @Slf4j @RuleNode( type = ComponentType.ACTION, - name = "device profile", + name = "device profile (deprecated)", customRelations = true, relationTypes = {"Alarm Created", "Alarm Updated", "Alarm Severity Updated", "Alarm Cleared", "Success", "Failure"}, version = 1, configClazz = TbDeviceProfileNodeConfiguration.class, - nodeDescription = "Process device messages based on device profile settings", + nodeDescription = "Process device messages based on device profile settings (deprecated)", nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. The output relation type is either " + - "'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.", + "'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.", configDirective = "tbActionNodeDeviceProfileConfig", docUrl = "https://thingsboard.io/docs/user-guide/rule-engine-2-0/nodes/action/device-profile/" ) +@Deprecated public class TbDeviceProfileNode implements TbNode { private TbDeviceProfileNodeConfiguration config; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeConfiguration.java index e58a5110ff..b01201820f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeConfiguration.java @@ -21,6 +21,7 @@ import org.thingsboard.rule.engine.api.NodeConfiguration; @Data @JsonIgnoreProperties(ignoreUnknown = true) +@Deprecated public class TbDeviceProfileNodeConfiguration implements NodeConfiguration { private boolean persistAlarmRulesState; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmRuleState.java index 7a6277ed27..35d8950c33 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmRuleState.java @@ -22,6 +22,7 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor +@Deprecated public class PersistedAlarmRuleState { private long lastEventTs; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmState.java index a0759763dd..ec5ba6fd6f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedAlarmState.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity; import java.util.Map; @Data +@Deprecated public class PersistedAlarmState { private Map createRuleStates; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedDeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedDeviceState.java index 974aad1f25..58dbf44c0f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedDeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/state/PersistedDeviceState.java @@ -20,6 +20,7 @@ import lombok.Data; import java.util.Map; @Data +@Deprecated public class PersistedDeviceState { Map alarmStates; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNode.java index b3dc9e5db8..cc635d8fb5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNode.java @@ -25,6 +25,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; @@ -40,13 +41,13 @@ import static org.thingsboard.server.common.data.DataConstants.SCOPE; @RuleNode( type = ComponentType.ACTION, - name = "calculated fields", + name = "calculated fields and alarm rules", configClazz = EmptyNodeConfiguration.class, - nodeDescription = "Pushes incoming messages to calculated fields service", - nodeDetails = "Node enables the processing of calculated fields without persisting incoming messages to the database. " + - "By default, the processing of calculated fields is triggered by the save attributes and save time series nodes. " + + nodeDescription = "Pushes incoming messages to calculated fields and alarm rules services", + nodeDetails = "Node enables the processing of calculated fields and alarm rules without persisting incoming messages to the database. " + + "By default, the processing of calculated fields and alarm rules is triggered by the save attributes and save time series nodes. " + "This rule node accepts the same messages as these nodes but allows you to trigger the processing of calculated " + - "fields independently, ensuring that derived data can be computed and utilized in real time without storing the original message in the database.", + "fields or alarm rules independently, ensuring that derived data can be computed and utilized in real time without storing the original message in the database.", configDirective = "tbNodeEmptyConfig", icon = "published_with_changes", docUrl = "https://thingsboard.io/docs/user-guide/rule-engine-2-0/nodes/action/calculated-fields/" @@ -101,11 +102,15 @@ public class TbCalculatedFieldsNode implements TbNode { ctx.tellSuccess(msg); return; } + AttributeScope scope = resolveScope(ctx, msg); + if (scope == null) { + return; + } AttributesSaveRequest attributesSaveRequest = AttributesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) - .scope(AttributeScope.valueOf(msg.getMetaData().getValue(SCOPE))) + .scope(scope) .entries(newAttributes) .strategy(AttributesSaveRequest.Strategy.CF_ONLY) .previousCalculatedFieldIds(msg.getPreviousCalculatedFieldIds()) @@ -116,4 +121,20 @@ public class TbCalculatedFieldsNode implements TbNode { ctx.getTelemetryService().saveAttributes(attributesSaveRequest); } + private AttributeScope resolveScope(TbContext ctx, TbMsg msg) { + String scopeStr = msg.getMetaData().getValue(SCOPE); + + if (StringUtils.isEmpty(scopeStr)) { + ctx.tellFailure(msg, new IllegalArgumentException("Attribute scope is missing")); + return null; + } + + try { + return AttributeScope.valueOf(scopeStr); + } catch (IllegalArgumentException e) { + ctx.tellFailure(msg, new IllegalArgumentException("Invalid attribute scope: " + scopeStr)); + return null; + } + } + } 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 b9ed731276..3a882ea317 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 @@ -41,10 +41,7 @@ import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import java.util.List; -import java.util.Map; -import java.util.Objects; import java.util.UUID; -import java.util.function.Function; import java.util.stream.Collectors; import static org.thingsboard.rule.engine.telemetry.settings.AttributesProcessingSettings.Advanced; @@ -54,6 +51,7 @@ import static org.thingsboard.rule.engine.telemetry.settings.AttributesProcessin 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; +import static org.thingsboard.server.dao.util.KvUtils.filterChangedAttr; @RuleNode( type = ComponentType.ACTION, @@ -216,24 +214,6 @@ public class TbMsgAttributesNode implements TbNode { .build()); } - private 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(AttributeScope scope) { return config.isSendAttributesUpdatedNotification() && AttributeScope.CLIENT_SCOPE != scope; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 8621236735..c707a1372d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -31,7 +31,6 @@ import org.thingsboard.rule.engine.telemetry.strategy.ProcessingStrategy; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -50,6 +48,7 @@ import static org.thingsboard.rule.engine.telemetry.settings.TimeseriesProcessin import static org.thingsboard.rule.engine.telemetry.settings.TimeseriesProcessingSettings.OnEveryMessage; import static org.thingsboard.rule.engine.telemetry.settings.TimeseriesProcessingSettings.WebSocketsOnly; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; +import static org.thingsboard.server.dao.util.KvUtils.toTsKvEntryList; @RuleNode( type = ComponentType.ACTION, @@ -148,12 +147,7 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.tellFailure(msg, new IllegalArgumentException("Msg body is empty: " + src)); return; } - List tsKvEntryList = new ArrayList<>(); - for (Map.Entry> tsKvEntry : tsKvMap.entrySet()) { - for (KvEntry kvEntry : tsKvEntry.getValue()) { - tsKvEntryList.add(new BasicTsKvEntry(tsKvEntry.getKey(), kvEntry)); - } - } + List tsKvEntryList = toTsKvEntryList(tsKvMap); String ttlValue = msg.getMetaData().getValue("TTL"); long ttl = !StringUtils.isEmpty(ttlValue) ? Long.parseLong(ttlValue) : config.getDefaultTTL(); if (ttl == 0L) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java index ae83a41856..74d2c7ffb1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java @@ -18,14 +18,13 @@ package org.thingsboard.rule.engine.util; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; import org.thingsboard.server.common.data.id.AiModelId; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CalculatedFieldId; -import org.thingsboard.server.common.data.id.CalculatedFieldLinkId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceId; @@ -170,20 +169,15 @@ public class TenantIdLoader { case CALCULATED_FIELD: tenantEntity = ctx.getCalculatedFieldService().findById(ctxTenantId, new CalculatedFieldId(id)); break; - case CALCULATED_FIELD_LINK: - CalculatedFieldLink calculatedFieldLink = ctx.getCalculatedFieldService().findCalculatedFieldLinkById(ctxTenantId, new CalculatedFieldLinkId(id)); - if (calculatedFieldLink != null) { - tenantEntity = ctx.getCalculatedFieldService().findById(ctxTenantId, calculatedFieldLink.getCalculatedFieldId()); - } else { - tenantEntity = null; - } - break; case JOB: tenantEntity = ctx.getJobService().findJobById(ctxTenantId, new JobId(id)); break; case AI_MODEL: tenantEntity = ctx.getAiModelService().findAiModelById(ctxTenantId, new AiModelId(id)).orElse(null); break; + case API_KEY: + tenantEntity = ctx.getApiKeyService().findApiKeyById(ctxTenantId, new ApiKeyId(id)); + break; default: throw new RuntimeException("Unexpected entity type: " + entityId.getEntityType()); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java index 30f7b8ab29..529625eba3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java @@ -17,7 +17,7 @@ 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.FluentFuture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -49,6 +49,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.function.Consumer; +import static com.google.common.util.concurrent.Futures.immediateFuture; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; @@ -112,8 +113,8 @@ class TbClearAlarmNodeTest { .endTs(oldEndDate) .build(); - when(alarmDetailsScriptMock.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); - when(alarmServiceMock.findLatestActiveByOriginatorAndType(tenantId, msgOriginator, "SomeType")).thenReturn(activeAlarm); + when(alarmDetailsScriptMock.executeJsonAsync(msg)).thenReturn(immediateFuture(null)); + when(alarmServiceMock.findLatestActiveByOriginatorAndTypeAsync(tenantId, msgOriginator, "SomeType")).thenReturn(FluentFuture.from(immediateFuture(activeAlarm))); when(alarmServiceMock.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class))) .thenReturn(AlarmApiCallResult.builder() .successful(true) @@ -172,8 +173,8 @@ class TbClearAlarmNodeTest { .build(); expectedAlarm.setId(id); - when(alarmDetailsScriptMock.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); - when(alarmServiceMock.findAlarmById(tenantId, id)).thenReturn(activeAlarm); + when(alarmDetailsScriptMock.executeJsonAsync(msg)).thenReturn(immediateFuture(null)); + when(alarmServiceMock.findAlarmByIdAsync(tenantId, id)).thenReturn(immediateFuture(activeAlarm)); when(alarmServiceMock.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class))) .thenReturn(AlarmApiCallResult.builder() .successful(true) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/ai/TbAiNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/ai/TbAiNodeTest.java index 1e01d5fac2..6acf820fc1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/ai/TbAiNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/ai/TbAiNodeTest.java @@ -67,7 +67,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.ai.AiModelService; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.resource.TbResourceDataCache; 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 fd8c931a5e..ae45d83684 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 @@ -264,7 +264,6 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); verify(ctx).enqueueForTellNext(theMsg2, "Alarm Updated"); - } @Test diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNodeTest.java new file mode 100644 index 0000000000..e4203d6497 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbCalculatedFieldsNodeTest.java @@ -0,0 +1,198 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.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.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; +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.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; +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.TimeseriesSaveRequest; +import org.thingsboard.server.common.data.AttributeScope; +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.TbMsgMetaData; + +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.assertArg; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +class TbCalculatedFieldsNodeTest { + + final TenantId tenantId = TenantId.fromUUID(UUID.fromString("7361ca62-7688-4d78-b374-bc3d77e12dba")); + final DeviceId deviceId = new DeviceId(UUID.fromString("21c55f8d-0c5c-47b3-a344-9657e194b0f6")); + + @Spy + TbCalculatedFieldsNode node; + + @Mock + TbContext ctxMock; + @Mock + RuleEngineTelemetryService telemetryServiceMock; + + @BeforeEach + void setUp() { + lenient().when(ctxMock.getTenantId()).thenReturn(tenantId); + lenient().when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); + var config = new EmptyNodeConfiguration().defaultConfiguration(); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + } + + @ParameterizedTest + @EnumSource(TbMsgType.class) + void givenInvalidMsgType_whenOnMsg_thenTellFailure(TbMsgType msgType) { + if (TbMsgType.POST_TELEMETRY_REQUEST == msgType || TbMsgType.POST_ATTRIBUTES_REQUEST == msgType) { + return; + } + + // GIVEN + var msg = TbMsg.newMsg() + .type(msgType) + .originator(deviceId) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(TbMsgMetaData.EMPTY) + .build(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); + then(ctxMock).should().tellFailure(eq(msg), actualError.capture()); + assertThat(actualError.getValue()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported msg type: " + msg.getType()); + } + + @Test + void givenTelemetryMsg_whenOnMsg_thenPushToTelemetryService() { + // GIVEN + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(TbMsgMetaData.EMPTY) + .build(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + then(telemetryServiceMock).should().saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.CF_ONLY) + )); + } + + @ParameterizedTest + @ValueSource(strings = {"POST_TELEMETRY_REQUEST", "POST_ATTRIBUTES_REQUEST"}) + void givenEmptyTelemetryOrAttributesMsg_whenOnMsg_thenTellSuccess(String msgType) { + // GIVEN + var msg = TbMsg.newMsg() + .type(TbMsgType.valueOf(msgType)) + .originator(deviceId) + .data(TbMsg.EMPTY_JSON_OBJECT) + .metaData(TbMsgMetaData.EMPTY) + .build(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + then(ctxMock).should().tellSuccess(msg); + } + + @Test + void givenEmptyAttributeScope_whenOnMsg_thenTellFailure() { + // GIVEN + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .data(JacksonUtil.newObjectNode().put("active", true).toString()) + .metaData(TbMsgMetaData.EMPTY) + .build(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); + then(ctxMock).should().tellFailure(eq(msg), actualError.capture()); + assertThat(actualError.getValue()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Attribute scope is missing"); + } + + @Test + void givenInvalidAttributeScope_whenOnMsg_thenTellFailure() { + // GIVEN + String invalidScope = "INVALID_SCOPE"; + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .data(JacksonUtil.newObjectNode().put("active", true).toString()) + .metaData(new TbMsgMetaData(Map.of("scope", invalidScope))) + .build(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); + then(ctxMock).should().tellFailure(eq(msg), actualError.capture()); + assertThat(actualError.getValue()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid attribute scope: " + invalidScope); + } + + @Test + void givenAttributesMsg_whenOnMsg_thenPushToTelemetryService() { + // GIVEN + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .data(JacksonUtil.newObjectNode().put("active", true).toString()) + .metaData(new TbMsgMetaData(Map.of("scope", AttributeScope.SERVER_SCOPE.name()))) + .build(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + then(telemetryServiceMock).should().saveAttributes(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest.getStrategy()).isEqualTo(AttributesSaveRequest.Strategy.CF_ONLY) + )); + } + +} 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 index c71d096045..d7aa914818 100644 --- 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 @@ -46,7 +46,7 @@ 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; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; import java.util.List; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 416a60a961..3ff3f76cd0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -49,7 +49,7 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; import java.util.ArrayList; 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 ef5e9db38c..23bc172402 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 @@ -29,7 +29,6 @@ import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache; import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; import org.thingsboard.rule.engine.api.RuleEngineRpcService; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -46,14 +45,12 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.cf.CalculatedFieldLink; import org.thingsboard.server.common.data.domain.Domain; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.job.Job; @@ -64,6 +61,7 @@ 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.oauth2.OAuth2Client; +import org.thingsboard.server.common.data.pat.ApiKey; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.rpc.Rpc; @@ -80,6 +78,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.domain.DomainService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.mobile.MobileAppBundleService; import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.notification.NotificationRequestService; @@ -88,11 +87,11 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -170,6 +169,8 @@ public class TenantIdLoaderTest { private JobService jobService; @Mock private AiModelService aiModelService; + @Mock + private ApiKeyService apiKeyService; private TenantId tenantId; private TenantProfileId tenantProfileId; @@ -208,159 +209,119 @@ public class TenantIdLoaderTest { case CUSTOMER: Customer customer = new Customer(); customer.setTenantId(tenantId); - when(ctx.getCustomerService()).thenReturn(customerService); doReturn(customer).when(customerService).findCustomerById(eq(tenantId), any()); - break; case USER: User user = new User(); user.setTenantId(tenantId); - when(ctx.getUserService()).thenReturn(userService); doReturn(user).when(userService).findUserById(eq(tenantId), any()); - break; case ASSET: Asset asset = new Asset(); asset.setTenantId(tenantId); - when(ctx.getAssetService()).thenReturn(assetService); doReturn(asset).when(assetService).findAssetById(eq(tenantId), any()); - break; case DEVICE: Device device = new Device(); device.setTenantId(tenantId); - when(ctx.getDeviceService()).thenReturn(deviceService); doReturn(device).when(deviceService).findDeviceById(eq(tenantId), any()); - break; case ALARM: Alarm alarm = new Alarm(); alarm.setTenantId(tenantId); - when(ctx.getAlarmService()).thenReturn(alarmService); doReturn(alarm).when(alarmService).findAlarmById(eq(tenantId), any()); - break; case RULE_CHAIN: RuleChain ruleChain = new RuleChain(); ruleChain.setTenantId(tenantId); - when(ctx.getRuleChainService()).thenReturn(ruleChainService); doReturn(ruleChain).when(ruleChainService).findRuleChainById(eq(tenantId), any()); - break; case ENTITY_VIEW: EntityView entityView = new EntityView(); entityView.setTenantId(tenantId); - when(ctx.getEntityViewService()).thenReturn(entityViewService); doReturn(entityView).when(entityViewService).findEntityViewById(eq(tenantId), any()); - break; case DASHBOARD: Dashboard dashboard = new Dashboard(); dashboard.setTenantId(tenantId); - when(ctx.getDashboardService()).thenReturn(dashboardService); doReturn(dashboard).when(dashboardService).findDashboardById(eq(tenantId), any()); - break; case EDGE: Edge edge = new Edge(); edge.setTenantId(tenantId); - when(ctx.getEdgeService()).thenReturn(edgeService); doReturn(edge).when(edgeService).findEdgeById(eq(tenantId), any()); - break; case OTA_PACKAGE: OtaPackage otaPackage = new OtaPackage(); otaPackage.setTenantId(tenantId); - when(ctx.getOtaPackageService()).thenReturn(otaPackageService); doReturn(otaPackage).when(otaPackageService).findOtaPackageInfoById(eq(tenantId), any()); - break; case ASSET_PROFILE: AssetProfile assetProfile = new AssetProfile(); assetProfile.setTenantId(tenantId); - when(ctx.getAssetProfileCache()).thenReturn(assetProfileCache); doReturn(assetProfile).when(assetProfileCache).get(eq(tenantId), any(AssetProfileId.class)); - break; case DEVICE_PROFILE: DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setTenantId(tenantId); - when(ctx.getDeviceProfileCache()).thenReturn(deviceProfileCache); doReturn(deviceProfile).when(deviceProfileCache).get(eq(tenantId), any(DeviceProfileId.class)); - break; case WIDGET_TYPE: WidgetType widgetType = new WidgetType(); widgetType.setTenantId(tenantId); - when(ctx.getWidgetTypeService()).thenReturn(widgetTypeService); doReturn(widgetType).when(widgetTypeService).findWidgetTypeById(eq(tenantId), any()); - break; case WIDGETS_BUNDLE: WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTenantId(tenantId); - when(ctx.getWidgetBundleService()).thenReturn(widgetsBundleService); doReturn(widgetsBundle).when(widgetsBundleService).findWidgetsBundleById(eq(tenantId), any()); - break; case RPC: Rpc rpc = new Rpc(); rpc.setTenantId(tenantId); - when(ctx.getRpcService()).thenReturn(rpcService); doReturn(rpc).when(rpcService).findRpcById(eq(tenantId), any()); - break; case QUEUE: Queue queue = new Queue(); queue.setTenantId(tenantId); - when(ctx.getQueueService()).thenReturn(queueService); doReturn(queue).when(queueService).findQueueById(eq(tenantId), any()); - break; case API_USAGE_STATE: ApiUsageState apiUsageState = new ApiUsageState(); apiUsageState.setTenantId(tenantId); - when(ctx.getRuleEngineApiUsageStateService()).thenReturn(ruleEngineApiUsageStateService); doReturn(apiUsageState).when(ruleEngineApiUsageStateService).findApiUsageStateById(eq(tenantId), any()); - break; case TB_RESOURCE: TbResource tbResource = new TbResource(); tbResource.setTenantId(tenantId); - when(ctx.getResourceService()).thenReturn(resourceService); doReturn(tbResource).when(resourceService).findResourceInfoById(eq(tenantId), any()); - break; case RULE_NODE: RuleNode ruleNode = new RuleNode(); - when(ctx.getRuleChainService()).thenReturn(ruleChainService); doReturn(ruleNode).when(ruleChainService).findRuleNodeById(eq(tenantId), any()); - break; case TENANT_PROFILE: TenantProfile tenantProfile = new TenantProfile(tenantProfileId); - when(ctx.getTenantProfile()).thenReturn(tenantProfile); - break; case NOTIFICATION_TARGET: NotificationTarget notificationTarget = new NotificationTarget(); @@ -422,12 +383,6 @@ public class TenantIdLoaderTest { when(ctx.getCalculatedFieldService()).thenReturn(calculatedFieldService); doReturn(calculatedField).when(calculatedFieldService).findById(eq(tenantId), any()); break; - case CALCULATED_FIELD_LINK: - CalculatedFieldLink calculatedFieldLink = new CalculatedFieldLink(); - calculatedFieldLink.setTenantId(tenantId); - when(ctx.getCalculatedFieldService()).thenReturn(calculatedFieldService); - doReturn(calculatedFieldLink).when(calculatedFieldService).findCalculatedFieldLinkById(eq(tenantId), any()); - break; case JOB: Job job = new Job(); job.setTenantId(tenantId); @@ -440,6 +395,12 @@ public class TenantIdLoaderTest { when(ctx.getAiModelService()).thenReturn(aiModelService); doReturn(Optional.of(aiModel)).when(aiModelService).findAiModelById(eq(tenantId), any()); break; + case API_KEY: + ApiKey apiKey = new ApiKey(); + apiKey.setTenantId(tenantId); + when(ctx.getApiKeyService()).thenReturn(apiKeyService); + doReturn(apiKey).when(apiKeyService).findApiKeyById(eq(tenantId), any()); + break; default: throw new RuntimeException("Unexpected originator EntityType " + entityType); } diff --git a/tools/pom.xml b/tools/pom.xml index 5a9fd3df24..4d94ff81df 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index f21aabfbfa..d9b28c7a5e 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-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 1c45984257..dde415bd91 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -187,15 +187,15 @@ coap: # CoAP DTLS bind port bind_port: "${COAP_DTLS_BIND_PORT:5684}" # CoAP DTLS connection ID length. RFC 9146, Connection Identifier for DTLS 1.2 - # Default: off + # Default: off.
# Control usage of DTLS connection ID length (CID). - # - 'off' to deactivate it. - # - 'on' to activate Connection ID support (same as CID 0 or more 0). - # - A positive value defines generated CID size in bytes. - # - A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic). - # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used - # - A value that are > 4: MultiNodeConnectionIdGenerator is used - connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + #
  • 'off' to deactivate it.
  • + #
  • 'on' to activate Connection ID support (same as CID 0 or more 0).
  • + #
  • A positive value defines generated CID size in bytes.
  • + #
  • A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic).
  • + #
  • A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used
  • + #
  • A value that are > 4: MultiNodeConnectionIdGenerator is used
+ connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:8}" # Specify the MTU (Maximum Transmission Unit). # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. # Default = 1024 diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 242b7c804f..a5d093e726 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 3d395cf641..5e744d7fca 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-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 8a57c5829b..21687bb697 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -166,16 +166,16 @@ transport: dtls: # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${LWM2M_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" - # CoAP DTLS connection ID length for LWM2M. RFC 9146, Connection Identifier for DTLS 1.2 - # Default: off + # LWM2M DTLS connection ID length for LWM2M. RFC 9146, Connection Identifier for DTLS 1.2 + # Default: off.
# Control usage of DTLS connection ID length (CID). - # - 'off' to deactivate it. - # - 'on' to activate Connection ID support (same as CID 0 or more 0). - # - A positive value defines generated CID size in bytes. - # - A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic). - # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used - # - A value that are > 4: MultiNodeConnectionIdGenerator is used - connection_id_length: "${LWM2M_DTLS_CONNECTION_ID_LENGTH:}" + #
  • 'off' to deactivate it.
  • + #
  • 'on' to activate Connection ID support (same as CID 0 or more 0).
  • + #
  • A positive value defines generated CID size in bytes.
  • + #
  • A value of 0 means we accept using CID but will not generate one for foreign peer (enables support but not for incoming traffic).
  • + #
  • A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used
  • + #
  • A value that are > 4: MultiNodeConnectionIdGenerator is used
+ connection_id_length: "${LWM2M_DTLS_CONNECTION_ID_LENGTH:8}" server: # LwM2M Server ID id: "${LWM2M_SERVER_ID:123}" diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 5c07946e55..c53b606704 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index edecc51d6e..83c0fccd83 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 136d0883ad..39f72755e5 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index b17c3e1d35..329f249912 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "4.2.1.2", + "version": "4.3.0.1", "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", diff --git a/ui-ngx/patches/@iplab+ngx-color-picker+18.0.1.patch b/ui-ngx/patches/@iplab+ngx-color-picker+18.0.1.patch new file mode 100644 index 0000000000..c8090076cc --- /dev/null +++ b/ui-ngx/patches/@iplab+ngx-color-picker+18.0.1.patch @@ -0,0 +1,97 @@ +diff --git a/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hex-input/hex-input.component.mjs b/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hex-input/hex-input.component.mjs +index bf5480b..02c8246 100644 +--- a/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hex-input/hex-input.component.mjs ++++ b/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hex-input/hex-input.component.mjs +@@ -29,10 +29,10 @@ export class HexComponent { + } + } + static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HexComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HexComponent, isStandalone: true, selector: "hex-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, prefixValue: { classPropertyName: "prefixValue", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } ++ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HexComponent, isStandalone: true, selector: "hex-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, prefixValue: { classPropertyName: "prefixValue", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HexComponent, decorators: [{ + type: Component, +- args: [{ selector: `hex-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] ++ args: [{ selector: `hex-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] + }] }); + //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGV4LWlucHV0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lwbGFiL25neC1jb2xvci1waWNrZXIvc3JjL2xpYi9jb21wb25lbnRzL3BhcnRzL2lucHV0cy9oZXgtaW5wdXQvaGV4LWlucHV0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lwbGFiL25neC1jb2xvci1waWNrZXIvc3JjL2xpYi9jb21wb25lbnRzL3BhcnRzL2lucHV0cy9oZXgtaW5wdXQvaGV4LWlucHV0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsdUJBQXVCLEVBQUUsZ0JBQWdCLEVBQWUsS0FBSyxFQUFFLEtBQUssRUFBZSxNQUFNLGVBQWUsQ0FBQztBQUM3SCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0saUNBQWlDLENBQUM7O0FBY3hELE1BQU0sT0FBTyxZQUFZO0lBWHpCO1FBYVcsVUFBSyxHQUF1QixLQUFLLENBQUMsUUFBUSxFQUFTLENBQUM7UUFFcEQsaUJBQVksR0FBeUIsS0FBSyxDQUFtQixLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxDQUFDLENBQUM7UUFFckgsZ0JBQVcsR0FBd0IsS0FBSyxDQUFTLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0tBMkJwRjtJQXpCRyxJQUFXLEtBQUs7UUFDWixPQUFPLElBQUksQ0FBQyxXQUFXLEVBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ2xJLENBQUM7SUFFTSxhQUFhLENBQUMsS0FBb0IsRUFBRSxVQUFrQjtRQUN6RCxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUV4RCxJQUNBLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxLQUFLLEVBQUUsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxLQUFLLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDO2VBQ2xGLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDMUMsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNoQyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBRWhDOzs7OztlQUtHO1lBQ0gsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLEtBQUssS0FBSyxFQUFFLENBQUM7Z0JBQ3ZFLE1BQU0sUUFBUSxHQUFHLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDeEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDN0IsQ0FBQztRQUNMLENBQUM7SUFDTCxDQUFDOzhHQWhDUSxZQUFZO2tHQUFaLFlBQVksZ2dCQ2Z6Qiw0TUFLTTs7MkZEVU8sWUFBWTtrQkFYeEIsU0FBUzsrQkFDSSxxQkFBcUIsbUJBT2QsdUJBQXVCLENBQUMsTUFBTSxjQUNuQyxJQUFJIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgYm9vbGVhbkF0dHJpYnV0ZSwgSW5wdXRTaWduYWwsIGlucHV0LCBtb2RlbCwgTW9kZWxTaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgQ29sb3IgfSBmcm9tICcuLi8uLi8uLi8uLi9oZWxwZXJzL2NvbG9yLmNsYXNzJztcclxuXHJcblxyXG5AQ29tcG9uZW50KHtcclxuICAgIHNlbGVjdG9yOiBgaGV4LWlucHV0LWNvbXBvbmVudGAsXHJcbiAgICB0ZW1wbGF0ZVVybDogYC4vaGV4LWlucHV0LmNvbXBvbmVudC5odG1sYCxcclxuICAgIHN0eWxlVXJsczogW1xyXG4gICAgICAgIGAuLy4uLy4uL2Jhc2Uuc3R5bGUuc2Nzc2AsXHJcbiAgICAgICAgYC4vLi4vaW5wdXQuY29tcG9uZW50LnNjc3NgLFxyXG4gICAgICAgIGAuL2hleC1pbnB1dC5jb21wb25lbnQuc2Nzc2BcclxuICAgIF0sXHJcbiAgICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcclxuICAgIHN0YW5kYWxvbmU6IHRydWVcclxufSlcclxuZXhwb3J0IGNsYXNzIEhleENvbXBvbmVudCB7XHJcblxyXG4gICAgcHVibGljIGNvbG9yOiBNb2RlbFNpZ25hbDxDb2xvcj4gPSBtb2RlbC5yZXF1aXJlZDxDb2xvcj4oKTtcclxuXHJcbiAgICBwdWJsaWMgbGFiZWxWaXNpYmxlOiBJbnB1dFNpZ25hbDxib29sZWFuPiA9IGlucHV0PGJvb2xlYW4sIGJvb2xlYW4+KGZhbHNlLCB7IGFsaWFzOiAnbGFiZWwnLCB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSk7XHJcblxyXG4gICAgcHVibGljIHByZWZpeFZhbHVlOiBJbnB1dFNpZ25hbDxzdHJpbmc+ID0gaW5wdXQ8c3RyaW5nPignJywgeyBhbGlhczogJ3ByZWZpeCcgfSk7XHJcblxyXG4gICAgcHVibGljIGdldCB2YWx1ZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5wcmVmaXhWYWx1ZSgpICsgKHRoaXMuY29sb3IoKSA/IHRoaXMuY29sb3IoKS50b0hleFN0cmluZyh0aGlzLmNvbG9yKCkuZ2V0UmdiYSgpLmFscGhhIDwgMSkucmVwbGFjZSgnIycsICcnKSA6ICcnKTtcclxuICAgIH1cclxuXHJcbiAgICBwdWJsaWMgb25JbnB1dENoYW5nZShldmVudDogS2V5Ym9hcmRFdmVudCwgaW5wdXRWYWx1ZTogc3RyaW5nKTogdm9pZCB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBpbnB1dFZhbHVlLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnIycsICcnKTtcclxuXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICgoZXZlbnQua2V5Q29kZSA9PT0gMTMgfHwgZXZlbnQua2V5LnRvTG93ZXJDYXNlKCkgPT09ICdlbnRlcicpICYmIHZhbHVlLmxlbmd0aCA9PT0gMylcclxuICAgICAgICB8fCB2YWx1ZS5sZW5ndGggPT09IDYgfHwgdmFsdWUubGVuZ3RoID09PSA4KSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGhleCA9IHBhcnNlSW50KHZhbHVlLCAxNik7XHJcbiAgICAgICAgICAgIGNvbnN0IGhleFN0ciA9IGhleC50b1N0cmluZygxNik7XHJcblxyXG4gICAgICAgICAgICAvKipcclxuICAgICAgICAgICAgICogaWYgdmFsdWUgaXMgdmFsaWRcclxuICAgICAgICAgICAgICogY2hhbmdlIGNvbG9yIGVsc2UgZG8gbm90aGluZ1xyXG4gICAgICAgICAgICAgKiBhZnRlciBwYXJzaW5nIG51bWJlciBsZWFkaW5nIDAgaXMgcmVtb3ZlZCxcclxuICAgICAgICAgICAgICogY29tcGFyZSBsZW5ndGggYW5kIGFkZCBsZWFkaW5nIDAgYmVmb3JlIGNvbXBhcmluZyB0d28gdmFsdWVzXHJcbiAgICAgICAgICAgICAqL1xyXG4gICAgICAgICAgICBpZiAoaGV4U3RyLnBhZFN0YXJ0KHZhbHVlLmxlbmd0aCwgJzAnKSA9PT0gdmFsdWUgJiYgdGhpcy52YWx1ZSAhPT0gdmFsdWUpIHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IG5ld0NvbG9yID0gbmV3IENvbG9yKGAjJHt2YWx1ZX1gKTtcclxuICAgICAgICAgICAgICAgIHRoaXMuY29sb3Iuc2V0KG5ld0NvbG9yKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG4iLCI8ZGl2IGNsYXNzPVwiY29sdW1uXCI+XHJcbiAgICA8aW5wdXQgI2VsUmVmIHR5cGU9XCJ0ZXh0XCIgW3ZhbHVlXT1cInZhbHVlXCIgKGtleXVwKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCBlbFJlZi52YWx1ZSlcIiAvPlxyXG4gICAgQGlmIChsYWJlbFZpc2libGUoKSkge1xyXG4gICAgICAgIDxzcGFuPkhFWDwvc3Bhbj5cclxuICAgIH1cclxuPC9kaXY+Il19 +diff --git a/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hsla-input/hsla-input.component.mjs b/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hsla-input/hsla-input.component.mjs +index 2454b27..dd007ce 100644 +--- a/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hsla-input/hsla-input.component.mjs ++++ b/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/hsla-input/hsla-input.component.mjs +@@ -21,10 +21,10 @@ export class HslaComponent { + this.color.set(newColor); + } + static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HslaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HslaComponent, isStandalone: true, selector: "hsla-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } ++ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HslaComponent, isStandalone: true, selector: "hsla-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HslaComponent, decorators: [{ + type: Component, +- args: [{ selector: `hsla-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] ++ args: [{ selector: `hsla-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] + }] }); + //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHNsYS1pbnB1dC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9pcGxhYi9uZ3gtY29sb3ItcGlja2VyL3NyYy9saWIvY29tcG9uZW50cy9wYXJ0cy9pbnB1dHMvaHNsYS1pbnB1dC9oc2xhLWlucHV0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lwbGFiL25neC1jb2xvci1waWNrZXIvc3JjL2xpYi9jb21wb25lbnRzL3BhcnRzL2lucHV0cy9oc2xhLWlucHV0L2hzbGEtaW5wdXQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSx1QkFBdUIsRUFBRSxnQkFBZ0IsRUFBZSxLQUFLLEVBQUUsS0FBSyxFQUFlLE1BQU0sZUFBZSxDQUFDO0FBQzdILE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMxRCxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSxxREFBcUQsQ0FBQzs7QUFlaEcsTUFBTSxPQUFPLGFBQWE7SUFaMUI7UUFjVyxVQUFLLEdBQXVCLEtBQUssQ0FBQyxRQUFRLEVBQVMsQ0FBQztRQUVwRCxpQkFBWSxHQUF5QixLQUFLLENBQW1CLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFLENBQUMsQ0FBQztRQUVySCxtQkFBYyxHQUF5QixLQUFLLENBQW1CLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFLENBQUMsQ0FBQztLQWdCaEk7SUFkRyxJQUFXLEtBQUs7UUFDWixPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNuQyxDQUFDO0lBRU0sYUFBYSxDQUFDLFFBQWdCLEVBQUUsS0FBNEI7UUFDL0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUN6QixNQUFNLEdBQUcsR0FBRyxLQUFLLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUM7UUFDakQsTUFBTSxVQUFVLEdBQUcsS0FBSyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDO1FBQy9ELE1BQU0sU0FBUyxHQUFHLEtBQUssS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztRQUM3RCxNQUFNLEtBQUssR0FBRyxLQUFLLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUM7UUFFckQsTUFBTSxRQUFRLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDeEUsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDN0IsQ0FBQzs4R0FyQlEsYUFBYTtrR0FBYixhQUFhLHNnQkNqQjFCLGlsQ0F5QkMseWVEVmEseUJBQXlCOzsyRkFFMUIsYUFBYTtrQkFaekIsU0FBUzsrQkFDSSxzQkFBc0IsbUJBT2YsdUJBQXVCLENBQUMsTUFBTSxjQUNuQyxJQUFJLFdBQ1AsQ0FBQyx5QkFBeUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIGJvb2xlYW5BdHRyaWJ1dGUsIElucHV0U2lnbmFsLCBpbnB1dCwgbW9kZWwsIE1vZGVsU2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbG9yIH0gZnJvbSAnLi8uLi8uLi8uLi8uLi9oZWxwZXJzL2NvbG9yLmNsYXNzJztcclxuaW1wb3J0IHsgQ29sb3JQaWNrZXJJbnB1dERpcmVjdGl2ZSB9IGZyb20gJy4uLy4uLy4uLy4uL2RpcmVjdGl2ZXMvY29sb3ItcGlja2VyLWlucHV0LmRpcmVjdGl2ZSc7XHJcblxyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgICBzZWxlY3RvcjogYGhzbGEtaW5wdXQtY29tcG9uZW50YCxcclxuICAgIHRlbXBsYXRlVXJsOiBgLi9oc2xhLWlucHV0LmNvbXBvbmVudC5odG1sYCxcclxuICAgIHN0eWxlVXJsczogW1xyXG4gICAgICAgIGAuLy4uLy4uL2Jhc2Uuc3R5bGUuc2Nzc2AsXHJcbiAgICAgICAgYC4vLi4vaW5wdXQuY29tcG9uZW50LnNjc3NgLFxyXG4gICAgICAgIGAuL2hzbGEtaW5wdXQuY29tcG9uZW50LnNjc3NgXHJcbiAgICBdLFxyXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXHJcbiAgICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gICAgaW1wb3J0czogW0NvbG9yUGlja2VySW5wdXREaXJlY3RpdmVdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBIc2xhQ29tcG9uZW50IHtcclxuXHJcbiAgICBwdWJsaWMgY29sb3I6IE1vZGVsU2lnbmFsPENvbG9yPiA9IG1vZGVsLnJlcXVpcmVkPENvbG9yPigpO1xyXG5cclxuICAgIHB1YmxpYyBsYWJlbFZpc2libGU6IElucHV0U2lnbmFsPGJvb2xlYW4+ID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbj4oZmFsc2UsIHsgYWxpYXM6ICdsYWJlbCcsIHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KTtcclxuXHJcbiAgICBwdWJsaWMgaXNBbHBoYVZpc2libGU6IElucHV0U2lnbmFsPGJvb2xlYW4+ID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbj4odHJ1ZSwgeyBhbGlhczogJ2FscGhhJywgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pO1xyXG5cclxuICAgIHB1YmxpYyBnZXQgdmFsdWUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY29sb3IoKT8uZ2V0SHNsYSgpO1xyXG4gICAgfVxyXG5cclxuICAgIHB1YmxpYyBvbklucHV0Q2hhbmdlKG5ld1ZhbHVlOiBudW1iZXIsIGNvbG9yOiAnSCcgfCAnUycgfCAnTCcgfCAnQScpIHtcclxuICAgICAgICBjb25zdCB2YWx1ZSA9IHRoaXMudmFsdWU7XHJcbiAgICAgICAgY29uc3QgaHVlID0gY29sb3IgPT09ICdIJyA/IG5ld1ZhbHVlIDogdmFsdWUuaHVlO1xyXG4gICAgICAgIGNvbnN0IHNhdHVyYXRpb24gPSBjb2xvciA9PT0gJ1MnID8gbmV3VmFsdWUgOiB2YWx1ZS5zYXR1cmF0aW9uO1xyXG4gICAgICAgIGNvbnN0IGxpZ2h0bmVzcyA9IGNvbG9yID09PSAnTCcgPyBuZXdWYWx1ZSA6IHZhbHVlLmxpZ2h0bmVzcztcclxuICAgICAgICBjb25zdCBhbHBoYSA9IGNvbG9yID09PSAnQScgPyBuZXdWYWx1ZSA6IHZhbHVlLmFscGhhO1xyXG5cclxuICAgICAgICBjb25zdCBuZXdDb2xvciA9IG5ldyBDb2xvcigpLnNldEhzbGEoaHVlLCBzYXR1cmF0aW9uLCBsaWdodG5lc3MsIGFscGhhKTtcclxuICAgICAgICB0aGlzLmNvbG9yLnNldChuZXdDb2xvcik7XHJcbiAgICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cImNvbHVtblwiPlxyXG4gICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcGF0dGVybj1cIlswLTldKlwiIG1pbj1cIjBcIiBtYXg9XCIzNjBcIiBbdmFsdWVdPVwidmFsdWU/LmdldEh1ZSgpLnRvU3RyaW5nKClcIiAoaW5wdXRDaGFuZ2UpPVwib25JbnB1dENoYW5nZSgkZXZlbnQsICdIJylcIiAvPlxyXG4gICAgQGlmIChsYWJlbFZpc2libGUoKSkge1xyXG4gICAgICAgIDxzcGFuPkg8L3NwYW4+XHJcbiAgICB9XHJcbjwvZGl2PlxyXG48ZGl2IGNsYXNzPVwiY29sdW1uXCI+XHJcbiAgICA8aW5wdXQgdHlwZT1cInRleHRcIiBwYXR0ZXJuPVwiWzAtOV0qXCIgbWluPVwiMFwiIG1heD1cIjEwMFwiIFt2YWx1ZV09XCJ2YWx1ZT8uZ2V0U2F0dXJhdGlvbigpICsgJyUnXCIgKGlucHV0Q2hhbmdlKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCAnUycpXCIgLz5cclxuICAgIEBpZiAobGFiZWxWaXNpYmxlKCkpIHtcclxuICAgICAgICA8c3Bhbj5TPC9zcGFuPlxyXG4gICAgfVxyXG48L2Rpdj5cclxuPGRpdiBjbGFzcz1cImNvbHVtblwiPlxyXG4gICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcGF0dGVybj1cIlswLTldKlwiIG1pbj1cIjBcIiBtYXg9XCIxMDBcIiBbdmFsdWVdPVwidmFsdWU/LmdldExpZ2h0bmVzcygpICsgJyUnXCIgKGlucHV0Q2hhbmdlKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCAnTCcpXCIgLz5cclxuICAgIEBpZiAobGFiZWxWaXNpYmxlKCkpIHtcclxuICAgICAgICA8c3Bhbj5MPC9zcGFuPlxyXG4gICAgfVxyXG48L2Rpdj5cclxuQGlmIChpc0FscGhhVmlzaWJsZSgpKSB7XHJcbiAgICA8ZGl2IGNsYXNzPVwiY29sdW1uXCI+XHJcbiAgICAgICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcGF0dGVybj1cIlswLTldKyhbXFwuLF1bMC05XXsxLDJ9KT9cIiBtaW49XCIwXCIgbWF4PVwiMVwiIFt2YWx1ZV09XCJ2YWx1ZT8uZ2V0QWxwaGEoKS50b1N0cmluZygpXCIgKGlucHV0Q2hhbmdlKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCAnQScpXCIgLz5cclxuICAgICAgICBAaWYgKGxhYmVsVmlzaWJsZSgpKSB7XHJcbiAgICAgICAgICAgIDxzcGFuPkE8L3NwYW4+XHJcbiAgICAgICAgfVxyXG4gICAgPC9kaXY+XHJcbn0iXX0= +diff --git a/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/rgba-input/rgba-input.component.mjs b/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/rgba-input/rgba-input.component.mjs +index 6d976e8..8989aad 100644 +--- a/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/rgba-input/rgba-input.component.mjs ++++ b/node_modules/@iplab/ngx-color-picker/esm2022/lib/components/parts/inputs/rgba-input/rgba-input.component.mjs +@@ -21,10 +21,10 @@ export class RgbaComponent { + this.color.set(newColor); + } + static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: RgbaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: RgbaComponent, isStandalone: true, selector: "rgba-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } ++ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: RgbaComponent, isStandalone: true, selector: "rgba-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: RgbaComponent, decorators: [{ + type: Component, +- args: [{ selector: `rgba-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] ++ args: [{ selector: `rgba-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] + }] }); + //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmdiYS1pbnB1dC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9pcGxhYi9uZ3gtY29sb3ItcGlja2VyL3NyYy9saWIvY29tcG9uZW50cy9wYXJ0cy9pbnB1dHMvcmdiYS1pbnB1dC9yZ2JhLWlucHV0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lwbGFiL25neC1jb2xvci1waWNrZXIvc3JjL2xpYi9jb21wb25lbnRzL3BhcnRzL2lucHV0cy9yZ2JhLWlucHV0L3JnYmEtaW5wdXQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSx1QkFBdUIsRUFBRSxnQkFBZ0IsRUFBZSxLQUFLLEVBQUUsS0FBSyxFQUFlLE1BQU0sZUFBZSxDQUFDO0FBQzdILE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMxRCxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSxxREFBcUQsQ0FBQzs7QUFlaEcsTUFBTSxPQUFPLGFBQWE7SUFaMUI7UUFjVyxVQUFLLEdBQXVCLEtBQUssQ0FBQyxRQUFRLEVBQVMsQ0FBQztRQUVwRCxpQkFBWSxHQUF5QixLQUFLLENBQW1CLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFLENBQUMsQ0FBQztRQUVySCxtQkFBYyxHQUF5QixLQUFLLENBQW1CLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFLENBQUMsQ0FBQztLQWdCaEk7SUFkRyxJQUFXLEtBQUs7UUFDWixPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNuQyxDQUFDO0lBRU0sYUFBYSxDQUFDLFFBQWdCLEVBQUUsS0FBNEI7UUFDL0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUN6QixNQUFNLEdBQUcsR0FBRyxLQUFLLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUM7UUFDakQsTUFBTSxLQUFLLEdBQUcsS0FBSyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQ3JELE1BQU0sSUFBSSxHQUFHLEtBQUssS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQztRQUNuRCxNQUFNLEtBQUssR0FBRyxLQUFLLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUM7UUFFckQsTUFBTSxRQUFRLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDOUQsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDN0IsQ0FBQzs4R0FyQlEsYUFBYTtrR0FBYixhQUFhLHNnQkNqQjFCLGlsQ0F5QkMseWVEVmEseUJBQXlCOzsyRkFFMUIsYUFBYTtrQkFaekIsU0FBUzsrQkFDSSxzQkFBc0IsbUJBT2YsdUJBQXVCLENBQUMsTUFBTSxjQUNuQyxJQUFJLFdBQ1AsQ0FBQyx5QkFBeUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIGJvb2xlYW5BdHRyaWJ1dGUsIElucHV0U2lnbmFsLCBpbnB1dCwgbW9kZWwsIE1vZGVsU2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbG9yIH0gZnJvbSAnLi8uLi8uLi8uLi8uLi9oZWxwZXJzL2NvbG9yLmNsYXNzJztcclxuaW1wb3J0IHsgQ29sb3JQaWNrZXJJbnB1dERpcmVjdGl2ZSB9IGZyb20gJy4uLy4uLy4uLy4uL2RpcmVjdGl2ZXMvY29sb3ItcGlja2VyLWlucHV0LmRpcmVjdGl2ZSc7XHJcblxyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgICBzZWxlY3RvcjogYHJnYmEtaW5wdXQtY29tcG9uZW50YCxcclxuICAgIHRlbXBsYXRlVXJsOiBgLi9yZ2JhLWlucHV0LmNvbXBvbmVudC5odG1sYCxcclxuICAgIHN0eWxlVXJsczogW1xyXG4gICAgICAgIGAuLy4uLy4uL2Jhc2Uuc3R5bGUuc2Nzc2AsXHJcbiAgICAgICAgYC4vLi4vaW5wdXQuY29tcG9uZW50LnNjc3NgLFxyXG4gICAgICAgIGAuL3JnYmEtaW5wdXQuY29tcG9uZW50LnNjc3NgXHJcbiAgICBdLFxyXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXHJcbiAgICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gICAgaW1wb3J0czogW0NvbG9yUGlja2VySW5wdXREaXJlY3RpdmVdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBSZ2JhQ29tcG9uZW50IHtcclxuXHJcbiAgICBwdWJsaWMgY29sb3I6IE1vZGVsU2lnbmFsPENvbG9yPiA9IG1vZGVsLnJlcXVpcmVkPENvbG9yPigpO1xyXG5cclxuICAgIHB1YmxpYyBsYWJlbFZpc2libGU6IElucHV0U2lnbmFsPGJvb2xlYW4+ID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbj4oZmFsc2UsIHsgYWxpYXM6ICdsYWJlbCcsIHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KTtcclxuXHJcbiAgICBwdWJsaWMgaXNBbHBoYVZpc2libGU6IElucHV0U2lnbmFsPGJvb2xlYW4+ID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbj4odHJ1ZSwgeyBhbGlhczogJ2FscGhhJywgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pO1xyXG5cclxuICAgIHB1YmxpYyBnZXQgdmFsdWUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY29sb3IoKT8uZ2V0UmdiYSgpO1xyXG4gICAgfVxyXG5cclxuICAgIHB1YmxpYyBvbklucHV0Q2hhbmdlKG5ld1ZhbHVlOiBudW1iZXIsIGNvbG9yOiAnUicgfCAnRycgfCAnQicgfCAnQScpIHtcclxuICAgICAgICBjb25zdCB2YWx1ZSA9IHRoaXMudmFsdWU7XHJcbiAgICAgICAgY29uc3QgcmVkID0gY29sb3IgPT09ICdSJyA/IG5ld1ZhbHVlIDogdmFsdWUucmVkO1xyXG4gICAgICAgIGNvbnN0IGdyZWVuID0gY29sb3IgPT09ICdHJyA/IG5ld1ZhbHVlIDogdmFsdWUuZ3JlZW47XHJcbiAgICAgICAgY29uc3QgYmx1ZSA9IGNvbG9yID09PSAnQicgPyBuZXdWYWx1ZSA6IHZhbHVlLmJsdWU7XHJcbiAgICAgICAgY29uc3QgYWxwaGEgPSBjb2xvciA9PT0gJ0EnID8gbmV3VmFsdWUgOiB2YWx1ZS5hbHBoYTtcclxuXHJcbiAgICAgICAgY29uc3QgbmV3Q29sb3IgPSBuZXcgQ29sb3IoKS5zZXRSZ2JhKHJlZCwgZ3JlZW4sIGJsdWUsIGFscGhhKTtcclxuICAgICAgICB0aGlzLmNvbG9yLnNldChuZXdDb2xvcik7XHJcbiAgICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cImNvbHVtblwiPlxyXG4gICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcGF0dGVybj1cIlswLTldKlwiIG1pbj1cIjBcIiBtYXg9XCIyNTVcIiBbdmFsdWVdPVwidmFsdWU/LmdldFJlZCgpLnRvU3RyaW5nKClcIiAoaW5wdXRDaGFuZ2UpPVwib25JbnB1dENoYW5nZSgkZXZlbnQsICdSJylcIiAvPlxyXG4gICAgQGlmIChsYWJlbFZpc2libGUoKSkge1xyXG4gICAgICAgIDxzcGFuPlI8L3NwYW4+XHJcbiAgICB9XHJcbjwvZGl2PlxyXG48ZGl2IGNsYXNzPVwiY29sdW1uXCI+XHJcbiAgICA8aW5wdXQgdHlwZT1cInRleHRcIiBwYXR0ZXJuPVwiWzAtOV0qXCIgbWluPVwiMFwiIG1heD1cIjI1NVwiIFt2YWx1ZV09XCJ2YWx1ZT8uZ2V0R3JlZW4oKS50b1N0cmluZygpXCIgKGlucHV0Q2hhbmdlKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCAnRycpXCIgLz5cclxuICAgIEBpZiAobGFiZWxWaXNpYmxlKCkpIHtcclxuICAgICAgICA8c3Bhbj5HPC9zcGFuPlxyXG4gICAgfVxyXG48L2Rpdj5cclxuPGRpdiBjbGFzcz1cImNvbHVtblwiPlxyXG4gICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcGF0dGVybj1cIlswLTldKlwiIG1pbj1cIjBcIiBtYXg9XCIyNTVcIiBbdmFsdWVdPVwidmFsdWU/LmdldEJsdWUoKS50b1N0cmluZygpXCIgKGlucHV0Q2hhbmdlKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCAnQicpXCIgLz5cclxuICAgIEBpZiAobGFiZWxWaXNpYmxlKCkpIHtcclxuICAgICAgICA8c3Bhbj5CPC9zcGFuPlxyXG4gICAgfVxyXG48L2Rpdj5cclxuQGlmIChpc0FscGhhVmlzaWJsZSgpKSB7XHJcbiAgICA8ZGl2IGNsYXNzPVwiY29sdW1uXCI+XHJcbiAgICAgICAgPGlucHV0IHR5cGU9XCJ0ZXh0XCIgcGF0dGVybj1cIlswLTldKyhbXFwuLF1bMC05XXsxLDJ9KT9cIiBtaW49XCIwXCIgbWF4PVwiMVwiIFt2YWx1ZV09XCJ2YWx1ZT8uZ2V0QWxwaGEoKS50b1N0cmluZygpXCIgKGlucHV0Q2hhbmdlKT1cIm9uSW5wdXRDaGFuZ2UoJGV2ZW50LCAnQScpXCIgLz5cclxuICAgICAgICBAaWYgKGxhYmVsVmlzaWJsZSgpKSB7XHJcbiAgICAgICAgICAgIDxzcGFuPkE8L3NwYW4+XHJcbiAgICAgICAgfVxyXG4gICAgPC9kaXY+XHJcbn0iXX0= +diff --git a/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs b/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs +index a3b270c..40bea7f 100644 +--- a/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs ++++ b/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs +@@ -1123,11 +1123,11 @@ class RgbaComponent { + this.color.set(newColor); + } + static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: RgbaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: RgbaComponent, isStandalone: true, selector: "rgba-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } ++ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: RgbaComponent, isStandalone: true, selector: "rgba-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: RgbaComponent, decorators: [{ + type: Component, +- args: [{ selector: `rgba-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] ++ args: [{ selector: `rgba-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] + }] }); + + class HslaComponent { +@@ -1149,11 +1149,11 @@ class HslaComponent { + this.color.set(newColor); + } + static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HslaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HslaComponent, isStandalone: true, selector: "hsla-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } ++ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HslaComponent, isStandalone: true, selector: "hsla-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HslaComponent, decorators: [{ + type: Component, +- args: [{ selector: `hsla-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] ++ args: [{ selector: `hsla-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ColorPickerInputDirective], template: "
\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n
\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n
\r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] + }] }); + + class HexComponent { +@@ -1184,11 +1184,11 @@ class HexComponent { + } + } + static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HexComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HexComponent, isStandalone: true, selector: "hex-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, prefixValue: { classPropertyName: "prefixValue", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } ++ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", type: HexComponent, isStandalone: true, selector: "hex-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, prefixValue: { classPropertyName: "prefixValue", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: HexComponent, decorators: [{ + type: Component, +- args: [{ selector: `hex-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] ++ args: [{ selector: `hex-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "
\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }] + }] }); + + const OpacityAnimation = trigger('opacityAnimation', [ diff --git a/ui-ngx/patches/@mat-datetimepicker+core+14.0.0.patch b/ui-ngx/patches/@mat-datetimepicker+core+14.0.0.patch index 293ac10a21..ac4f7434e9 100644 --- a/ui-ngx/patches/@mat-datetimepicker+core+14.0.0.patch +++ b/ui-ngx/patches/@mat-datetimepicker+core+14.0.0.patch @@ -15,8 +15,39 @@ index 7ecfae7..08363d3 100644 date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), this._adapter.getHour(this.activeDate), value); } this._timeChanged = true; +diff --git a/node_modules/@mat-datetimepicker/core/esm2022/datetimepicker/datetimepicker.mjs b/node_modules/@mat-datetimepicker/core/esm2022/datetimepicker/datetimepicker.mjs +index 5b82377..30e35ce 100644 +--- a/node_modules/@mat-datetimepicker/core/esm2022/datetimepicker/datetimepicker.mjs ++++ b/node_modules/@mat-datetimepicker/core/esm2022/datetimepicker/datetimepicker.mjs +@@ -48,7 +48,7 @@ export class MatDatetimepickerContentComponent { + } + } + /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.4", ngImport: i0, type: MatDatetimepickerContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.4", type: MatDatetimepickerContentComponent, selector: "mat-datetimepicker-content", host: { listeners: { "keydown": "_handleKeydown($event)" }, properties: { "class.mat-datetimepicker-content-touch": "datetimepicker?.touchUi" }, classAttribute: "mat-datetimepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: MatDatetimepickerCalendarComponent, descendants: true, static: true }], ngImport: i0, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed;z-index:1000}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;transition:opacity .4s cubic-bezier(.25,.8,.25,1);opacity:0}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.48}.cdk-overlay-dark-backdrop{background:#0009}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"], dependencies: [{ kind: "directive", type: i1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: i2.MatDatetimepickerCalendarComponent, selector: "mat-datetimepicker-calendar", inputs: ["multiYearSelector", "startView", "twelvehour", "timeInterval", "dateFilter", "ariaLabel", "ariaNextMonthLabel", "ariaPrevMonthLabel", "ariaNextYearLabel", "ariaPrevYearLabel", "ariaNextMultiYearLabel", "ariaPrevMultiYearLabel", "preventSameDateTimeSelection", "type", "startAt", "selected", "minDate", "maxDate"], outputs: ["_userSelection", "selectedChange", "viewChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } ++ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.4", type: MatDatetimepickerContentComponent, selector: "mat-datetimepicker-content", host: { listeners: { "keydown": "_handleKeydown($event)" }, properties: { "class.mat-datetimepicker-content-touch": "datetimepicker?.touchUi" }, classAttribute: "mat-datetimepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: MatDatetimepickerCalendarComponent, descendants: true, static: true }], ngImport: i0, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"], dependencies: [{ kind: "directive", type: i1.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: i2.MatDatetimepickerCalendarComponent, selector: "mat-datetimepicker-calendar", inputs: ["multiYearSelector", "startView", "twelvehour", "timeInterval", "dateFilter", "ariaLabel", "ariaNextMonthLabel", "ariaPrevMonthLabel", "ariaNextYearLabel", "ariaPrevYearLabel", "ariaNextMultiYearLabel", "ariaPrevMultiYearLabel", "preventSameDateTimeSelection", "type", "startAt", "selected", "minDate", "maxDate"], outputs: ["_userSelection", "selectedChange", "viewChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.4", ngImport: i0, type: MatDatetimepickerContentComponent, decorators: [{ + type: Component, +@@ -56,7 +56,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.4", ngImpor + class: 'mat-datetimepicker-content', + '[class.mat-datetimepicker-content-touch]': 'datetimepicker?.touchUi', + '(keydown)': '_handleKeydown($event)', +- }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed;z-index:1000}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;transition:opacity .4s cubic-bezier(.25,.8,.25,1);opacity:0}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.48}.cdk-overlay-dark-backdrop{background:#0009}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"] }] ++ }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"] }] + }], propDecorators: { _calendar: [{ + type: ViewChild, + args: [MatDatetimepickerCalendarComponent, { static: true }] +@@ -301,7 +301,7 @@ export class MatDatetimepickerComponent { + const overlayConfig = new OverlayConfig({ + positionStrategy: this._createPopupPositionStrategy(), + hasBackdrop: true, +- backdropClass: 'mat-overlay-transparent-backdrop', ++ backdropClass: 'cdk-overlay-transparent-backdrop', + direction: this._dir ? this._dir.value : 'ltr', + scrollStrategy: this._scrollStrategy(), + panelClass: 'mat-datetimepicker-popup', diff --git a/node_modules/@mat-datetimepicker/core/fesm2022/mat-datetimepicker-core.mjs b/node_modules/@mat-datetimepicker/core/fesm2022/mat-datetimepicker-core.mjs -index 00f4a52..df688e3 100644 +index 00f4a52..99c0ac1 100644 --- a/node_modules/@mat-datetimepicker/core/fesm2022/mat-datetimepicker-core.mjs +++ b/node_modules/@mat-datetimepicker/core/fesm2022/mat-datetimepicker-core.mjs @@ -946,9 +946,9 @@ class MatDatetimepickerClockComponent { @@ -32,3 +63,30 @@ index 00f4a52..df688e3 100644 date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), this._adapter.getHour(this.activeDate), value); } this._timeChanged = true; +@@ -1911,7 +1911,7 @@ class MatDatetimepickerContentComponent { + } + } + /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.4", ngImport: i0, type: MatDatetimepickerContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } +- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.4", type: MatDatetimepickerContentComponent, selector: "mat-datetimepicker-content", host: { listeners: { "keydown": "_handleKeydown($event)" }, properties: { "class.mat-datetimepicker-content-touch": "datetimepicker?.touchUi" }, classAttribute: "mat-datetimepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: MatDatetimepickerCalendarComponent, descendants: true, static: true }], ngImport: i0, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed;z-index:1000}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;transition:opacity .4s cubic-bezier(.25,.8,.25,1);opacity:0}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.48}.cdk-overlay-dark-backdrop{background:#0009}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"], dependencies: [{ kind: "directive", type: i1$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: MatDatetimepickerCalendarComponent, selector: "mat-datetimepicker-calendar", inputs: ["multiYearSelector", "startView", "twelvehour", "timeInterval", "dateFilter", "ariaLabel", "ariaNextMonthLabel", "ariaPrevMonthLabel", "ariaNextYearLabel", "ariaPrevYearLabel", "ariaNextMultiYearLabel", "ariaPrevMultiYearLabel", "preventSameDateTimeSelection", "type", "startAt", "selected", "minDate", "maxDate"], outputs: ["_userSelection", "selectedChange", "viewChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } ++ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.4", type: MatDatetimepickerContentComponent, selector: "mat-datetimepicker-content", host: { listeners: { "keydown": "_handleKeydown($event)" }, properties: { "class.mat-datetimepicker-content-touch": "datetimepicker?.touchUi" }, classAttribute: "mat-datetimepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: MatDatetimepickerCalendarComponent, descendants: true, static: true }], ngImport: i0, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"], dependencies: [{ kind: "directive", type: i1$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: MatDatetimepickerCalendarComponent, selector: "mat-datetimepicker-calendar", inputs: ["multiYearSelector", "startView", "twelvehour", "timeInterval", "dateFilter", "ariaLabel", "ariaNextMonthLabel", "ariaPrevMonthLabel", "ariaNextYearLabel", "ariaPrevYearLabel", "ariaNextMultiYearLabel", "ariaPrevMultiYearLabel", "preventSameDateTimeSelection", "type", "startAt", "selected", "minDate", "maxDate"], outputs: ["_userSelection", "selectedChange", "viewChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } + } + i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.4", ngImport: i0, type: MatDatetimepickerContentComponent, decorators: [{ + type: Component, +@@ -1919,7 +1919,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.4", ngImpor + class: 'mat-datetimepicker-content', + '[class.mat-datetimepicker-content-touch]': 'datetimepicker?.touchUi', + '(keydown)': '_handleKeydown($event)', +- }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed;z-index:1000}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;transition:opacity .4s cubic-bezier(.25,.8,.25,1);opacity:0}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.48}.cdk-overlay-dark-backdrop{background:#0009}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"] }] ++ }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "\n\n", styles: [".mat-datetimepicker-content{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;display:block;background-color:#fff;border-radius:2px;overflow:hidden}.mat-datetimepicker-calendar{width:296px;height:405px}.mat-datetimepicker-calendar[mode=landscape]{width:446px;height:328px}@media (min-width: 480px){.mat-datetimepicker-calendar[mode=auto]{width:446px;height:328px}}.mat-datetimepicker-content-touch{box-shadow:0 0 #0003,0 0 #00000024,0 0 #0000001f;display:block;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f}.mat-datetimepicker-dialog .mat-dialog-container{padding:0}\n"] }] + }], propDecorators: { _calendar: [{ + type: ViewChild, + args: [MatDatetimepickerCalendarComponent, { static: true }] +@@ -2164,7 +2164,7 @@ class MatDatetimepickerComponent { + const overlayConfig = new OverlayConfig({ + positionStrategy: this._createPopupPositionStrategy(), + hasBackdrop: true, +- backdropClass: 'mat-overlay-transparent-backdrop', ++ backdropClass: 'cdk-overlay-transparent-backdrop', + direction: this._dir ? this._dir.value : 'ltr', + scrollStrategy: this._scrollStrategy(), + panelClass: 'mat-datetimepicker-popup', diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 60abdc15d0..eaca4ec83c 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0.1-SNAPSHOT thingsboard org.thingsboard 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 5572730923..f92db80cc4 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -192,6 +192,7 @@ export interface IStateController { getStateIdAtIndex(index: number): string; getEntityId(entityParamName: string): EntityId; getCurrentStateName(): string; + reInit(): void; } export interface SubscriptionInfo { diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 213b327892..2194835dfe 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -1456,20 +1456,18 @@ export class WidgetSubscription implements IWidgetSubscription { this.datasources.forEach((datasource) => { datasource.dataKeys.forEach((dataKey) => { if (datasource.generated || datasource.isAdditional) { - dataKey._hash = Math.random(); dataKey.color = this.ctx.utils.getMaterialColor(index); } index++; }); - if (datasource.latestDataKeys) { - datasource.latestDataKeys.forEach((dataKey) => { - if (datasource.generated || datasource.isAdditional) { - dataKey._hash = Math.random(); - // dataKey.color = this.ctx.utils.getMaterialColor(index); - } - // index++; - }); - } + // if (datasource.latestDataKeys) { + // datasource.latestDataKeys.forEach((dataKey) => { + // if (datasource.generated || datasource.isAdditional) { + // // dataKey.color = this.ctx.utils.getMaterialColor(index); + // } + // // index++; + // }); + // } }); if (this.comparisonEnabled) { this.datasourcePages.forEach(datasourcePage => { @@ -1499,9 +1497,9 @@ export class WidgetSubscription implements IWidgetSubscription { private entityDataToDatasourceData(datasource: Datasource, data: Array): Array { let datasourceDataArray: Array = []; datasourceDataArray = datasourceDataArray.concat(datasource.dataKeys.map((dataKey, keyIndex) => { - dataKey.hidden = !!dataKey.settings.hideDataByDefault; - dataKey.inLegend = dataKey.settings.showInLegend || - (isUndefined(dataKey.settings.showInLegend) && !dataKey.settings.removeFromLegend); + dataKey.hidden = !!dataKey.settings?.hideDataByDefault; + dataKey.inLegend = dataKey.settings?.showInLegend || + (isUndefined(dataKey.settings?.showInLegend) && !dataKey.settings?.removeFromLegend); dataKey.label = this.ctx.utils.customTranslation(dataKey.label, dataKey.label); const datasourceData: DatasourceData = { datasource, diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index 344c44f6c7..623ceefadd 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -31,8 +31,14 @@ export interface SysParamsState { maxDebugModeDurationMinutes: number; maxDataPointsPerRollingArg: number; maxArgumentsPerCF: number; + minAllowedDeduplicationIntervalInSecForCF: number; + minAllowedAggregationIntervalInSecForCF: number; + minAllowedScheduledUpdateIntervalInSecForCF: number; + maxRelationLevelPerCfArgument: number; + maxRelatedEntitiesToReturnPerCfArgument: number; ruleChainDebugPerTenantLimitsConfiguration?: string; calculatedFieldDebugPerTenantLimitsConfiguration?: string; + intermediateAggregationIntervalInSecForCF: number; trendzSettings: TrendzSettings; } diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index ca46344bbb..51e02b1ab8 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -33,8 +33,14 @@ const emptyUserAuthState: AuthPayload = { mobileQrEnabled: false, maxResourceSize: 0, maxArgumentsPerCF: 0, + minAllowedDeduplicationIntervalInSecForCF: 0, + minAllowedAggregationIntervalInSecForCF: 0, + minAllowedScheduledUpdateIntervalInSecForCF: 0, + maxRelationLevelPerCfArgument: 0, + maxRelatedEntitiesToReturnPerCfArgument: 0, maxDataPointsPerRollingArg: 0, maxDebugModeDurationMinutes: 0, + intermediateAggregationIntervalInSecForCF: 0, userSettings: initialUserSettings, trendzSettings: initialTrendzSettings }; diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 1c6ac90f3b..07a8c5b7a2 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -68,6 +68,7 @@ export class AuthService { redirectUrl: string; oauth2Clients: Array = null; twoFactorAuthProviders: Array = null; + forceTwoFactorAuthProviders: Array = null; private refreshTokenSubject: ReplaySubject = null; private jwtHelper = new JwtHelperService(); @@ -117,6 +118,9 @@ export class AuthService { if (loginResponse.scope === Authority.PRE_VERIFICATION_TOKEN) { this.router.navigateByUrl(`login/mfa`); } + if (loginResponse.scope === Authority.MFA_CONFIGURATION_TOKEN) { + this.router.navigateByUrl(`login/force-mfa`); + } } )); } @@ -164,8 +168,8 @@ export class AuthService { )); } - public getUserPasswordPolicy() { - return this.http.get(`/api/noauth/userPasswordPolicy`, defaultHttpOptions()); + public getUserPasswordPolicy(config?: RequestConfig) { + return this.http.get(`/api/noauth/userPasswordPolicy`, defaultHttpOptionsFromConfig(config)); } public activateByEmailCode(emailCode: string): Observable { @@ -239,6 +243,15 @@ export class AuthService { ); } + public getAvailableTwoFaProviders(): Observable> { + return this.http.get>(`/api/2fa/providers`, defaultHttpOptions()).pipe( + catchError(() => of([])), + tap((providers) => { + this.forceTwoFactorAuthProviders = providers; + }) + ); + } + public forceDefaultPlace(authState?: AuthState, path?: string, params?: any): boolean { if (authState && authState.authUser) { if (authState.authUser.authority === Authority.TENANT_ADMIN || authState.authUser.authority === Authority.CUSTOMER_USER) { @@ -266,6 +279,8 @@ export class AuthService { if (isAuthenticated) { if (authState.authUser.authority === Authority.PRE_VERIFICATION_TOKEN) { result = this.router.parseUrl('login/mfa'); + } else if (authState.authUser.authority === Authority.MFA_CONFIGURATION_TOKEN) { + result = this.router.parseUrl('login/force-mfa'); } else if (!path || path === 'login' || this.forceDefaultPlace(authState, path, params)) { if (this.redirectUrl) { const redirectUrl = this.redirectUrl; @@ -399,7 +414,7 @@ export class AuthService { loadUserSubject.error(err); } ); - } else if (authPayload.authUser?.authority === Authority.PRE_VERIFICATION_TOKEN) { + } else if (authPayload.authUser?.authority === Authority.PRE_VERIFICATION_TOKEN || authPayload.authUser?.authority === Authority.MFA_CONFIGURATION_TOKEN) { loadUserSubject.next(authPayload); loadUserSubject.complete(); } else if (authPayload.authUser?.userId) { diff --git a/ui-ngx/src/app/core/guards/auth.guard.ts b/ui-ngx/src/app/core/guards/auth.guard.ts index b72df84c9b..0937ed58f6 100644 --- a/ui-ngx/src/app/core/guards/auth.guard.ts +++ b/ui-ngx/src/app/core/guards/auth.guard.ts @@ -104,6 +104,16 @@ export class AuthGuard { } this.authService.logout(); return of(this.authService.defaultUrl(false)); + } else if (path === 'login.force-mfa') { + if (authState.authUser?.authority === Authority.MFA_CONFIGURATION_TOKEN) { + return this.authService.getAvailableTwoFaProviders().pipe( + map(() => { + return true; + }) + ); + } + this.authService.logout(); + return of(this.authService.defaultUrl(false)); } else { return of(true); } @@ -121,7 +131,7 @@ export class AuthGuard { } } if (this.mobileService.isMobileApp() && !path.startsWith('dashboard.')) { - this.mobileService.handleMobileNavigation(path, params); + this.mobileService.handleMobileNavigation(path, params, lastChild.queryParams); return of(false); } if (authState.authUser.authority === Authority.PRE_VERIFICATION_TOKEN) { diff --git a/ui-ngx/src/app/core/http/api-key.service.ts b/ui-ngx/src/app/core/http/api-key.service.ts new file mode 100644 index 0000000000..a1b85ae1a6 --- /dev/null +++ b/ui-ngx/src/app/core/http/api-key.service.ts @@ -0,0 +1,54 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; +import { Observable } from 'rxjs'; +import { PageLink } from '@shared/models/page/page-link'; +import { PageData } from '@shared/models/page/page-data'; +import { ApiKeyInfo, ApiKey } from '@shared/models/api-key.models'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiKeyService { + + constructor( + private http: HttpClient + ) { + } + + public saveApiKey(apiKey: ApiKeyInfo, config?: RequestConfig): Observable { + return this.http.post('/api/apiKey', apiKey, defaultHttpOptionsFromConfig(config)); + } + + public deleteApiKey(id: string, config?: RequestConfig): Observable { + return this.http.delete(`/api/apiKey/${id}`, defaultHttpOptionsFromConfig(config)); + } + + public updateApiKeyDescription(id: string, description: string, config?: RequestConfig): Observable { + return this.http.put(`/api/apiKey/${id}/description`, description, defaultHttpOptionsFromConfig(config)); + } + + public enableApiKey(id: string, enabledValue: boolean, config?: RequestConfig): Observable { + return this.http.put(`/api/apiKey/${id}/enabled/${enabledValue}`, defaultHttpOptionsFromConfig(config)); + } + + public getUserApiKeys(userId: string, pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/apiKeys/${userId}${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 d98486b827..f86a0f35f9 100644 --- a/ui-ngx/src/app/core/http/asset-profile.service.ts +++ b/ui-ngx/src/app/core/http/asset-profile.service.ts @@ -38,6 +38,11 @@ export class AssetProfileService { return this.http.get>(`/api/assetProfiles${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); } + public getAssetProfilesByIds(assetProfileIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/assetProfileInfos?assetProfileIds=${assetProfileIds.join(',')}`, + defaultHttpOptionsFromConfig(config)); + } + public getAssetProfile(assetProfileId: string, config?: RequestConfig): Observable { return this.http.get(`/api/assetProfile/${assetProfileId}`, 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 0d3bd97850..4773121e59 100644 --- a/ui-ngx/src/app/core/http/asset.service.ts +++ b/ui-ngx/src/app/core/http/asset.service.ts @@ -15,7 +15,7 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { PageLink } from '@shared/models/page/page-link'; @@ -23,6 +23,7 @@ import { PageData } from '@shared/models/page/page-data'; 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'; +import { SaveEntityParams } from '@shared/models/entity.models'; @Injectable({ providedIn: 'root' @@ -69,8 +70,10 @@ export class AssetService { return this.http.get(`/api/asset/info/${assetId}`, defaultHttpOptionsFromConfig(config)); } - public saveAsset(asset: Asset, config?: RequestConfig): Observable { - return this.http.post('/api/asset', asset, defaultHttpOptionsFromConfig(config)); + public saveAsset(asset: Asset, config?: RequestConfig): Observable; + public saveAsset(asset: Asset, saveParams: SaveEntityParams, config?: RequestConfig): Observable; + public saveAsset(asset: Asset, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable { + return this.http.post('/api/asset', asset, createDefaultHttpOptions(saveParamsOrConfig, config)); } public deleteAsset(assetId: string, config?: RequestConfig) { diff --git a/ui-ngx/src/app/core/http/attribute.service.ts b/ui-ngx/src/app/core/http/attribute.service.ts index fab486b3c8..a574d06fb5 100644 --- a/ui-ngx/src/app/core/http/attribute.service.ts +++ b/ui-ngx/src/app/core/http/attribute.service.ts @@ -15,7 +15,7 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { defaultHttpOptionsFromConfig, defaultHttpOptionsFromParams, RequestConfig } from './http-utils'; import { forkJoin, Observable, of } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { EntityId } from '@shared/models/id/entity-id'; @@ -35,47 +35,37 @@ export class AttributeService { public getEntityAttributes(entityId: EntityId, attributeScope?: AttributeScope, keys?: Array, config?: RequestConfig): Observable> { let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes`; - + let queryParams: object = null; if (attributeScope) { url += `/${attributeScope}`; } if (keys && keys.length) { - url += `?keys=${keys.join(',')}`; + queryParams = {key: keys}; } - return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + return this.http.get>(url, defaultHttpOptionsFromParams(queryParams, config)); } public deleteEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, attributes: Array, config?: RequestConfig): Observable { - const keys = attributes.map(attribute => encodeURIComponent(attribute.key)).join(','); - return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/${attributeScope}` + - `?keys=${keys}`, - defaultHttpOptionsFromConfig(config)); + return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/${attributeScope}`, + defaultHttpOptionsFromParams({key: attributes.map(attribute => attribute.key)}, config)); } public deleteEntityTimeseries(entityId: EntityId, timeseries: Array, deleteAllDataForKeys = false, 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}`; - if (isDefinedAndNotNull(deleteAllDataForKeys)) { - url += `&deleteAllDataForKeys=${deleteAllDataForKeys}`; - } - if (isDefinedAndNotNull(rewriteLatestIfDeleted)) { - url += `&rewriteLatestIfDeleted=${rewriteLatestIfDeleted}`; - } - if (isDefinedAndNotNull(deleteLatest)) { - url += `&deleteLatest=${deleteLatest}`; - } - if (isDefinedAndNotNull(startTs)) { - url += `&startTs=${startTs}`; - } - if (isDefinedAndNotNull(endTs)) { - url += `&endTs=${endTs}`; - } - return this.http.delete(url, defaultHttpOptionsFromConfig(config)); + const queryParams = { + key: timeseries.map(key => key.key), + deleteAllDataForKeys: deleteAllDataForKeys, + rewriteLatestIfDeleted: rewriteLatestIfDeleted, + deleteLatest: deleteLatest, + startTs: startTs, + endTs: endTs + }; + return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete`, + defaultHttpOptionsFromParams(queryParams, config)); } public saveEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, attributes: Array, @@ -138,32 +128,29 @@ export class AttributeService { limit: number = 100, agg: AggregationType = AggregationType.NONE, interval?: number, orderBy: DataSortOrder = DataSortOrder.DESC, useStrictDataTypes: boolean = false, config?: RequestConfig): Observable { - let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries?keys=${keys.join(',')}&startTs=${startTs}&endTs=${endTs}`; - if (isDefinedAndNotNull(limit)) { - url += `&limit=${limit}`; - } - if (isDefinedAndNotNull(agg)) { - url += `&agg=${agg}`; - } - if (isDefinedAndNotNull(interval)) { - url += `&interval=${interval}`; - } - if (isDefinedAndNotNull(orderBy)) { - url += `&orderBy=${orderBy}`; - } - if (isDefinedAndNotNull(useStrictDataTypes)) { - url += `&useStrictDataTypes=${useStrictDataTypes}`; - } - - return this.http.get(url, defaultHttpOptionsFromConfig(config)); + const queryParams = { + key: keys, + startTs: startTs, + endTs: endTs, + limit: limit, + agg: agg, + interval: interval, + orderBy: orderBy, + useStrictDataTypes: useStrictDataTypes + } + return this.http.get(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`, + defaultHttpOptionsFromParams(queryParams, config)); } public getEntityTimeseriesLatest(entityId: EntityId, keys?: Array, useStrictDataTypes = false, config?: RequestConfig): Observable { - let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries?useStrictDataTypes=${useStrictDataTypes}`; + const queryParams: Record = { + useStrictDataTypes: useStrictDataTypes + } if (isDefinedAndNotNull(keys) && keys.length) { - url += `&keys=${keys.join(',')}`; + queryParams.key = keys; } - return this.http.get(url, defaultHttpOptionsFromConfig(config)); + return this.http.get(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`, + defaultHttpOptionsFromParams(queryParams, config)); } } diff --git a/ui-ngx/src/app/core/http/audit-log.service.ts b/ui-ngx/src/app/core/http/audit-log.service.ts index f4ab583426..7c209c2219 100644 --- a/ui-ngx/src/app/core/http/audit-log.service.ts +++ b/ui-ngx/src/app/core/http/audit-log.service.ts @@ -15,12 +15,12 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { createDefaultHttpOptions, RequestConfig } from './http-utils'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { TimePageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; -import { AuditLog } from '@shared/models/audit-log.models'; +import { AuditLog, AuditLogFilter } from '@shared/models/audit-log.models'; import { EntityId } from '@shared/models/id/entity-id'; @Injectable({ @@ -32,28 +32,58 @@ export class AuditLogService { private http: HttpClient ) { } - public getAuditLogs(pageLink: TimePageLink, - config?: RequestConfig): Observable> { - return this.http.get>(`/api/audit/logs${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getAuditLogs(pageLink: TimePageLink, config?: RequestConfig): Observable>; + public getAuditLogs(pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable>; + public getAuditLogs( + pageLink: TimePageLink, + filtersOrConfig?: AuditLogFilter | RequestConfig, + config?: RequestConfig + ): Observable> { + return this.http.get>( + `/api/audit/logs${pageLink.toQuery()}`, + createDefaultHttpOptions(filtersOrConfig, config) + ); } - public getAuditLogsByCustomerId(customerId: string, pageLink: TimePageLink, - config?: RequestConfig): Observable> { - return this.http.get>(`/api/audit/logs/customer/${customerId}${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getAuditLogsByCustomerId(customerId: string, pageLink: TimePageLink, config?: RequestConfig): Observable>; + public getAuditLogsByCustomerId(customerId: string, pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable>; + public getAuditLogsByCustomerId( + customerId: string, + pageLink: TimePageLink, + filtersOrConfig?: AuditLogFilter | RequestConfig, + config?: RequestConfig + ): Observable> { + return this.http.get>( + `/api/audit/logs/customer/${customerId}${pageLink.toQuery()}`, + createDefaultHttpOptions(filtersOrConfig, config) + ); } - public getAuditLogsByUserId(userId: string, pageLink: TimePageLink, - config?: RequestConfig): Observable> { - return this.http.get>(`/api/audit/logs/user/${userId}${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getAuditLogsByUserId(userId: string, pageLink: TimePageLink, config?: RequestConfig): Observable>; + public getAuditLogsByUserId(userId: string, pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable>; + public getAuditLogsByUserId( + userId: string, + pageLink: TimePageLink, + filtersOrConfig?: AuditLogFilter | RequestConfig, + config?: RequestConfig + ): Observable> { + return this.http.get>( + `/api/audit/logs/user/${userId}${pageLink.toQuery()}`, + createDefaultHttpOptions(filtersOrConfig, config) + ); } - public getAuditLogsByEntityId(entityId: EntityId, pageLink: TimePageLink, - config?: RequestConfig): Observable> { - return this.http.get>(`/api/audit/logs/entity/${entityId.entityType}/${entityId.id}${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getAuditLogsByEntityId(entityId: EntityId, pageLink: TimePageLink, config?: RequestConfig): Observable>; + public getAuditLogsByEntityId(entityId: EntityId, pageLink: TimePageLink, filters: AuditLogFilter, config?: RequestConfig): Observable>; + public getAuditLogsByEntityId( + entityId: EntityId, + pageLink: TimePageLink, + filtersOrConfig?: AuditLogFilter | RequestConfig, + config?: RequestConfig + ): Observable> { + return this.http.get>( + `/api/audit/logs/entity/${entityId.entityType}/${entityId.id}${pageLink.toQuery()}`, + createDefaultHttpOptions(filtersOrConfig, config) + ); } - } diff --git a/ui-ngx/src/app/core/http/calculated-fields.service.ts b/ui-ngx/src/app/core/http/calculated-fields.service.ts index b262f7c3e2..1fa9abd29b 100644 --- a/ui-ngx/src/app/core/http/calculated-fields.service.ts +++ b/ui-ngx/src/app/core/http/calculated-fields.service.ts @@ -15,11 +15,17 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { defaultHttpOptionsFromConfig, defaultHttpOptionsFromParams, RequestConfig } from './http-utils'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { PageData } from '@shared/models/page/page-data'; -import { CalculatedField, CalculatedFieldTestScriptInputParams } from '@shared/models/calculated-field.models'; +import { + CalculatedField, + CalculatedFieldInfo, + CalculatedFieldsQuery, + CalculatedFieldTestScriptInputParams, + CalculatedFieldType +} from '@shared/models/calculated-field.models'; import { PageLink } from '@shared/models/page/page-link'; import { EntityId } from '@shared/models/id/entity-id'; import { EntityTestScriptResult } from '@shared/models/entity.models'; @@ -46,9 +52,12 @@ export class CalculatedFieldsService { return this.http.delete(`/api/calculatedField/${calculatedFieldId}`, defaultHttpOptionsFromConfig(config)); } - public getCalculatedFields({ entityType, id }: EntityId, pageLink: PageLink, config?: RequestConfig): Observable> { - return this.http.get>(`/api/${entityType}/${id}/calculatedFields${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getCalculatedFields(pageLink: PageLink, query: CalculatedFieldsQuery, config?: RequestConfig): Observable> { + return this.http.get>(`/api/calculatedFields${pageLink.toQuery()}`, defaultHttpOptionsFromParams(query, config)); + } + + public getCalculatedFieldsByEntityId({ entityType, id }: EntityId, pageLink: PageLink, type?: CalculatedFieldType, config?: RequestConfig): Observable> { + return this.http.get>(`/api/${entityType}/${id}/calculatedFields${pageLink.toQuery()}`, defaultHttpOptionsFromParams({type} , config)); } public testScript(inputParams: CalculatedFieldTestScriptInputParams, config?: RequestConfig): Observable { @@ -58,4 +67,8 @@ export class CalculatedFieldsService { public getLatestCalculatedFieldDebugEvent(id: string, config?: RequestConfig): Observable { return this.http.get(`/api/calculatedField/${id}/debug`, defaultHttpOptionsFromConfig(config)); } + + public getCalculatedFieldNames(pageLink: PageLink, type: CalculatedFieldType, config?: RequestConfig): Observable> { + return this.http.get>(`/api/calculatedFields/names${pageLink.toQuery()}`, defaultHttpOptionsFromParams({type}, config)); + } } diff --git a/ui-ngx/src/app/core/http/customer.service.ts b/ui-ngx/src/app/core/http/customer.service.ts index 03177f2f87..34f370ad80 100644 --- a/ui-ngx/src/app/core/http/customer.service.ts +++ b/ui-ngx/src/app/core/http/customer.service.ts @@ -15,12 +15,13 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; 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 { Customer } from '@shared/models/customer.model'; +import { SaveEntityParams } from '@shared/models/entity.models'; @Injectable({ providedIn: 'root' @@ -40,8 +41,14 @@ export class CustomerService { return this.http.get(`/api/customer/${customerId}`, defaultHttpOptionsFromConfig(config)); } - public saveCustomer(customer: Customer, config?: RequestConfig): Observable { - return this.http.post('/api/customer', customer, defaultHttpOptionsFromConfig(config)); + public getCustomersByIds(customerIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/customers?customerIds=${customerIds.join(',')}`, defaultHttpOptionsFromConfig(config)); + } + + public saveCustomer(customer: Customer, config?: RequestConfig): Observable; + public saveCustomer(customer: Customer, saveParams: SaveEntityParams, config?: RequestConfig): Observable; + public saveCustomer(customer: Customer, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable { + return this.http.post('/api/customer', customer, createDefaultHttpOptions(saveParamsOrConfig, config)); } public deleteCustomer(customerId: string, config?: RequestConfig) { diff --git a/ui-ngx/src/app/core/http/dashboard.service.ts b/ui-ngx/src/app/core/http/dashboard.service.ts index 512a34fc7f..f3281b863f 100644 --- a/ui-ngx/src/app/core/http/dashboard.service.ts +++ b/ui-ngx/src/app/core/http/dashboard.service.ts @@ -83,6 +83,11 @@ export class DashboardService { return this.http.get(`/api/dashboard/info/${dashboardId}`, defaultHttpOptionsFromConfig(config)); } + public getDashboards(dashboardIds: string[], config?: RequestConfig): Observable> { + return this.http.get>(`/api/dashboards?dashboardIds=${dashboardIds.join(',')}`, + defaultHttpOptionsFromConfig(config)); + } + public saveDashboard(dashboard: Dashboard, config?: RequestConfig): Observable { return this.http.post('/api/dashboard', dashboard, 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 7daedd85cb..a49375d933 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -50,6 +50,11 @@ export class DeviceProfileService { return this.http.get>(`/api/deviceProfiles${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); } + public getDeviceProfilesByIds(deviceProfileIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/deviceProfileInfos?deviceProfileIds=${deviceProfileIds.join(',')}`, + defaultHttpOptionsFromConfig(config)); + } + public getDeviceProfile(deviceProfileId: string, config?: RequestConfig): Observable { return this.http.get(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/device.service.ts b/ui-ngx/src/app/core/http/device.service.ts index ad2e12852a..f6126f02ad 100644 --- a/ui-ngx/src/app/core/http/device.service.ts +++ b/ui-ngx/src/app/core/http/device.service.ts @@ -15,8 +15,9 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; -import { Observable, ReplaySubject } from 'rxjs'; +import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { catchError, Observable, of, ReplaySubject, throwError, timeout } from 'rxjs'; +import { map, switchMap } from "rxjs/operators"; import { HttpClient } from '@angular/common/http'; import { PageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; @@ -28,13 +29,15 @@ import { DeviceInfo, DeviceInfoQuery, DeviceSearchQuery, - PublishTelemetryCommand + PublishTelemetryCommand, + SaveDeviceParams } from '@shared/models/device.models'; import { EntitySubtype } from '@shared/models/entity-type.models'; import { AuthService } from '@core/auth/auth.service'; 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'; +import { SaveEntityParams } from '@shared/models/entity.models'; @Injectable({ providedIn: 'root' @@ -87,15 +90,19 @@ export class DeviceService { return this.http.get(`/api/device/info/${deviceId}`, defaultHttpOptionsFromConfig(config)); } - public saveDevice(device: Device, config?: RequestConfig): Observable { - return this.http.post('/api/device', device, defaultHttpOptionsFromConfig(config)); + public saveDevice(device: Device, config?: RequestConfig): Observable; + public saveDevice(device: Device, saveParams?: SaveDeviceParams, config?: RequestConfig): Observable; + public saveDevice(device: Device, saveParamsOrConfig?: SaveDeviceParams | RequestConfig, config?: RequestConfig): Observable { + return this.http.post('/api/device', device, createDefaultHttpOptions(saveParamsOrConfig, config)); } - public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, config?: RequestConfig): Observable { + public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, config?: RequestConfig): Observable; + public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, saveParams: SaveEntityParams, config?: RequestConfig): Observable; + public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable { return this.http.post('/api/device-with-credentials', { device, credentials - }, defaultHttpOptionsFromConfig(config)); + }, createDefaultHttpOptions(saveParamsOrConfig, config)); } public deleteDevice(deviceId: string, config?: RequestConfig) { @@ -219,4 +226,71 @@ export class DeviceService { public downloadGatewayDockerComposeFile(deviceId: string): Observable { return this.resourcesService.downloadResource(`/api/device-connectivity/gateway-launch/${deviceId}/docker-compose/download`); } + + public rebootDevice(deviceId: string, isBootstrapServer: boolean, config?: RequestConfig): Observable<{ + result: string, + msg: string + }> { + const rebootName = isBootstrapServer ? 'Bootstrap-Request Trigger' : 'Registration Update Trigger'; + return this.sendTwoWayRpcCommand(deviceId, {method: 'DiscoverAll'}, config).pipe( + timeout(10000), + switchMap((response: any) => { + if (response.result && response.result.toUpperCase() === 'CONTENT') { + const resourceId = isBootstrapServer ? 9 : 8; + const resourcePath = `/1/0/${resourceId}`; + return this.rebootTrigger(deviceId, resourcePath, config).pipe( + map((responseReboot: any) => { + if (responseReboot.result === 'CHANGED') { + return { + result: 'SUCCESS', + msg: `"${rebootName}" - Started Successfully.` + }; + } else { + return { + result: 'ERROR', + msg: `"${rebootName}" failed:
${JSON.stringify(responseReboot, null, 2)}
` + } + } + }), + catchError(err => + of({ + result: 'ERROR', + msg: `"${rebootName}" failed.
Error: ${err.message || err}` + }) + ) + ); + } else { + return of({ + result: 'ERROR', + msg: `"${rebootName}" failed.
Bad registration device with id = ${deviceId}.
"DiscoverAll" - RPC result is not "CONTENT"` + }); + } + }), + catchError(err => + of({ + result: 'ERROR', + msg: `"${rebootName}" failed.
Bad registration device with id = ${deviceId}.
Error: ${err.message || err}` + }) + ) + ); + } + + private rebootTrigger(deviceId: string, resourcePath: string, config?: RequestConfig): Observable<{ result: string, msg?: string }> { + return this.sendTwoWayRpcCommand(deviceId, {method: 'Execute', params: {id: resourcePath}}, config).pipe( + timeout(10000), + map(res => { + if (res?.result?.toUpperCase() === 'CHANGED') { + return {result: 'CHANGED'}; + } else { + return { + result: `${res?.result}`, + msg: `${res?.error}` + } + } + }), + catchError(err => { + return throwError(() => err); + }) + ); + } } diff --git a/ui-ngx/src/app/core/http/entity-view.service.ts b/ui-ngx/src/app/core/http/entity-view.service.ts index 04ab621e53..5741921c41 100644 --- a/ui-ngx/src/app/core/http/entity-view.service.ts +++ b/ui-ngx/src/app/core/http/entity-view.service.ts @@ -15,13 +15,14 @@ /// import { Injectable } from '@angular/core'; -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; 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 { EntityView, EntityViewInfo, EntityViewSearchQuery } from '@app/shared/models/entity-view.models'; +import { SaveEntityParams } from '@shared/models/entity.models'; @Injectable({ providedIn: 'root' @@ -47,12 +48,19 @@ export class EntityViewService { return this.http.get(`/api/entityView/${entityViewId}`, defaultHttpOptionsFromConfig(config)); } + public getEntityViews(entityViewIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/entityViews?entityViewIds=${entityViewIds.join(',')}`, + defaultHttpOptionsFromConfig(config)); + } + public getEntityViewInfo(entityViewId: string, config?: RequestConfig): Observable { return this.http.get(`/api/entityView/info/${entityViewId}`, defaultHttpOptionsFromConfig(config)); } - public saveEntityView(entityView: EntityView, config?: RequestConfig): Observable { - return this.http.post('/api/entityView', entityView, defaultHttpOptionsFromConfig(config)); + public saveEntityView(entityView: EntityView, config?: RequestConfig): Observable; + public saveEntityView(entityView: EntityView, saveParams: SaveEntityParams, config?: RequestConfig): Observable; + public saveEntityView(entityView: EntityView, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable { + return this.http.post('/api/entityView', entityView, createDefaultHttpOptions(saveParamsOrConfig, config)); } public deleteEntityView(entityViewId: string, config?: RequestConfig) { diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 868485653e..6f4db51e24 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -189,6 +189,12 @@ export class EntityService { case EntityType.AI_MODEL: observable = this.aiModelService.getAiModelById(entityId, config); break; + case EntityType.DEVICE_PROFILE: + observable = this.deviceProfileService.getDeviceProfile(entityId, config); + break; + case EntityType.ASSET_PROFILE: + observable = this.assetProfileService.getAssetProfile(entityId, config); + break; } return observable; } @@ -239,50 +245,34 @@ export class EntityService { observable = this.edgeService.getEdges(entityIds, config); break; case EntityType.ENTITY_VIEW: - observable = this.getEntitiesByIdsObservable( - (id) => this.entityViewService.getEntityView(id, config), - entityIds); + observable = this.entityViewService.getEntityViews(entityIds, config); break; case EntityType.TENANT: - observable = this.getEntitiesByIdsObservable( - (id) => this.tenantService.getTenant(id, config), - entityIds); + observable = this.tenantService.getTenantsByIds(entityIds, config); break; case EntityType.CUSTOMER: - observable = this.getEntitiesByIdsObservable( - (id) => this.customerService.getCustomer(id, config), - entityIds); + observable = this.customerService.getCustomersByIds(entityIds, config); break; case EntityType.DASHBOARD: - observable = this.getEntitiesByIdsObservable( - (id) => this.dashboardService.getDashboardInfo(id, config), - entityIds); + observable = this.dashboardService.getDashboards(entityIds, config); break; case EntityType.USER: - observable = this.getEntitiesByIdsObservable( - (id) => this.userService.getUser(id, config), - entityIds); + observable = this.userService.getUsersByIds(entityIds, config); break; case EntityType.ALARM: console.error('Get Alarm Entity is not implemented!'); break; case EntityType.DEVICE_PROFILE: - observable = this.getEntitiesByIdsObservable( - (id) => this.deviceProfileService.getDeviceProfileInfo(id, config), - entityIds); + observable = this.deviceProfileService.getDeviceProfilesByIds(entityIds, config); break; case EntityType.TENANT_PROFILE: observable = this.tenantProfileService.getTenantProfilesByIds(entityIds, config); break; case EntityType.ASSET_PROFILE: - observable = this.getEntitiesByIdsObservable( - (id) => this.assetProfileService.getAssetProfileInfo(id, config), - entityIds); + observable = this.assetProfileService.getAssetProfilesByIds(entityIds, config); break; case EntityType.WIDGETS_BUNDLE: - observable = this.getEntitiesByIdsObservable( - (id) => this.widgetService.getWidgetsBundle(id, config), - entityIds); + observable = this.widgetService.getWidgetsBundlesByIds(entityIds, config); break; case EntityType.NOTIFICATION_TARGET: observable = this.notificationService.getNotificationTargetsByIds(entityIds, config); @@ -294,9 +284,7 @@ export class EntityService { observable = this.oauth2Service.findTenantOAuth2ClientInfosByIds(entityIds, config); break; case EntityType.RULE_CHAIN: - observable = this.getEntitiesByIdsObservable( - (id) => this.ruleChainService.getRuleChain(id, config), - entityIds); + observable = this.ruleChainService.getRuleChainsByIds(entityIds, config); break; case EntityType.TB_RESOURCE: observable = this.resourceService.getResourcesByIds(entityIds, config); @@ -817,6 +805,7 @@ export class EntityService { switch (entityType) { case EntityType.USER: entityFieldKeys.push(entityFields.name.keyName); + entityFieldKeys.push(entityFields.displayName.keyName); entityFieldKeys.push(entityFields.email.keyName); entityFieldKeys.push(entityFields.firstName.keyName); entityFieldKeys.push(entityFields.lastName.keyName); @@ -846,6 +835,7 @@ export class EntityService { case EntityType.EDGE: case EntityType.ASSET: entityFieldKeys.push(entityFields.name.keyName); + entityFieldKeys.push(entityFields.displayName.keyName); entityFieldKeys.push(entityFields.type.keyName); entityFieldKeys.push(entityFields.label.keyName); entityFieldKeys.push(entityFields.ownerName.keyName); diff --git a/ui-ngx/src/app/core/http/http-utils.ts b/ui-ngx/src/app/core/http/http-utils.ts index 9853d67b85..8b90821c09 100644 --- a/ui-ngx/src/app/core/http/http-utils.ts +++ b/ui-ngx/src/app/core/http/http-utils.ts @@ -18,32 +18,81 @@ import { InterceptorHttpParams } from '../interceptors/interceptor-http-params'; import { HttpHeaders } from '@angular/common/http'; import { InterceptorConfig } from '../interceptors/interceptor-config'; +export type QueryParams = { [param:string]: any }; + export interface RequestConfig { ignoreLoading?: boolean; ignoreErrors?: boolean; resendRequest?: boolean; + queryParams?: QueryParams; +} + +export function hasRequestConfig(config?: any): boolean { + if (!config) { + return false; + } + return config.hasOwnProperty('ignoreLoading') || config.hasOwnProperty('ignoreErrors') || config.hasOwnProperty('resendRequest') || config.hasOwnProperty('queryParams'); +} + +export function createDefaultHttpOptions(queryParamsOrConfig?: QueryParams | RequestConfig, config?: RequestConfig) { + if (hasRequestConfig(queryParamsOrConfig)) { + return defaultHttpOptionsFromConfig(queryParamsOrConfig as RequestConfig); + } + return defaultHttpOptionsFromParams(queryParamsOrConfig as QueryParams, config); +} + +export function defaultHttpOptionsFromParams(queryParams?: QueryParams, config?: RequestConfig) { + const finalConfig = { + ...config, + ...(queryParams && { queryParams }), + }; + return defaultHttpOptionsFromConfig(finalConfig); } export function defaultHttpOptionsFromConfig(config?: RequestConfig) { if (!config) { config = {}; } - return defaultHttpOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest); + return defaultHttpOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest, config.queryParams); } export function defaultHttpOptions(ignoreLoading: boolean = false, ignoreErrors: boolean = false, - resendRequest: boolean = false) { + resendRequest: boolean = false, + queryParams?: QueryParams) { + const cleanedParams = cleanQueryParams(queryParams); + return { headers: new HttpHeaders({'Content-Type': 'application/json'}), - params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest)) + params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), cleanedParams) }; } export function defaultHttpUploadOptions(ignoreLoading: boolean = false, ignoreErrors: boolean = false, - resendRequest: boolean = false) { + resendRequest: boolean = false, + queryParams?: QueryParams) { + const cleanedParams = cleanQueryParams(queryParams); + return { - params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest)) + params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), cleanedParams) }; } + +function cleanQueryParams(params?: QueryParams): QueryParams | undefined { + if (!params) { + return undefined; + } + + const entries = Object.entries(params); + + const cleanedEntries = entries.filter( + ([_, value]) => value !== null && value !== undefined + ); + + if (!cleanedEntries.length) { + return undefined; + } + + return Object.fromEntries(cleanedEntries); +} diff --git a/ui-ngx/src/app/core/http/notification.service.ts b/ui-ngx/src/app/core/http/notification.service.ts index 6501e88330..574ad9003c 100644 --- a/ui-ngx/src/app/core/http/notification.service.ts +++ b/ui-ngx/src/app/core/http/notification.service.ts @@ -37,6 +37,7 @@ import { } from '@shared/models/notification.models'; import { User } from '@shared/models/user.model'; import { isNotEmptyStr } from '@core/utils'; +import { EntityType } from '@shared/models/entity-type.models'; @Injectable({ providedIn: 'root' @@ -69,6 +70,10 @@ export class NotificationService { return this.http.post('/api/notification/request', notification, defaultHttpOptionsFromConfig(config)); } + public sendEntitiesLimitIncreaseRequest(entityType: EntityType, config?: RequestConfig): Observable { + return this.http.post(`/api/notification/entitiesLimitIncreaseRequest/${entityType}`, defaultHttpOptionsFromConfig(config)); + } + public getNotificationRequestById(id: string, config?: RequestConfig): Observable { return this.http.get(`/api/notification/request/${id}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/resource.service.ts b/ui-ngx/src/app/core/http/resource.service.ts index 76511385f2..810f6ed7c5 100644 --- a/ui-ngx/src/app/core/http/resource.service.ts +++ b/ui-ngx/src/app/core/http/resource.service.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { PageLink } from '@shared/models/page/page-link'; -import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; +import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; import { forkJoin, Observable, of } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { Resource, ResourceInfo, ResourceSubType, ResourceType, TBResourceScope } from '@shared/models/resource.models'; @@ -90,6 +90,54 @@ export class ResourceService { return this.http.post('/api/resource', resource, defaultHttpOptionsFromConfig(config)); } + public uploadResources(resources: Resource[], config?: RequestConfig): Observable { + let partSize = 100; + partSize = resources.length > partSize ? partSize : resources.length; + const resourceObservables: Observable[] = []; + for (let i = 0; i < partSize; i++) { + resourceObservables.push(this.uploadResource(resources[i], config).pipe(catchError(() => of({} as Resource)))); + } + return forkJoin(resourceObservables).pipe( + mergeMap((resource) => { + resources.splice(0, partSize); + if (resources.length) { + return this.uploadResources(resources, config); + } else { + return of(resource); + } + }) + ); + } + + public uploadResource(resource: Resource, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', resource.data); + formData.append('title', resource.title); + formData.append('resourceType', resource.resourceType); + if (resource.resourceSubType) { + formData.append('resourceSubType', resource.resourceSubType); + } + return this.http.post('/api/resource/upload', formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public updatedResourceInfo(resourceId: string, updatedResources: Partial>, config?: RequestConfig): Observable { + return this.http.put(`/api/resource/${resourceId}/info`, updatedResources, defaultHttpOptionsFromConfig(config)); + } + + public updatedResourceData(resourceId: string, data: File, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', data); + return this.http.put(`/api/resource/${resourceId}/data`, formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + public deleteResource(resourceId: string, force = false, config?: RequestConfig) { return this.http.delete(`/api/resource/${resourceId}?force=${force}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/rule-chain.service.ts b/ui-ngx/src/app/core/http/rule-chain.service.ts index 5450071ee5..545d29c6af 100644 --- a/ui-ngx/src/app/core/http/rule-chain.service.ts +++ b/ui-ngx/src/app/core/http/rule-chain.service.ts @@ -68,6 +68,11 @@ export class RuleChainService { defaultHttpOptionsFromConfig(config)); } + public getRuleChainsByIds(ruleChainIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/ruleChains?&ruleChainIds=${ruleChainIds.join(',')}`, + defaultHttpOptionsFromConfig(config)); + } + public getRuleChain(ruleChainId: string, config?: RequestConfig): Observable { return this.http.get(`/api/ruleChain/${ruleChainId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/tenant.service.ts b/ui-ngx/src/app/core/http/tenant.service.ts index 361922986f..650032b9f7 100644 --- a/ui-ngx/src/app/core/http/tenant.service.ts +++ b/ui-ngx/src/app/core/http/tenant.service.ts @@ -35,6 +35,10 @@ export class TenantService { return this.http.get>(`/api/tenants${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); } + public getTenantsByIds(tenantIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/tenants?tenantIds=${tenantIds.join(',')}`, defaultHttpOptionsFromConfig(config)); + } + public getTenantInfos(pageLink: PageLink, config?: RequestConfig): Observable> { return this.http.get>(`/api/tenantInfos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/user.service.ts b/ui-ngx/src/app/core/http/user.service.ts index f1c1e1d72b..ce16facdb3 100644 --- a/ui-ngx/src/app/core/http/user.service.ts +++ b/ui-ngx/src/app/core/http/user.service.ts @@ -61,6 +61,10 @@ export class UserService { return this.http.get(`/api/user/${userId}`, defaultHttpOptionsFromConfig(config)); } + public getUsersByIds(userIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/users?userIds=${userIds.join(',')}`, defaultHttpOptionsFromConfig(config)); + } + public saveUser(user: User, sendActivationMail: boolean = false, config?: RequestConfig): Observable { let url = '/api/user'; diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index 06a6c86ddd..b6a677f22b 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -331,6 +331,11 @@ export class WidgetService { this.widgetsInfoInMemoryCache.delete(fullFqn); } + public getWidgetsBundlesByIds(widgetsBundleIds: Array, config?: RequestConfig): Observable> { + return this.http.get>(`/api/widgetsBundles?widgetsBundleIds=${widgetsBundleIds.join(',')}`, + defaultHttpOptionsFromConfig(config)); + } + private loadWidgetsBundleCache(config?: RequestConfig): Observable { if (!this.allWidgetsBundles) { if (!this.loadWidgetsBundleCacheSubject) { diff --git a/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts b/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts index c210c54a0b..d008b8e5c7 100644 --- a/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts +++ b/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts @@ -109,6 +109,10 @@ export class GlobalHttpInterceptor implements HttpInterceptor { } else if (errorCode !== Constants.serverErrorCode.credentialsExpired) { unhandled = true; } + } else if (errorCode && errorCode === Constants.serverErrorCode.entitiesLimitExceeded) { + if (!ignoreErrors) { + this.dialogService.entitiesLimitExceeded(errorResponse.error); + } } else if (errorResponse.status === 429) { if (resendRequest) { return this.retryRequest(req, next); diff --git a/ui-ngx/src/app/core/interceptors/interceptor-http-params.ts b/ui-ngx/src/app/core/interceptors/interceptor-http-params.ts index ba6e3199ba..332e6cac14 100644 --- a/ui-ngx/src/app/core/interceptors/interceptor-http-params.ts +++ b/ui-ngx/src/app/core/interceptors/interceptor-http-params.ts @@ -20,7 +20,7 @@ import { InterceptorConfig } from './interceptor-config'; export class InterceptorHttpParams extends HttpParams { constructor( public interceptorConfig: InterceptorConfig, - params?: { [param: string]: string | string[] } + params?: { [param: string]: string | number | boolean | ReadonlyArray; } ) { super({ fromObject: params }); } diff --git a/ui-ngx/src/app/core/services/calculated-field-form.service.ts b/ui-ngx/src/app/core/services/calculated-field-form.service.ts new file mode 100644 index 0000000000..1bedbf99a7 --- /dev/null +++ b/ui-ngx/src/app/core/services/calculated-field-form.service.ts @@ -0,0 +1,117 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { DestroyRef, inject, Injectable } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { pairwise, switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { + CalculatedField, + CalculatedFieldConfiguration, + CalculatedFieldEventArguments, + CalculatedFieldType, + OutputStrategyType +} from '@shared/models/calculated-field.models'; +import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { isDefined } from '@core/utils'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Injectable({ providedIn: 'root' }) +export class CalculatedFieldFormService { + private fb = inject(FormBuilder); + private calculatedFieldsService = inject(CalculatedFieldsService); + + buildForm(): FormGroup { + return this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + entityId: [null, Validators.required], + type: [CalculatedFieldType.SIMPLE], + debugSettings: [], + configuration: this.fb.control({} as CalculatedFieldConfiguration), + }); + } + + buildAlarmRuleForm(): FormGroup { + return this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + entityId: [null, Validators.required], + type: [CalculatedFieldType.ALARM], + debugSettings: [], + configuration: this.fb.group({ + type: [CalculatedFieldType.ALARM], + arguments: this.fb.control({}, Validators.required), + propagate: [false], + propagateToOwner: [false], + propagateToTenant: [false], + propagateRelationTypes: [null], + createRules: [null, Validators.required], + clearRule: [null], + }), + }); + } + + setupTypeChange(form: FormGroup, destroyRef: DestroyRef, isEditActive?: () => boolean): void { + form.get('type').valueChanges.pipe( + pairwise(), + takeUntilDestroyed(destroyRef) + ).subscribe(([prevType, nextType]) => { + const shouldCheck = isEditActive ? isEditActive() : true; + if (shouldCheck && prevType !== nextType) { + if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || + ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { + form.get('configuration').setValue({} as CalculatedFieldConfiguration, { emitEvent: false }); + } + } + }); + } + + prepareConfig(configuration: CalculatedFieldConfiguration): CalculatedFieldConfiguration { + const config = configuration || ({} as CalculatedFieldConfiguration); + if (config.type !== CalculatedFieldType.ALARM) { + if (isDefined(config?.output) && !config?.output?.strategy) { + config.output.strategy = { type: OutputStrategyType.RULE_CHAIN }; + } + } + return config; + } + + testScript( + calculatedFieldId: string, + formValue: CalculatedField, + testDialogFn: (calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit?: boolean, expression?: string) => Observable, + destroyRef: DestroyRef, + expression?: string, + ): Observable { + if (calculatedFieldId) { + return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) + .pipe( + switchMap(event => { + let args = null; + if (event?.arguments) { + try { + args = JSON.parse(event.arguments); + } catch (e) {} + } + return testDialogFn(formValue, args, false, expression); + }), + takeUntilDestroyed(destroyRef) + ); + } + return testDialogFn(formValue, null, false, expression); + } +} 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 b185c77c2d..6704bebbab 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -35,9 +35,18 @@ import { LayoutType, WidgetLayout } from '@shared/models/dashboard.models'; -import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined } from '@core/utils'; +import { + deepClean, + deepClone, + isDefined, + isDefinedAndNotNull, + isNotEmptyStr, + isString, + isUndefined +} from '@core/utils'; import { Datasource, + datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, DatasourceType, defaultLegendConfig, @@ -62,7 +71,11 @@ import { MediaBreakpoints } from '@shared/models/constants'; import { TranslateService } from '@ngx-translate/core'; import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models'; import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models'; -import { findWidgetModelDefinition } from '@shared/models/widget/widget-model.definition'; +import { + findWidgetModelDefinition, + widgetHasTimewindow, + WidgetModelDefinition +} from '@shared/models/widget/widget-model.definition'; @Injectable({ providedIn: 'root' @@ -78,7 +91,10 @@ export class DashboardUtilsService { public validateAndUpdateDashboard(dashboard: Dashboard): Dashboard { if (!dashboard.configuration) { - dashboard.configuration = {}; + dashboard.configuration = { + entityAliases: {}, + filters: {}, + }; } if (isUndefined(dashboard.configuration.widgets)) { dashboard.configuration.widgets = {}; @@ -166,9 +182,8 @@ export class DashboardUtilsService { dashboard.configuration.filters = {}; } - if (isUndefined(dashboard.configuration.timewindow)) { - dashboard.configuration.timewindow = this.timeService.defaultTimewindow(); - } + dashboard.configuration.timewindow = initModelFromDefaultTimewindow(dashboard.configuration.timewindow, + false, false, this.timeService, true, true); if (isUndefined(dashboard.configuration.settings)) { dashboard.configuration.settings = {}; dashboard.configuration.settings.stateControllerId = 'entity'; @@ -232,8 +247,14 @@ export class DashboardUtilsService { } public validateAndUpdateWidget(widget: Widget): Widget { - widget.config = this.validateAndUpdateWidgetConfig(widget.config, widget.type); + const widgetDefinition = widget.config ? findWidgetModelDefinition(widget) : null; + if (widgetDefinition) { + widget.config = this.validateAndUpdateWidgetConfigWithModelDefinition(widget, widgetDefinition); + } else { + widget.config = this.validateAndUpdateWidgetConfig(widget.config, widget.type); + } widget = this.validateAndUpdateWidgetTypeFqn(widget); + this.removeTimewindowConfigIfUnused(widget); if (isDefined((widget as any).title)) { delete (widget as any).title; } @@ -294,8 +315,11 @@ export class DashboardUtilsService { } widgetConfig.datasources = this.validateAndUpdateDatasources(widgetConfig.datasources); if (type === widgetType.latest) { - const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); - widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, onlyHistoryTimewindow, this.timeService); + if (datasourcesHasAggregation(widgetConfig.datasources)) { + const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); + widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, + onlyHistoryTimewindow, this.timeService, false); + } } else if (type === widgetType.rpc) { if (widgetConfig.targetDeviceAliasIds && widgetConfig.targetDeviceAliasIds.length) { widgetConfig.targetDevice = { @@ -345,7 +369,34 @@ export class DashboardUtilsService { } } } - return widgetConfig; + return deepClean(widgetConfig, {cleanKeys: ['_hash'], cleanOnlyKey: true}); + } + + public validateAndUpdateWidgetConfigWithModelDefinition(widget: Widget, widgetDefinition: WidgetModelDefinition): WidgetConfig { + const widgetConfig = widget.config; + if (widget.type === widgetType.latest && widgetDefinition.hasTimewindow(widget)) { + widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, + widgetDefinition.datasourcesHasOnlyComparisonAggregation(widget), this.timeService, false); + } + return deepClean(widgetConfig, {cleanKeys: ['_hash'], cleanOnlyKey: true}); + } + + private removeTimewindowConfigIfUnused(widget: Widget) { + const hasTimewindow = widgetHasTimewindow(widget); + if (!hasTimewindow || widget.config.useDashboardTimewindow) { + delete widget.config.displayTimewindow; + delete widget.config.timewindow; + + if (!hasTimewindow) { + delete widget.config.useDashboardTimewindow; + } + } + } + + public prepareWidgetForSaving(widget: Widget): Widget { + this.removeTimewindowConfigIfUnused(widget); + widget = deepClean(widget, {cleanKeys: ['_hash'], cleanOnlyKey: true}); + return widget; } public prepareWidgetForScadaLayout(widget: Widget, isScada: boolean): Widget { diff --git a/ui-ngx/src/app/core/services/dialog.service.ts b/ui-ngx/src/app/core/services/dialog.service.ts index d87cdfc2db..c73de0435b 100644 --- a/ui-ngx/src/app/core/services/dialog.service.ts +++ b/ui-ngx/src/app/core/services/dialog.service.ts @@ -36,6 +36,11 @@ import { ErrorAlertDialogData } from '@shared/components/dialog/error-alert-dialog.component'; import { TodoDialogComponent } from '@shared/components/dialog/todo-dialog.component'; +import { EntityType } from '@shared/models/entity-type.models'; +import { + EntityLimitExceededDialogComponent, + EntityLimitExceededDialogData +} from '@shared/components/dialog/entity-limit-exceeded-dialog.component'; @Injectable({ providedIn: 'root' @@ -125,6 +130,16 @@ export class DialogService { }).afterClosed(); } + entitiesLimitExceeded(entityLimitData: {entityType: EntityType, limit: number}): Observable { + return this.dialog.open(EntityLimitExceededDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: entityLimitData, + autoFocus: false + }).afterClosed(); + } + private permissionDenied() { this.alert( this.translate.instant('access.permission-denied'), diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index a3863f8b5e..c2d4fc9e21 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -85,7 +85,9 @@ export enum MenuId { domains = 'domains', clients = 'clients', audit_log = 'audit_log', + alarms_center = 'alarms_center', alarms = 'alarms', + alarm_rules = 'alarm_rules', dashboards = 'dashboards', entities = 'entities', devices = 'devices', @@ -96,6 +98,7 @@ export enum MenuId { device_profiles = 'device_profiles', asset_profiles = 'asset_profiles', customers = 'customers', + calculated_fields = 'calculated_fields', rule_chains = 'rule_chains', edge_management = 'edge_management', edges = 'edges', @@ -495,15 +498,35 @@ export const menuSectionMap = new Map([ } ], [ - MenuId.alarms, + MenuId.alarms_center, { - id: MenuId.alarms, + id: MenuId.alarms_center, name: 'alarm.alarms', type: 'link', path: '/alarms', icon: 'mdi:alert-outline' } ], + [ + MenuId.alarms, + { + id: MenuId.alarms, + name: 'alarm.alarm-list', + type: 'link', + path: '/alarms/alarms', + icon: 'mdi:alert-outline' + } + ], + [ + MenuId.alarm_rules, + { + id: MenuId.alarm_rules, + name: 'alarm-rule.alarm-rules', + type: 'link', + path: '/alarms/alarm-rules', + icon: 'tune' + } + ], [ MenuId.dashboards, { @@ -604,6 +627,16 @@ export const menuSectionMap = new Map([ icon: 'supervisor_account' } ], + [ + MenuId.calculated_fields, + { + id: MenuId.calculated_fields, + name: 'entity.type-calculated-fields', + type: 'link', + path: '/calculatedFields', + icon: 'mdi:function-variant', + } + ], [ MenuId.rule_chains, { @@ -792,7 +825,13 @@ const defaultUserMenuMap = new Map([ Authority.TENANT_ADMIN, [ {id: MenuId.home}, - {id: MenuId.alarms}, + { + id: MenuId.alarms_center, + pages: [ + {id: MenuId.alarms}, + {id: MenuId.alarm_rules} + ] + }, {id: MenuId.dashboards}, { id: MenuId.entities, @@ -811,6 +850,7 @@ const defaultUserMenuMap = new Map([ ] }, {id: MenuId.customers}, + {id: MenuId.calculated_fields}, {id: MenuId.rule_chains}, { id: MenuId.edge_management, diff --git a/ui-ngx/src/app/core/services/mobile.service.ts b/ui-ngx/src/app/core/services/mobile.service.ts index 295605bab1..5d2f82bf37 100644 --- a/ui-ngx/src/app/core/services/mobile.service.ts +++ b/ui-ngx/src/app/core/services/mobile.service.ts @@ -106,9 +106,9 @@ export class MobileService { } } - public handleMobileNavigation(path?: string, params?: Params) { + public handleMobileNavigation(path?: string, params?: Params, queryParams?: Params) { if (this.mobileApp) { - this.mobileChannel.callHandler(navigationHandler, path, params); + this.mobileChannel.callHandler(navigationHandler, path, params, queryParams); } } diff --git a/ui-ngx/src/app/core/services/time.service.ts b/ui-ngx/src/app/core/services/time.service.ts index 1b9c90d13b..aedf250758 100644 --- a/ui-ngx/src/app/core/services/time.service.ts +++ b/ui-ngx/src/app/core/services/time.service.ts @@ -146,8 +146,8 @@ export class TimeService { return this.boundMaxInterval(max); } - public defaultTimewindow(): Timewindow { - return defaultTimewindow(this); + public defaultTimewindow(isDashboard = false): Timewindow { + return defaultTimewindow(this, isDashboard); } private toBound(value: number, min: number, max: number, defValue: number): number { diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index e234412632..f32ad194dc 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -97,7 +97,6 @@ export class UtilsService { color: this.getMaterialColor(0), funcBody: this.getPredefinedFunctionBody('Sin'), settings: {}, - _hash: Math.random() }; defaultDatasource: Datasource = { @@ -153,8 +152,7 @@ export class UtilsService { type: DataKeyType.alarm, label: this.translate.instant(alarmFields[name].name), color: this.getMaterialColor(i), - settings: {}, - _hash: Math.random() + settings: {} }; this.defaultAlarmDataKeys.push(dataKey); } @@ -267,8 +265,7 @@ export class UtilsService { type, label, funcBody: keyInfo.funcBody, - settings: {}, - _hash: Math.random() + settings: {} }; if (keyInfo.units) { dataKey.units = keyInfo.units; @@ -457,11 +454,11 @@ export class UtilsService { return base64toObj(b64Encoded); } - public applyCssToElement(renderer: Renderer2, element: any, cssClassPrefix: string, css: string): string { + public applyCssToElement(renderer: Renderer2, element: any, cssClassPrefix: string, css: string, addTbDefaultClass: boolean = false): string { const cssParser = new cssjs(); cssParser.testMode = false; const cssClass = `${cssClassPrefix}-${guid()}`; - cssParser.cssPreviewNamespace = cssClass; + cssParser.cssPreviewNamespace = addTbDefaultClass ? 'tb-default .' + cssClass : cssClass; cssParser.createStyleElement(cssClass, css); renderer.addClass(element, cssClass); return cssClass; diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 4c7a241931..bf57dd4441 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -201,6 +201,23 @@ export function deleteNullProperties(obj: any) { }); } +export function deleteFalseProperties(obj: Record): void { + if (isUndefinedOrNull(obj)) { + return; + } + Object.keys(obj).forEach((propName) => { + if (obj[propName] === false || isUndefinedOrNull(obj[propName])) { + delete obj[propName]; + } else if (isObject(obj[propName])) { + deleteFalseProperties(obj[propName]); + } else if (Array.isArray(obj[propName])) { + (obj[propName] as any[]).forEach((elem) => { + deleteFalseProperties(elem); + }); + } + }); +} + export function objToBase64(obj: any): string { const json = JSON.stringify(obj); return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g, @@ -778,6 +795,74 @@ export function deepTrim(obj: T): T { }, (Array.isArray(obj) ? [] : {}) as T); } +const isValidValue = (value: any): boolean => { + return ( + value !== undefined && + value !== null && + value !== '' && + !Number.isNaN(value) + ); +}; + +export function deepClean | any[]>(obj: T, { + cleanKeys = [], + cleanOnlyKey = false +} = {}): T { + const keysToRemove = new Set(cleanKeys); + + const clean = (input: any): any => { + if (Array.isArray(input)) { + const result: any[] = []; + for (const item of input) { + const value = clean(item); + + if (cleanOnlyKey) { + result.push(value); + continue; + } + + if (isValidValue(value)) { + const isEmptyArray = Array.isArray(value) && value.length === 0; + const isEmptyObj = isLiteralObject(value) && Object.keys(value).length === 0; + + if (!isEmptyArray && !isEmptyObj) { + result.push(value); + } + } + } + return result; + } + + if (isLiteralObject(input)) { + const result: Record = {}; + + for (const key in input) { + if (keysToRemove.has(key)) continue; + + const value = clean(input[key]); + + if (cleanOnlyKey) { + result[key] = value; + continue; + } + + if (isValidValue(value)) { + const isEmptyArray = Array.isArray(value) && value.length === 0; + const isEmptyObj = isLiteralObject(value) && Object.keys(value).length === 0; + + if (!isEmptyArray && !isEmptyObj) { + result[key] = value; + } + } + } + return result; + } + return input; + }; + + return clean(obj); +} + export function generateSecret(length?: number): string { if (isUndefined(length) || length == null) { length = 1; diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 8690676e53..361ee71ec4 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -121,6 +121,7 @@ import * as TbErrorComponent from '@shared/components/tb-error.component'; import * as TbCheatSheetComponent from '@shared/components/cheatsheet.component'; import * as BreadcrumbComponent from '@shared/components/breadcrumb.component'; import * as UserMenuComponent from '@shared/components/user-menu.component'; +import * as TimeUnitInputComponent from '@shared/components/time-unit-input.component'; import * as TimewindowComponent from '@shared/components/time/timewindow.component'; import * as TimewindowPanelComponent from '@shared/components/time/timewindow-panel.component'; import * as TimeintervalComponent from '@shared/components/time/timeinterval.component'; @@ -178,6 +179,10 @@ import * as OtaPackageAutocompleteComponent from '@shared/components/ota-package import * as WidgetsBundleSearchComponent from '@shared/components/widgets-bundle-search.component'; import * as CopyButtonComponent from '@shared/components/button/copy-button.component'; import * as TogglePasswordComponent from '@shared/components/button/toggle-password.component'; +import * as WidgetButtonComponent from '@shared/components/button/widget-button.component'; +import * as WidgetButtonToggleComponent from '@shared/components/button/widget-button-toggle.component'; +import * as PhoneInputComponent from '@shared/components/phone-input.component'; +import * as TbPopoverService from '@shared/components/popover.service'; 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'; @@ -190,8 +195,6 @@ import * as HintTooltipIconComponent from '@shared/components/hint-tooltip-icon. 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 TbPopoverService from '@shared/components/popover.service'; - import * as CssUnitSelectComponent from '@home/components/widget/lib/settings/common/css-unit-select.component'; import * as WidgetActionsPanelComponent from '@home/components/widget/config/basic/common/widget-actions-panel.component'; @@ -456,6 +459,7 @@ class ModulesMap implements IModulesMap { '@shared/components/cheatsheet.component': TbCheatSheetComponent, '@shared/components/breadcrumb.component': BreadcrumbComponent, '@shared/components/user-menu.component': UserMenuComponent, + '@shared/components/time-unit-input.component': TimeUnitInputComponent, '@shared/components/time/timewindow.component': TimewindowComponent, '@shared/components/time/timewindow-panel.component': TimewindowPanelComponent, '@shared/components/time/timeinterval.component': TimeintervalComponent, @@ -520,6 +524,10 @@ class ModulesMap implements IModulesMap { '@shared/components/widgets-bundle-search.component': WidgetsBundleSearchComponent, '@shared/components/button/copy-button.component': CopyButtonComponent, '@shared/components/button/toggle-password.component': TogglePasswordComponent, + '@shared/components/button/widget-button.component': WidgetButtonComponent, + '@shared/components/button/widget-button-toggle.component': WidgetButtonToggleComponent, + '@shared/components/popover.service': TbPopoverService, + '@shared/components/phone-input.component': PhoneInputComponent, '@shared/components/protobuf-content.component': ProtobufContentComponent, '@shared/components/slack-conversation-autocomplete.component': SlackConversationAutocompleteComponent, '@shared/components/string-items-list.component': StringItemsListComponent, @@ -532,8 +540,6 @@ class ModulesMap implements IModulesMap { '@shared/components/grid/scroll-grid.component': ScrollGridComponent, '@shared/components/image/gallery-image-input.component': GalleryImageInputComponent, '@shared/components/image/multiple-gallery-image-input.component': MultipleGalleryImageInputComponent, - '@shared/components/popover.service': TbPopoverService, - '@home/components/alarm/alarm-filter-config.component': AlarmFilterConfigComponent, '@home/components/alarm/alarm-comment-dialog.component': AlarmCommentDialogComponent, diff --git a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.html b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.html index 165f83e8e5..4aafb9b441 100644 --- a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.html @@ -225,6 +225,7 @@ additionalClass="tb-suffix-show-on-hover" appearance="outline" panelWidth="" + showInlineError required [label]="(provider === aiProvider.AZURE_OPENAI ? 'ai-models.deployment-name': 'ai-models.model-id') | translate" [errorText]="(provider === aiProvider.AZURE_OPENAI ? 'ai-models.deployment-name-required': 'ai-models.model-id-required') | translate" diff --git a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts index 570e1e7cb4..379b8f7dbc 100644 --- a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts @@ -43,6 +43,7 @@ import { TranslateService } from '@ngx-translate/core'; export interface AIModelDialogData { AIModel?: AiModel; isAdd?: boolean; + name?: string; } @Component({ @@ -129,6 +130,10 @@ export class AIModelDialogComponent extends DialogComponent { diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-details-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-details-dialog.component.html new file mode 100644 index 0000000000..413b72e614 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-details-dialog.component.html @@ -0,0 +1,51 @@ + +
+ +

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

+ +
+
+
+ + + +
+ +
+
+
+
+ + @if (!data.readonly) { + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-details-dialog.component.ts new file mode 100644 index 0000000000..fc55602e35 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-details-dialog.component.ts @@ -0,0 +1,60 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { FormBuilder, FormControl } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; + +export interface AlarmRuleDetailsDialogData { + alarmDetails: string; + readonly: boolean; +} + +@Component({ + selector: 'tb-edit-alarm-details-dialog', + templateUrl: './alarm-rule-details-dialog.component.html', + providers: [], + styleUrls: ['./cf-alarm-rules-dialog.component.scss'], +}) +export class AlarmRuleDetailsDialogComponent extends DialogComponent { + + alarmDetailsControl: FormControl; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: AlarmRuleDetailsDialogData, + public dialogRef: MatDialogRef, + private fb: FormBuilder) { + super(store, router, dialogRef); + + this.alarmDetailsControl = this.fb.control(this.data.alarmDetails); + if (this.data.readonly) { + this.alarmDetailsControl.disable(); + } + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + this.dialogRef.close(this.alarmDetailsControl.value); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html new file mode 100644 index 0000000000..3b5379a9d9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html @@ -0,0 +1,173 @@ + +
+ +

{{ 'alarm-rule.alarm-rule' | translate}}

+ +
+ +
+
+
+
+
{{ 'common.general' | translate }}
+
+ + {{ 'alarm-rule.alarm-type' | translate }} + + alarm-rule.alarm-type-hint + @if (fieldFormGroup.get('name').errors && fieldFormGroup.get('name').touched) { + + @if (fieldFormGroup.get('name').hasError('required')) { + {{ 'alarm-rule.alarm-type-required' | translate }} + } @else if (fieldFormGroup.get('name').hasError('pattern')) { + {{ 'alarm-rule.alarm-type-pattern' | translate }} + } @else if (fieldFormGroup.get('name').hasError('maxlength')) { + {{ 'alarm-rule.alarm-type-max-length' | translate }} + } + + } + + +
+ @if (!data.entityId) { + + + } +
+ +
+
{{ 'calculated-fields.arguments' | translate }}
+ +
+
+
{{ 'alarm-rule.create-conditions' | translate }}
+
+ + +
+
+
+
{{ 'alarm-rule.clear-condition' | translate }}
+
+
+ + +
+ +
+
+ alarm-rule.no-clear-alarm-rule +
+
+ +
+
+
+ + + {{ 'alarm-rule.advanced-settings' | translate }} + + +
+
+ + {{ 'alarm-rule.propagate-alarm' | translate }} + +
+ @if (configFormGroup.get('propagate').value) { + + + } +
+
+ + {{ 'alarm-rule.propagate-alarm-to-owner' | translate }} + +
+
+ + {{ 'alarm-rule.propagate-alarm-to-tenant' | translate }} + +
+
+
+
+
+
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.scss new file mode 100644 index 0000000000..fc9590f115 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.scss @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.calculated-field-dialog-container { + width: 869px; + max-width: 100%; + + .tb-disabled { + color: rgba(0, 0, 0, 0.38); + } +} + +.clear-alarm-rule { + border: 1px solid rgba(0, 0, 0, 0.12); + border-left-width: 4px; + border-left-color: green; + border-radius: 4px; + padding: 8px; + min-width: 0; +} + +.button-icon { + color: rgba(0, 0, 0, 0.38); + min-width: 40px; +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts new file mode 100644 index 0000000000..f054616f20 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts @@ -0,0 +1,238 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, Inject, ViewChild, ViewEncapsulation } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormGroup } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { CalculatedField, CalculatedFieldArgument, CalculatedFieldType } from '@shared/models/calculated-field.models'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ScriptLanguage } from '@shared/models/rule-node.models'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { EntityId } from '@shared/models/id/entity-id'; +import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model'; +import { COMMA, ENTER, SEMICOLON } from "@angular/cdk/keycodes"; +import { + AlarmRule, + AlarmRuleConditionType, + alarmRuleEntityTypeList, + AlarmRuleExpressionType, + AlarmRuleTestScriptFn +} from "@shared/models/alarm-rule.models"; +import { deepTrim } from "@core/utils"; +import { combineLatest, Observable } from "rxjs"; +import { debounceTime, startWith } from "rxjs/operators"; +import { RelationTypes } from "@shared/models/relation.models"; +import { StringItemsOption } from "@shared/components/string-items-list.component"; +import { BaseData } from "@shared/models/base-data"; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; +import { EntitySelectComponent } from '@shared/components/entity/entity-select.component'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { AssetInfo } from '@shared/models/asset.models'; +import { DeviceInfo } from '@shared/models/device.models'; + +export interface AlarmRuleDialogData { + value?: CalculatedField; + buttonTitle: string; + entityId: EntityId; + tenantId: string; + entityName?: string; + ownerId: EntityId; + additionalDebugActionConfig: AdditionalDebugActionConfig<(calculatedField: CalculatedField) => void>; + isDirty?: boolean; + getTestScriptDialogFn: AlarmRuleTestScriptFn, +} + +@Component({ + selector: 'tb-alarm-rule-dialog', + templateUrl: './alarm-rule-dialog.component.html', + styleUrls: ['./alarm-rule-dialog.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class AlarmRuleDialogComponent extends DialogComponent { + + @ViewChild('entitySelect') entitySelect!: EntitySelectComponent; + + fieldFormGroup: FormGroup ; + + additionalDebugActionConfig = this.data.value?.id ? { + ...this.data.additionalDebugActionConfig, + action: () => this.data.additionalDebugActionConfig.action({ id: this.data.value.id, ...this.fromGroupValue }), + } : null; + + readonly EntityType = EntityType; + readonly entityTypeTranslations = entityTypeTranslations; + readonly alarmRuleEntityTypeList = alarmRuleEntityTypeList; + readonly CalculatedFieldType = CalculatedFieldType; + readonly ScriptLanguage = ScriptLanguage; + + separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; + + entityName = this.data.entityName; + ownerId = this.data.ownerId; + + disabledClearRuleButton = false; + disabledArguments = false; + isLoading = false; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: AlarmRuleDialogData, + protected dialogRef: MatDialogRef, + private calculatedFieldsService: CalculatedFieldsService, + private destroyRef: DestroyRef, + private cfFormService: CalculatedFieldFormService) { + super(store, router, dialogRef); + this.fieldFormGroup = this.cfFormService.buildAlarmRuleForm(); + this.applyDialogData(); + this.updateRulesValidators(); + + if (this.data.isDirty) { + this.fieldFormGroup.markAsDirty(); + } + + this.fieldFormGroup.get('configuration.arguments').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateRulesValidators(); + }); + + if (!this.data.entityId) { + combineLatest([ + this.fieldFormGroup.get('entityId')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('entityId')!.value)), + this.fieldFormGroup.get('name')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('name')!.value)) + ]).pipe( + debounceTime(50), + takeUntilDestroyed() + ).subscribe(([entityId, name]) => { + this.disabledArguments = !entityId || !name?.length; + const argsControl = this.fieldFormGroup.get('configuration.arguments')!; + if (this.disabledArguments) { + argsControl.disable({ emitEvent: false }); + } else { + argsControl.enable({ emitEvent: false }); + } + }); + } + } + + get configFormGroup(): FormGroup { + return this.fieldFormGroup.get('configuration') as FormGroup; + } + + get arguments(): Record { + return this.fieldFormGroup.get('configuration.arguments').value; + } + + public removeClearAlarmRule() { + this.configFormGroup.patchValue({clearRule: null}); + this.fieldFormGroup.markAsDirty(); + } + + public addClearAlarmRule() { + const clearAlarmRule: AlarmRule = { + condition: { + type: AlarmRuleConditionType.SIMPLE, + expression: { + type: AlarmRuleExpressionType.SIMPLE + } + } + }; + this.configFormGroup.patchValue({clearRule: clearAlarmRule}); + } + + get fromGroupValue(): CalculatedField { + return deepTrim(this.fieldFormGroup.value as CalculatedField); + } + + cancel(): void { + this.dialogRef.close(null); + } + + add(): void { + if (this.fieldFormGroup.valid && Object.keys(this.arguments ?? {}).length > 0) { + this.isLoading = true; + const alarmRule = { entityId: this.data.entityId, ...(this.data.value ?? {}), ...this.fromGroupValue}; + alarmRule.configuration.type = CalculatedFieldType.ALARM; + + this.calculatedFieldsService.saveCalculatedField(alarmRule) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe({ + next: calculatedField => this.dialogRef.close(calculatedField), + error: () => this.isLoading = false + }); + } else { + this.fieldFormGroup.get('name').markAsTouched(); + this.entitySelect.entityAutocompleteMarkAsTouched(); + } + } + + private applyDialogData(): void { + const { configuration = {}, type = CalculatedFieldType.ALARM, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.data.entityId, ...value } = this.data.value ?? {}; + this.fieldFormGroup.patchValue({ configuration, type, debugSettings, entityId, ...value }, {emitEvent: false}); + } + + onTestScript(expression: string): Observable { + return this.cfFormService.testScript( + this.data.value?.id?.id, + this.fromGroupValue, + this.data.getTestScriptDialogFn, + this.destroyRef, + expression + ); + } + + private updateRulesValidators(): void { + if (Object.keys(this.arguments ?? {}).length > 0) { + this.fieldFormGroup.get('configuration.createRules').enable({emitEvent: false}); + this.fieldFormGroup.get('configuration.clearRule').enable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagate').enable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagateToOwner').enable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagateToTenant').enable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagateRelationTypes').enable({emitEvent: false}); + this.disabledClearRuleButton = false; + } else { + this.fieldFormGroup.get('configuration.createRules').disable({emitEvent: false}); + this.fieldFormGroup.get('configuration.clearRule').disable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagate').disable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagateToOwner').disable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagateToTenant').disable({emitEvent: false}); + this.fieldFormGroup.get('configuration.propagateRelationTypes').disable({emitEvent: false}); + this.disabledClearRuleButton = true; + } + } + get predefinedTypeValues(): StringItemsOption[] { + return RelationTypes.map(type => ({ + name: type, + value: type + })); + } + + changeEntity(entity: BaseData): void { + this.entityName = entity.name; + if (this.isAssignedToCustomer(entity as AssetInfo | DeviceInfo)) { + this.ownerId = (entity as AssetInfo | DeviceInfo).customerId; + } + } + + private isAssignedToCustomer(entity: AssetInfo | DeviceInfo): boolean { + return entity && entity.customerId && entity.customerId.id !== NULL_UUID; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.html new file mode 100644 index 0000000000..1382bf014b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.html @@ -0,0 +1,94 @@ + + + + + + + + + + +
+ +
+ + + + +
+
+
+ +
+
+
alarm-rule.alarm-rule-type-list
+ + +
+
+
entity.entity-type
+ + + {{ 'alarm-rule.any-type' | translate }} + + {{ entityTypeTranslations.get(type)?.type | translate }} + + + +
+ @if (alarmRuleFilterConfigForm.get('entityType').value) { +
+
entity.entities
+ + +
+ } +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.scss new file mode 100644 index 0000000000..c34e56296f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.scss @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + display: flex; + max-width: 100%; + .mdc-button { + max-width: 100%; + } +} + +:host ::ng-deep { + .mdc-button { + .mat-icon { + min-width: 24px; + } + .mdc-button__label { + overflow: hidden; + text-overflow: ellipsis; + } + } +} + +::ng-deep { + .tb-alarm-filter-config-component { + max-width: 100%; + width: 600px; + min-width: 100%; + flex: 1; + + + tb-entity-subtype-list { + flex: 1; + @media #{$mat-gt-xs} { + width: 180px; + } + .mdc-evolution-chip-set__chips { + width: 100%; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts new file mode 100644 index 0000000000..daf0408182 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts @@ -0,0 +1,291 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + DestroyRef, + ElementRef, + forwardRef, + Inject, + InjectionToken, + Input, + OnInit, + Optional, + TemplateRef, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { TranslateService } from '@ngx-translate/core'; +import { deepClone, isArraysEqualIgnoreUndefined, isDefinedAndNotNull, isEmpty, isUndefinedOrNull } from '@core/utils'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { fromEvent, Subscription } from 'rxjs'; +import { POSITION_MAP } from '@shared/models/overlay.models'; +import { UtilsService } from '@core/services/utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { alarmRuleEntityTypeList, AlarmRuleFilterConfig } from "@shared/models/alarm-rule.models"; + +export const ALARM_FILTER_CONFIG_DATA = new InjectionToken('AlarmRuleFilterConfigData'); + +export interface AlarmRuleFilterConfigData { + panelMode: boolean; + userMode: boolean; + alarmRuleFilterConfig: AlarmRuleFilterConfig; + initialAlarmRuleFilterConfig?: AlarmRuleFilterConfig; +} + +@Component({ + selector: 'tb-alarm-rule-filter-config', + templateUrl: './alarm-rule-filter-config.component.html', + styleUrls: ['./alarm-rule-filter-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmRuleFilterConfigComponent), + multi: true + } + ] +}) +export class AlarmRuleFilterConfigComponent implements OnInit, ControlValueAccessor { + + @ViewChild('alarmRuleFilterPanel') + alarmRuleFilterPanel: TemplateRef; + + @Input() disabled: boolean; + + @coerceBoolean() + @Input() + buttonMode = true; + + @Input() + initialAlarmRuleFilterConfig: AlarmRuleFilterConfig = { + name: [], + entityType: null, + entities: [] + }; + + panelMode = false; + + buttonDisplayValue = this.translate.instant('alarm-rule.alarm-rule-filter-title'); + + alarmRuleFilterConfigForm: FormGroup; + + alarmFilterOverlayRef: OverlayRef; + + panelResult: AlarmRuleFilterConfig = null; + + entityType = EntityType; + + listEntityTypes = alarmRuleEntityTypeList; + entityTypeTranslations = entityTypeTranslations; + + private alarmRuleFilterConfig: AlarmRuleFilterConfig; + private resizeWindows: Subscription; + + private propagateChange = (_: any) => {}; + + constructor(@Optional() @Inject(ALARM_FILTER_CONFIG_DATA) + private data: AlarmRuleFilterConfigData | undefined, + @Optional() + private overlayRef: OverlayRef, + private fb: FormBuilder, + private translate: TranslateService, + private overlay: Overlay, + private nativeElement: ElementRef, + private viewContainerRef: ViewContainerRef, + private utils: UtilsService, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + if (this.data) { + this.panelMode = this.data.panelMode; + this.alarmRuleFilterConfig = this.data.alarmRuleFilterConfig; + this.initialAlarmRuleFilterConfig = this.data.initialAlarmRuleFilterConfig; + if (this.panelMode && !this.initialAlarmRuleFilterConfig) { + this.initialAlarmRuleFilterConfig = deepClone(this.alarmRuleFilterConfig); + } + } + this.alarmRuleFilterConfigForm = this.fb.group({ + name: [null, []], + entityType: [null, []], + entities: [null, []] + }); + this.alarmRuleFilterConfigForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + if (!this.buttonMode) { + this.alarmRuleConfigUpdated(this.alarmRuleFilterConfigForm.value); + } + } + ); + if (this.panelMode) { + this.updateAlarmRuleConfigForm(this.alarmRuleFilterConfig); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.alarmRuleFilterConfigForm.disable({emitEvent: false}); + } else { + this.alarmRuleFilterConfigForm.enable({emitEvent: false}); + } + } + + writeValue(alarmRuleFilterConfig?: AlarmRuleFilterConfig): void { + this.alarmRuleFilterConfig = alarmRuleFilterConfig; + if (!this.initialAlarmRuleFilterConfig && alarmRuleFilterConfig) { + this.initialAlarmRuleFilterConfig = deepClone(alarmRuleFilterConfig); + } + this.updateButtonDisplayValue(); + this.updateAlarmRuleConfigForm(alarmRuleFilterConfig); + } + + toggleAlarmRuleFilterPanel($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const config = new OverlayConfig({ + panelClass: 'tb-filter-panel', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + maxHeight: '80vh', + height: 'min-content', + minWidth: '' + }); + config.hasBackdrop = true; + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(this.nativeElement) + .withPositions([POSITION_MAP.bottomLeft]); + + this.alarmFilterOverlayRef = this.overlay.create(config); + this.alarmFilterOverlayRef.backdropClick().subscribe(() => { + this.alarmFilterOverlayRef.dispose(); + }); + this.alarmFilterOverlayRef.attach(new TemplatePortal(this.alarmRuleFilterPanel, + this.viewContainerRef)); + this.resizeWindows = fromEvent(window, 'resize').subscribe(() => { + this.alarmFilterOverlayRef.updatePosition(); + }); + } + + cancel() { + this.updateAlarmRuleConfigForm(this.alarmRuleFilterConfig); + this.alarmRuleFilterConfigForm.markAsPristine(); + if (this.overlayRef) { + this.overlayRef.dispose(); + } else { + this.resizeWindows.unsubscribe(); + this.alarmFilterOverlayRef.dispose(); + } + } + + update() { + this.alarmRuleConfigUpdated(this.alarmRuleFilterConfigForm.value); + this.alarmRuleFilterConfigForm.markAsPristine(); + if (this.panelMode) { + this.panelResult = this.alarmRuleFilterConfig; + } + if (this.overlayRef) { + this.overlayRef.dispose(); + } else { + this.resizeWindows.unsubscribe(); + this.alarmFilterOverlayRef.dispose(); + } + } + + reset() { + const alarmRuleFilterConfig = this.alarmRuleFilterConfigFromFormValue(this.alarmRuleFilterConfigForm.value); + if (!this.alarmRuleFilterConfigEquals(alarmRuleFilterConfig, this.initialAlarmRuleFilterConfig)) { + this.updateAlarmRuleConfigForm(this.initialAlarmRuleFilterConfig); + this.alarmRuleFilterConfigForm.markAsDirty(); + } + } + + private alarmRuleFilterConfigEquals = (filter1?: AlarmRuleFilterConfig, filter2?: AlarmRuleFilterConfig): 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.name, filter2.name)) { + return false; + } + if (!isArraysEqualIgnoreUndefined(filter1.entities, filter2.entities)) { + return false; + } + return filter1.entityType === filter2.entityType; + } + return false; + }; + + private updateAlarmRuleConfigForm(alarmRuleFilterConfig?: AlarmRuleFilterConfig) { + this.alarmRuleFilterConfigForm.patchValue({ + name: alarmRuleFilterConfig?.name ?? [], + entityType: alarmRuleFilterConfig?.entityType ?? null, + entities: alarmRuleFilterConfig?.entities ?? [], + }, {emitEvent: false}); + } + + private alarmRuleConfigUpdated(formValue: any) { + this.alarmRuleFilterConfig = this.alarmRuleFilterConfigFromFormValue(formValue); + this.updateButtonDisplayValue(); + this.propagateChange(this.alarmRuleFilterConfig); + } + + private alarmRuleFilterConfigFromFormValue(formValue: any): AlarmRuleFilterConfig { + return { + name: formValue?.name ?? [], + entityType: formValue?.entityType ?? null, + entities: formValue?.entities ?? [], + }; + } + + private updateButtonDisplayValue() { + if (this.buttonMode) { + const filterTextParts: string[] = []; + if (this.alarmRuleFilterConfig?.name?.length) { + filterTextParts.push(this.alarmRuleFilterConfig.name.map((type) => this.customTranslate(type)).join(', ')); + } + if (this.alarmRuleFilterConfig?.entityType) { + filterTextParts.push(this.translate.instant( entityTypeTranslations.get(this.alarmRuleFilterConfig.entityType).type)); + } + if (!filterTextParts.length) { + this.buttonDisplayValue = this.translate.instant('alarm-rule.alarm-rule-filter-title'); + } else { + this.buttonDisplayValue = this.translate.instant('alarm-rule.filter-title') + `: ${filterTextParts.join(', ')}`; + } + } + } + + private customTranslate(entity: string) { + return this.utils.customTranslation(entity, entity); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.html new file mode 100644 index 0000000000..3c357b842d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.html @@ -0,0 +1,20 @@ + + + diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.scss new file mode 100644 index 0000000000..c6571e8450 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.scss @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + padding-right: 8px; + overflow: hidden; + max-width: 100%; +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.ts new file mode 100644 index 0000000000..95781decdb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-table-header.component.ts @@ -0,0 +1,43 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { EntityTableHeaderComponent } from '../../components/entity/entity-table-header.component'; +import { CalculatedFieldAlarmRule, CalculatedFieldsQuery } from "@shared/models/calculated-field.models"; +import { AlarmRulesTableConfig } from "@home/components/alarm-rules/alarm-rules-table-config"; + +@Component({ + selector: 'tb-alarm-rule-table-header', + templateUrl: './alarm-rule-table-header.component.html', + styleUrls: ['./alarm-rule-table-header.component.scss'] +}) +export class AlarmRuleTableHeaderComponent extends EntityTableHeaderComponent { + + get alarmRuleTableConfig(): AlarmRulesTableConfig { + return this.entitiesTableConfig as AlarmRulesTableConfig; + } + + constructor(protected store: Store) { + super(store); + } + + alarmRuleFilterChanged(alarmRuleFilterConfig: CalculatedFieldsQuery) { + this.alarmRuleTableConfig.alarmRuleFilterConfig = alarmRuleFilterConfig; + this.alarmRuleTableConfig.getTable().resetSortAndFilter(true, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts new file mode 100644 index 0000000000..858b23f3dd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts @@ -0,0 +1,91 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { AlarmRuleDialogComponent } from "@home/components/alarm-rules/alarm-rule-dialog.component"; +import { CreateCfAlarmRulesComponent } from "@home/components/alarm-rules/create-cf-alarm-rules.component"; +import { CfAlarmRuleComponent } from "@home/components/alarm-rules/cf-alarm-rule.component"; +import { CfAlarmRuleConditionComponent } from "@home/components/alarm-rules/cf-alarm-rule-condition.component"; +import { + CfAlarmRuleConditionDialogComponent +} from "@home/components/alarm-rules/cf-alarm-rule-condition-dialog.component"; +import { CfAlarmScheduleComponent } from "@home/components/alarm-rules/cf-alarm-schedule.component"; +import { CfAlarmScheduleDialogComponent } from "@home/components/alarm-rules/cf-alarm-schedule-dialog.component"; +import { + EntityDebugSettingsButtonComponent +} from "@home/components/entity/debug/entity-debug-settings-button.component"; +import { AlarmRuleFilterTextComponent } from "@home/components/alarm-rules/filter/alarm-rule-filter-text.component"; +import { + CalculatedFieldArgumentsTableModule +} from "@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module"; +import { + AlarmRuleFilterPredicateListComponent +} from "@home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component"; +import { + AlarmRuleFilterPredicateComponent +} from "@home/components/alarm-rules/filter/alarm-rule-filter-predicate.component"; +import { + AlarmRuleFilterPredicateValueComponent +} from "@home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component"; +import { + AlarmRuleComplexFilterPredicateDialogComponent +} from "@home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component"; +import { AlarmRuleFilterListComponent } from "@home/components/alarm-rules/filter/alarm-rule-filter-list.component"; +import { AlarmRuleFilterDialogComponent } from "@home/components/alarm-rules/filter/alarm-rule-filter-dialog.component"; +import { AlarmRuleDetailsDialogComponent } from "@home/components/alarm-rules/alarm-rule-details-dialog.component"; +import { AlarmRuleFilterConfigComponent } from "@home/components/alarm-rules/alarm-rule-filter-config.component"; +import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alarm-rule-table-header.component"; +import { + AlarmRuleFilterPredicateNoDataValueComponent +} from "@home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component"; +import { AlarmRulesComponent } from '@home/components/alarm-rules/alarm-rules.component'; + +@NgModule({ + declarations: [ + AlarmRuleDialogComponent, + CreateCfAlarmRulesComponent, + CfAlarmRuleComponent, + CfAlarmRuleConditionComponent, + CfAlarmRuleConditionDialogComponent, + CfAlarmScheduleComponent, + CfAlarmScheduleDialogComponent, + AlarmRuleFilterTextComponent, + AlarmRuleFilterListComponent, + AlarmRuleFilterDialogComponent, + AlarmRuleFilterPredicateListComponent, + AlarmRuleFilterPredicateComponent, + AlarmRuleFilterPredicateValueComponent, + AlarmRuleComplexFilterPredicateDialogComponent, + AlarmRuleDetailsDialogComponent, + AlarmRuleFilterConfigComponent, + AlarmRuleTableHeaderComponent, + AlarmRuleFilterPredicateNoDataValueComponent, + AlarmRulesComponent + ], + imports: [ + CommonModule, + SharedModule, + EntityDebugSettingsButtonComponent, + CalculatedFieldArgumentsTableModule + ], + exports: [ + AlarmRuleDialogComponent, + AlarmRulesComponent + ] +}) +export class AlarmRuleModule { } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts new file mode 100644 index 0000000000..cadac572c6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts @@ -0,0 +1,437 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + checkBoxCell, + DateEntityTableColumn, + EntityLinkTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { Direction } from '@shared/models/page/sort-order'; +import { MatDialog } from '@angular/material/dialog'; +import { PageLink } from '@shared/models/page/page-link'; +import { EMPTY, Observable, of } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { EntityId } from '@shared/models/id/entity-id'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { DestroyRef, Renderer2 } from '@angular/core'; +import { EntityDebugSettings } from '@shared/models/entity.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { catchError, filter, first, switchMap, tap } from 'rxjs/operators'; +import { + ArgumentEntityType, + ArgumentType, + CalculatedField, + CalculatedFieldAlarmRule, + CalculatedFieldEventArguments, + CalculatedFieldInfo, + CalculatedFieldsQuery, + CalculatedFieldType, + getCalculatedFieldArgumentsEditorCompleter, + getCalculatedFieldArgumentsHighlights, +} from '@shared/models/calculated-field.models'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; +import { DatePipe } from '@angular/common'; +import { + AlarmRuleDialogComponent, + AlarmRuleDialogData +} from "@home/components/alarm-rules/alarm-rule-dialog.component"; +import { AlarmSeverity, alarmSeverityTranslations } from "@shared/models/alarm.models"; +import { UtilsService } from "@core/services/utils.service"; +import { deepClone, getEntityDetailsPageURL, isObject } from "@core/utils"; +import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alarm-rule-table-header.component"; +import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-dialog.component'; +import { DebugEventType, EventType } from '@shared/models/event.models'; +import { ActionNotificationShow } from "@core/notification/notification.actions"; +import { + CalculatedFieldScriptTestDialogComponent, + CalculatedFieldTestScriptDialogData +} from "@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component"; +import { AlarmRulesTabsComponent } from '@home/pages/alarm/alarm-rules-tabs.component'; +import { Router } from '@angular/router'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { AlarmRulesComponent } from '@home/components/alarm-rules/alarm-rules.component'; + +type AlarmRuleTableEntity = CalculatedField | CalculatedFieldInfo; + +export class AlarmRulesTableConfig extends EntityTableConfig { + + readonly tenantId = getCurrentAuthUser(this.store).tenantId; + additionalDebugActionConfig = { + title: this.translate.instant('action.see-debug-events'), + action: (calculatedField: AlarmRuleTableEntity) => this.openDebugEventsDialog.call(this, null, calculatedField), + }; + + alarmRuleFilterConfig: CalculatedFieldsQuery; + + constructor(private calculatedFieldsService: CalculatedFieldsService, + private translate: TranslateService, + private dialog: MatDialog, + private datePipe: DatePipe, + private entityId: EntityId = null, + private store: Store, + private destroyRef: DestroyRef, + private renderer: Renderer2, + private entityName: string, + private ownerId: EntityId = null, + private importExportService: ImportExportService, + private entityDebugSettingsService: EntityDebugSettingsService, + private utilsService: UtilsService, + private router: Router, + public pageMode: boolean = false, + ) { + super(); + if (this.pageMode) { + this.headerComponent = AlarmRuleTableHeaderComponent; + this.entityComponent = AlarmRulesComponent; + this.entityTabsComponent = AlarmRulesTabsComponent; + this.rowPointer = true; + } + this.tableTitle = this.pageMode ? '' : this.translate.instant('alarm-rule.alarm-rules'); + this.detailsPanelEnabled = this.pageMode; + this.entityResources = { + helpLinkId: 'alarmRules' + }; + this.entityType = EntityType.CALCULATED_FIELD; + this.entityTranslations = { + type: 'alarm-rule.alarm-rule', + typePlural: 'alarm-rule.alarm-rules', + list: 'alarm-rule.list', + add: 'action.add', + details: 'alarm-rule.details', + noEntities: 'alarm-rule.no-found', + search: 'action.search', + selectedEntities: 'alarm-rule.selected-fields' + }; + + this.entitiesFetchFunction = (pageLink: PageLink) => this.fetchCalculatedFields(pageLink); + this.addEntity = this.getCalculatedAlarmDialog.bind(this); + this.loadEntity = id => this.calculatedFieldsService.getCalculatedFieldById(id.id); + this.saveEntity = (alarmRule) => this.calculatedFieldsService.saveCalculatedField(alarmRule); + + this.deleteEntityTitle = (field) => this.translate.instant('alarm-rule.delete-title', {title: field.name}); + this.deleteEntityContent = () => this.translate.instant('alarm-rule.delete-text'); + this.deleteEntitiesTitle = count => this.translate.instant('alarm-rule.delete-multiple-title', {count}); + this.deleteEntitiesContent = () => this.translate.instant('alarm-rule.delete-multiple-text'); + this.deleteEntity = id => this.calculatedFieldsService.deleteCalculatedField(id.id); + + this.onEntityAction = action => this.onCFAction(action); + + this.addActionDescriptors = [ + { + name: this.translate.instant('alarm-rule.create'), + icon: 'insert_drive_file', + isEnabled: () => true, + onAction: ($event) => this.getTable().addEntity($event) + }, + { + name: this.translate.instant('alarm-rule.import'), + icon: 'file_upload', + isEnabled: () => true, + onAction: () => this.importCalculatedField() + } + ]; + + this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; + this.columns.push(new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px')); + this.columns.push(new EntityTableColumn('name', 'alarm-rule.alarm-type', '33%', + entity => this.utilsService.customTranslation(entity.name, entity.name))); + if (this.pageMode) { + this.columns.push(new EntityTableColumn('entityType', 'entity.entity-type', '10%', + entity => this.translate.instant(entityTypeTranslations.get(entity.entityId.entityType).type))); + this.columns.push(new EntityLinkTableColumn('entityName', 'entity.entity', '33%', + entity => this.utilsService.customTranslation(entity.entityName, entity.entityName), + entity => getEntityDetailsPageURL(entity.entityId?.id, entity.entityId?.entityType as EntityType), false)); + } + this.columns.push(new EntityTableColumn('createRule', 'alarm-rule.severities', this.pageMode ? '23%' : '67%', + entity => Object.keys(entity.configuration.createRules).map((severity) => this.translate.instant(alarmSeverityTranslations.get(severity as AlarmSeverity))).join(', '), + () => ({}), false)); + this.columns.push(new EntityTableColumn('clearRule', 'alarm-rule.cleared', '90px', + entity => checkBoxCell(!!entity.configuration.clearRule), ()=> { return {padding: 0, textAlign: 'center'}}, false)); + + this.cellActionDescriptors.push( + { + name: this.translate.instant('alarm-rule.copy'), + icon: 'content_copy', + isEnabled: () => true, + onAction: ($event, entity) => this.copyCalculatedField($event, entity) + } + ); + this.cellActionDescriptors.push( + { + name: this.translate.instant('action.export'), + icon: 'file_download', + isEnabled: () => true, + onAction: ($event, entity) => this.exportAlarmRule($event, entity), + }, + { + name: this.translate.instant('entity-view.events'), + icon: 'mdi:clipboard-text-clock', + isEnabled: () => true, + onAction: ($event, entity) => + this.pageMode ? this.openDebugTab($event, entity) : this.openDebugEventsDialog($event, entity), + }, + { + name: '', + nameFunction: entity => this.entityDebugSettingsService.getDebugConfigLabel(entity?.debugSettings), + icon: 'mdi:bug', + isEnabled: () => true, + iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline', + onAction: ($event, entity) => this.onOpenDebugConfig($event, entity), + } + ); + if (!this.pageMode) { + this.cellActionDescriptors.push( + { + name: this.translate.instant('action.edit'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.editCalculatedField($event, entity), + } + ) + } + } + + fetchCalculatedFields(pageLink: PageLink): Observable> { + return this.pageMode ? + this.calculatedFieldsService.getCalculatedFields(pageLink, {types: [CalculatedFieldType.ALARM], ...this.alarmRuleFilterConfig}) : + this.calculatedFieldsService.getCalculatedFieldsByEntityId(this.entityId, pageLink, CalculatedFieldType.ALARM); + } + + onOpenDebugConfig($event: Event, calculatedField: AlarmRuleTableEntity): void { + $event?.stopPropagation(); + const { debugSettings = {}, id } = calculatedField; + const additionalActionConfig = { + ...this.additionalDebugActionConfig, + action: () => this.openDebugEventsDialog($event, calculatedField) + }; + + const { viewContainerRef, renderer } = this.entityDebugSettingsService; + if (!viewContainerRef || !renderer) { + this.entityDebugSettingsService.viewContainerRef = this.getTable().viewContainerRef; + this.entityDebugSettingsService.renderer = this.renderer; + } + + this.entityDebugSettingsService.openDebugStrategyPanel({ + debugSettings, + debugConfig: { + entityType: EntityType.CALCULATED_FIELD, + entityLabel: 'alarm-rule.alarm-rule', + additionalActionConfig, + }, + onSettingsAppliedFn: settings => this.onDebugConfigChanged(id.id, settings) + }, $event.target as Element); + } + + private editCalculatedField($event: Event, calculatedField: AlarmRuleTableEntity, isDirty = false): void { + $event?.stopPropagation(); + this.getCalculatedAlarmDialog(calculatedField, 'action.apply', isDirty) + .subscribe((res) => { + if (res) { + this.updateData(); + } + }); + } + + private copyCalculatedField($event: Event, calculatedField: AlarmRuleTableEntity, isDirty = false): void { + $event?.stopPropagation(); + const copyCalculatedAlarmRule = deepClone(calculatedField); + if (this.pageMode) { + copyCalculatedAlarmRule.entityId = null; + delete (copyCalculatedAlarmRule as CalculatedFieldInfo).entityName; + } + delete copyCalculatedAlarmRule.id; + this.getCalculatedAlarmDialog(copyCalculatedAlarmRule, 'action.apply', isDirty) + .subscribe((res) => { + if (res) { + this.updateData(); + } + }); + } + + private getCalculatedAlarmDialog(value?: AlarmRuleTableEntity, buttonTitle = 'action.add', isDirty = false): Observable { + const entityId = this.entityId || value?.entityId; + const entityName = this.entityName || (value as CalculatedFieldInfo)?.entityName; + return this.dialog.open(AlarmRuleDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + value, + buttonTitle, + entityId, + entityName, + tenantId: this.tenantId, + ownerId: this.ownerId ?? {entityType: EntityType.TENANT, id: this.tenantId}, + additionalDebugActionConfig: this.additionalDebugActionConfig, + isDirty, + getTestScriptDialogFn: this.getTestScriptDialog.bind(this), + }, + enterAnimationDuration: isDirty ? 0 : null, + }) + .afterClosed() + .pipe(filter(Boolean)); + } + + private openDebugEventsDialog($event: Event, calculatedField: AlarmRuleTableEntity): void { + $event?.stopPropagation(); + this.dialog.open(EventsDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + title: 'alarm-rule.debugging', + tenantId: this.tenantId, + entityId: calculatedField.id, + debugEventTypes:[DebugEventType.DEBUG_CALCULATED_FIELD], + disabledEventTypes:[EventType.LC_EVENT, EventType.ERROR, EventType.STATS], + defaultEventType: DebugEventType.DEBUG_CALCULATED_FIELD, + debugActionDisabled: true + } + }) + .afterClosed() + .subscribe(); + } + + private openDebugTab($event: Event, calculatedField: AlarmRuleTableEntity) { + const table = this.getTable(); + if (!table.isDetailsOpen) { + table.toggleEntityDetails($event, calculatedField); + if (table.entityDetailsPanel.matTabGroup._tabs.length > 1) { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + } else { + table.entityDetailsPanel.matTabGroup._tabs.changes.pipe( + first() + ).subscribe(() => { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + }) + } + } + } + + private exportAlarmRule($event: Event, calculatedField: AlarmRuleTableEntity): void { + $event?.stopPropagation(); + this.importExportService.exportCalculatedField(calculatedField.id.id); + } + + private importCalculatedField(): void { + this.importExportService.openCalculatedFieldImportDialog('alarm-rule.import', 'alarm-rule.file') + .pipe( + filter(Boolean), + switchMap(calculatedField => { + if (calculatedField.type !== CalculatedFieldType.ALARM) { + this.store.dispatch(new ActionNotificationShow({ + message: this.translate.instant('alarm-rule.import-invalid-alarm-rule-type'), + type: 'error', + verticalPosition: 'top', + horizontalPosition: 'left', + duration: 5000 + })); + return EMPTY; + } + return of(calculatedField); + }), + switchMap(calculatedField => this.getCalculatedAlarmDialog(this.updateImportedCalculatedField(calculatedField), 'action.add', true)), + filter(Boolean), + switchMap(calculatedField => this.calculatedFieldsService.saveCalculatedField(calculatedField)), + filter(Boolean), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => this.updateData()); + } + + private updateImportedCalculatedField(calculatedField: CalculatedField): CalculatedField { + if (calculatedField.type === CalculatedFieldType.ALARM) { + calculatedField.configuration.arguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { + const arg = calculatedField.configuration.arguments[key]; + acc[key] = arg.refEntityId?.entityType === ArgumentEntityType.Tenant + ? { ...arg, refEntityId: { id: this.tenantId, entityType: ArgumentEntityType.Tenant } } + : arg; + return acc; + }, {}); + } + + return calculatedField; + } + + private onDebugConfigChanged(id: string, debugSettings: EntityDebugSettings): void { + this.calculatedFieldsService.getCalculatedFieldById(id).pipe( + switchMap(field => this.calculatedFieldsService.saveCalculatedField({ ...field, debugSettings })), + catchError(() => of(null)), + takeUntilDestroyed(this.destroyRef), + ).subscribe(() => this.updateData()); + } + + private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable { + if (calculatedField.type === CalculatedFieldType.ALARM) { + const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { + const type = calculatedField.configuration.arguments[key].refEntityKey.type; + acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key) + ? {...argumentsObj[key], type} + : type === ArgumentType.Rolling ? {values: [], type} : {value: '', type, ts: new Date().getTime()}; + return acc; + }, {}); + return this.dialog.open(CalculatedFieldScriptTestDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'], + data: { + arguments: resultArguments, + expression, + argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(calculatedField.configuration.arguments), + argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(calculatedField.configuration.arguments), + openCalculatedFieldEdit + } + }).afterClosed() + .pipe( + filter(Boolean), + tap(expression => { + if (openCalculatedFieldEdit) { + this.editCalculatedField(null, { + entityId: this.entityId, ...calculatedField, + configuration: {...calculatedField.configuration, expression} as any + }, true) + } + }), + ); + } else { + return of(null); + } + } + + private openCalculatedField($event: Event, entity: AlarmRuleTableEntity) { + $event?.stopPropagation(); + const url = this.router.createUrlTree(['alarms', 'alarm-rules', entity.id.id]); + this.router.navigateByUrl(url); + } + + private onCFAction(action: EntityAction): boolean { + switch (action.action) { + case 'open': + this.openCalculatedField(action.event, action.entity); + return true; + case 'export': + this.exportAlarmRule(action.event, action.entity); + return true; + } + return false; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.html new file mode 100644 index 0000000000..01a3475345 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.html @@ -0,0 +1,20 @@ + +@if (alarmRulesTableConfig) { + +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.scss new file mode 100644 index 0000000000..175df5b779 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.scss @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .white-background { + --mat-sidenav-content-background-color: white; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts new file mode 100644 index 0000000000..8ae5af0b57 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts @@ -0,0 +1,99 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + DestroyRef, + effect, + input, + Renderer2, + ViewChild, +} from '@angular/core'; +import { EntityId } from '@shared/models/id/entity-id'; +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; +import { DatePipe } from '@angular/common'; +import { AlarmRulesTableConfig } from "@home/components/alarm-rules/alarm-rules-table-config"; +import { UtilsService } from "@core/services/utils.service"; +import { ActivatedRoute, Router } from "@angular/router"; + +@Component({ + selector: 'tb-alarm-rules-table', + templateUrl: './alarm-rules-table.component.html', + styleUrls: ['./alarm-rules-table.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [EntityDebugSettingsService] +}) +export class AlarmRulesTableComponent { + + @ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; + + active = input(); + entityId = input(); + entityName = input(); + ownerId = input(); + + alarmRulesTableConfig: AlarmRulesTableConfig; + + pageMode: boolean = false; + + constructor(private calculatedFieldsService: CalculatedFieldsService, + private translate: TranslateService, + private dialog: MatDialog, + private store: Store, + private datePipe: DatePipe, + private cd: ChangeDetectorRef, + private renderer: Renderer2, + private importExportService: ImportExportService, + private entityDebugSettingsService: EntityDebugSettingsService, + private utilsService: UtilsService, + private destroyRef: DestroyRef, + private route: ActivatedRoute, + private router: Router + ) { + this.pageMode = !!this.route.snapshot.data.isPage; + effect(() => { + if (this.active() || this.pageMode) { + this.alarmRulesTableConfig = new AlarmRulesTableConfig( + this.calculatedFieldsService, + this.translate, + this.dialog, + this.datePipe, + this.entityId(), + this.store, + this.destroyRef, + this.renderer, + this.entityName(), + this.ownerId(), + this.importExportService, + this.entityDebugSettingsService, + this.utilsService, + this.router, + this.pageMode, + ); + this.cd.markForCheck(); + } + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html new file mode 100644 index 0000000000..7b1f13756c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html @@ -0,0 +1,163 @@ + +
+ + + +
+
+
+
+
{{ 'common.general' | translate }}
+
+ + {{ 'entity-field.title' | translate }} + + @if (entityForm.get('name').errors && entityForm.get('name').touched) { + + @if (entityForm.get('name').hasError('required')) { + {{ 'common.hint.title-required' | translate }} + } @else if (entityForm.get('name').hasError('pattern')) { + {{ 'common.hint.title-pattern' | translate }} + } @else if (entityForm.get('name').hasError('maxlength')) { + {{ 'common.hint.title-max-length' | translate }} + } + + } + + + +
+ + +
+ +
+
{{ 'calculated-fields.arguments' | translate }}
+ +
+
+
{{ 'alarm-rule.create-conditions' | translate }}
+
+ + +
+
+
+
{{ 'alarm-rule.clear-condition' | translate }}
+
+
+ + +
+ @if (isEditValue) { + + } +
+
+ alarm-rule.no-clear-alarm-rule +
+
+ +
+
+
+ + + {{ 'alarm-rule.advanced-settings' | translate }} + + +
+
+ + {{ 'alarm-rule.propagate-alarm' | translate }} + +
+ @if (configFormGroup.get('propagate').value) { + + + } +
+
+ + {{ 'alarm-rule.propagate-alarm-to-owner' | translate }} + +
+
+ + {{ 'alarm-rule.propagate-alarm-to-tenant' | translate }} + +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts new file mode 100644 index 0000000000..dbb5693d52 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts @@ -0,0 +1,200 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, inject, Inject, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityComponent } from '@home/components/entity/entity.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { EntityType } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { + CalculatedFieldArgument, + CalculatedFieldConfiguration, + CalculatedFieldInfo, + CalculatedFieldType +} from '@shared/models/calculated-field.models'; +import { EntityId } from '@shared/models/id/entity-id'; +import { BaseData } from '@shared/models/base-data'; +import { Observable } from 'rxjs'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { TenantId } from '@shared/models/id/tenant-id'; +import { StringItemsOption } from '@shared/components/string-items-list.component'; +import { RelationTypes } from '@shared/models/relation.models'; +import { + AlarmRule, + AlarmRuleConditionType, + alarmRuleEntityTypeList, + AlarmRuleExpressionType +} from '@shared/models/alarm-rule.models'; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; +import { AssetInfo } from '@shared/models/asset.models'; +import { DeviceInfo } from '@shared/models/device.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { EntityService } from '@core/http/entity.service'; + +@Component({ + selector: 'tb-alarm-rules', + templateUrl: './alarm-rules.component.html', + styleUrls: ['./alarm-rule-dialog.component.scss'] +}) +export class AlarmRulesComponent extends EntityComponent { + + @Input() + standalone = false; + + @Input() + entityName: string; + + ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId); + readonly tenantId = getCurrentAuthUser(this.store).tenantId; + readonly EntityType = EntityType; + readonly alarmRuleEntityTypeList = alarmRuleEntityTypeList; + readonly CalculatedFieldType = CalculatedFieldType; + + private cfFormService = inject(CalculatedFieldFormService); + private destroyRef = inject(DestroyRef); + + constructor(protected store: Store, + protected translate: TranslateService, + @Inject('entity') protected entityValue: CalculatedFieldInfo, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, + protected fb: FormBuilder, + protected cd: ChangeDetectorRef, + private entityService: EntityService) { + super(store, fb, entityValue, entitiesTableConfigValue, cd); + } + + hideDelete() { + if (this.entitiesTableConfig) { + return !this.entitiesTableConfig.deleteEnabled(this.entity); + } else { + return false; + } + } + + additionalDebugActionConfig = { + ...this.entitiesTableConfig.additionalDebugActionConfig, + action: () => this.entitiesTableConfig.additionalDebugActionConfig.action( + { id: this.entity.id, ...this.entityFormValue() }, false, + (expression) => { + if (expression) { + this.entityForm.get('configuration').setValue({...this.entityFormValue().configuration, expression}); + this.entityForm.get('configuration').markAsDirty(); + } + }), + }; + + get entityId(): EntityId { + return this.entityForm.get('entityId').value; + } + + get entitiesTableConfig(): CalculatedFieldsTableConfig { + return this.entitiesTableConfigValue; + } + + changeEntity(entity: BaseData): void { + this.entityName = entity?.name; + } + + buildForm(_entity?: CalculatedFieldInfo): FormGroup { + return inject(CalculatedFieldFormService).buildAlarmRuleForm(); + } + + updateForm(entity: CalculatedFieldInfo) { + const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.ALARM, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.entityId, ...value } = entity ?? {}; + this.entityForm.patchValue({ configuration, debugSettings, entityId, ...value }, {emitEvent: false}); + if (!entityId) { + this.entityForm.get('configuration').disable({emitEvent: false}); + } + } + + onTestScript(expression?: string): Observable { + return this.cfFormService.testScript( + this.entity?.id?.id, + this.entityFormValue(), + this.entitiesTableConfig.getTestScriptDialog.bind(this.entitiesTableConfig), + this.destroyRef, + expression + ); + } + + updateFormState() { + if (this.entityForm) { + if (this.isEditValue) { + this.entityForm.enable({emitEvent: false}); + this.entityForm.get('entityId').disable({emitEvent: false}); + this.getOwnerId(this.entityId); + } else { + this.entityForm.disable({emitEvent: false}); + } + } + } + + get arguments(): Record { + return this.entityForm.get('configuration.arguments').value; + } + + get predefinedTypeValues(): StringItemsOption[] { + return RelationTypes.map(type => ({ + name: type, + value: type + })); + } + + get configFormGroup(): FormGroup { + return this.entityForm.get('configuration') as FormGroup; + } + + public removeClearAlarmRule() { + this.configFormGroup.patchValue({clearRule: null}); + this.entityForm.markAsDirty(); + } + + public addClearAlarmRule() { + const clearAlarmRule: AlarmRule = { + condition: { + type: AlarmRuleConditionType.SIMPLE, + expression: { + type: AlarmRuleExpressionType.SIMPLE + } + }, + alarmDetails: null + }; + this.configFormGroup.patchValue({clearRule: clearAlarmRule}); + } + + getOwnerId(entityId: EntityId) { + if (entityId?.entityType === EntityType.DEVICE || entityId?.entityType === EntityType.ASSET) { + this.entityService.getEntity(entityId.entityType, entityId.id, { ignoreLoading: true, ignoreErrors: true }).subscribe( + (entity: AssetInfo | DeviceInfo) => { + if (this.isAssignedToCustomer(entity)) { + this.ownerId = entity.customerId; + } + } + ); + } + } + + private isAssignedToCustomer(entity: AssetInfo | DeviceInfo): boolean { + return entity && entity.customerId && entity.customerId.id !== NULL_UUID; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html new file mode 100644 index 0000000000..b9f91a9203 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html @@ -0,0 +1,251 @@ + +
+ +

{{ (readonly ? 'alarm-rule.alarm-rule-condition' : 'alarm-rule.edit-alarm-rule-condition') | translate }}

+ + +
+
+
+
+
+
{{ 'alarm-rule.mode' | translate }}
+ + {{ 'alarm-rule.expression-type.simple' | translate }} + {{ 'alarm-rule.expression-type.script' | translate }} + +
+
+
+ @if (conditionFormGroup.get('expression.type').value === AlarmRuleExpressionType.SIMPLE) { +
+
+
{{ 'alarm-rule.argument-filters' | translate }}
+ + {{ complexOperationTranslationMap.get(ComplexOperation.AND) | translate }} + {{ complexOperationTranslationMap.get(ComplexOperation.OR) | translate }} + +
+ + +
+ } @else { +
+
{{ 'alarm-rule.script' | translate }}
+ +
{{ 'alarm-rule.tbel' | translate }} +
+ +
+
+ +
+
+ } +
+ + @if (conditionFormGroup.get('expression.type').value === AlarmRuleExpressionType.SIMPLE) { +
+ + + {{ 'alarm-rule.filter-preview' | translate }} + + +
+ @if (specText) { + {{ specText }} + } + @if (conditionFormGroup.get('expression.filters').value?.length) { + + + } @else { + {{ 'alarm-rule.no-filter-preview' | translate }} + } +
+
+
+
+ } +
+
+ +
+
+
{{ 'alarm-rule.type' | translate }}
+ + + {{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }} + + +
+ @if (isNoData) { +
+ {{ 'alarm-rule.condition-type-hint' | translate }} +
+ } + @if (conditionFormGroup.get('type').value === AlarmConditionType.DURATION) { +
+
+
{{ 'alarm-rule.value' | translate }}
+ + {{ 'alarm-rule.static' | translate }} + {{ 'alarm-rule.dynamic' | translate }} + +
+
+
+ +
+
+ +
+
+ + + + {{ timeUnitTranslations.get(timeUnit) | translate }} + + + + {{ 'alarm-rule.condition-duration-time-unit-required' | translate }} + + +
+
+
+ } @else if (conditionFormGroup.get('type').value === AlarmConditionType.REPEATING) { +
+
+
{{ 'alarm-rule.value' | translate }}
+ + {{ 'alarm-rule.static' | translate }} + {{ 'alarm-rule.dynamic' | translate }} + +
+
+
+ +
+
+ +
+
+
+ } +
+
+
+
+ + @if (!readonly) { + + } +
+ + +
+ + + {{ defaultValuePlaceholder | translate }} + @if (conditionFormGroup.get(groupName).get('staticValue').hasError('required')) { + {{ defaultValueRequiredError | translate }} + } @else if (conditionFormGroup.get(groupName).get('staticValue').hasError('min')) { + {{ defaultValueRangeError | translate }} + } @else if (conditionFormGroup.get(groupName).get('staticValue').hasError('max')) { + {{ defaultValueRangeError | translate }} + } @else if (conditionFormGroup.get(groupName).get('staticValue').hasError('pattern')) { + {{ defaultValuePatternError | translate }} + } + @if (type === AlarmConditionType.REPEATING) { + alarm-rule.condition-repeating-value-hint + } + +
+
+ + + + alarm-rule.value-argument + + @for (argument of argumentsList; track argument) { + {{ argument }} + } + + @if (conditionFormGroup.get(groupName).get('dynamicValueArgument').hasError('required')) { + + {{ 'calculated-fields.hint.argument-name-required' | translate }} + + } + @if (type === AlarmConditionType.REPEATING) { + alarm-rule.condition-repeating-value-hint + } + + + +
+ diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.ts new file mode 100644 index 0000000000..f39ed364fd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.ts @@ -0,0 +1,340 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { FormBuilder, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ScriptLanguage } from "@shared/models/rule-node.models"; +import { + CalculatedFieldArgument, + getCalculatedFieldArgumentsEditorCompleter, + getCalculatedFieldArgumentsHighlights +} from "@shared/models/calculated-field.models"; +import { TbEditorCompleter } from "@shared/models/ace/completion.models"; +import { AceHighlightRules } from "@shared/models/ace/ace.models"; +import { ComplexOperation } from "@shared/models/query/query.models"; +import { Observable } from "rxjs"; +import { TranslateService } from "@ngx-translate/core"; +import { + AlarmRuleCondition, + AlarmRuleConditionType, + AlarmRuleConditionTypeTranslationMap, + alarmRuleDefaultScript, + AlarmRuleExpressionType, + AlarmRuleFilter, + areFiltersAndPredicateArgumentsValid, + filterOperationTranslationMap +} from "@shared/models/alarm-rule.models"; + +export interface CfAlarmRuleConditionDialogData { + readonly: boolean; + condition: AlarmRuleCondition; + arguments?: Record; + testScript: (expression: string) => Observable; +} + +@Component({ + selector: 'tb-cf-alarm-rule-condition-dialog', + templateUrl: './cf-alarm-rule-condition-dialog.component.html', + providers: [], + styleUrls: ['./cf-alarm-rules-dialog.component.scss'], +}) +export class CfAlarmRuleConditionDialogComponent extends DialogComponent { + + AlarmRuleExpressionType = AlarmRuleExpressionType; + + timeUnits = Object.values(TimeUnit); + timeUnitTranslations = timeUnitTranslationMap; + alarmConditionTypes = Object.values(AlarmRuleConditionType); + AlarmConditionType = AlarmRuleConditionType; + alarmConditionTypeTranslation = AlarmRuleConditionTypeTranslationMap; + readonly = this.data.readonly; + condition = this.data.condition; + + conditionFormGroup = this.fb.group({ + expression: this.fb.group({ + type: [AlarmRuleExpressionType.SIMPLE], + expression: ['', [Validators.required]], + operation: [ComplexOperation.AND], + filters: [null], + }), + type: this.fb.control(AlarmRuleConditionType.SIMPLE, Validators.required), + unit: this.fb.control(TimeUnit.SECONDS, Validators.required), + value: this.fb.group({ + staticValue: this.fb.control(null, [Validators.required, Validators.min(1), Validators.max(2147483647), Validators.pattern('[0-9]*')]), + dynamicValueArgument: this.fb.control('', Validators.required), + }), + count: this.fb.group({ + staticValue: this.fb.control(null, [Validators.required, Validators.min(1), Validators.max(2147483647), Validators.pattern('[0-9]*')]), + dynamicValueArgument: this.fb.control('', Validators.required), + }), + }); + + readonly scriptLanguage = ScriptLanguage; + + defaultValuePlaceholder = ''; + defaultValueRequiredError = ''; + defaultValueRangeError = ''; + defaultValuePatternError = ''; + + durationDynamicModeControl = this.fb.control(false); + repeatingDynamicModeControl = this.fb.control(false); + + ComplexOperation = ComplexOperation; + complexOperationTranslationMap = filterOperationTranslationMap; + + specText = ''; + + filtersValid: boolean = false; + + functionArgs: Array; + argumentsEditorCompleter: TbEditorCompleter; + argumentsHighlightRules: AceHighlightRules; + + arguments = this.data.arguments; + argumentsList: Array; + + isNoData: boolean = false; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: CfAlarmRuleConditionDialogData, + public dialogRef: MatDialogRef, + private fb: FormBuilder, + private translate: TranslateService) { + super(store, router, dialogRef); + + this.functionArgs = ['ctx', ...Object.keys(this.data.arguments)]; + this.argumentsEditorCompleter = getCalculatedFieldArgumentsEditorCompleter(this.data.arguments); + this.argumentsHighlightRules = getCalculatedFieldArgumentsHighlights(this.data.arguments); + this.argumentsList = this.arguments ? Object.keys(this.arguments): []; + + this.conditionFormGroup.patchValue({ + expression: { + type: this.condition?.expression?.type ?? AlarmRuleExpressionType.SIMPLE, + expression: this.condition?.expression?.expression ?? alarmRuleDefaultScript, + filters: this.condition?.expression?.filters ?? [], + operation: this.condition?.expression?.operation ?? ComplexOperation.AND + }, + type: this.condition?.type ?? AlarmRuleConditionType.SIMPLE, + unit: this.condition?.unit ?? TimeUnit.SECONDS, + value: { + staticValue: this.condition?.value?.staticValue ?? null, + dynamicValueArgument: Object.keys(this.data.arguments).includes(this.condition?.value?.dynamicValueArgument) ? this.condition?.value?.dynamicValueArgument : null, + }, + count: { + staticValue: this.condition?.count?.staticValue ?? null, + dynamicValueArgument: Object.keys(this.data.arguments).includes(this.condition?.count?.dynamicValueArgument) ? this.condition?.count?.dynamicValueArgument : null + } + }, {emitEvent: false}); + + + this.durationDynamicModeControl.patchValue(!!this.condition?.value?.dynamicValueArgument, {emitEvent: false}); + this.repeatingDynamicModeControl.patchValue(!!this.condition?.count?.dynamicValueArgument, {emitEvent: false}); + + this.filtersValid = areFiltersAndPredicateArgumentsValid(this.condition?.expression?.filters, this.data.arguments); + this.checkIsNoData(this.condition?.expression?.filters); + + this.conditionFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((type) => { + this.updateValidators(type); + }); + + this.conditionFormGroup.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value) => { + this.updateSpecText(value.type); + }) + + this.conditionFormGroup.get('expression.filters').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((filters) => { + this.filtersValid = areFiltersAndPredicateArgumentsValid(filters, this.data.arguments); + this.checkIsNoData(filters); + }); + + this.conditionFormGroup.get('expression.type').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((type) => { + this.updateExpressionTypeValidator(type); + this.updateValidators(this.conditionFormGroup.get('type').value ?? AlarmRuleConditionType.SIMPLE); + }); + + this.durationDynamicModeControl.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((mode) => { + this.updateStaticValueValidator(AlarmRuleConditionType.DURATION, mode); + this.updateSpecText(this.conditionFormGroup.get('type').value); + }); + this.repeatingDynamicModeControl.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((mode) => { + this.updateStaticValueValidator(AlarmRuleConditionType.REPEATING, mode); + this.updateSpecText(this.conditionFormGroup.get('type').value); + }); + + this.updateValidators(this.conditionFormGroup.get('type').value ?? AlarmRuleConditionType.SIMPLE); + this.updateSpecText(this.conditionFormGroup.get('type').value ?? AlarmRuleConditionType.SIMPLE); + this.updateExpressionTypeValidator(this.condition?.expression?.type ?? 'SIMPLE'); + + if (this.readonly) { + this.conditionFormGroup.disable({emitEvent: false}); + } + } + + updateStaticValueValidator(type: AlarmRuleConditionType, dynamicValue: boolean) { + const control = this.conditionFormGroup.get(type === AlarmRuleConditionType.DURATION ? 'value' : 'count'); + if (dynamicValue) { + control.get('staticValue').disable({emitEvent: false}); + control.get('dynamicValueArgument').enable({emitEvent: false}); + } else { + control.get('staticValue').enable({emitEvent: false}); + control.get('dynamicValueArgument').disable({emitEvent: false}); + } + } + + updateExpressionTypeValidator(type: 'SIMPLE' | 'TBEL') { + if (type === 'SIMPLE') { + this.conditionFormGroup.get(`expression.expression`).disable({emitEvent: false}); + this.conditionFormGroup.get(`expression.filters`).enable({emitEvent: false}); + } else { + this.conditionFormGroup.get(`expression.expression`).enable({emitEvent: false}); + this.conditionFormGroup.get(`expression.filters`).disable({emitEvent: false}); + } + } + + private checkIsNoData(filters: Array) { + this.isNoData = this.hasNoData(filters); + if (this.isNoData && this.conditionFormGroup.get('type').value !== AlarmRuleConditionType.SIMPLE) { + this.conditionFormGroup.get('type').patchValue(AlarmRuleConditionType.SIMPLE); + } + } + + private hasNoData(data: Array) { + const search = (filter) => { + if (!filter) return false; + if (Array.isArray(filter)) return filter.some(search); + if (typeof filter !== 'object') return false; + if (filter.type === 'NO_DATA') return true; + if (filter.predicates?.length) return filter.predicates.some(search); + return false; + }; + return search(data); + }; + + private updateValidators(type: AlarmRuleConditionType) { + switch (type) { + case AlarmRuleConditionType.DURATION: + this.conditionFormGroup.get('unit').enable({emitEvent: false}); + this.conditionFormGroup.get('value').enable({emitEvent: false}); + this.conditionFormGroup.get('count').disable({emitEvent: false}); + this.updateStaticValueValidator(type, this.durationDynamicModeControl.value); + this.defaultValuePlaceholder = 'alarm-rule.condition-duration-value'; + this.defaultValueRequiredError = 'alarm-rule.condition-duration-value-required'; + this.defaultValueRangeError = 'alarm-rule.condition-duration-value-range'; + this.defaultValuePatternError = 'alarm-rule.condition-duration-value-pattern'; + break; + case AlarmRuleConditionType.REPEATING: + this.conditionFormGroup.get('count').enable({emitEvent: false}); + this.conditionFormGroup.get('value').disable({emitEvent: false}); + this.conditionFormGroup.get('unit').disable({emitEvent: false}); + this.updateStaticValueValidator(type, this.repeatingDynamicModeControl.value); + this.defaultValuePlaceholder = 'alarm-rule.condition-repeating-value'; + this.defaultValueRequiredError = 'alarm-rule.condition-repeating-value-required'; + this.defaultValueRangeError = 'alarm-rule.condition-repeating-value-range'; + this.defaultValuePatternError = 'alarm-rule.condition-repeating-value-pattern'; + break; + case AlarmRuleConditionType.SIMPLE: + this.conditionFormGroup.get('value').disable({emitEvent: false}); + this.conditionFormGroup.get('count').disable({emitEvent: false}); + this.conditionFormGroup.get('unit').disable({emitEvent: false}); + break; + } + } + + private updateSpecText(type: AlarmRuleConditionType) { + this.specText = ''; + const value = this.conditionFormGroup.get('value').value; + const count = this.conditionFormGroup.get('count').value; + switch (type) { + case AlarmRuleConditionType.SIMPLE: + break; + case AlarmRuleConditionType.DURATION: + let duringText = ''; + switch (this.conditionFormGroup.get('unit').value) { + case TimeUnit.SECONDS: + duringText = this.translate.instant('timewindow.seconds', {seconds: value.staticValue}); + break; + case TimeUnit.MINUTES: + duringText = this.translate.instant('timewindow.minutes', {minutes: value.staticValue}); + break; + case TimeUnit.HOURS: + duringText = this.translate.instant('timewindow.hours', {hours: value.staticValue}); + break; + case TimeUnit.DAYS: + duringText = this.translate.instant('timewindow.days', {days: value.staticValue}); + break; + } + if (this.durationDynamicModeControl.value) { + this.specText = this.translate.instant('alarm-rule.condition-during-dynamic', { + attribute: `${value.dynamicValueArgument ?? ''}` + }) + ' ' + this.translate.instant(this.timeUnitTranslations.get(this.conditionFormGroup.get('unit').value)).toLowerCase(); + } else { + this.specText = this.translate.instant('alarm-rule.condition-during', { + during: duringText.trim() + }); + } + break; + case AlarmRuleConditionType.REPEATING: + if (this.repeatingDynamicModeControl.value) { + this.specText = this.translate.instant('alarm-rule.condition-repeat-times-dynamic', { + attribute: `${count.dynamicValueArgument ?? ''}` + }); + } else { + this.specText = this.translate.instant('alarm-rule.condition-repeat-times', + {count: count.staticValue}); + } + break; + } + if (this.specText.length > 0) { + this.specText = this.specText + ':'; + } + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + this.dialogRef.close(this.conditionFormGroup.value as AlarmRuleCondition); + } + + onTestScript($event: Event) { + $event?.preventDefault(); + this.data.testScript(this.conditionFormGroup.get('expression.expression').value).subscribe( + (expression) => { + this.conditionFormGroup.get('expression.expression').setValue(expression); + this.conditionFormGroup.get('expression.expression').markAsDirty(); + }) + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html new file mode 100644 index 0000000000..8768a91231 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html @@ -0,0 +1,58 @@ + +
+
+
{{ 'alarm-rule.condition' | translate }}
+ +
+
+
{{ 'alarm-rule.schedule-title' | translate }}
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.scss new file mode 100644 index 0000000000..62876c2476 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.scss @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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: flex; + flex: 1; + + .tb-alarm-rule-condition { + display: flex; + flex: 1; + &-button { + --mat-outlined-button-horizontal-padding: 3px 0px 12px; + display: block; + width: 100%; + } + &-label { + display: block; + text-align: start; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts new file mode 100644 index 0000000000..8ff7e2c083 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts @@ -0,0 +1,344 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, SimpleChanges } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator +} from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { deepClone, isDefinedAndNotNull } from '@core/utils'; +import { TranslateService } from '@ngx-translate/core'; +import { + dayOfWeekTranslations, + getAlarmScheduleRangeText, + utcTimestampToTimeOfDay +} from '@shared/models/device.models'; +import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; +import { + CfAlarmRuleConditionDialogComponent, + CfAlarmRuleConditionDialogData +} from "@home/components/alarm-rules/cf-alarm-rule-condition-dialog.component"; +import { + AlarmRuleCondition, + AlarmRuleConditionType, + AlarmRuleExpressionType, + AlarmRuleSchedule, + AlarmRuleScheduleType, + checkPredicates +} from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { + AlarmRuleScheduleDialogData, + CfAlarmScheduleDialogComponent +} from "@home/components/alarm-rules/cf-alarm-schedule-dialog.component"; +import { coerceBoolean } from "@shared/decorators/coercion"; +import { Observable } from "rxjs"; + +@Component({ + selector: 'tb-cf-alarm-rule-condition', + templateUrl: './cf-alarm-rule-condition.component.html', + styleUrls: ['./cf-alarm-rule-condition.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CfAlarmRuleConditionComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CfAlarmRuleConditionComponent), + multi: true, + } + ] +}) +export class CfAlarmRuleConditionComponent implements ControlValueAccessor, Validator, OnChanges { + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + @coerceBoolean() + required: boolean; + + @Input() + arguments: Record; + + @Input() + @coerceBoolean() + isClearCondition = false; + + @Input({required: true}) + testScript: (expression: string) => Observable; + + alarmRuleConditionFormGroup = this.fb.group({ + type: ['SIMPLE'], + expression: [{type: AlarmRuleExpressionType.SIMPLE}], + schedule: [null], + }); + + specText = ''; + + filtersArgumentsValid: boolean = true; + schedulerArgumentsValid: boolean = true; + + scheduleText = ''; + + private modelValue: AlarmRuleCondition; + + private propagateChange = (v: any) => { }; + private onValidatorChange = () => { }; + + constructor(private dialog: MatDialog, + private fb: FormBuilder, + private cd: ChangeDetectorRef, + private translate: TranslateService) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + registerOnValidatorChange(fn: () => void): void { + this.onValidatorChange = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.alarmRuleConditionFormGroup.disable({emitEvent: false}); + } else { + this.alarmRuleConditionFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: AlarmRuleCondition): void { + this.modelValue = value; + this.updateConditionInfo(); + this.recalculateArgumentValidity(); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.arguments) { + if (changes.arguments && !changes.arguments.firstChange) { + this.recalculateArgumentValidity(); + } + } + } + + private recalculateArgumentValidity(): void { + if (!this.modelValue || !this.arguments) { + this.filtersArgumentsValid = true; + this.schedulerArgumentsValid = true; + return; + } + this.filtersArgumentsValid = this.areFilterAndPredicateArgumentsValid(this.modelValue, this.arguments); + this.schedulerArgumentsValid = this.isScheduleArgumentValid(this.modelValue, Object.keys(this.arguments)); + } + + private isScheduleArgumentValid(obj: any, validArguments: string[]): boolean { + const arg = obj?.schedule?.dynamicValueArgument; + return !arg || validArguments.includes(arg); + } + + private areFilterAndPredicateArgumentsValid(obj: any, args: Record): boolean { + const validSet = new Set(Object.keys(args)); + const filters = obj?.expression?.filters || obj?.filters || []; + for (const filter of filters) { + if (filter.argument && !validSet.has(filter.argument)) { + return false; + } + } + for (const filter of filters) { + if (Array.isArray(filter.predicates)) { + if (!checkPredicates(filter.predicates, validSet)) { + return false; + } + } + } + return true; + } + + public conditionSet(): boolean { + return !!this.modelValue && !!(this.modelValue.expression?.expression || this.modelValue.expression?.filters); + } + + public validate(control: AbstractControl): ValidationErrors | null { + const hasCondition = this.conditionSet(); + const argsValid = this.filtersArgumentsValid && this.schedulerArgumentsValid; + return (hasCondition && argsValid) ? null : { alarmRuleCondition: { valid: false } }; + } + + public openFilterDialog($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(CfAlarmRuleConditionDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + readonly: this.disabled, + condition: this.disabled ? this.modelValue : deepClone(this.modelValue), + arguments: this.arguments, + testScript: this.testScript + } + }).afterClosed().subscribe((result) => { + if (result) { + this.modelValue = {...this.modelValue, ...result}; + this.updateModel(); + } + }); + } + + private updateConditionInfo() { + this.alarmRuleConditionFormGroup.patchValue( + this.modelValue ? { + type: this.modelValue?.type, + expression: this.modelValue?.expression, + schedule: this.modelValue?.schedule, + } : null, {emitEvent: false} + ); + this.updateScheduleText(); + this.updateSpecText(); + } + + private updateSpecText() { + this.specText = ''; + if (this.modelValue && this.modelValue.type) { + const type = this.modelValue.type; + switch (type) { + case AlarmRuleConditionType.SIMPLE: + break; + case AlarmRuleConditionType.DURATION: + let duringText = ''; + switch (this.modelValue.unit) { + case TimeUnit.SECONDS: + duringText = this.translate.instant('timewindow.seconds', {seconds: this.modelValue.value.staticValue}); + break; + case TimeUnit.MINUTES: + duringText = this.translate.instant('timewindow.minutes', {minutes: this.modelValue.value.staticValue}); + break; + case TimeUnit.HOURS: + duringText = this.translate.instant('timewindow.hours', {hours: this.modelValue.value.staticValue}); + break; + case TimeUnit.DAYS: + duringText = this.translate.instant('timewindow.days', {days: this.modelValue.value.staticValue}); + break; + } + if (this.modelValue.value.dynamicValueArgument) { + this.specText = this.translate.instant('alarm-rule.condition-during-dynamic', { + attribute: `${this.modelValue.value.dynamicValueArgument}` + }) + ' ' + this.translate.instant(timeUnitTranslationMap.get(this.modelValue.unit)).toLowerCase(); + } else { + this.specText = this.translate.instant('alarm-rule.condition-during', { + during: duringText + }); + } + break; + case AlarmRuleConditionType.REPEATING: + if (this.modelValue.count.dynamicValueArgument) { + this.specText = this.translate.instant('alarm-rule.condition-repeat-times-dynamic', { + attribute: `${this.modelValue.count.dynamicValueArgument}` + }); + } else { + this.specText = this.translate.instant('alarm-rule.condition-repeat-times', + {count: this.modelValue.count.staticValue}); + } + break; + } + } + } + + private updateModel() { + this.updateConditionInfo(); + this.recalculateArgumentValidity(); + this.propagateChange(this.modelValue); + this.onValidatorChange(); + this.cd.detectChanges(); + } + + public openScheduleDialog($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(CfAlarmScheduleDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + readonly: this.disabled, + alarmSchedule: this.disabled ? this.modelValue?.schedule : deepClone(this.modelValue?.schedule), + arguments: this.arguments + } + }).afterClosed().subscribe((result) => { + if (result) { + this.modelValue.schedule = result; + this.updateModel(); + } + }); + } + + private updateScheduleText() { + const schedule = this.modelValue?.schedule; + this.scheduleText = ''; + if (isDefinedAndNotNull(schedule)) { + if (schedule.dynamicValueArgument) { + this.scheduleText = this.translate.instant('alarm-rule.value-argument') + ': ' + schedule?.dynamicValueArgument + } else { + switch (schedule.staticValue.type) { + case AlarmRuleScheduleType.ANY_TIME: + this.scheduleText = this.translate.instant('alarm-rule.schedule.any-time'); + break; + case AlarmRuleScheduleType.SPECIFIC_TIME: + for (const day of schedule.staticValue.daysOfWeek) { + if (this.scheduleText.length) { + this.scheduleText += ', '; + } + this.scheduleText += this.translate.instant(dayOfWeekTranslations[day - 1]); + } + this.scheduleText += ' ' + getAlarmScheduleRangeText(utcTimestampToTimeOfDay(schedule.staticValue.startsOn), + utcTimestampToTimeOfDay(schedule.staticValue.endsOn)) + ''; + break; + case AlarmRuleScheduleType.CUSTOM: + for (const item of schedule.staticValue.items) { + if (item.enabled) { + if (this.scheduleText.length) { + this.scheduleText += ', '; + } + this.scheduleText += this.translate.instant(dayOfWeekTranslations[item.dayOfWeek - 1]); + this.scheduleText += ' ' + getAlarmScheduleRangeText(utcTimestampToTimeOfDay(item.startsOn), + utcTimestampToTimeOfDay(item.endsOn)) + ''; + } + } + break; + } + } + } + if (!this.scheduleText.length) { + this.scheduleText = this.translate.instant('alarm-rule.schedule.any-time'); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html new file mode 100644 index 0000000000..47b6281fdd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html @@ -0,0 +1,46 @@ + +
+ + +
+
+ alarm-rule.alarm-rule-additional-info +
+ + + + +
+
+
+ alarm-rule.alarm-rule-mobile-dashboard +
+ + +
+
diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.scss similarity index 70% rename from ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.scss rename to ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.scss index ed5e4c4a64..53283aad68 100644 --- a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.scss @@ -13,17 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import '../../../../../scss/constants'; - :host { - display: flex; - flex: 1 1 0; - .tb-request-password-reset-content { - background-color: #eee; - .tb-request-password-reset-card { - @media #{$mat-gt-xs} { - width: 450px !important; - } + width: 100%; + .tb-alarm-rule-details, .tb-alarm-rule-dashboard { + padding: 4px; + &.title { + opacity: 0.7; + overflow: visible; } } + .tb-alarm-rule-details { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + cursor: pointer; + } } + diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.ts new file mode 100644 index 0000000000..1ba769a19e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.ts @@ -0,0 +1,157 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { isDefinedAndNotNull } from '@core/utils'; +import { DashboardId } from '@shared/models/id/dashboard-id'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { AlarmRule, AlarmRuleCondition } from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { + AlarmRuleDetailsDialogComponent, + AlarmRuleDetailsDialogData +} from "@home/components/alarm-rules/alarm-rule-details-dialog.component"; +import { coerceBoolean } from "@shared/decorators/coercion"; +import { Observable } from "rxjs"; + +@Component({ + selector: 'tb-cf-alarm-rule', + templateUrl: './cf-alarm-rule.component.html', + styleUrls: ['./cf-alarm-rule.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CfAlarmRuleComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CfAlarmRuleComponent), + multi: true, + } + ] +}) +export class CfAlarmRuleComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + @coerceBoolean() + required: boolean; + + @Input() + arguments: Record; + + @Input() + @coerceBoolean() + isClearCondition = false; + + @Input({required: true}) + testScript: (expression: string) => Observable; + + private modelValue: AlarmRule; + + alarmRuleFormGroup = this.fb.group({ + condition: this.fb.control(null, Validators.required), + alarmDetails: [null], + dashboardId: [null] + }); + + private propagateChange = (v: any) => { }; + + constructor(private dialog: MatDialog, + private fb: FormBuilder, + private destroyRef: DestroyRef) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.alarmRuleFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.alarmRuleFormGroup.disable({emitEvent: false}); + } else { + this.alarmRuleFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: AlarmRule): void { + this.modelValue = value; + const model = this.modelValue ? { + ...this.modelValue, + dashboardId: this.modelValue.dashboardId?.id + } : null; + this.alarmRuleFormGroup.patchValue(model, {emitEvent: false}); + } + + public openEditDetailsDialog($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(AlarmRuleDetailsDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + alarmDetails: this.alarmRuleFormGroup.get('alarmDetails').value, + readonly: this.disabled + } + }).afterClosed().subscribe((alarmDetails) => { + if (isDefinedAndNotNull(alarmDetails)) { + this.alarmRuleFormGroup.patchValue({alarmDetails}); + } + }); + } + + public validate(): ValidationErrors | null { + return (!this.required && !this.modelValue || this.alarmRuleFormGroup.valid) ? null : { + alarmRule: { + valid: false, + }, + }; + } + + private updateModel() { + const value = this.alarmRuleFormGroup.value; + this.modelValue = {...value, dashboardId: value.dashboardId ? new DashboardId(value.dashboardId) : null} as AlarmRule; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rules-dialog.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rules-dialog.component.scss new file mode 100644 index 0000000000..f38df65c29 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rules-dialog.component.scss @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + form { + width: 900px; + max-width: 100%; + display: grid; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + } + + .spec-text { + font-size: 14px; + color: rgba(0, 0, 0, 0.54); + } +} +.tbel-script-lang-chip { + line-height: 20px; + font-size: 14px; + font-weight: 500; + color: white; + border-radius: 100px; + width: 70px; + min-width: 70px; + display: flex; + justify-content: center; + margin-top: 2px; + margin-right: 4px; +} +.tb-js-func { + .ace_tb { + &.ace_calculated-field { + &-ctx { + color: #C52F00; + } + &-args { + color: #185F2A; + } + &-key { + color: #c24c1a; + } + &-time-window, &-values, &-func, &-value, &-ts, &-latestTs { + color: #7214D0; + } + &-start-ts, &-end-ts { + color: #2CAA00; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule-dialog.component.html new file mode 100644 index 0000000000..3908d919b3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule-dialog.component.html @@ -0,0 +1,60 @@ + +
+ +

{{ (readonly ? 'alarm-rule.schedule-title' : 'alarm-rule.edit-schedule') | translate }}

+ + +
+
+
+
+
+
{{ 'alarm-rule.mode' | translate }}
+ + {{ 'alarm-rule.static-schedule' | translate }} + {{ 'alarm-rule.dynamic-schedule' | translate }} + +
+ + +
+
+
+
+ + @if (!readonly) { + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule-dialog.component.ts new file mode 100644 index 0000000000..132fbfeed5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule-dialog.component.ts @@ -0,0 +1,70 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { FormBuilder } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { AlarmRuleSchedule } from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; + +export interface AlarmRuleScheduleDialogData { + readonly: boolean; + alarmSchedule: AlarmRuleSchedule; + arguments: Record; +} + +@Component({ + selector: 'tb-cf-alarm-schedule-dialog', + templateUrl: './cf-alarm-schedule-dialog.component.html', + providers: [], + styleUrls: ['./cf-alarm-rules-dialog.component.scss'], +}) +export class CfAlarmScheduleDialogComponent extends DialogComponent{ + + readonly = this.data.readonly; + alarmSchedule = this.data.alarmSchedule; + arguments = this.data.arguments; + + alarmScheduleControl = this.fb.control(null); + + dynamicModeControl = this.fb.control(false); + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: AlarmRuleScheduleDialogData, + public dialogRef: MatDialogRef, + private fb: FormBuilder) { + super(store, router, dialogRef); + this.alarmScheduleControl.patchValue(this.alarmSchedule, {emitEvent: false}); + this.dynamicModeControl.patchValue(!!this.alarmSchedule?.dynamicValueArgument, {emitEvent: false}); + if (this.readonly) { + this.alarmScheduleControl.disable({emitEvent: false}); + this.dynamicModeControl.disable({emitEvent: false}); + } + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + this.dialogRef.close(this.alarmScheduleControl.value); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule.component.html new file mode 100644 index 0000000000..f1d3ae9277 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule.component.html @@ -0,0 +1,125 @@ + +
+ @if (!dynamicMode) { + + + + @for (scheduleType of alarmScheduleTypes; track scheduleType) { + {{ alarmScheduleTypeTranslate.get(scheduleType) | translate }} + } + + @if (alarmScheduleForm.get('staticValue.type').hasError('required')) { + {{ 'alarm-rule.schedule-type-required' | translate }} + } + + + @if (alarmScheduleForm.get('staticValue.type').value !== alarmScheduleType.ANY_TIME) { +
+
+ + + @if (alarmScheduleForm.get('staticValue.type').value === alarmScheduleType.SPECIFIC_TIME) { +
+ + + {{ dayOfWeekTranslationsArray[day] | translate }} + + + +
+
+ + alarm-rule.schedule-time-from + + + + + + alarm-rule.schedule-time-to + + + + +
+
+
+
+
+
+ } + @if (alarmScheduleForm.get('staticValue.type').value === alarmScheduleType.CUSTOM) { +
+
+
+ + {{ dayOfWeekTranslationsArray[day] | translate }} + +
+ + alarm-rule.schedule-time-from + + + + + + alarm-rule.schedule-time-to + + + + +
+
+
+
+
+ +
+ } +
+
+ } + } @else { +
+ + alarm-rule.value-argument + + @for (argument of argumentsList; track argument) { + {{ argument }} + } + + @if (alarmScheduleForm.get('dynamicValueArgument').hasError('required')) { + {{ 'calculated-fields.hint.argument-name-required' | translate }} + } + +
+
+
+ } +
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule.component.ts new file mode 100644 index 0000000000..397e37d2a5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-schedule.component.ts @@ -0,0 +1,322 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { + CustomTimeSchedulerItem, + dayOfWeekTranslations, + getAlarmScheduleRangeText, + timeOfDayToUTCTimestamp, + utcTimestampToTimeOfDay +} from '@shared/models/device.models'; +import { isDefined } from '@core/utils'; +import { getDefaultTimezone } from '@shared/models/time/time.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + AlarmRuleSchedule, + AlarmRuleScheduleType, + AlarmRuleScheduleTypeTranslationMap +} from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { MatChipSelectionChange } from "@angular/material/chips"; +import { coerceBoolean } from "@shared/decorators/coercion"; + +@Component({ + selector: 'tb-cf-alarm-schedule', + templateUrl: './cf-alarm-schedule.component.html', + styleUrls: [], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CfAlarmScheduleComponent), + multi: true + }, { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CfAlarmScheduleComponent), + multi: true + }] +}) +export class CfAlarmScheduleComponent implements ControlValueAccessor, Validator, OnInit, OnChanges { + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + arguments: Record; + + @Input() + @coerceBoolean() + dynamicMode: boolean; + + alarmScheduleForm = this.fb.group({ + staticValue: this.fb.group({ + type: [AlarmRuleScheduleType.ANY_TIME, Validators.required], + timezone: ['', Validators.required], + daysOfWeek: this.fb.control(null, Validators.required), + startsOn: this.fb.control(0, Validators.required), + endsOn: this.fb.control(0, Validators.required), + items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems), + }), + dynamicValueArgument: ['', Validators.required] + }); + + alarmScheduleTypes = Object.keys(AlarmRuleScheduleType) as Array; + alarmScheduleType = AlarmRuleScheduleType; + alarmScheduleTypeTranslate = AlarmRuleScheduleTypeTranslationMap; + dayOfWeekTranslationsArray = dayOfWeekTranslations; + + argumentsList: Array; + + allDays = Array(7).fill(0).map((x, i) => i); + + private modelValue: AlarmRuleSchedule; + + private defaultItems = Array.from({length: 7}, (value, i) => ({ + enabled: true, + dayOfWeek: i + 1 + })) as CustomTimeSchedulerItem[]; + + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + this.argumentsList = this.arguments ? Object.keys(this.arguments): []; + this.alarmScheduleForm.get('staticValue.type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((type) => { + const defaultTimezone = getDefaultTimezone(); + const staticValue = {...this.alarmScheduleForm.get('staticValue').value, type, items: this.defaultItems, timezone: defaultTimezone}; + this.alarmScheduleForm.get('staticValue').patchValue(staticValue, {emitEvent: false}); + this.alarmScheduleForm.get('dynamicValueArgument').patchValue(null, {emitEvent: false}); + this.updateValidators(type); + }); + this.alarmScheduleForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + + this.alarmScheduleForm.get('staticValue.items').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((items) => { + items.forEach((item, index) => this.disabledSelectedTime(item.enabled, index, false)) + }); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.dynamicMode) { + const dynamicModeChanges = changes.dynamicMode; + if (!dynamicModeChanges.firstChange && dynamicModeChanges.currentValue !== dynamicModeChanges.previousValue) { + this.updateModeValidators(dynamicModeChanges.currentValue); + this.updateModel(); + } + } + } + + validateItems(control: AbstractControl): ValidationErrors | null { + const items: any[] = control.value; + if (!items || !items.length || !items.find(v => v.enabled === true)) { + return { + dayOfWeeks: true + }; + } + return null; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.alarmScheduleForm.disable({emitEvent: false}); + } else { + this.updateModeValidators(this.dynamicMode); + } + } + + writeValue(value: AlarmRuleSchedule): void { + if (value) { + this.modelValue = value; + if (this.modelValue.dynamicValueArgument) { + this.alarmScheduleForm.get('dynamicValueArgument').patchValue(Object.keys(this.arguments).includes(this.modelValue.dynamicValueArgument) ? this.modelValue.dynamicValueArgument : null, {emitEvent: false}); + } else { + switch (this.modelValue.staticValue.type) { + case AlarmRuleScheduleType.SPECIFIC_TIME: + this.alarmScheduleForm.patchValue({ + staticValue: { + type: this.modelValue.staticValue.type, + timezone: this.modelValue.staticValue.timezone, + daysOfWeek: this.modelValue.staticValue.daysOfWeek, + startsOn: utcTimestampToTimeOfDay(this.modelValue.staticValue.startsOn), + endsOn: utcTimestampToTimeOfDay(this.modelValue.staticValue.endsOn), + }, + }, {emitEvent: false}); + break; + case AlarmRuleScheduleType.CUSTOM: + if (this.modelValue?.dynamicValueArgument) { + this.alarmScheduleForm.patchValue({ + staticValue: { + type: this.modelValue.staticValue.type, + }, + }, {emitEvent: false}); + } else if (this.modelValue.staticValue?.items) { + const alarmDays = []; + this.modelValue.staticValue.items + .sort((a, b) => a.dayOfWeek - b.dayOfWeek) + .forEach((item, index) => { + this.disabledSelectedTime(item.enabled, index); + alarmDays.push({ + enabled: item.enabled, + startsOn: utcTimestampToTimeOfDay(item.startsOn), + endsOn: utcTimestampToTimeOfDay(item.endsOn) + }); + }); + this.alarmScheduleForm.patchValue({ + staticValue: { + type: this.modelValue.staticValue.type, + timezone: this.modelValue.staticValue.timezone, + items: alarmDays, + }, + }, {emitEvent: false}); + } + break; + default: + this.alarmScheduleForm.patchValue(this.modelValue || undefined, {emitEvent: false}); + } + this.updateValidators(this.modelValue.staticValue.type); + } + this.updateModeValidators(this.dynamicMode); + } + } + + validate(control: FormGroup): ValidationErrors | null { + return this.alarmScheduleForm.valid ? null : { + alarmScheduler: { + valid: false + } + }; + } + + private updateModeValidators(mode: boolean) { + if (mode) { + this.alarmScheduleForm.get('staticValue').disable({emitEvent: false}); + this.alarmScheduleForm.get('dynamicValueArgument').enable({emitEvent: false}); + } else { + this.alarmScheduleForm.get('staticValue').enable({emitEvent: false}); + this.alarmScheduleForm.get('dynamicValueArgument').disable({emitEvent: false}); + this.updateValidators(this.alarmScheduleForm.get('staticValue.type').value); + } + } + + private updateValidators(type: AlarmRuleScheduleType){ + switch (type){ + case AlarmRuleScheduleType.ANY_TIME: + this.alarmScheduleForm.get('staticValue.timezone').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.daysOfWeek').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.startsOn').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.endsOn').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.items').disable({emitEvent: false}); + break; + case AlarmRuleScheduleType.SPECIFIC_TIME: + this.alarmScheduleForm.get('staticValue.timezone').enable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.daysOfWeek').enable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.startsOn').enable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.endsOn').enable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.items').disable({emitEvent: false}); + break; + case AlarmRuleScheduleType.CUSTOM: + this.alarmScheduleForm.get('staticValue.timezone').enable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.daysOfWeek').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.startsOn').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.endsOn').disable({emitEvent: false}); + this.alarmScheduleForm.get('staticValue.items').enable({emitEvent: false}); + break; + } + } + + private updateModel() { + const value = this.alarmScheduleForm.value as AlarmRuleSchedule; + if (!this.dynamicMode) { + if (isDefined(value.staticValue.startsOn) && value.staticValue.startsOn !== 0) { + value.staticValue.startsOn = timeOfDayToUTCTimestamp(value.staticValue.startsOn); + } + if (isDefined(value.staticValue.endsOn) && value.staticValue.endsOn !== 0) { + value.staticValue.endsOn = timeOfDayToUTCTimestamp(value.staticValue.endsOn); + } + if (isDefined(value.staticValue.items)){ + value.staticValue.items = this.alarmScheduleForm.getRawValue().staticValue.items as CustomTimeSchedulerItem[]; + value.staticValue.items = value.staticValue.items.map((item) => { + return { ...item, startsOn: timeOfDayToUTCTimestamp(item.startsOn), endsOn: timeOfDayToUTCTimestamp(item.endsOn)}; + }); + } + } + this.modelValue = value; + this.propagateChange(this.modelValue); + } + + + private defaultItemsScheduler(index: number): FormGroup { + return this.fb.group({ + enabled: [true], + dayOfWeek: [index + 1], + startsOn: [0, Validators.required], + endsOn: [0, Validators.required] + }); + } + + changeCustomScheduler($event: MatChipSelectionChange, index: number) { + const value = $event.selected; + this.disabledSelectedTime(value, index, true); + } + + private disabledSelectedTime(enable: boolean, index: number, emitEvent = false) { + if (enable) { + this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false}); + this.itemsSchedulerForm.at(index).get('endsOn').enable({emitEvent}); + } else { + this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false}); + this.itemsSchedulerForm.at(index).get('endsOn').disable({emitEvent}); + } + } + + getSchedulerRangeText(control: FormGroup | AbstractControl): string { + return getAlarmScheduleRangeText(control.get('startsOn').value, control.get('endsOn').value); + } + + get itemsSchedulerForm(): FormArray { + return this.alarmScheduleForm.get('staticValue.items') as FormArray; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.html new file mode 100644 index 0000000000..18ac406c97 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.html @@ -0,0 +1,63 @@ + +
+ @for (createAlarmRuleControl of createAlarmRulesFormArray().controls; track createAlarmRuleControl; let index = $index) { +
+
+
+
alarm.severity
+ + + + {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }} + + + +
+ + +
+ +
+ } + @if (!createAlarmRulesFormArray().controls.length) { + + alarm-rule.add-create-alarm-rule-prompt + + } +
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.scss new file mode 100644 index 0000000000..7250c86ab2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.scss @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .create-alarm-rule { + border: 1px solid rgba(0, 0, 0, .12); + border-left-width: 4px; + border-radius: 4px; + padding: 8px; + min-width: 0; + } +} + +:host ::ng-deep { + .mat-mdc-form-field.severity { + .mat-mdc-form-field-infix { + width: 160px; + } + } + .button-icon { + color: rgba(0, 0, 0, 0.38); + min-width: 40px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts new file mode 100644 index 0000000000..1c56cdbbad --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts @@ -0,0 +1,189 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { AlarmSeverity, alarmSeverityColors, alarmSeverityTranslations } from '@shared/models/alarm.models'; +import { AlarmRule } from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { coerceBoolean } from "@shared/decorators/coercion"; +import { Observable } from "rxjs"; + +@Component({ + selector: 'tb-create-cf-alarm-rules', + templateUrl: './create-cf-alarm-rules.component.html', + styleUrls: ['./create-cf-alarm-rules.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CreateCfAlarmRulesComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CreateCfAlarmRulesComponent), + multi: true, + } + ] +}) +export class CreateCfAlarmRulesComponent implements ControlValueAccessor, Validator { + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + arguments: Record; + + @Input({required: true}) + testScript: (expression: string) => Observable; + + alarmSeverities = Object.keys(AlarmSeverity); + alarmSeverityEnum = AlarmSeverity; + alarmSeverityTranslationMap = alarmSeverityTranslations; + + AlarmSeverityNotificationColors = alarmSeverityColors; + + createAlarmRulesFormGroup = this.fb.group({ + createAlarmRules: this.fb.array<{severity: AlarmSeverity, alarmRule: AlarmRule}>([]) + }); + + private usedSeverities: AlarmSeverity[] = []; + + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + this.createAlarmRulesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => this.updateModel()); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + createAlarmRulesFormArray(): UntypedFormArray { + return this.createAlarmRulesFormGroup.get('createAlarmRules') as UntypedFormArray; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.createAlarmRulesFormGroup.disable({emitEvent: false}); + } else { + this.createAlarmRulesFormGroup.enable({emitEvent: false}); + } + } + + writeValue(createAlarmRules: Record): void { + const createAlarmRulesControls: Array = []; + if (createAlarmRules) { + Object.keys(createAlarmRules).forEach((severity) => { + const createAlarmRule = createAlarmRules[severity]; + if (severity === 'empty') { + severity = null; + } + createAlarmRulesControls.push(this.fb.group({ + severity: [severity, Validators.required], + alarmRule: [{value: createAlarmRule, disabled: this.disabled}, Validators.required] + })); + }); + } + const formArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray; + formArray.clear({emitEvent: false}); + createAlarmRulesControls.forEach(c => formArray.push(c, {emitEvent: false})); + if (this.disabled) { + this.createAlarmRulesFormGroup.disable({emitEvent: false}); + } else { + this.createAlarmRulesFormGroup.enable({emitEvent: false}); + } + this.updateUsedSeverities(); + if (!this.disabled && !this.createAlarmRulesFormGroup.valid) { + this.updateModel(); + } + } + + public removeCreateAlarmRule(index: number) { + (this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray).removeAt(index); + } + + public addCreateAlarmRule() { + const createAlarmRulesArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray; + createAlarmRulesArray.push(this.fb.group({ + severity: [this.getFirstUnusedSeverity(), Validators.required], + alarmRule: [null, Validators.required] + })); + this.createAlarmRulesFormGroup.updateValueAndValidity(); + if (!this.createAlarmRulesFormGroup.valid) { + this.updateModel(); + } + } + + private getFirstUnusedSeverity(): AlarmSeverity { + for (const severityKey of Object.keys(AlarmSeverity)) { + const severity = AlarmSeverity[severityKey]; + if (this.usedSeverities.indexOf(severity) === -1) { + return severity; + } + } + return null; + } + + public validate(): ValidationErrors | null { + return this.createAlarmRulesFormGroup.valid && this.createAlarmRulesFormArray().length > 0 ? null : { + createAlarmRules: { + valid: false, + }, + }; + } + + public isDisabledSeverity(severity: AlarmSeverity, index: number): boolean { + const usedIndex = this.usedSeverities.indexOf(severity); + return usedIndex > -1 && usedIndex !== index; + } + + private updateUsedSeverities() { + this.usedSeverities = []; + const value = this.createAlarmRulesFormGroup.get('createAlarmRules').value; + value.forEach((rule, index) => { + this.usedSeverities[index] = AlarmSeverity[rule.severity]; + }); + } + + private updateModel() { + const value = this.createAlarmRulesFormGroup.get('createAlarmRules').value; + const createAlarmRules = {} as Record; + value.forEach(v => createAlarmRules[v.severity] = v.alarmRule); + this.updateUsedSeverities(); + this.propagateChange(createAlarmRules); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component.html new file mode 100644 index 0000000000..5ca3cde37b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component.html @@ -0,0 +1,63 @@ + +
+ +

filter.complex-filter

+ +
+
+
+
+
+ {{ 'alarm-rule.filters' | translate }} +
+ + {{ complexOperationTranslations.get(complexOperationEnum.AND) | translate }} + {{ complexOperationTranslations.get(complexOperationEnum.OR) | translate }} + +
+ + +
+
+
+ + @if (!readonly) { + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component.ts new file mode 100644 index 0000000000..017cefb890 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component.ts @@ -0,0 +1,94 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { FormBuilder, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { ComplexOperation, EntityKeyValueType } from '@shared/models/query/query.models'; +import { + AlarmRuleFilterPredicate, + AlarmRuleFilterPredicateType, + ComplexAlarmRuleFilterPredicate, + filterOperationTranslationMap +} from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; + +export interface AlarmRuleComplexFilterPredicateDialogData { + complexPredicate: ComplexAlarmRuleFilterPredicate; + isAdd: boolean; + valueType: EntityKeyValueType; + arguments: Record; + argumentInUse: string; + readonly: boolean; +} + +@Component({ + selector: 'tb-alarm-rule-complex-filter-predicate-dialog', + templateUrl: './alarm-rule-complex-filter-predicate-dialog.component.html', + providers: [], + styleUrls: [] +}) + +export class AlarmRuleComplexFilterPredicateDialogComponent extends + DialogComponent { + + complexFilterFormGroup = this.fb.group( + { + operation: [ComplexOperation.AND, [Validators.required]], + predicates: this.fb.control(null, Validators.required) + } + ); + + EntityKeyValueType = EntityKeyValueType; + complexOperationEnum = ComplexOperation; + complexOperationTranslations = filterOperationTranslationMap; + + isAdd: boolean; + + arguments = this.data.arguments; + + readonly = this.data.readonly; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: AlarmRuleComplexFilterPredicateDialogData, + public dialogRef: MatDialogRef, + private fb: FormBuilder) { + super(store, router, dialogRef); + + this.isAdd = this.data.isAdd; + + this.complexFilterFormGroup.patchValue(this.data.complexPredicate, {emitEvent: false}); + + if (this.readonly) { + this.complexFilterFormGroup.disable({emitEvent: false}); + } + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + const predicate = this.complexFilterFormGroup.value as ComplexAlarmRuleFilterPredicate; + predicate.type = AlarmRuleFilterPredicateType.COMPLEX; + this.dialogRef.close(predicate); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.html new file mode 100644 index 0000000000..8f6cd68c6a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.html @@ -0,0 +1,104 @@ + +
+ +

{{(data.isAdd ? 'alarm-rule.add-filter' : 'alarm-rule.edit-filter') | translate}}

+ +
+
+
+
+
{{ 'alarm-rule.general' | translate }}
+
+ + alarm-rule.value-argument + + @for (argument of argumentsList; track argument) { + {{ argument }} + } + + @if (filterFormGroup.get('argument').touched && filterFormGroup.get('argument').hasError('required')) { + + warning + + } + + + filter.value-type.value-type + + + + {{ entityKeyValueTypes.get(filterFormGroup.get('valueType').value)?.name | translate }} + + + + {{ entityKeyValueTypes.get(entityKeyValueTypeEnum[valueType]).name | translate }} + + + + {{ 'filter.value-type-required' | translate }} + + +
+
+ +
+
+
+ {{ 'alarm-rule.filters' | translate }} +
+ + {{ complexOperationTranslationMap.get(ComplexOperation.AND) | translate }} + {{ complexOperationTranslationMap.get(ComplexOperation.OR) | translate }} + +
+ + +
+
+
+
+ + @if (!readonly) { + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.scss new file mode 100644 index 0000000000..4330e0090f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.scss @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .mat-mdc-form-field.tb-value-type { + mat-select-trigger { + .mat-icon { + vertical-align: middle; + margin-right: 8px; + svg { + vertical-align: initial; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.ts new file mode 100644 index 0000000000..ba3adc1695 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-dialog.component.ts @@ -0,0 +1,137 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, 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 '@app/shared/components/dialog.component'; + import { ComplexOperation, EntityKeyValueType, entityKeyValueTypesMap } from '@shared/models/query/query.models'; + import { DialogService } from '@core/services/dialog.service'; + import { TranslateService } from '@ngx-translate/core'; + import { + AlarmRuleFilter, + AlarmRuleFilterPredicate, + filterOperationTranslationMap, + isPredicateArgumentsValid + } from "@shared/models/alarm-rule.models"; + import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; + import { FormControlsFrom } from "@shared/models/tenant.model"; + import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; + + export interface AlarmRuleFilterDialogData { + filter: AlarmRuleFilter; + isAdd: boolean; + arguments: Record; + usedArguments: Array; + readonly: boolean; +} + +@Component({ + selector: 'tb-alarm-rule-filter-dialog', + templateUrl: './alarm-rule-filter-dialog.component.html', + providers: [], + styleUrls: ['./alarm-rule-filter-dialog.component.scss'] +}) +export class AlarmRuleFilterDialogComponent extends DialogComponent { + + filterFormGroup: FormGroup>; + + entityKeyValueTypesKeys = Object.keys(EntityKeyValueType); + + entityKeyValueTypeEnum = EntityKeyValueType; + + entityKeyValueTypes = entityKeyValueTypesMap; + + complexOperationTranslationMap = filterOperationTranslationMap; + + predicatesValid: boolean = false; + + ComplexOperation = ComplexOperation; + + arguments = this.data.arguments; + argumentsList: Array; + + readonly = this.data.readonly; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: AlarmRuleFilterDialogData, + public dialogRef: MatDialogRef, + private dialogs: DialogService, + private translate: TranslateService, + private fb: FormBuilder, + private destroyRef: DestroyRef) { + super(store, router, dialogRef); + + this.argumentsList = this.arguments ? Object.keys(this.arguments) : []; + + this.filterFormGroup = this.fb.group( + { + argument: [this.argumentsList.includes(this.data.filter.argument) ? this.data.filter.argument : '' , [Validators.required]], + valueType: [this.data.filter.valueType ?? EntityKeyValueType.STRING, [Validators.required]], + predicates: [this.data.filter.predicates, [Validators.required]], + operation: [this.data.filter.operation ?? ComplexOperation.AND] + } + ); + + this.predicatesValid = isPredicateArgumentsValid(this.data.filter.predicates, this.arguments); + this.filterFormGroup.get('predicates').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(predicates => { + this.predicatesValid = isPredicateArgumentsValid(predicates, this.arguments); + }); + + this.filterFormGroup.get('valueType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((valueType: EntityKeyValueType) => { + const prevValueType: EntityKeyValueType = this.filterFormGroup.value.valueType; + const predicates: AlarmRuleFilterPredicate[] = this.filterFormGroup.get('predicates').value; + if (prevValueType && prevValueType !== valueType) { + if (predicates && predicates.length) { + this.dialogs.confirm(this.translate.instant('filter.key-value-type-change-title'), + this.translate.instant('filter.key-value-type-change-message')).subscribe( + (result) => { + if (result) { + this.filterFormGroup.get('predicates').setValue([]); + } else { + this.filterFormGroup.get('valueType').setValue(prevValueType, {emitEvent: false}); + } + } + ); + } + } + }); + + if (this.readonly) { + this.filterFormGroup.disable({emitEvent: false}); + } + } + + argumentInUse(argument: string): boolean { + return this.data.usedArguments.includes(argument); + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + this.dialogRef.close(this.filterFormGroup.value as AlarmRuleFilter); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.html new file mode 100644 index 0000000000..950d19abd0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.html @@ -0,0 +1,85 @@ + +@if (filtersFormArray.length) { +
+
+ +
+ + +   +
+
+ +
+ @for (filterControl of filtersFormArray.controls; track filterControl; let index = $index) { +
+
+ @if ($index) { +
+ {{ complexOperationTranslationMap.get(operation) | translate }} +
+ } +
+
+
+
{{ filterControl.value?.argument }}
+
{{ entityKeyValueTypes.get(entityKeyValueTypeEnum[filterControl.value?.valueType]).name | translate }}
+ + +
+
+ @if (index) { + + } +
+ } +
+
+} @else { + alarm-rule.no-filter +} + + diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.scss new file mode 100644 index 0000000000..fea71605c8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.scss @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .filter-title { + padding: 12px 0; + font-size: 14px; + font-weight: 500; + } + .no-data-found { + height: 50px; + font-size: 16px; + } + .filter-list { + overflow: auto; + max-height: 300px; + + &-divider { + border-top: 1px solid rgba(0, 0, 0, 0.12); + } + + .filters-text { + font-size: 14px; + } + } + .filters-operation { + display: flex; + justify-content: center; + margin-top: -14px; + &-container { + background-color: white; + } + &-label { + font-size: 15px; + font-weight: 400; + color: $tb-primary-color; + padding: 0 8px; + border-radius: 4px; + border: 1px solid rgba($tb-primary-color, 0.32); + background-color: rgba($tb-primary-color, 0.04); + } + } + + .tb-budge-button { + --mat-badge-legacy-small-size-container-size: 8px; + --mat-badge-small-size-container-overlap-offset: -5px; + --mat-badge-small-size-text-size: 0; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.ts new file mode 100644 index 0000000000..5bb05ab1eb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.ts @@ -0,0 +1,190 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { Observable } from 'rxjs'; +import { + ComplexOperation, + complexOperationTranslationMap, + EntityKeyValueType, + entityKeyValueTypesMap +} from '@shared/models/query/query.models'; +import { MatDialog } from '@angular/material/dialog'; +import { deepClone } from '@core/utils'; +import { + AlarmRuleFilterDialogComponent, + AlarmRuleFilterDialogData +} from "@home/components/alarm-rules/filter/alarm-rule-filter-dialog.component"; +import { AlarmRuleFilter, areFilterAndPredicateArgumentsValid } from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; + +@Component({ + selector: 'tb-alarm-rule-filter-list', + templateUrl: './alarm-rule-filter-list.component.html', + styleUrls: ['./alarm-rule-filter-list.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmRuleFilterListComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AlarmRuleFilterListComponent), + multi: true + } + ] +}) +export class AlarmRuleFilterListComponent implements ControlValueAccessor, Validator { + + @Input() + arguments: Record; + + @Input() + operation: ComplexOperation = ComplexOperation.AND; + + @Input() + readonly = false; + + filterListFormGroup = this.fb.group({ + filters: this.fb.array([]) + }); + + disabled = false; + + areFilterAndPredicateArgumentsValid = areFilterAndPredicateArgumentsValid; + + complexOperationTranslationMap = complexOperationTranslationMap; + entityKeyValueTypes = entityKeyValueTypesMap; + entityKeyValueTypeEnum = EntityKeyValueType; + + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder, + private dialog: MatDialog, + private destroyRef: DestroyRef) { + this.filterListFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => this.updateModel()); + } + + + get filtersFormArray(): FormArray { + return this.filterListFormGroup.get('filters') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + validate(): ValidationErrors | null { + return this.filterListFormGroup.valid && this.filterListFormGroup.get('filters').value?.length ? null : { + filterList: {valid: false} + }; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.filterListFormGroup.disable({emitEvent: false}); + } else { + this.filterListFormGroup.enable({emitEvent: false}); + } + } + + writeValue(filters: Array): void { + const keyFilterControls: Array = []; + if (filters) { + for (const filter of filters) { + keyFilterControls.push(this.fb.control(filter, [Validators.required])); + } + } + this.filtersFormArray.clear(); + keyFilterControls.forEach(c => this.filtersFormArray.push(c)); + } + + public removeFilter(index: number) { + (this.filterListFormGroup.get('filters') as FormArray).removeAt(index); + } + + public addFilter() { + const filtersFormArray = this.filterListFormGroup.get('filters') as FormArray; + this.openFilterDialog(null).subscribe(result => { + if (result) { + filtersFormArray.push(this.fb.control(result, [Validators.required])); + } + }); + } + + public editFilter(index: number, readonly = false) { + const filter: AlarmRuleFilter = + (this.filterListFormGroup.get('filters') as FormArray).at(index).value; + this.openFilterDialog(filter, readonly).subscribe(result => { + if (result) { + (this.filterListFormGroup.get('filters') as FormArray).at(index).patchValue(result); + } + }); + } + + private openFilterDialog(filter?: AlarmRuleFilter, readonly = false): Observable { + const isAdd = !filter; + if (isAdd) { + filter = { + argument: null, + valueType: EntityKeyValueType.STRING, + operation: ComplexOperation.AND, + predicates: [] + }; + } + return this.dialog.open(AlarmRuleFilterDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + filter: filter ? deepClone(filter) : null, + isAdd, + arguments: this.arguments, + usedArguments: this.getUsedArguments, + readonly + } + }).afterClosed(); + } + + get getUsedArguments(): Array { + const filters = this.filterListFormGroup.get('filters').value as Array; + return filters.length ? filters.map((filter: AlarmRuleFilter) => filter.argument) : []; + } + + private updateModel() { + const filters = this.filterListFormGroup.value.filters as Array; + this.propagateChange(filters); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.html new file mode 100644 index 0000000000..1dd6f5f453 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.html @@ -0,0 +1,80 @@ + +
+ @if (predicatesFormArray.length) { +
+
+
+
+ + + +
+   +
+
+ +
+ @for (predicateControl of predicatesFormArray.controls; track predicateControl; let index = $index) { +
+ @if (index) { +
+ {{ complexOperationTranslations.get(operation) | translate }} +
+ } +
+
+ + + +
+
+
+ } +
+
+ } @else { + alarm-rule.no-filter + } +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.scss new file mode 100644 index 0000000000..e5e6dbeba4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.scss @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .filter-title { + padding: 12px 8px; + font-size: 14px; + font-weight: 500; + } + .no-data-found { + height: 50px; + font-size: 16px; + } + + .key-filter-list-divider { + border-top: 1px solid rgba(0, 0, 0, 0.12); + } + .filters-operation { + display: flex; + justify-content: center; + margin-top: -14px; + &-container { + position: absolute; + top: -12px; + left: 10px; + background-color: white; + } + &-label { + font-size: 15px; + font-weight: 400; + color: $tb-primary-color; + padding: 0 8px; + border-radius: 4px; + border: 1px solid rgba($tb-primary-color, 0.32); + background-color: rgba($tb-primary-color, 0.04); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.ts new file mode 100644 index 0000000000..7bf08c9ac9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.ts @@ -0,0 +1,213 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { Observable, of } from 'rxjs'; +import { + ComplexOperation, + complexOperationTranslationMap, + EntityKeyValueType, + entityKeyValueTypeToFilterPredicateType +} from '@shared/models/query/query.models'; +import { MatDialog } from '@angular/material/dialog'; +import { map } from 'rxjs/operators'; +import { + AlarmRuleComplexFilterPredicateDialogComponent, + AlarmRuleComplexFilterPredicateDialogData +} from "@home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component"; +import { + AlarmRuleBooleanOperation, + AlarmRuleFilterPredicate, + AlarmRuleFilterPredicateType, + AlarmRuleNumericOperation, + AlarmRulePredicateInfo, + AlarmRuleStringOperation, + ComplexAlarmRuleFilterPredicate +} from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; + +@Component({ + selector: 'tb-alarm-rule-filter-predicate-list', + templateUrl: './alarm-rule-filter-predicate-list.component.html', + styleUrls: ['./alarm-rule-filter-predicate-list.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmRuleFilterPredicateListComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AlarmRuleFilterPredicateListComponent), + multi: true + } + ] +}) +export class AlarmRuleFilterPredicateListComponent implements ControlValueAccessor, Validator { + + @Input() disabled: boolean; + + @Input() valueType: EntityKeyValueType; + + @Input() operation: ComplexOperation = ComplexOperation.AND; + + @Input() arguments: Record; + + @Input() argumentInUse: string; + + @Input() readonly: boolean; + + filterListFormGroup = this.fb.group({ + predicates: this.fb.array([]) + }); + + valueTypeEnum = EntityKeyValueType; + + complexOperationTranslations = complexOperationTranslationMap; + + private propagateChange= (v: any) => { }; + + constructor(private fb: FormBuilder, + private dialog: MatDialog, + private destroyRef: DestroyRef) { + this.filterListFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => this.updateModel()); + } + + get predicatesFormArray(): FormArray { + return this.filterListFormGroup.get('predicates') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.filterListFormGroup.disable({emitEvent: false}); + } else { + this.filterListFormGroup.enable({emitEvent: false}); + } + } + + validate(control: AbstractControl): ValidationErrors | null { + return this.filterListFormGroup.valid ? null : { + filterList: {valid: false} + }; + } + + writeValue(predicates: Array): void { + const predicateControls: Array = []; + if (predicates) { + for (const predicate of predicates) { + predicateControls.push(this.fb.control(predicate, [Validators.required])); + } + } + this.predicatesFormArray.clear(); + predicateControls.forEach(predicate => this.predicatesFormArray.push(predicate)); + } + + public removePredicate(index: number) { + this.predicatesFormArray.removeAt(index); + } + + public addPredicate(complex: boolean) { + const predicatesFormArray = this.filterListFormGroup.get('predicates') as FormArray; + const predicate = this.createDefaultFilterPredicate(this.valueType, complex); + let observable: Observable; + if (complex) { + observable = this.openComplexFilterDialog(predicate as ComplexAlarmRuleFilterPredicate); + } else { + observable = of(predicate); + } + observable.subscribe((result) => { + if (result) { + predicatesFormArray.push(this.fb.control(result, [Validators.required])); + } + }); + } + + private createDefaultFilterPredicate(valueType: EntityKeyValueType, complex: boolean): AlarmRuleFilterPredicate { + const predicate = { + type: complex ? AlarmRuleFilterPredicateType.COMPLEX : entityKeyValueTypeToFilterPredicateType(valueType) + } as AlarmRuleFilterPredicate; + switch (predicate.type) { + case AlarmRuleFilterPredicateType.STRING: + predicate.operation = AlarmRuleStringOperation.STARTS_WITH; + predicate.value = { + staticValue: '' + }; + predicate.ignoreCase = false; + break; + case AlarmRuleFilterPredicateType.NUMERIC: + predicate.operation = AlarmRuleNumericOperation.EQUAL; + predicate.value = { + staticValue: valueType === EntityKeyValueType.DATE_TIME ? Date.now() : 0 + }; + break; + case AlarmRuleFilterPredicateType.BOOLEAN: + predicate.operation = AlarmRuleBooleanOperation.EQUAL; + predicate.value = { + staticValue: false + }; + break; + case AlarmRuleFilterPredicateType.COMPLEX: + predicate.operation = ComplexOperation.AND; + predicate.predicates = []; + break; + } + return predicate; + } + + private openComplexFilterDialog(predicate: ComplexAlarmRuleFilterPredicate): Observable { + return this.dialog.open(AlarmRuleComplexFilterPredicateDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + complexPredicate: predicate as ComplexAlarmRuleFilterPredicate, + valueType: this.valueType, + isAdd: true, + arguments: this.arguments, + argumentInUse: this.argumentInUse, + readonly: this.readonly + } + }).afterClosed().pipe( + map(result => result) + ); + } + + private updateModel() { + this.propagateChange(this.filterListFormGroup.get('predicates').value); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component.html new file mode 100644 index 0000000000..09b468aeb9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component.html @@ -0,0 +1,77 @@ + +
+
+ + + {{'alarm-rule.static' | translate}} + {{'alarm-rule.dynamic' | translate}} + + + + @if (!dynamicModeControl.value) { + + + @if (filterPredicateValueNoDataFormGroup.get('duration.staticValue').touched && filterPredicateValueNoDataFormGroup.get('duration.staticValue').errors) { + + warning + + } + + } @else { + + + @for (argument of argumentsList; track argument) { + {{ argument }} + } + + @if (filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').touched && filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').errors) { + + warning + + } + @if (filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').hasError('argumentInUse')) { + + warning + + } + + } + + + + + {{ timeUnitsTranslationMap.get(timeUnit) | translate }} + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component.ts new file mode 100644 index 0000000000..7f9608bd5c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component.ts @@ -0,0 +1,166 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { TimeUnit, timeUnitTranslations } from "@home/components/rule-node/rule-node-config.models"; +import { AlarmRuleFilterPredicateType, NoDataAlarmRuleFilterPredicate } from "@shared/models/alarm-rule.models"; +import { isDefinedAndNotNull } from "@core/utils"; + +@Component({ + selector: 'tb-alarm-rule-filter-predicate-no-data-value', + templateUrl: './alarm-rule-filter-predicate-no-data-value.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmRuleFilterPredicateNoDataValueComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AlarmRuleFilterPredicateNoDataValueComponent), + multi: true + } + ] +}) +export class AlarmRuleFilterPredicateNoDataValueComponent implements ControlValueAccessor, Validator, OnInit, OnChanges { + + @Input() + arguments: Record; + + @Input() + valueType: AlarmRuleFilterPredicateType; + + @Input() + argumentInUse: string; + + valueTypeEnum = AlarmRuleFilterPredicateType; + + filterPredicateValueNoDataFormGroup = this.fb.group({ + type: ['NO_DATA'], + unit: [TimeUnit.MINUTES, Validators.required], + duration: this.fb.group({ + staticValue: [null as null | number, [Validators.required, Validators.min(1)]], + dynamicValueArgument: ['', Validators.required] + }) + }); + + timeUnits = [TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS]; + timeUnitsTranslationMap = timeUnitTranslations; + + + dynamicModeControl = this.fb.control(false); + + argumentsList: Array; + + private propagateChange= (v: any) => { }; + + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + this.argumentsList = this.arguments ? Object.keys(this.arguments): []; + this.filterPredicateValueNoDataFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + this.dynamicModeControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => this.updateValueModeValidators(value)); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.argumentInUse) { + const argumentInUseChanges = changes.argumentInUse; + if (!argumentInUseChanges.firstChange && argumentInUseChanges.currentValue !== argumentInUseChanges.previousValue) { + if (this.dynamicModeControl.value) { + if (this.argumentInUse === this.filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').value) { + this.filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').setErrors({argumentInUse: true}); + this.filterPredicateValueNoDataFormGroup.updateValueAndValidity(); + } + } + } + } + } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.filterPredicateValueNoDataFormGroup.disable({emitEvent: false}); + this.dynamicModeControl.disable({emitEvent: false}); + } else { + this.filterPredicateValueNoDataFormGroup.enable({emitEvent: false}); + this.dynamicModeControl.enable({emitEvent: false}); + this.updateValueModeValidators(this.dynamicModeControl.value); + } + } + + private updateValueModeValidators(isDynamicMode: boolean): void { + if (isDynamicMode) { + this.filterPredicateValueNoDataFormGroup.get('duration.staticValue').disable({emitEvent: false}); + this.filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').enable(); + setTimeout(()=> { + if (this.filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').value && this.argumentInUse === this.filterPredicateValueNoDataFormGroup.get('dynamicValueArgument').value) { + this.filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').setErrors({argumentInUse: true}); + this.filterPredicateValueNoDataFormGroup.updateValueAndValidity(); + } + }, 0); + } else { + this.filterPredicateValueNoDataFormGroup.get('duration.dynamicValueArgument').disable({emitEvent: false}); + this.filterPredicateValueNoDataFormGroup.get('duration.staticValue').enable(); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + validate(): ValidationErrors | null { + return this.filterPredicateValueNoDataFormGroup.valid ? null : { + filterPredicateValue: {valid: false} + }; + } + + writeValue(predicateValue: NoDataAlarmRuleFilterPredicate): void { + if (isDefinedAndNotNull(predicateValue?.duration?.dynamicValueArgument)) { + const availableArgument = this.argumentsList.filter(arg => arg !== this.argumentInUse); + if (!availableArgument.includes(predicateValue.duration.dynamicValueArgument)) { + predicateValue.duration.dynamicValueArgument = ''; + } + this.dynamicModeControl.patchValue(true, {emitEvent: false}); + } + this.filterPredicateValueNoDataFormGroup.patchValue(predicateValue, {emitEvent: false}); + } + + private updateModel() { + this.propagateChange(this.filterPredicateValueNoDataFormGroup.value); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component.html new file mode 100644 index 0000000000..9db8a856bf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component.html @@ -0,0 +1,82 @@ + +
+
+ + + {{'alarm-rule.static' | translate}} + {{'alarm-rule.dynamic' | translate}} + + + @if (!dynamicModeControl.value) { + @switch (valueType) { + @case (valueTypeEnum.STRING) { + + + + } + @case (valueTypeEnum.NUMERIC) { + + + + } + @case (valueTypeEnum.BOOLEAN) { + + {{ (filterPredicateValueFormGroup.get('staticValue').value ? 'value.true' : 'value.false') | translate }} + + } + @case (valueTypeEnum.DATE_TIME) { + + } + } + } @else { + + + @for (argument of argumentsList; track argument) { + {{ argument }} + } + + @if (filterPredicateValueFormGroup.get('dynamicValueArgument').touched && filterPredicateValueFormGroup.get('dynamicValueArgument').hasError('required')) { + + warning + + } + @if (filterPredicateValueFormGroup.get('dynamicValueArgument').hasError('argumentInUse')) { + + warning + + } + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component.ts new file mode 100644 index 0000000000..bd6bacb762 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-value.component.ts @@ -0,0 +1,182 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { EntityKeyValueType } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { AlarmRuleValue } from "@shared/models/alarm-rule.models"; +import { FormControlsFrom } from "@shared/models/tenant.model"; +import { isDefinedAndNotNull } from "@core/utils"; + +@Component({ + selector: 'tb-alarm-rule-filter-predicate-value', + templateUrl: './alarm-rule-filter-predicate-value.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmRuleFilterPredicateValueComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AlarmRuleFilterPredicateValueComponent), + multi: true + } + ] +}) +export class AlarmRuleFilterPredicateValueComponent implements ControlValueAccessor, Validator, OnInit, OnChanges { + + @Input() + arguments: Record; + + @Input() + valueType: EntityKeyValueType; + + @Input() + argumentInUse: string; + + valueTypeEnum = EntityKeyValueType; + + filterPredicateValueFormGroup: FormGroup>>; + + dynamicModeControl = this.fb.control(false); + + argumentsList: Array; + + private propagateChange= (v: any) => { }; + + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + this.argumentsList = this.arguments ? Object.keys(this.arguments): []; + let defaultValue: string | number | boolean; + let defaultValueValidators: ValidatorFn[]; + switch (this.valueType) { + case EntityKeyValueType.STRING: + defaultValue = ''; + defaultValueValidators = []; + break; + case EntityKeyValueType.NUMERIC: + defaultValue = 0; + defaultValueValidators = [Validators.required]; + break; + case EntityKeyValueType.BOOLEAN: + defaultValue = false; + defaultValueValidators = []; + break; + case EntityKeyValueType.DATE_TIME: + defaultValue = Date.now(); + defaultValueValidators = [Validators.required]; + break; + } + this.filterPredicateValueFormGroup = this.fb.group({ + staticValue: [defaultValue, defaultValueValidators], + dynamicValueArgument: ['', Validators.required] + }); + this.filterPredicateValueFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + this.dynamicModeControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => this.updateValueModeValidators(value)); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.argumentInUse) { + const argumentInUseChanges = changes.argumentInUse; + if (!argumentInUseChanges.firstChange && argumentInUseChanges.currentValue !== argumentInUseChanges.previousValue) { + if (this.dynamicModeControl.value) { + if (this.argumentInUse === this.filterPredicateValueFormGroup.get('dynamicValueArgument').value) { + this.filterPredicateValueFormGroup.get('dynamicValueArgument').setErrors({argumentInUse: true}); + this.filterPredicateValueFormGroup.updateValueAndValidity(); + } + } + } + } + } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.filterPredicateValueFormGroup.disable({emitEvent: false}); + this.dynamicModeControl.disable({emitEvent: false}); + } else { + this.filterPredicateValueFormGroup.enable({emitEvent: false}); + this.dynamicModeControl.enable({emitEvent: false}); + this.updateValueModeValidators(this.dynamicModeControl.value); + } + } + + private updateValueModeValidators(isDynamicMode: boolean): void { + if (isDynamicMode) { + this.filterPredicateValueFormGroup.get('staticValue').disable({emitEvent: false}); + this.filterPredicateValueFormGroup.get('dynamicValueArgument').enable(); + setTimeout(()=> { + if (this.filterPredicateValueFormGroup.get('dynamicValueArgument').value && this.argumentInUse === this.filterPredicateValueFormGroup.get('dynamicValueArgument').value) { + this.filterPredicateValueFormGroup.get('dynamicValueArgument').setErrors({argumentInUse: true}); + this.filterPredicateValueFormGroup.updateValueAndValidity(); + } + }, 0); + } else { + this.filterPredicateValueFormGroup.get('dynamicValueArgument').disable({emitEvent: false}); + this.filterPredicateValueFormGroup.get('staticValue').enable(); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + validate(): ValidationErrors | null { + return this.filterPredicateValueFormGroup.valid ? null : { + filterPredicateValue: {valid: false} + }; + } + + writeValue(predicateValue: AlarmRuleValue): void { + if (isDefinedAndNotNull(predicateValue?.dynamicValueArgument)) { + const availableArgument = this.argumentsList.filter(arg => arg !== this.argumentInUse); + if (!availableArgument.includes(predicateValue.dynamicValueArgument)) { + predicateValue.dynamicValueArgument = ''; + } + this.dynamicModeControl.patchValue(true, {emitEvent: false}); + } + this.filterPredicateValueFormGroup.patchValue(predicateValue, {emitEvent: false}); + } + + private updateModel() { + this.propagateChange(this.filterPredicateValueFormGroup.value); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate.component.html new file mode 100644 index 0000000000..9d3ad7286a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate.component.html @@ -0,0 +1,86 @@ + +
+
+ @switch (type) { + @case (filterPredicateType.STRING) { +
+ + + + {{stringOperationTranslationMap.get(stringOperation[operation]) | translate}} + + + + @if (filterPredicateFormGroup.get('operation').value !== stringOperation.NO_DATA) { + + {{ 'alarm-rule.ignore-case' | translate }} + + } +
+ } + @case (filterPredicateType.NUMERIC) { +
+ + + + {{numericOperationTranslations.get(numericOperationEnum[operation]) | translate}} + + + +
+ } + @case (filterPredicateType.BOOLEAN) { +
+ + + + {{booleanOperationTranslations.get(booleanOperationEnum[operation]) | translate}} + + + +
+ } + @case (filterPredicateType.COMPLEX) { +
+ +
+ } + } + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate.component.ts new file mode 100644 index 0000000000..77cc962747 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate.component.ts @@ -0,0 +1,216 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { booleanAttribute, Component, DestroyRef, forwardRef, Input } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator +} from '@angular/forms'; +import { EntityKeyValueType } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + AlarmRuleBooleanOperation, + alarmRuleBooleanOperationTranslationMap, + AlarmRuleFilterPredicate, + AlarmRuleFilterPredicateType, + AlarmRuleNumericOperation, + alarmRuleNumericOperationTranslationMap, + AlarmRuleStringOperation, + alarmRuleStringOperationTranslationMap, + checkPredicates, + ComplexAlarmRuleFilterPredicate +} from "@shared/models/alarm-rule.models"; +import { MatDialog } from "@angular/material/dialog"; +import { + AlarmRuleComplexFilterPredicateDialogComponent, + AlarmRuleComplexFilterPredicateDialogData +} from "@home/components/alarm-rules/filter/alarm-rule-complex-filter-predicate-dialog.component"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; + +@Component({ + selector: 'tb-alarm-rule-filter-predicate', + templateUrl: './alarm-rule-filter-predicate.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmRuleFilterPredicateComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AlarmRuleFilterPredicateComponent), + multi: true + } + ] +}) +export class AlarmRuleFilterPredicateComponent implements ControlValueAccessor, Validator { + + @Input({ transform: booleanAttribute }) + disabled: boolean; + + @Input() + valueType: EntityKeyValueType; + + @Input() + arguments: Record; + + @Input() + argumentInUse: string; + + filterPredicateFormGroup = this.fb.group({ + operation: [], + ignoreCase: false, + predicates: [], + value: [], + duration: [] + }); + + type: AlarmRuleFilterPredicateType; + + filterPredicateType = AlarmRuleFilterPredicateType; + + stringOperations = Object.keys(AlarmRuleStringOperation); + stringOperation = AlarmRuleStringOperation; + stringOperationTranslationMap = alarmRuleStringOperationTranslationMap; + + numericOperations = Object.keys(AlarmRuleNumericOperation); + numericOperationEnum = AlarmRuleNumericOperation; + numericOperationTranslations = alarmRuleNumericOperationTranslationMap; + + booleanOperations = Object.keys(AlarmRuleBooleanOperation); + booleanOperationEnum = AlarmRuleBooleanOperation; + booleanOperationTranslations = alarmRuleBooleanOperationTranslationMap; + + predicateValid: boolean = false; + + private propagateChange= (v: any) => { }; + + constructor(private fb: FormBuilder, + private dialog: MatDialog, + private destroyRef: DestroyRef) { + this.filterPredicateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + + this.filterPredicateFormGroup.get('operation').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => { + if (value === 'NO_DATA') { + this.filterPredicateFormGroup.get('duration').enable({emitEvent: false}); + this.filterPredicateFormGroup.get('value').disable({emitEvent: false}); + } else { + this.filterPredicateFormGroup.get('duration').disable({emitEvent: false}); + this.filterPredicateFormGroup.get('value').enable({emitEvent: false}); + } + }) + + this.filterPredicateFormGroup.get('predicates').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(predicates => { + this.predicateValid = this.isPredicateArgumentsValid(predicates); + }) + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + validate(): ValidationErrors | null { + return this.filterPredicateFormGroup.valid ? null : { + filterPredicate: {valid: false} + }; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.filterPredicateFormGroup.disable({emitEvent: false}); + } else { + this.filterPredicateFormGroup.enable({emitEvent: false}); + if (this.filterPredicateFormGroup.get('operation').value === 'NO_DATA') { + this.filterPredicateFormGroup.get('duration').enable({emitEvent: false}); + this.filterPredicateFormGroup.get('value').disable({emitEvent: false}); + } else { + this.filterPredicateFormGroup.get('duration').disable({emitEvent: false}); + this.filterPredicateFormGroup.get('value').enable({emitEvent: false}); + } + } + } + + private isPredicateArgumentsValid(predicates: AlarmRuleFilterPredicate[]): boolean { + const validSet = new Set(Object.keys(this.arguments)); + if (Array.isArray(predicates)) { + if (!checkPredicates(predicates, validSet)) { + return false; + } + } + return true; + } + + writeValue(predicate: AlarmRuleFilterPredicate): void { + this.type = predicate.type; + if ((predicate as ComplexAlarmRuleFilterPredicate)?.predicates) { + this.predicateValid = this.isPredicateArgumentsValid((predicate as ComplexAlarmRuleFilterPredicate)?.predicates); + } + if (predicate.type === AlarmRuleFilterPredicateType.NO_DATA) { + this.type = AlarmRuleFilterPredicateType[this.valueType]; + this.filterPredicateFormGroup.patchValue({operation: 'NO_DATA', duration: predicate}, {emitEvent: false}); + } else { + this.filterPredicateFormGroup.patchValue(predicate, {emitEvent: false}); + } + } + + private updateModel() { + const predicate = this.filterPredicateFormGroup.value; + if (predicate.operation === 'NO_DATA') { + this.propagateChange(predicate.duration); + } else { + this.propagateChange({type: this.type, ...predicate}); + } + } + + public openComplexFilterDialog() { + this.dialog.open(AlarmRuleComplexFilterPredicateDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + complexPredicate: this.filterPredicateFormGroup.value as ComplexAlarmRuleFilterPredicate, + valueType: this.valueType, + isAdd: false, + arguments: this.arguments, + argumentInUse: this.argumentInUse, + readonly: this.disabled + } + }).afterClosed().subscribe( + (result) => { + if (result) { + this.filterPredicateFormGroup.patchValue(result); + } + } + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.html new file mode 100644 index 0000000000..9e20a079b4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.html @@ -0,0 +1,25 @@ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.scss b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.scss new file mode 100644 index 0000000000..5f621d7bb1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.scss @@ -0,0 +1,91 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + text-overflow: ellipsis; + overflow: hidden; + .tb-filter-text { + overflow-y: auto; + text-align: start; + &.required { + color: #f44336; + padding: 0; + } + &.disabled { + color: rgba(0,0,0,0.38); + } + &.nowrap { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + &.wrap { + white-space: pre-wrap !important; + } + } +} + +:host ::ng-deep { + .tb-filter-text { + line-height: 1.8em; + span { + display: inline-block; + vertical-align: middle; + line-height: 1.4em; + } + .tb-filter-predicate { + padding-right: 4px; + padding-left: 4px; + } + .tb-filter-entity-key, .tb-filter-value, .tb-filter-dynamic-source { + font-weight: bold; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + padding-left: 4px; + padding-right: 4px; + } + .tb-filter-entity-key, .tb-filter-value { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 150px; + } + .tb-filter-dynamic-source { + } + .tb-filter-entity-key { + color: #305680; + } + .tb-filter-value { + color: #ff5722; + } + .tb-filter-simple-operation { + font-size: 0.9em; + } + .tb-filter-complex-operation { + font-weight: 400; + font-style: italic; + } + .tb-filter-dynamic-value { + .tb-filter-dynamic-source, .tb-filter-value { + color: #0c959c; + } + } + .tb-filter-bracket { + .tb-left-bracket, .tb-right-bracket { + font-size: 1.2em; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts new file mode 100644 index 0000000000..7bb8bada5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts @@ -0,0 +1,204 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + ComplexOperation, + complexOperationTranslationMap, + EntityKeyValueType +} from '@shared/models/query/query.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { + alarmRuleBooleanOperationTranslationMap, + AlarmRuleExpression, + AlarmRuleExpressionType, + AlarmRuleFilter, + AlarmRuleFilterPredicate, + AlarmRuleFilterPredicateType, + alarmRuleNumericOperationTranslationMap, + AlarmRuleStringOperation, + alarmRuleStringOperationTranslationMap, + ComplexAlarmRuleFilterPredicate +} from "@shared/models/alarm-rule.models"; +import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +import { coerceBoolean } from "@shared/decorators/coercion"; +import { timeUnitTranslationMap } from "@shared/models/time/time.models"; + +@Component({ + selector: 'tb-alarm-rule-filter-text', + templateUrl: './alarm-rule-filter-text.component.html', + styleUrls: ['./alarm-rule-filter-text.component.scss'], + providers: [] +}) +export class AlarmRuleFilterTextComponent { + + @Input() + @coerceBoolean() + required = false; + + @Input() + noFilterText = this.translate.instant('filter.no-filter-text'); + + @Input() + addFilterPrompt = this.translate.instant('filter.add-filter-prompt'); + + @Input() + @coerceBoolean() + nowrap = false; + + @Input() + arguments: Record; + + @Input() + @coerceBoolean() + disabled = false; + + private alarmRuleExpressionValue: AlarmRuleExpression; + get alarmRuleExpression(): AlarmRuleExpression { + return this.alarmRuleExpressionValue; + } + + @Input() + set alarmRuleExpression(value: AlarmRuleExpression) { + if (value !== this.alarmRuleExpressionValue) { + this.alarmRuleExpressionValue = value; + this.updateFilterText(value); + } + }; + + private specTextValue: string; + get specText(): string { + return this.specTextValue; + } + @Input() + set specText(value: string) { + if (value !== this.specTextValue) { + this.specTextValue = value; + this.updateFilterText(this.alarmRuleExpression); + } + } + + isRequired = false; + + public filterText: string; + + constructor(private translate: TranslateService, + private datePipe: DatePipe) { + } + + private updateFilterText(value: AlarmRuleExpression) { + this.isRequired = false; + if (value && (value.expression || value.filters?.length)) { + if (value.type === AlarmRuleExpressionType.SIMPLE) { + this.filterText = this.keyFiltersToText(this.translate, this.datePipe, value.filters, value.operation); + } else { + this.filterText = 'function expression(ctx, ' + (this.arguments ? Object.keys(this.arguments).join(', ') : '' ) + ')'; + } + if (this.specText?.length) { + this.filterText = this.specText + ': ' + this.filterText; + } + } else { + if (this.required) { + this.filterText = this.addFilterPrompt; + this.isRequired = true; + } else { + this.filterText = this.noFilterText; + } + } + } + + private keyFiltersToText(translate: TranslateService, datePipe: DatePipe, keyFilters: Array, operation: ComplexOperation): string { + const filtersText = keyFilters.map(keyFilter => + this.filterPredicateToText(translate, datePipe, keyFilter, keyFilter.predicates)); + let result: string; + if (filtersText.length > 1) { + const operationText = translate.instant(complexOperationTranslationMap.get(operation)); + result = filtersText.join(' ' + operationText + ' '); + } else { + result = filtersText[0]; + } + return result; + } + + private filterPredicateToText(translate: TranslateService, + datePipe: DatePipe, + keyFilter: AlarmRuleFilter, + keyFilterPredicates: AlarmRuleFilterPredicate[], + complexOperation?: ComplexOperation): string { + const key = keyFilter.argument; + const filterOperation: ComplexOperation = complexOperation ? complexOperation : (keyFilter.operation ?? ComplexOperation.AND); + + const predicates = keyFilterPredicates.map((keyFilterPredicate: AlarmRuleFilterPredicate) => { + if (keyFilterPredicate.type === AlarmRuleFilterPredicateType.COMPLEX) { + const complexPredicate = keyFilterPredicate as ComplexAlarmRuleFilterPredicate; + const complexOperation = complexPredicate.operation ?? ComplexOperation.AND; + return this.filterPredicateToText(translate, datePipe, keyFilter, complexPredicate.predicates, complexOperation); + } else { + let operation: string; + let value: string; + const val = keyFilterPredicate.type === AlarmRuleFilterPredicateType.NO_DATA ? keyFilterPredicate.duration : keyFilterPredicate.value; + const dynamicValue = val?.dynamicValueArgument?.length; + if (dynamicValue) { + value = '' + val?.dynamicValueArgument + ''; + } + switch (keyFilterPredicate.type) { + case AlarmRuleFilterPredicateType.STRING: + operation = translate.instant(alarmRuleStringOperationTranslationMap.get(keyFilterPredicate.operation)); + if (keyFilterPredicate.ignoreCase) { + operation += ' ' + translate.instant('filter.ignore-case'); + } + if (!dynamicValue) { + value = `'${keyFilterPredicate.value.staticValue}'`; + } + break; + case AlarmRuleFilterPredicateType.NUMERIC: + operation = translate.instant(alarmRuleNumericOperationTranslationMap.get(keyFilterPredicate.operation)); + if (!dynamicValue) { + if (keyFilter.valueType === EntityKeyValueType.DATE_TIME) { + value = datePipe.transform(keyFilterPredicate.value.staticValue, 'yyyy-MM-dd HH:mm'); + } else { + value = keyFilterPredicate.value.staticValue + ''; + } + } + break; + case AlarmRuleFilterPredicateType.BOOLEAN: + operation = translate.instant(alarmRuleBooleanOperationTranslationMap.get(keyFilterPredicate.operation)); + if (!dynamicValue) { + value = translate.instant(keyFilterPredicate.value.staticValue ? 'value.true' : 'value.false'); + } + break; + case AlarmRuleFilterPredicateType.NO_DATA: + operation = translate.instant(alarmRuleStringOperationTranslationMap.get(AlarmRuleStringOperation.NO_DATA)); + if (!dynamicValue) { + value = keyFilterPredicate.duration.staticValue + ' ' + translate.instant(timeUnitTranslationMap.get(keyFilterPredicate.unit)).toLowerCase(); + } + break; + } + if (!dynamicValue) { + value = `${value}`; + } + return `${key} ${operation} ${value}` + } + }); + if (predicates.length > 1) { + return '(' + predicates.join(` ${translate.instant(complexOperationTranslationMap.get(filterOperation))} `)+ ')'; + } else { + return predicates.toString(); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts index 3020d73831..d35e87fdbe 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts @@ -27,12 +27,20 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MAX_SAFE_PAGE_SIZE, PageLink } from '@shared/models/page/page-link'; import { DateAgoPipe } from '@shared/pipe/date-ago.pipe'; import { map } from 'rxjs/operators'; -import { AlarmComment, AlarmCommentType, getUserDisplayName } from '@shared/models/alarm.models'; +import { + AlarmComment, + AlarmCommentInfo, + AlarmCommentType, + AlarmMessage, + AlarmSeverity, + alarmSeverityTranslations, + getUserDisplayName +} from '@shared/models/alarm.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityType } from '@shared/models/entity-type.models'; import { DatePipe } from '@angular/common'; import { ImportExportService } from '@shared/import-export/import-export.service'; -import { isNotEmptyStr } from '@core/utils'; +import { deepClone, isNotEmptyStr } from '@core/utils'; interface AlarmCommentsDisplayData { commentId?: string; @@ -121,7 +129,7 @@ export class AlarmCommentComponent implements OnInit { const displayDataElement = {} as AlarmCommentsDisplayData; displayDataElement.createdTime = this.datePipe.transform(alarmComment.createdTime, 'yyyy-MM-dd HH:mm:ss'); displayDataElement.createdDateAgo = this.dateAgoPipe.transform(alarmComment.createdTime); - displayDataElement.commentText = alarmComment.comment.text; + displayDataElement.commentText = this.parseSystemComment(alarmComment); displayDataElement.isSystemComment = alarmComment.type === AlarmCommentType.SYSTEM; if (alarmComment.type === AlarmCommentType.OTHER) { displayDataElement.commentId = alarmComment.id.id; @@ -144,6 +152,28 @@ export class AlarmCommentComponent implements OnInit { ); } + private parseSystemComment(alarm: AlarmCommentInfo): string { + const subTypeKey = alarm.comment?.subtype; + if (subTypeKey && AlarmMessage[subTypeKey]) { + const alarmComment = deepClone(alarm.comment); + const translationKey = AlarmMessage[subTypeKey]; + if (alarmComment?.newSeverity) { + alarmComment.newSeverity = + (alarmSeverityTranslations.has(alarmComment.newSeverity) + ? this.translate.instant(alarmSeverityTranslations.get(alarmComment.newSeverity)) + : alarmComment.newSeverity) as AlarmSeverity; + } + if (alarmComment?.oldSeverity) { + alarmComment.oldSeverity = + (alarmSeverityTranslations.has(alarmComment.oldSeverity) + ? this.translate.instant(alarmSeverityTranslations.get(alarmComment.oldSeverity)) + : alarmComment.oldSeverity) as AlarmSeverity; + } + return this.translate.instant(translationKey, alarmComment); + } + return alarm.comment.text; + } + changeSortDirection() { const currentDirection = this.alarmCommentSortOrder.direction; this.alarmCommentSortOrder.direction = currentDirection === Direction.DESC ? Direction.ASC : Direction.DESC; diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts index 0c4344e257..c36d528b4e 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts @@ -107,7 +107,7 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal alarmSeverityTranslationMap = alarmSeverityTranslations; - buttonDisplayValue = this.translate.instant('alarm.alarm-filter'); + buttonDisplayValue = this.translate.instant('alarm.alarm-filter-title'); alarmFilterConfigForm: UntypedFormGroup; @@ -224,6 +224,7 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal cancel() { this.updateAlarmConfigForm(this.alarmFilterConfig); + this.alarmFilterConfigForm.markAsPristine(); if (this.overlayRef) { this.overlayRef.dispose(); } else { diff --git a/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.html b/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.html new file mode 100644 index 0000000000..738372a74e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.html @@ -0,0 +1,87 @@ + + +

{{ 'api-key.generate-title' | translate }}

+ +
+ +
+@if (isLoading$ | async) { + +} +
+
+
+ api-key.generate-text +
+ + api-key.description + + + {{ 'asset.description-required' | translate }} + + + + {{ 'api-key.enable' | translate }} + +
+ + api-key.expiration-date + + @for (value of expirationTimeOptions; track value) { + + {{ value | dateExpiration }} + + } + {{'api-key.expiration-time-never' | translate}} + {{'api-key.expiration-time-custom' | translate}} + + + @if (isCustomExpirationTime()) { + + api-key.date + + + + + } +
+
+
+
+ + +
+ diff --git a/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.scss b/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.scss new file mode 100644 index 0000000000..7f05b2ca75 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.scss @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 '../../src/scss/constants'; + +:host { + display: grid; + width: 700px; + height: 100%; + max-width: 100%; + max-height: 100vh; + grid-template-rows: min-content 4px minmax(auto, 1fr) min-content; + + .mat-mdc-dialog-content { + grid-row: 3; + } + .mat-mdc-dialog-actions { + grid-row: 4; + } + .api-key-text { + position: relative; + padding: 8px 16px 8px 16px; + &::before { + content: ''; + position: absolute; + inset: 0; + background-color: $tb-primary-color; + border-radius: 6px; + opacity: 0.04; + } + + span { + font-size: 12px; + color: rgba(0, 0, 0, 0.54); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.ts b/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.ts new file mode 100644 index 0000000000..b51d7607b9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/add-api-key-dialog.component.ts @@ -0,0 +1,103 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { FormBuilder, Validators } from '@angular/forms'; +import { deepTrim } from '@core/utils'; +import { ApiKeyService } from '@core/http/api-key.service'; +import { ApiKeyInfo } from '@shared/models/api-key.models'; +import { ApiKeysTableDialogData } from '@home/components/api-key/api-keys-table-dialog.component'; +import { DAY } from '@shared/models/time/time.models'; + +@Component({ + selector: 'tb-add-api-key-dialog', + templateUrl: './add-api-key-dialog.component.html', + styleUrls: ['./add-api-key-dialog.component.scss'] +}) +export class AddApiKeyDialogComponent extends DialogComponent { + + readonly startDate = new Date(); + readonly expirationTimeOptions: Array = [7, 30, 60, 90].map(days => days * DAY); + readonly apiKeyForm = this.fb.group({ + description: [{value: null, disabled: false}, [Validators.required]], + enabled: [{value: true, disabled: false}, []], + expirationTime: [{value: this.expirationTimeOptions[1] as string | number, disabled: false}, [Validators.required]], + customExpirationTime: [{value: null, disabled: true}, []], + }); + + constructor( + protected store: Store, + protected router: Router, + public dialogRef: MatDialogRef, + private fb: FormBuilder, + private apiKeyService: ApiKeyService, + @Inject(MAT_DIALOG_DATA) public data: ApiKeysTableDialogData, + ) { + super(store, router, dialogRef); + } + + close(): void { + this.dialogRef.close(null); + } + + add(): void { + const formValue = this.apiKeyForm.value; + const userId = this.data.userId; + const expirationTime = this.calcExpirationTime(); + const apiKey = { + ...deepTrim(formValue), + expirationTime, + userId, + } as ApiKeyInfo; + this.apiKeyService.saveApiKey(apiKey).subscribe( + (res) => { + this.dialogRef.close(res); + } + ); + } + + isCustomExpirationTime() { + return this.apiKeyForm.value?.expirationTime === 'custom'; + } + + onExpirationDateChange() { + const customExpirationTimeControl = this.apiKeyForm.get('customExpirationTime'); + if (this.isCustomExpirationTime()) { + customExpirationTimeControl.enable({emitEvent: false}); + } else { + customExpirationTimeControl.disable({emitEvent: false}); + } + } + + private calcExpirationTime(): number { + const expirationTimeValue = this.apiKeyForm.get('expirationTime').value; + let value: number; + if (this.isCustomExpirationTime()) { + value = this.apiKeyForm.get('customExpirationTime').value.getTime(); + } else if (expirationTimeValue === 'never') { + value = 0; + } else { + value = expirationTimeValue as number + Date.now(); + } + return value; + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.html b/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.html new file mode 100644 index 0000000000..70e7ae7396 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.html @@ -0,0 +1,105 @@ + + +

{{ 'api-key.generated-api-key-title' | translate }}

+ + +
+
+
+
api-key.generated-api-key-copy
+
+ +
+
+
api-key.generated-api-key-command
+ + + + + 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.execute-following-command
+
+ warning + {{ 'api-key.generated-api-key-insecure-url' | translate }} +
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.scss b/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.scss new file mode 100644 index 0000000000..4c4e2e1f30 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.scss @@ -0,0 +1,108 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 '../../src/scss/constants'; + +:host { + display: grid; + height: 100%; + max-width: 100%; + max-height: 100vh; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + + .tb-install-instruction-text { + min-height: 42px; + } + + .insecure-url-warning { + .mat-icon { + color: #FAA405; + } + } + + @media #{$mat-sm} { + width: 470px; + } + + @media #{$mat-gt-sm} { + width: 720px; + } +} + +:host ::ng-deep { + .tb-markdown-view { + .tb-command-code { + .code-wrapper { + padding: 0; + pre[class*=language-] { + margin: 0; + background: #F3F6FA; + border-color: $tb-primary-color; + padding-right: 38px; + overflow: hidden; + overflow-x: auto; + padding-bottom: 4px; + min-height: 42px; + scrollbar-width: thin; + + &::-webkit-scrollbar { + width: 4px; + height: 4px; + } + } + } + button.clipboard-btn { + right: -2px; + p { + color: $tb-primary-color; + } + p, div { + background-color: #F3F6FA; + } + 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); + -webkit-mask-image: url(/assets/copy-code-icon.svg); + mask-repeat: no-repeat; + -webkit-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/components/api-key/api-key-generated-dialog.component.ts b/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.ts new file mode 100644 index 0000000000..a6fed8376e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-key-generated-dialog.component.ts @@ -0,0 +1,80 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { 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 { userInfoCommand, ApiKey } from '@shared/models/api-key.models'; +import { getOS } from '@core/utils'; + +export interface ApiKeyGeneratedDialogData { + apiKey: ApiKey; +} + +@Component({ + selector: 'tb-api-key-generated-dialog', + templateUrl: './api-key-generated-dialog.component.html', + styleUrls: ['api-key-generated-dialog.component.scss'] +}) +export class ApiKeyGeneratedDialogComponent extends DialogComponent { + + private baseUrl = window.location.origin; + + apiKeyCommand = userInfoCommand(this.baseUrl, this.data.apiKey.value); + secureUrl = this.baseUrl.startsWith('https'); + selectedTab: number; + + constructor(protected store: Store, + protected router: Router, + protected dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: ApiKeyGeneratedDialogData) { + super(store, router, dialogRef); + this.selectTabIndexForUserOS(); + } + + close(): void { + this.dialogRef.close(null); + } + + createMarkDownCommand(command: string): string { + return '```bash\n' + + command + + '{:copy-code}\n' + + '```'; + } + + private selectTabIndexForUserOS() { + const currentOS = getOS(); + switch (currentOS) { + case 'linux': + case 'android': + this.selectedTab = 2; + break; + case 'macos': + case 'ios': + this.selectedTab = 1; + break; + case 'windows': + this.selectedTab = 0; + break; + default: + this.selectedTab = 2; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-config.ts b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-config.ts new file mode 100644 index 0000000000..b2b26ee51e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-config.ts @@ -0,0 +1,211 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + DateEntityTableColumn, + EntityTableColumn, + EntityTableConfig, + CellActionDescriptor +} from '@home/models/entity/entities-table-config.models'; +import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { Direction } from '@shared/models/page/sort-order'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Injectable, Renderer2, ViewContainerRef } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { Observable } from 'rxjs'; +import { ApiKeyInfo, ApiKey } from '@shared/models/api-key.models'; +import { ApiKeyService } from '@core/http/api-key.service'; +import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { map } from 'rxjs/operators'; +import { UserId } from '@shared/models/id/user-id'; +import { AddApiKeyDialogComponent } from '@home/components/api-key/add-api-key-dialog.component'; +import { EditApiKeyDescriptionPanelComponent } from '@home/components/api-key/edit-api-key-description-panel.component'; +import { ApiKeysTableDialogData } from '@home/components/api-key/api-keys-table-dialog.component'; +import { + ApiKeyGeneratedDialogComponent, + ApiKeyGeneratedDialogData +} from '@home/components/api-key/api-key-generated-dialog.component'; + +@Injectable() +export class ApiKeysTableConfig extends EntityTableConfig { + + constructor( + private apiKeyService: ApiKeyService, + private translate: TranslateService, + private customTranslate: CustomTranslatePipe, + private dialog: MatDialog, + private datePipe: DatePipe, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private userId: UserId, + ) { + super(); + + this.entityType = EntityType.API_KEY; + this.detailsPanelEnabled = false; + this.addAsTextButton = true; + this.pageMode = false; + + this.entityTranslations = entityTypeTranslations.get(EntityType.API_KEY); + this.entityResources = {} as EntityTypeResource; + this.tableTitle = this.translate.instant('api-key.api-keys'); + this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; + + this.entitiesFetchFunction = pageLink => this.apiKeyService.getUserApiKeys(this.userId.id, pageLink); + this.addEntity = () => this.addApiKey(); + + this.deleteEntityTitle = entity => this.translate.instant('api-key.delete-api-key-title', {name: entity.description}); + this.deleteEntityContent = () => this.translate.instant('api-key.delete-api-key-text'); + this.deleteEntitiesTitle = count => this.translate.instant('api-key.delete-api-keys-title', {count}); + this.deleteEntitiesContent = () => this.translate.instant('api-key.delete-api-keys-text'); + this.deleteEntity = id => this.apiKeyService.deleteApiKey(id.id); + + this.cellActionDescriptors = this.configureCellActions(); + this.columns.push( + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '170px'), + new EntityTableColumn('description', 'api-key.description', '100%', + (entity) => this.customTranslate.transform(entity?.description), () => ({}), true, () => ({}), + (entity) => entity?.description.length > 40 ? this.customTranslate.transform(entity.description) : undefined, false, + { + name: this.translate.instant('api-key.edit-description'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.updateApiKeyDescription($event, entity) + }), + new EntityTableColumn('active', 'api-key.status', '80px', + entity => this.apiKeyStatus(entity), entity => this.apiKeyStatusStyle(entity), false), + new EntityTableColumn('expirationTime', 'api-key.expiration-time', '120px', + (entity) => entity.expirationTime != 0 ? + this.datePipe.transform(entity.expirationTime, 'dd/MM/yyyy, HH:mm') : + this.translate.instant('api-key.expiration-time-never'), + ), + ); + } + + private configureCellActions(): Array> { + const actions: Array> = []; + actions.push( + { + name: '', + nameFunction: (entity) => this.translate.instant(entity.enabled ? 'api-key.disable' : 'api-key.enable'), + icon: 'mdi:toggle-switch', + isEnabled: (entity) => !entity.expired, + iconFunction: (entity) => entity.enabled ? 'mdi:toggle-switch' : 'mdi:toggle-switch-off-outline', + onAction: ($event, entity) => this.toggleEnableMode($event, entity) + } + ) + return actions; + } + + private addApiKey(): Observable { + return this.dialog.open(AddApiKeyDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + userId: this.userId + } + }).afterClosed().pipe(map(res => { + if (res) { + this.apiKeyGenerated(res); + } else { + return null; + } + })); + } + + private apiKeyGenerated(apiKey: ApiKey) { + this.dialog.open(ApiKeyGeneratedDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + apiKey + } + }).afterClosed() + .subscribe(() => { + this.updateData(); + }); + } + + private toggleEnableMode($event: Event, entity: ApiKeyInfo): void { + if ($event) { + $event.stopPropagation(); + } + this.apiKeyService.enableApiKey(entity.id.id, !entity.enabled, {ignoreLoading: true}) + .subscribe( + () => this.updateData() + ); + } + + private apiKeyStatus(apiKey: ApiKeyInfo): string { + let translateKey = 'api-key.status-active'; + let backgroundColor = 'rgba(25, 128, 56, 0.08)'; + if (apiKey.expired) { + translateKey = 'api-key.status-expired'; + backgroundColor = 'rgba(0, 0, 0, 0.04)'; + } else if (!apiKey.enabled) { + translateKey = 'api-key.status-inactive'; + backgroundColor = 'rgba(209, 39, 48, 0.08)'; + } + return `
+ ${this.translate.instant(translateKey)} +
`; + } + + private apiKeyStatusStyle(apiKey: ApiKeyInfo): object { + const styleObj = { + fontSize: '14px', + color: '#198038', + cursor: 'pointer' + }; + if (apiKey.expired) { + styleObj.color = 'rgba(0, 0, 0, 0.54)'; + } else if (!apiKey.enabled) { + styleObj.color = '#d12730'; + } + return styleObj; + } + + private updateApiKeyDescription($event: Event, entity: ApiKeyInfo) { + if ($event) { + $event.stopPropagation(); + } + const trigger = ($event.target || $event.srcElement || $event.currentTarget) as Element; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const editSecretDescriptionPanelPopover = this.popoverService.displayPopover({ + trigger, + renderer: this.renderer, + componentType: EditApiKeyDescriptionPanelComponent, + hostView: this.viewContainerRef, + preferredPlacement: ['right', 'bottom', 'top'], + context: { + apiKeyId: entity.id.id, + description: entity.description + }, + isModal: true + }); + editSecretDescriptionPanelPopover.tbComponentRef.instance.descriptionApplied.subscribe(() => { + editSecretDescriptionPanelPopover.hide(); + this.updateData(); + }); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.html b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.html new file mode 100644 index 0000000000..22aea2c7a2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.html @@ -0,0 +1,39 @@ + +
+ +

{{ 'api-key.manage-api-keys' | translate }}

+ +
+ +
+
+ +
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.scss b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.scss new file mode 100644 index 0000000000..ebd123b857 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.scss @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .api-keys-dialog-container { + width: 1080px; + max-width: 100%; + + .api-keys-dialog-content { + height: 65vh; + border-radius: 0; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.ts b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.ts new file mode 100644 index 0000000000..aaba7b9f1b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table-dialog.component.ts @@ -0,0 +1,47 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { UserId } from '@shared/models/id/user-id'; + +export interface ApiKeysTableDialogData { + userId: UserId; +} + +@Component({ + selector: 'tb-api-keys-table-dialog', + templateUrl: './api-keys-table-dialog.component.html', + styleUrls: ['api-keys-table-dialog.component.scss'] +}) +export class ApiKeysTableDialogComponent { + + constructor( + protected store: Store, + protected router: Router, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: ApiKeysTableDialogData, + ) { + } + + close(): void { + this.dialogRef.close(null); + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.html b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.html new file mode 100644 index 0000000000..969ae31ad5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.html @@ -0,0 +1,20 @@ + +@if (apiKeysTableConfig) { + +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.scss b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.scss new file mode 100644 index 0000000000..78646c14fe --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.scss @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 '../../src/scss/constants.scss'; +@import '../../src/scss/mixins'; + +:host ::ng-deep { + tb-entities-table { + .mat-drawer-container { + background-color: white; + + mat-cell.mat-column-description { + white-space: nowrap; + .mat-mdc-icon-button { + vertical-align: middle; + margin-left: 8px; + @include tb-mat-icon-button-size(32); + .mat-icon { + @include tb-mat-icon-size(20); + } + } + span { + display: inline-block; + max-width: 40ch; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + pointer-events: none; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.ts b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.ts new file mode 100644 index 0000000000..18730803a4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/api-keys-table.component.ts @@ -0,0 +1,80 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + effect, + input, + Renderer2, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { DatePipe } from '@angular/common'; +import { ApiKeysTableConfig } from '@home/components/api-key/api-keys-table-config'; +import { ApiKeyService } from '@core/http/api-key.service'; +import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { UserId } from '@shared/models/id/user-id'; + +@Component({ + selector: 'tb-api-keys-table', + templateUrl: './api-keys-table.component.html', + styleUrls: ['./api-keys-table.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ApiKeysTableComponent { + + @ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; + + active = input(); + userId = input(); + + apiKeysTableConfig: ApiKeysTableConfig; + + constructor( + private apiKeyService: ApiKeyService, + private translate: TranslateService, + private customTranslate: CustomTranslatePipe, + private dialog: MatDialog, + private datePipe: DatePipe, + private cd: ChangeDetectorRef, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + ) { + effect(() => { + if (this.active()) { + this.apiKeysTableConfig = new ApiKeysTableConfig( + this.apiKeyService, + this.translate, + this.customTranslate, + this.dialog, + this.datePipe, + this.popoverService, + this.renderer, + this.viewContainerRef, + this.userId(), + ); + this.cd.markForCheck(); + } + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.html b/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.html new file mode 100644 index 0000000000..1a3f159af4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.html @@ -0,0 +1,43 @@ + +
+
{{ 'api-key.edit-description' | translate }}
+ + api-key.description + + + {{ 'asset.description-required' | translate }} + + {{ input.value?.length || 0 }}/255 + +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.scss b/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.scss new file mode 100644 index 0000000000..5dcf029470 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.scss @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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-edit-api-key-description-panel { + --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12); + + width: 400px; + max-width: 90vw; + display: flex; + flex-direction: column; + gap: 16px; + .tb-edit-api-key-description-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-edit-api-key-description-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/api-key/edit-api-key-description-panel.component.ts b/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.ts new file mode 100644 index 0000000000..1614ea1ed3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/api-key/edit-api-key-description-panel.component.ts @@ -0,0 +1,61 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { FormBuilder, Validators } from '@angular/forms'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { ApiKeyService } from '@core/http/api-key.service'; + +@Component({ + selector: 'tb-edit-api-key-description-panel', + templateUrl: './edit-api-key-description-panel.component.html', + styleUrls: ['./edit-api-key-description-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class EditApiKeyDescriptionPanelComponent implements OnInit { + + @Input() + apiKeyId: string; + + @Input() + description: string; + + @Output() + descriptionApplied = new EventEmitter(); + + descriptionFormControl = this.fb.control(null, Validators.required); + + constructor(private fb: FormBuilder, + private popover: TbPopoverComponent, + private apiKeyService: ApiKeyService) {} + + ngOnInit(): void { + this.descriptionFormControl.setValue(this.description, {emitEvent: false}); + } + + cancel() { + this.popover.hide(); + } + + applyDescription() { + const description = this.descriptionFormControl.value.trim(); + this.apiKeyService.updateApiKeyDescription(this.apiKeyId, description).subscribe(() => { + this.descriptionApplied.emit(description); + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html b/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html index 9cbd85e15b..fa791d479e 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html @@ -29,17 +29,19 @@
-
- - attribute.key - - - {{ (isTelemetry ? 'attribute.telemetry-key-required' : 'attribute.key-required') | translate }} - - - {{ 'attribute.key-max-length' | translate }} - - +
+ + 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 c80f1cacd7..056b155e20 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 @@ -23,13 +23,23 @@ import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Valida import { EntityId } from '@shared/models/id/entity-id'; import { Router } from '@angular/router'; import { DialogComponent } from '@app/shared/components/dialog.component'; -import { AttributeData, AttributeScope, LatestTelemetry, TelemetryType } 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'; +import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; +import { map } from 'rxjs/operators'; +import { ErrorMessageConfig } from '@shared/components/string-autocomplete.component'; +import { TranslateService } from '@ngx-translate/core'; export interface AddAttributeDialogData { entityId: EntityId; attributeScope: TelemetryType; + datasource?: AttributeDatasource; } @Component({ @@ -47,19 +57,22 @@ export class AddAttributeDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: AddAttributeDialogData, private attributeService: AttributeService, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, public dialogRef: MatDialogRef, - public fb: FormBuilder) { + public fb: FormBuilder, + private translate: TranslateService) { super(store, router, dialogRef); } ngOnInit(): void { this.attributeFormGroup = this.fb.group({ - key: ['', [Validators.required, Validators.maxLength(255)]], + key: ['', this.keyValidators], value: [null, [Validators.required]] }); this.isTelemetry = this.data.attributeScope === LatestTelemetry.LATEST_TELEMETRY; @@ -97,4 +110,18 @@ export class AddAttributeDialogComponent extends DialogComponent this.dialogRef.close(true)); } + + fetchOptions(searchText: string): Observable> { + const search = searchText ? searchText?.toLowerCase() : ''; + return this.data.datasource?.getAllAttributes(this.data.entityId,this.data.attributeScope).pipe( + map(attributes => attributes?.filter(attribute => attribute.key.toLowerCase().includes(search)).map(a => a.key)), + ) + } + + get attributeErrorMessages(): ErrorMessageConfig { + return { + required: this.translate.instant(this.isTelemetry ? 'attribute.telemetry-key-required' : 'attribute.key-required'), + maxlength: this.translate.instant('attribute.key-max-length') + } + } } 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 a1b954b8ab..94ad550c33 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 @@ -76,7 +76,6 @@ import { AliasController } from '@core/api/alias-controller'; import { EntityAlias, EntityAliases } from '@shared/models/alias.models'; import { UtilsService } from '@core/services/utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; import { WidgetService } from '@core/http/widget.service'; import { toWidgetInfo } from '../../models/widget-component.models'; import { EntityService } from '@core/http/entity.service'; @@ -89,6 +88,8 @@ import { Filters } from '@shared/models/query/query.models'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component'; import { FormBuilder } from '@angular/forms'; +import { AggregationType, defaultTimewindow } from '@shared/models/time/time.models'; +import { TimeService } from '@core/services/time.service'; @Component({ @@ -201,7 +202,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI private zone: NgZone, private cd: ChangeDetectorRef, private elementRef: ElementRef, - private fb: FormBuilder) { + private fb: FormBuilder, + private timeService: TimeService) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; @@ -315,13 +317,19 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI if ($event) { $event.stopPropagation(); } + const data: AddAttributeDialogData = { + entityId: this.entityIdValue, + attributeScope: this.attributeScope, + }; + + if(this.attributeScope === LatestTelemetry.LATEST_TELEMETRY) { + data.datasource = this.dataSource; + } + this.dialog.open(AddAttributeDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - entityId: this.entityIdValue, - attributeScope: this.attributeScope - } + data }).afterClosed().subscribe( (res) => { if (res) { @@ -541,7 +549,6 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI type: dataKeyType, color: this.utils.getMaterialColor(i), settings: {}, - _hash: Math.random() }; this.widgetDatasource.dataKeys.push(dataKey); } @@ -568,7 +575,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI this.widgetsLoaded = false; this.widgetService.getBundleWidgetTypes(widgetsBundle.id.id).subscribe( (widgetTypes) => { - widgetTypes = widgetTypes.sort((a, b) => { + widgetTypes = widgetTypes.filter(widget => !widget.deprecated).sort((a, b) => { let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]); if (result === 0) { result = b.createdTime - a.createdTime; @@ -592,6 +599,11 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI }; widget.config.title = widgetInfo.widgetName; widget.config.datasources = [this.widgetDatasource]; + if (widget.type === widgetType.timeseries && widget.config.useDashboardTimewindow) { + widget.config.useDashboardTimewindow = false; + widget.config.timewindow = defaultTimewindow(this.timeService); + widget.config.timewindow.aggregation.type = AggregationType.NONE; + } if ((this.attributeScope === LatestTelemetry.LATEST_TELEMETRY && widgetInfo.type !== widgetType.rpc) || widgetInfo.type === widgetType.latest) { const length = this.widgetsListCache.push([widget]); diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html index a27409c21c..9db295dc26 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html @@ -24,13 +24,14 @@ close -
- -
+
+
+ audit-log.action-data +
- - -
+
+ audit-log.failure-details +
diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss index 14ea242150..98805e29a5 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss @@ -13,13 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +@import '../../../../../scss/constants'; + :host { - .tb-audit-log-action-data, - .tb-audit-log-failure-details { - width: 100%; - min-width: 400px; - height: 100%; - min-height: 50px; + display: grid; + height: 100%; + max-width: 100%; + max-height: 100vh; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + + @media #{$mat-gt-xs} { + .mat-mdc-dialog-content { + max-height: 80vh; + } + } + + .tb-audit-log { + min-width: max(400px, 100%); border: 1px solid #c0c0c0; + @media #{$mat-xs} { + min-width: 100%; + } } } diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts index 12305365e0..17675a913a 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts @@ -22,7 +22,11 @@ import { ActionStatus, AuditLog } from '@shared/models/audit-log.models'; import { Ace } from 'ace-builds'; import { DialogComponent } from '@shared/components/dialog.component'; import { Router } from '@angular/router'; -import { getAce } from '@shared/models/ace/ace.models'; +import { getAce, updateEditorSize } from '@shared/models/ace/ace.models'; +import { Observable, of } from 'rxjs'; +import { isObject } from '@core/utils'; +import { ContentType, contentTypesMap } from '@shared/models/constants'; +import { beautifyJs } from '@shared/models/beautify.models'; export interface AuditLogDetailsDialogData { auditLog: AuditLog; @@ -41,11 +45,10 @@ export class AuditLogDetailsDialogComponent extends DialogComponent, protected router: Router, @@ -58,12 +61,10 @@ export class AuditLogDetailsDialogComponent extends DialogComponent = { - 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}; - getAce().subscribe( - (ace) => { - const editor = ace.edit(editorElement, editorOptions); - this.aceEditors.push(editor); - editor.session.setUseWrapMode(false); - editor.setValue(content, -1); - this.updateEditorSize(editorElement, content, editor); - } - ); - } - - updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { - let newHeight = 200; - let newWidth = 600; - if (content && content.length > 0) { - const lines = content.split('\n'); - newHeight = 18 * lines.length + 16; - let maxLineLength = 0; - lines.forEach((row) => { - const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); - const lineLength = line.length; - maxLineLength = Math.max(maxLineLength, lineLength); - }); - newWidth = 9 * maxLineLength + 16; + let mode = 'java'; + let content$: Observable = null; + let contentType = ContentType.TEXT; + if (content && isObject(content)) { + contentType = ContentType.JSON; + mode = contentTypesMap.get(contentType).code; + content$ = beautifyJs(JSON.stringify(content), {indent_size: 2}); + } + if (!content$) { + content$ = of(content as string); } - // newHeight = Math.min(400, newHeight); - this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px'); - this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); - this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px'); - editor.resize(); + content$.subscribe((processedContent) => { + const isJSON = contentType === ContentType.JSON + const editorOptions: Partial = { + mode: `ace/mode/${mode}`, + theme: 'ace/theme/github', + showGutter: isJSON, + showFoldWidgets: true, + foldStyle: 'markbeginend', + showPrintMargin: false, + readOnly: true, + enableSnippets: false, + enableBasicAutocompletion: false, + enableLiveAutocompletion: false, + }; + getAce().subscribe( + (ace) => { + const editor = ace.edit(editorElement, editorOptions); + this.aceEditors.push(editor); + editor.session.setUseWrapMode(false); + editor.setValue(processedContent, -1); + updateEditorSize(editorElement, processedContent, editor, this.renderer, {showGutter: isJSON}); + } + ) + }); } } diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.html b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.html new file mode 100644 index 0000000000..fd5311ba42 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.html @@ -0,0 +1,60 @@ + + + +
+
+
+
audit-log.filter-types
+ + +
+
+
+ + + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.scss b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.scss new file mode 100644 index 0000000000..501a5e2419 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.scss @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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: flex; + max-width: 100%; + padding-right: 20px; + .mdc-button { + max-width: 100%; + } +} + +:host ::ng-deep { + .mdc-button { + .mat-icon { + min-width: 24px; + } + .mdc-button__label { + overflow: hidden; + text-overflow: ellipsis; + } + } +} + +::ng-deep { + .tb-audit-log-config-component { + max-width: 100%; + width: 600px; + min-width: 100%; + flex: 1; + } +} diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts new file mode 100644 index 0000000000..98e76c6fc7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts @@ -0,0 +1,211 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { + ChangeDetectorRef, + Component, + DestroyRef, + ElementRef, + forwardRef, + Input, + OnInit, + TemplateRef, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { isDefined, isDefinedAndNotNull } from '@app/core/utils'; +import { StringItemsOption } from '@app/shared/components/string-items-list.component'; +import { + ActionType, + actionTypeTranslations, + AuditLogFilter, + auditLogFilterEquals, + POSITION_MAP, +} from '@app/shared/public-api'; +import { TranslateService } from '@ngx-translate/core'; + +// @dynamic +@Component({ + selector: 'tb-audit-log-filter', + templateUrl: './audit-log-filter.component.html', + styleUrls: ['./audit-log-filter.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AuditLogFilterComponent), + multi: true + } + ] +}) +export class AuditLogFilterComponent implements OnInit, ControlValueAccessor { + + @ViewChild('auditLogFilterPanel') + auditLogFilterPanel: TemplateRef; + + @Input() disabled: boolean; + + ActionType = ActionType; + + actionTypes = Object.values(ActionType); + + actionTypeTranslations = actionTypeTranslations; + + buttonDisplayValue = this.translate.instant('audit-log.audit-log-filter-title'); + + auditLogFilterForm: FormGroup; + + auditLogOverlayRef: OverlayRef; + + initialAuditLogFilter: AuditLogFilter; + + private auditLogFilter: AuditLogFilter; + + private propagateChange = (_: any) => {}; + + constructor(private fb: FormBuilder, + private translate: TranslateService, + private overlay: Overlay, + private nativeElement: ElementRef, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) {} + + ngOnInit(): void { + this.auditLogFilterForm = this.fb.group({ + actionTypes: [[]] + }); + this.auditLogFilterForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + this.updateValidators(); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.auditLogFilterForm.disable({emitEvent: false}); + } else { + this.auditLogFilterForm.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(auditLogFilter?: AuditLogFilter): void { + this.auditLogFilter = auditLogFilter; + if(!this.initialAuditLogFilter && isDefined(this.auditLogFilter)) { + this.initialAuditLogFilter = this.auditLogFilterForm.getRawValue(); + } + this.updateButtonDisplayValue(); + this.updateAuditLogFilterForm(auditLogFilter); + } + + get predefinedTypeValues (): StringItemsOption[] { + return [...this.actionTypes].map(type => ({ + name: this.translate.instant(this.actionTypeTranslations.get(type)), + value: type + })); + } + + private updateValidators() { + } + + toggleAuditLogFilterPanel($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const config = new OverlayConfig({ + panelClass: 'tb-filter-panel', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + maxHeight: '80vh', + height: 'min-content', + minWidth: '' + }); + config.hasBackdrop = true; + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(this.nativeElement) + .withPositions([POSITION_MAP.bottomLeft]); + + this.auditLogOverlayRef = this.overlay.create(config); + this.auditLogOverlayRef.backdropClick().subscribe(() => { + this.auditLogOverlayRef.dispose(); + }); + this.auditLogOverlayRef.attach(new TemplatePortal(this.auditLogFilterPanel, + this.viewContainerRef)); + } + + cancel() { + this.updateAuditLogFilterForm(this.auditLogFilter); + this.auditLogFilterForm.markAsPristine(); + this.auditLogOverlayRef.dispose(); + } + + update() { + this.auditLogFilterUpdated(this.auditLogFilterForm.value); + this.auditLogFilterForm.markAsPristine(); + this.auditLogOverlayRef.dispose(); + } + + reset() { + if (this.initialAuditLogFilter) { + if (!auditLogFilterEquals(this.auditLogFilterForm.value, this.initialAuditLogFilter)) { + this.updateAuditLogFilterForm(this.initialAuditLogFilter); + this.auditLogFilterForm.markAsDirty(); + } + } + } + + private updateAuditLogFilterForm(auditLogFilter?: AuditLogFilter) { + this.auditLogFilterForm.patchValue({ + actionTypes: auditLogFilter?.actionTypes, + }, {emitEvent: false}); + this.updateValidators(); + } + + private auditLogFilterUpdated(auditLogFilter: AuditLogFilter) { + this.auditLogFilter = auditLogFilter; + this.updateButtonDisplayValue(); + this.propagateChange(this.auditLogFilter); + } + + private updateButtonDisplayValue() { + const filterTextParts: string[] = []; + if (isDefinedAndNotNull(this.auditLogFilter?.actionTypes)) { + const actionTypes = this.auditLogFilter.actionTypes; + actionTypes.forEach(actionType => filterTextParts.push(this.translate.instant(this.actionTypeTranslations.get(actionType as ActionType)))) + } + if (!filterTextParts.length) { + this.buttonDisplayValue = this.translate.instant('audit-log.audit-log-filter-title'); + } else { + this.buttonDisplayValue = this.translate.instant('audit-log.filter-title') + `: ${filterTextParts.join(', ')}`; + } + this.cd.detectChanges(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.html b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.html new file mode 100644 index 0000000000..c82257803d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.html @@ -0,0 +1,20 @@ + + diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldInitService.java b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.scss similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldInitService.java rename to ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.scss index edd3a35220..0d057f16ca 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldInitService.java +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.scss @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.cf; -public interface CalculatedFieldInitService { +:host { + padding-right: 8px; + overflow: hidden; + max-width: 100%; } diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.ts new file mode 100644 index 0000000000..8b8009a838 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-header.component.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { AppState } from '@app/core/public-api'; +import { AuditLog, AuditLogFilter, TimePageLink } from '@app/shared/public-api'; +import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component'; +import { Store } from '@ngrx/store'; + +@Component({ + selector: 'tb-audit-log-header', + templateUrl: './audit-log-header.component.html', + styles: `` +}) +export class AuditLogHeaderComponent extends EntityTableHeaderComponent { + + constructor(protected store: Store) { + super(store) + } + + auditLogFiltersChanged(auditLogFilter: AuditLogFilter) { + this.entitiesTableConfig.componentsData.auditLogFilter = auditLogFilter; + this.entitiesTableConfig.getTable().resetSortAndFilter(true, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts index 5b2a48e24a..a5905fd13c 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts @@ -25,7 +25,8 @@ import { ActionType, actionTypeTranslations, AuditLog, - AuditLogMode + AuditLogMode, + AuditLogFilter } from '@shared/models/audit-log.models'; import { AliasEntityType, @@ -48,6 +49,8 @@ import { AuditLogDetailsDialogComponent, AuditLogDetailsDialogData } from '@home/components/audit-log/audit-log-details-dialog.component'; +import { deepClone } from '@app/core/utils'; +import { AuditLogHeaderComponent } from '@home/components/audit-log/audit-log-header.component'; export class AuditLogTableConfig extends EntityTableConfig { @@ -71,6 +74,7 @@ export class AuditLogTableConfig extends EntityTableConfig; + this.componentsData = { + auditLogFilter: [] + }; + this.entitiesFetchFunction = pageLink => this.fetchAuditLogs(pageLink); this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; @@ -139,15 +147,16 @@ export class AuditLogTableConfig extends EntityTableConfig> { + const auditLogFilter: AuditLogFilter = deepClone(this.componentsData?.auditLogFilter) || {}; switch (this.auditLogMode) { case AuditLogMode.TENANT: - return this.auditLogService.getAuditLogs(pageLink); + return this.auditLogService.getAuditLogs(pageLink, auditLogFilter); case AuditLogMode.ENTITY: - return this.auditLogService.getAuditLogsByEntityId(this.entityId, pageLink); + return this.auditLogService.getAuditLogsByEntityId(this.entityId, pageLink, auditLogFilter); case AuditLogMode.USER: - return this.auditLogService.getAuditLogsByUserId(this.userId.id, pageLink); + return this.auditLogService.getAuditLogsByUserId(this.userId.id, pageLink, auditLogFilter); case AuditLogMode.CUSTOMER: - return this.auditLogService.getAuditLogsByCustomerId(this.customerId.id, pageLink); + return this.auditLogService.getAuditLogsByCustomerId(this.customerId.id, pageLink, auditLogFilter); } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html new file mode 100644 index 0000000000..39692168aa --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html @@ -0,0 +1,151 @@ + +
+ + + +
+
+
+
+
{{ 'common.general' | translate }}
+
+ + {{ 'entity-field.title' | translate }} + + @if (entityForm.get('name').errors && entityForm.get('name').touched) { + + @if (entityForm.get('name').hasError('required')) { + {{ 'common.hint.title-required' | translate }} + } @else if (entityForm.get('name').hasError('pattern')) { + {{ 'common.hint.title-pattern' | translate }} + } @else if (entityForm.get('name').hasError('maxlength')) { + {{ 'common.hint.title-max-length' | translate }} + } + + } + + + +
+ + + + {{ 'common.type' | translate }} + + + {{ CalculatedFieldTypeTranslations.has(entityForm.get('type').value) + ? (CalculatedFieldTypeTranslations.get(entityForm.get('type').value).name | translate) + : entityForm.get('type').value }} + + @for (type of fieldTypes; track type) { + + + {{ CalculatedFieldTypeTranslations.get(type).name | translate }} + +
+ + {{ CalculatedFieldTypeTranslations.get(type).hint | translate }} + +
+ } +
+
+
+ @switch (entityForm.get('type').value) { + @case (CalculatedFieldType.GEOFENCING) { + + } + @case (CalculatedFieldType.PROPAGATION) { + + } + @case (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) { + + } + @case (CalculatedFieldType.ENTITY_AGGREGATION) { + + } + @default { + + } + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss new file mode 100644 index 0000000000..695df5063a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +::ng-deep { + .tbel-script-lang-chip { + line-height: 20px; + font-size: 14px; + font-weight: 500; + color: white; + border-radius: 100px; + width: 70px; + min-width: 70px; + display: flex; + justify-content: center; + margin-top: 2px; + margin-right: 4px; + } + + .tb-js-func { + .ace_tb { + &.ace_calculated-field { + &-ctx { + color: #C52F00; + } + &-args { + color: #185F2A; + } + &-key { + color: #c24c1a; + } + &-time-window, &-values, &-func, &-value, &-ts, &-latestTs { + color: #7214D0; + } + &-start-ts, &-end-ts { + color: #2CAA00; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts new file mode 100644 index 0000000000..425069239f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -0,0 +1,168 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, inject, Inject, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityComponent } from '../../components/entity/entity.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { EntityType } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { + CalculatedFieldConfiguration, + CalculatedFieldInfo, + calculatedFieldsEntityTypeList, + CalculatedFieldType, + calculatedFieldTypes, + CalculatedFieldTypeTranslations +} from '@shared/models/calculated-field.models'; +import { EntityId } from '@shared/models/id/entity-id'; +import { BaseData } from '@shared/models/base-data'; +import { Observable } from 'rxjs'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { TenantId } from '@shared/models/id/tenant-id'; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; +import { AssetInfo } from '@shared/models/asset.models'; +import { DeviceInfo } from '@shared/models/device.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { EntityService } from '@core/http/entity.service'; + +@Component({ + selector: 'tb-calculated-field', + templateUrl: './calculated-field.component.html', + styleUrls: ['./calculated-field.component.scss'] +}) +export class CalculatedFieldComponent extends EntityComponent { + + @Input() + standalone = false; + + @Input() + entityName: string; + + disabledConfiguration = false; + + ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId); + readonly tenantId = getCurrentAuthUser(this.store).tenantId; + readonly EntityType = EntityType; + readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList; + readonly CalculatedFieldType = CalculatedFieldType; + readonly fieldTypes = calculatedFieldTypes; + readonly CalculatedFieldTypeTranslations = CalculatedFieldTypeTranslations; + + private cfFormService = inject(CalculatedFieldFormService); + private destroyRef = inject(DestroyRef); + + constructor(protected store: Store, + protected translate: TranslateService, + @Inject('entity') protected entityValue: CalculatedFieldInfo, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, + protected fb: FormBuilder, + protected cd: ChangeDetectorRef, + private entityService: EntityService) { + super(store, fb, entityValue, entitiesTableConfigValue, cd); + } + + hideDelete() { + if (this.entitiesTableConfig) { + return !this.entitiesTableConfig.deleteEnabled(this.entity); + } else { + return false; + } + } + + additionalDebugActionConfig = { + ...this.entitiesTableConfig.additionalDebugActionConfig, + action: () => this.entitiesTableConfig.additionalDebugActionConfig.action( + { id: this.entity.id, ...this.entityFormValue() }, false, + (expression) => { + if (expression) { + this.entityForm.get('configuration').setValue({...this.entityFormValue().configuration, expression}); + this.entityForm.get('configuration').markAsDirty(); + } + }), + }; + + get entityId(): EntityId { + return this.entityForm.get('entityId').value; + } + + get entitiesTableConfig(): CalculatedFieldsTableConfig { + return this.entitiesTableConfigValue; + } + + changeEntity(entity: BaseData): void { + this.entityName = entity?.name; + } + + buildForm(_entity?: CalculatedFieldInfo): FormGroup { + const form = inject(CalculatedFieldFormService).buildForm(); + inject(CalculatedFieldFormService).setupTypeChange(form, inject(DestroyRef), () => this.isEditValue); + return form; + } + + updateForm(entity: CalculatedFieldInfo) { + const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.entityId, ...value } = entity ?? {}; + const preparedConfig = this.cfFormService.prepareConfig(configuration); + this.entityForm.patchValue({ type }, {emitEvent: false, onlySelf: true}); + setTimeout(() => { + this.entityForm.patchValue({ configuration: preparedConfig, debugSettings, entityId, ...value }, {emitEvent: false}); + this.entityForm.get('type').updateValueAndValidity(); + }); + } + + onTestScript(expression?: string): Observable { + return this.cfFormService.testScript( + this.entity?.id?.id, + this.entityValue, + this.entitiesTableConfig.getTestScriptDialog.bind(this.entitiesTableConfig), + this.destroyRef, + expression + ); + } + + updateFormState() { + if (this.entityForm) { + if (this.isEditValue) { + this.entityForm.enable({emitEvent: false}); + this.entityForm.get('entityId').disable({emitEvent: false}); + this.getOwnerId(this.entityId); + } else { + this.entityForm.disable({emitEvent: false}); + } + } + } + + getOwnerId(entityId: EntityId) { + if (entityId?.entityType === EntityType.DEVICE || entityId?.entityType === EntityType.ASSET) { + this.entityService.getEntity(entityId.entityType, entityId.id, { ignoreLoading: true, ignoreErrors: true }).subscribe( + (entity: AssetInfo | DeviceInfo) => { + if (this.isAssignedToCustomer(entity)) { + this.ownerId = entity.customerId; + } + } + ); + } + } + + private isAssignedToCustomer(entity: AssetInfo | DeviceInfo): boolean { + return entity && entity.customerId && entity.customerId.id !== NULL_UUID; + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts new file mode 100644 index 0000000000..c58b595ba1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts @@ -0,0 +1,80 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldDialogComponent +} from '@home/components/calculated-fields/components/dialog/calculated-field-dialog.component'; +import { + CalculatedFieldScriptTestDialogComponent +} from '@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component'; +import { + CalculatedFieldTestArgumentsComponent +} from '@home/components/calculated-fields/components/test-arguments/calculated-field-test-arguments.component'; +import { + EntityDebugSettingsButtonComponent +} from '@home/components/entity/debug/entity-debug-settings-button.component'; +import { + GeofencingConfigurationModule +} from '@home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.module'; +import { + SimpleConfigurationModule +} from '@home/components/calculated-fields/components/simple-configuration/simple-configuration.module'; +import { + PropagationConfigurationModule +} from '@home/components/calculated-fields/components/propagation-configuration/propagation-configuration.module'; +import { + RelatedEntitiesAggregationComponentModule +} from '@home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.module'; +import { + EntityAggregationComponentModule +} from '@home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.module'; +import { + CalculatedFieldsHeaderComponent +} from '@home/components/calculated-fields/table-header/calculated-fields-header.component'; +import { + CalculatedFieldsFilterConfigComponent +} from '@home/components/calculated-fields/table-header/calculated-fields-filter-config.component'; +import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component'; + +@NgModule({ + declarations: [ + CalculatedFieldDialogComponent, + CalculatedFieldScriptTestDialogComponent, + CalculatedFieldTestArgumentsComponent, + CalculatedFieldsHeaderComponent, + CalculatedFieldsFilterConfigComponent, + CalculatedFieldComponent, + ], + imports: [ + CommonModule, + SharedModule, + GeofencingConfigurationModule, + EntityDebugSettingsButtonComponent, + SimpleConfigurationModule, + PropagationConfigurationModule, + RelatedEntitiesAggregationComponentModule, + EntityAggregationComponentModule, + ], + exports: [ + CalculatedFieldDialogComponent, + CalculatedFieldScriptTestDialogComponent, + CalculatedFieldComponent, + ] +}) +export class CalculatedFieldsModule {} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index 72805454ff..efb767f92e 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -16,15 +16,16 @@ import { DateEntityTableColumn, + EntityLinkTableColumn, EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { Direction } from '@shared/models/page/sort-order'; -import { MatDialog } from '@angular/material/dialog'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { PageLink } from '@shared/models/page/page-link'; -import { Observable, of } from 'rxjs'; +import { EMPTY, Observable, of } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { EntityId } from '@shared/models/id/entity-id'; import { Store } from '@ngrx/store'; @@ -34,64 +35,100 @@ import { DestroyRef, Renderer2 } from '@angular/core'; import { EntityDebugSettings } from '@shared/models/entity.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; -import { catchError, filter, switchMap, tap } from 'rxjs/operators'; +import { catchError, filter, first, switchMap, tap } from 'rxjs/operators'; import { ArgumentEntityType, ArgumentType, CalculatedField, CalculatedFieldEventArguments, + CalculatedFieldInfo, + CalculatedFieldScriptConfiguration, + CalculatedFieldsQuery, CalculatedFieldType, CalculatedFieldTypeTranslations, + debugCfActionEnabled, getCalculatedFieldArgumentsEditorCompleter, getCalculatedFieldArgumentsHighlights, + PropagationWithExpression, } from '@shared/models/calculated-field.models'; import { - CalculatedFieldDebugDialogComponent, - CalculatedFieldDebugDialogData, CalculatedFieldDialogComponent, CalculatedFieldDialogData, CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestScriptDialogData } from './components/public-api'; import { ImportExportService } from '@shared/import-export/import-export.service'; -import { isObject } from '@core/utils'; +import { deepClone, getEntityDetailsPageURL, isObject } from '@core/utils'; import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; import { DatePipe } from '@angular/common'; +import { UtilsService } from "@core/services/utils.service"; +import { ActionNotificationShow } from "@core/notification/notification.actions"; +import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models'; +import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-dialog.component'; +import { + CalculatedFieldsHeaderComponent +} from '@home/components/calculated-fields/table-header/calculated-fields-header.component'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component'; +import { Router } from '@angular/router'; +import { CalculatedFieldsTabsComponent } from '@home/pages/calculated-fields/calculated-fields-tabs.component'; + +export type CalculatedFieldsTableEntity = CalculatedField | CalculatedFieldInfo; -export class CalculatedFieldsTableConfig extends EntityTableConfig { +export class CalculatedFieldsTableConfig extends EntityTableConfig { readonly tenantId = getCurrentAuthUser(this.store).tenantId; additionalDebugActionConfig = { - title: this.translate.instant('calculated-fields.see-debug-events'), - action: (calculatedField: CalculatedField) => this.openDebugEventsDialog.call(this, calculatedField), + title: this.translate.instant('action.see-debug-events'), + action: (calculatedField: CalculatedFieldsTableEntity, + openCalculatedFieldEdit = true, + afterCloseCallback?: (expression: string) => void) => this.openDebugEventsDialog.call(this, null, calculatedField, openCalculatedFieldEdit, afterCloseCallback), }; + calculatedFieldFilterConfig: CalculatedFieldsQuery; + constructor(private calculatedFieldsService: CalculatedFieldsService, private translate: TranslateService, private dialog: MatDialog, private datePipe: DatePipe, - public entityId: EntityId = null, + private entityId: EntityId = null, private store: Store, private destroyRef: DestroyRef, private renderer: Renderer2, - public entityName: string, + private entityName: string, + private ownerId: EntityId = null, private importExportService: ImportExportService, private entityDebugSettingsService: EntityDebugSettingsService, + private utilsService: UtilsService, + private router: Router, + public pageMode = false, ) { super(); + if (this.pageMode) { + this.headerComponent = CalculatedFieldsHeaderComponent; + + this.entityComponent = CalculatedFieldComponent; + this.entityTabsComponent = CalculatedFieldsTabsComponent; + this.rowPointer = true; + } this.tableTitle = this.translate.instant('entity.type-calculated-fields'); - this.detailsPanelEnabled = false; - this.pageMode = false; + this.detailsPanelEnabled = this.pageMode; this.entityType = EntityType.CALCULATED_FIELD; this.entityTranslations = entityTypeTranslations.get(EntityType.CALCULATED_FIELD); + this.entityResources = entityTypeResources.get(EntityType.CALCULATED_FIELD); this.entitiesFetchFunction = (pageLink: PageLink) => this.fetchCalculatedFields(pageLink); this.addEntity = this.getCalculatedFieldDialog.bind(this); - this.deleteEntityTitle = (field: CalculatedField) => this.translate.instant('calculated-fields.delete-title', {title: field.name}); + this.saveEntity = (cf) => this.calculatedFieldsService.saveCalculatedField(cf); + this.loadEntity = id => this.calculatedFieldsService.getCalculatedFieldById(id.id); + this.deleteEntityTitle = (field) => this.translate.instant('calculated-fields.delete-title', {title: field.name}); this.deleteEntityContent = () => this.translate.instant('calculated-fields.delete-text'); this.deleteEntitiesTitle = count => this.translate.instant('calculated-fields.delete-multiple-title', {count}); this.deleteEntitiesContent = () => this.translate.instant('calculated-fields.delete-multiple-text'); this.deleteEntity = id => this.calculatedFieldsService.deleteCalculatedField(id.id); + + this.onEntityAction = action => this.onCFAction(action); + this.addActionDescriptors = [ { name: this.translate.instant('calculated-fields.create'), @@ -109,34 +146,37 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig('expression', 'calculated-fields.expression', '300px'); - expressionColumn.sortable = false; - expressionColumn.cellContentFunction = entity => { - const expressionLabel = this.getExpressionLabel(entity); - return expressionLabel.length < 45 ? expressionLabel : `${expressionLabel.substring(0, 44)}…`; - } - expressionColumn.cellTooltipFunction = entity => { - const expressionLabel = this.getExpressionLabel(entity); - return expressionLabel.length < 45 ? null : expressionLabel - }; - this.columns.push(new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px')); - this.columns.push(new EntityTableColumn('name', 'common.name', '33%')); - this.columns.push(new EntityTableColumn('type', 'common.type', '50px', entity => this.translate.instant(CalculatedFieldTypeTranslations.get(entity.type)))); - this.columns.push(expressionColumn); + this.columns.push(new EntityTableColumn('name', 'common.name', this.pageMode ? '33%' : '60%', + entity => this.utilsService.customTranslation(entity.name, entity.name))); + if (this.pageMode) { + this.columns.push(new EntityTableColumn('entityType', 'entity.entity-type', '10%', + entity => this.translate.instant(entityTypeTranslations.get(entity.entityId.entityType).type))); + this.columns.push(new EntityLinkTableColumn('entityName', 'entity.entity', '33%', + entity => this.utilsService.customTranslation(entity.entityName, entity.entityName), + entity => getEntityDetailsPageURL(entity.entityId?.id, entity.entityId?.entityType as EntityType), false)); + } + this.columns.push(new EntityTableColumn('type', 'common.type', this.pageMode ? '23%' : '40%', entity => this.translate.instant(CalculatedFieldTypeTranslations.get(entity.type).name), () => ({whiteSpace: 'nowrap' }))); this.cellActionDescriptors.push( + { + name: this.translate.instant('action.copy'), + icon: 'content_copy', + isEnabled: () => true, + onAction: ($event, entity) => this.copyCalculatedField($event, entity), + }, { name: this.translate.instant('action.export'), icon: 'file_download', isEnabled: () => true, - onAction: (event$, entity) => this.exportCalculatedField(event$, entity), + onAction: ($event, entity) => this.exportCalculatedField($event, entity), }, { name: this.translate.instant('entity-view.events'), icon: 'mdi:clipboard-text-clock', isEnabled: () => true, - onAction: (_, entity) => this.openDebugEventsDialog(entity), + onAction: ($event, entity) => + this.pageMode ? this.openDebugTab($event, entity) : this.openDebugEventsDialog($event, entity), }, { name: '', @@ -145,37 +185,31 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig true, iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline', onAction: ($event, entity) => this.onOpenDebugConfig($event, entity), - }, - { + } + ); + if (!this.pageMode) { + this.cellActionDescriptors.push({ name: this.translate.instant('action.edit'), icon: 'edit', isEnabled: () => true, - onAction: (_, entity) => this.editCalculatedField(entity), - } - ); - } - - private getExpressionLabel(entity: CalculatedField): string { - if (entity.type === CalculatedFieldType.SCRIPT) { - return 'function calculate(ctx, ' + Object.keys(entity.configuration.arguments).join(', ') + ')'; - } else { - return entity.configuration.expression; + onAction: ($event, entity) => this.editCalculatedField($event, entity), + }) } } - fetchCalculatedFields(pageLink: PageLink): Observable> { - return this.calculatedFieldsService.getCalculatedFields(this.entityId, pageLink); + fetchCalculatedFields(pageLink: PageLink): Observable> { + return this.pageMode ? + this.calculatedFieldsService.getCalculatedFields(pageLink, this.calculatedFieldFilterConfig): + this.calculatedFieldsService.getCalculatedFieldsByEntityId(this.entityId, pageLink); } - onOpenDebugConfig($event: Event, calculatedField: CalculatedField): void { + private onOpenDebugConfig($event: Event, calculatedField: CalculatedFieldsTableEntity): void { + $event?.stopPropagation(); const { debugSettings = {}, id } = calculatedField; const additionalActionConfig = { ...this.additionalDebugActionConfig, - action: () => this.openDebugEventsDialog(calculatedField) + action: () => this.openDebugEventsDialog($event, calculatedField) }; - if ($event) { - $event.stopPropagation(); - } const { viewContainerRef, renderer } = this.entityDebugSettingsService; if (!viewContainerRef || !renderer) { @@ -193,7 +227,24 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig 1) { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + } else { + table.entityDetailsPanel.matTabGroup._tabs.changes.pipe( + first() + ).subscribe(() => { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + }) + } + } + } + + private editCalculatedField($event: Event, calculatedField: CalculatedFieldsTableEntity, isDirty = false): void { + $event?.stopPropagation(); this.getCalculatedFieldDialog(calculatedField, 'action.apply', isDirty) .subscribe((res) => { if (res) { @@ -202,19 +253,23 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig { + private getCalculatedFieldDialog(value?: CalculatedFieldsTableEntity, buttonTitle = 'action.add', isDirty = false, disabledSelectType = false): Observable { + const entityId = this.entityId || value?.entityId; + const entityName = this.entityName || (value as CalculatedFieldInfo)?.entityName; return this.dialog.open(CalculatedFieldDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { value, buttonTitle, - entityId: this.entityId, + entityId, + entityName, tenantId: this.tenantId, - entityName: this.entityName, + ownerId: this.ownerId ?? {entityType: EntityType.TENANT, id: this.tenantId}, additionalDebugActionConfig: this.additionalDebugActionConfig, getTestScriptDialogFn: this.getTestScriptDialog.bind(this), isDirty, + disabledSelectType }, enterAnimationDuration: isDirty ? 0 : null, }) @@ -222,32 +277,75 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig(CalculatedFieldDebugDialogComponent, { + private openDebugEventsDialog($event: Event, calculatedField: CalculatedFieldsTableEntity, openCalculatedFieldEdit = true, afterCloseCallback?: (expression: string) => void ): void { + $event?.stopPropagation(); + + const onDebugEventSelected = (event: CalculatedFieldEventBody, dialogRef: MatDialogRef) => { + this.getTestScriptDialog(calculatedField, JSON.parse(event.arguments), openCalculatedFieldEdit) + .subscribe(expression => dialogRef.close(expression)); + }; + + this.dialog.open(EventsDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { + title: 'calculated-fields.debugging', tenantId: this.tenantId, - value: calculatedField, - getTestScriptDialogFn: this.getTestScriptDialog.bind(this), + entityId: calculatedField.id, + debugEventTypes:[DebugEventType.DEBUG_CALCULATED_FIELD], + disabledEventTypes:[EventType.LC_EVENT, EventType.ERROR, EventType.STATS], + defaultEventType: DebugEventType.DEBUG_CALCULATED_FIELD, + onDebugEventSelected, + debugActionDisabled: !debugCfActionEnabled(calculatedField) } }) .afterClosed() - .subscribe(); + .subscribe(value => { + if (afterCloseCallback) { + afterCloseCallback(value) + } + }); } - private exportCalculatedField($event: Event, calculatedField: CalculatedField): void { - if ($event) { - $event.stopPropagation(); - } + private exportCalculatedField($event: Event, calculatedField: CalculatedFieldsTableEntity): void { + $event?.stopPropagation(); this.importExportService.exportCalculatedField(calculatedField.id.id); } + private copyCalculatedField($event: Event, calculatedField: CalculatedField): void { + $event?.stopPropagation(); + const copyCalculatedField = deepClone(calculatedField); + if (this.pageMode) { + copyCalculatedField.entityId = null; + delete (copyCalculatedField as CalculatedFieldInfo).entityName; + } + delete copyCalculatedField.id; + this.getCalculatedFieldDialog(copyCalculatedField, 'action.apply', false) + .subscribe((res) => { + if (res) { + this.updateData(); + } + }); + } + private importCalculatedField(): void { this.importExportService.openCalculatedFieldImportDialog() .pipe( filter(Boolean), - switchMap(calculatedField => this.getCalculatedFieldDialog(this.updateImportedCalculatedField(calculatedField), 'action.add', true)), + switchMap(calculatedField => { + if (calculatedField.type === CalculatedFieldType.ALARM || !CalculatedFieldType[calculatedField.type]) { + this.store.dispatch(new ActionNotificationShow({ + message: this.translate.instant('calculated-fields.hint.import-invalid-calculated-field-type'), + type: 'error', + verticalPosition: 'top', + horizontalPosition: 'left', + duration: 5000 + })); + return EMPTY; + } + return of(calculatedField); + }), + switchMap(calculatedField => this.getCalculatedFieldDialog(this.updateImportedCalculatedField(calculatedField), 'action.add', true, true)), filter(Boolean), switchMap(calculatedField => this.calculatedFieldsService.saveCalculatedField(calculatedField)), filter(Boolean), @@ -257,13 +355,23 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig { - const arg = calculatedField.configuration.arguments[key]; - acc[key] = arg.refEntityId?.entityType === ArgumentEntityType.Tenant - ? { ...arg, refEntityId: { id: this.tenantId, entityType: ArgumentEntityType.Tenant } } - : arg; - return acc; - }, {}); + if (calculatedField.type === CalculatedFieldType.GEOFENCING) { + calculatedField.configuration.zoneGroups = Object.keys(calculatedField.configuration.zoneGroups).reduce((acc, key) => { + const arg = calculatedField.configuration.zoneGroups[key]; + acc[key] = arg.refEntityId?.entityType === ArgumentEntityType.Tenant + ? { ...arg, refEntityId: { id: this.tenantId, entityType: ArgumentEntityType.Tenant } } + : arg; + return acc; + }, {}); + } else { + calculatedField.configuration.arguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { + const arg = calculatedField.configuration.arguments[key]; + acc[key] = arg.refEntityId?.entityType === ArgumentEntityType.Tenant + ? { ...arg, refEntityId: { id: this.tenantId, entityType: ArgumentEntityType.Tenant } } + : arg; + return acc; + }, {}); + } return calculatedField; } @@ -276,33 +384,62 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig this.updateData()); } - private getTestScriptDialog(calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable { - const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { - const type = calculatedField.configuration.arguments[key].refEntityKey.type; - acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key) - ? { ...argumentsObj[key], type } - : type === ArgumentType.Rolling ? { values: [], type } : { value: '', type, ts: new Date().getTime() }; - return acc; - }, {}); - return this.dialog.open(CalculatedFieldScriptTestDialogComponent, - { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'], - data: { - arguments: resultArguments, - expression: calculatedField.configuration.expression, - argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(calculatedField.configuration.arguments), - argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(calculatedField.configuration.arguments), - openCalculatedFieldEdit - } - }).afterClosed() - .pipe( - filter(Boolean), - tap(expression => { - if (openCalculatedFieldEdit) { - this.editCalculatedField({ entityId: this.entityId, ...calculatedField, configuration: {...calculatedField.configuration, expression } }, true) + getTestScriptDialog(calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable { + if ( + calculatedField.type === CalculatedFieldType.SCRIPT || + calculatedField.type === CalculatedFieldType.RELATED_ENTITIES_AGGREGATION || + (calculatedField.type === CalculatedFieldType.PROPAGATION && calculatedField.configuration.applyExpressionToResolvedArguments === true) + ) { + const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { + const type = calculatedField.configuration.arguments[key].refEntityKey.type; + acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key) + ? {...argumentsObj[key], type} + : type === ArgumentType.Rolling ? {values: [], type} : {value: '', type, ts: new Date().getTime()}; + return acc; + }, {}); + return this.dialog.open(CalculatedFieldScriptTestDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'], + data: { + arguments: resultArguments, + expression: expression ?? (calculatedField.configuration as CalculatedFieldScriptConfiguration | PropagationWithExpression).expression, + argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(calculatedField.configuration.arguments), + argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(calculatedField.configuration.arguments), + openCalculatedFieldEdit } - }), - ); + }).afterClosed() + .pipe( + filter(Boolean), + tap(expression => { + if (openCalculatedFieldEdit) { + this.editCalculatedField(null, { + entityId: this.entityId, ...calculatedField, + configuration: {...calculatedField.configuration, expression} as any + }, true) + } + }), + ); + } else { + return of(null); + } + } + + private openCalculatedField($event: Event, entity: CalculatedFieldsTableEntity) { + $event?.stopPropagation(); + const url = this.router.createUrlTree(['calculatedFields', entity.id.id]); + this.router.navigateByUrl(url); + } + + private onCFAction(action: EntityAction): boolean { + switch (action.action) { + case 'open': + this.openCalculatedField(action.event, action.entity); + return true; + case 'export': + this.exportCalculatedField(action.event, action.entity); + return true; + } + return false; } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.html index 7c2317dbac..102a334402 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.html @@ -16,5 +16,5 @@ --> @if (calculatedFieldsTableConfig) { - + } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.scss index f7fd0d1dc6..175df5b779 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.scss @@ -14,9 +14,7 @@ * limitations under the License. */ :host ::ng-deep { - tb-entities-table { - .mat-drawer-container { - background-color: white; - } + .white-background { + --mat-sidenav-content-background-color: white; } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts index 666a347564..15e622c468 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts @@ -35,6 +35,8 @@ import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { ImportExportService } from '@shared/import-export/import-export.service'; import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; import { DatePipe } from '@angular/common'; +import { UtilsService } from "@core/services/utils.service"; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'tb-calculated-fields-table', @@ -50,9 +52,12 @@ export class CalculatedFieldsTableComponent { active = input(); entityId = input(); entityName = input(); + ownerId = input(); calculatedFieldsTableConfig: CalculatedFieldsTableConfig; + pageMode: boolean = false; + constructor(private calculatedFieldsService: CalculatedFieldsService, private translate: TranslateService, private dialog: MatDialog, @@ -62,10 +67,14 @@ export class CalculatedFieldsTableComponent { private renderer: Renderer2, private importExportService: ImportExportService, private entityDebugSettingsService: EntityDebugSettingsService, - private destroyRef: DestroyRef) { - + private utilsService: UtilsService, + private destroyRef: DestroyRef, + private route: ActivatedRoute, + private router: Router + ) { + this.pageMode = !!this.route.snapshot.data.isPage; effect(() => { - if (this.active()) { + if (this.active() || this.pageMode) { this.calculatedFieldsTableConfig = new CalculatedFieldsTableConfig( this.calculatedFieldsService, this.translate, @@ -76,8 +85,12 @@ export class CalculatedFieldsTableComponent { this.destroyRef, this.renderer, this.entityName(), + this.ownerId(), this.importExportService, this.entityDebugSettingsService, + this.utilsService, + this.router, + this.pageMode, ); this.cd.markForCheck(); } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.html new file mode 100644 index 0000000000..56f6cb0331 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.html @@ -0,0 +1,252 @@ + +
+
{{ 'calculated-fields.argument-settings' | translate }}
+
+ @if (hint) { +
+ {{ hint | translate }} +
+ } +
+ @if (!watchKeyChange) { + + } + + @if (!hiddenEntityTypes) { +
+
{{ 'entity.entity-type' | translate }}
+ + + @for (type of argumentEntityTypes; track type) { + {{ ArgumentEntityTypeTranslations.get(type) | translate }} + } + + @if (argumentType.touched && argumentType.hasError('required')) { + + warning + + } + +
+ } + @if (ArgumentEntityTypeParamsMap.has(entityType)) { +
+
{{ ArgumentEntityTypeParamsMap.get(entityType).title | translate }}
+ +
+ } +
+ + @if (!hiddenEntityKeyTypes) { +
+
{{ 'calculated-fields.argument-type' | translate }}
+ + + @for (type of argumentTypes; track type) { + {{ ArgumentTypeTranslations.get(type) | translate }} + } + + @if (refEntityKeyFormGroup.get('type').hasError('required') && refEntityKeyFormGroup.get('type').touched) { + + warning + + } + +
+ } + @if (entityFilter.singleEntity?.id || entityType === ArgumentEntityType.Current || entityType === ArgumentEntityType.Tenant) { + @if (refEntityKeyFormGroup.get('type').value !== ArgumentType.Attribute) { +
+
{{ 'calculated-fields.timeseries-key' | translate }}
+ @if (refEntityKeyFormGroup.get('type').value === ArgumentType.LatestTelemetry) { + + } @else { + + } + + + +
+ } @else { + @if (enableAttributeScopeSelection) { +
+
{{ 'calculated-fields.attribute-scope' | translate }}
+ + + + {{ 'calculated-fields.server-attributes' | translate }} + + + {{ 'calculated-fields.client-attributes' | translate }} + + + {{ 'calculated-fields.shared-attributes' | translate }} + + + +
+ } +
+
{{ 'calculated-fields.attribute-key' | translate }}
+ +
+ } + } +
+ @if (watchKeyChange) { + + } + @if (refEntityKeyFormGroup.get('type').value !== ArgumentType.Rolling) { + @if (!hiddenDefaultValue) { +
+
{{ 'calculated-fields.default-value' | translate }}
+ + + @if (argumentFormGroup.get('defaultValue').touched && argumentFormGroup.get('defaultValue').hasError('required')) { + + warning + + } + +
+ } + } @else { +
+
{{ 'calculated-fields.time-window' | translate }}
+ +
+ @if (maxDataPointsPerRollingArg) { +
+
{{ 'calculated-fields.limit' | translate }}
+
+ + + + + + +
+
+ } + } +
+
+
+ + @if (!readonly) { + + } +
+
+ + +
+
{{ label | translate }}
+ + + @if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('required')) { + + warning + + } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('duplicateName')) { + + warning + + } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('pattern')) { + + warning + + } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('maxlength')) { + + warning + + } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('forbiddenName')) { + + warning + + } + +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.scss similarity index 79% rename from ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.scss rename to ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.scss index 1254a30aee..36280dafb7 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.scss @@ -15,22 +15,9 @@ */ @use '../../../../../../../scss/constants' as constants; -$panel-width: 520px; - :host { - display: flex; - width: $panel-width; - max-width: 100%; - max-height: 100vh; - - .fixed-title-width { - @media #{constants.$mat-xs} { - min-width: 120px; - } - } - .limit-field-row { - @media screen and (max-width: $panel-width) { + @media screen and (max-width: 520px) { display: flex; flex-direction: column; diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts similarity index 56% rename from ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts rename to ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts index cd32d96076..37362a29ee 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts @@ -14,9 +14,18 @@ /// limitations under the License. /// -import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, output, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + DestroyRef, + Input, + OnInit, + output, + ViewChild +} from '@angular/core'; import { TbPopoverComponent } from '@shared/components/popover.component'; -import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { charsWithNumRegex, oneSpaceInsideRegex } from '@shared/models/regex.constants'; import { ArgumentEntityType, @@ -25,10 +34,12 @@ import { ArgumentType, ArgumentTypeTranslations, CalculatedFieldArgumentValue, - CalculatedFieldType, - getCalculatedFieldCurrentEntityFilter + FORBIDDEN_NAMES, + forbiddenNamesValidator, + getCalculatedFieldCurrentEntityFilter, + uniqueNameValidator } from '@shared/models/calculated-field.models'; -import { debounceTime, delay, distinctUntilChanged, filter } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged, filter } from 'rxjs/operators'; import { EntityType } from '@shared/models/entity-type.models'; import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DatasourceType } from '@shared/models/widget.models'; @@ -43,11 +54,12 @@ import { AppState } from '@core/core.state'; import { Store } from '@ngrx/store'; import { EntityAutocompleteComponent } from '@shared/components/entity/entity-autocomplete.component'; import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { TenantId } from '@shared/models/id/tenant-id'; @Component({ selector: 'tb-calculated-field-argument-panel', templateUrl: './calculated-field-argument-panel.component.html', - styleUrls: ['./calculated-field-argument-panel.component.scss'] + styleUrls: ['../common/calculated-field-panel.scss', './calculated-field-argument-panel.component.scss'] }) export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewInit { @@ -56,22 +68,40 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI @Input() entityId: EntityId; @Input() tenantId: string; @Input() entityName: string; - @Input() calculatedFieldType: CalculatedFieldType; + @Input() ownerId: EntityId; + @Input() isScript: boolean; @Input() usedArgumentNames: string[]; + @Input() watchKeyChange = false; + @Input() hiddenEntityTypes = false; + @Input() hiddenEntityKeyTypes = false; + @Input() hiddenDefaultValue = false; + @Input() defaultValueRequired = false; + @Input() hint: string; + @Input() predefinedEntityFilter: EntityFilter; + @Input() forbiddenNames = FORBIDDEN_NAMES; + @Input() argumentEntityTypes = Object.values(ArgumentEntityType).filter(value => value !== ArgumentEntityType.RelationQuery) as ArgumentEntityType[]; + @Input() argumentNameContext: {[key: string]: string} = { + label: 'calculated-fields.argument-name', + required: 'calculated-fields.hint.argument-name-required', + duplicate: 'calculated-fields.hint.argument-name-duplicate', + pattern: 'calculated-fields.hint.argument-name-pattern', + maxlength: 'calculated-fields.hint.argument-name-max-length', + forbidden: 'calculated-fields.hint.argument-name-forbidden' + }; + @Input() readonly = false; @ViewChild('entityAutocomplete') entityAutocomplete: EntityAutocompleteComponent; argumentsDataApplied = output(); + argumentType = this.fb.control(ArgumentEntityType.Current, Validators.required); + readonly maxDataPointsPerRollingArg = getCurrentAuthState(this.store).maxDataPointsPerRollingArg; readonly defaultLimit = Math.floor(this.maxDataPointsPerRollingArg / 10); argumentFormGroup = this.fb.group({ - argumentName: ['', [Validators.required, this.uniqNameRequired(), this.forbiddenArgumentNameValidator(), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]], - refEntityId: this.fb.group({ - entityType: [ArgumentEntityType.Current], - id: [''] - }), + argumentName: ['', [Validators.required, Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]], + refEntityId: [null], refEntityKey: this.fb.group({ type: [ArgumentType.LatestTelemetry, [Validators.required]], key: ['', [Validators.pattern(oneSpaceInsideRegex)]], @@ -86,7 +116,8 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI entityFilter: EntityFilter; entityNameSubject = new BehaviorSubject(null); - readonly argumentEntityTypes = Object.values(ArgumentEntityType) as ArgumentEntityType[]; + enableAutocomplete = false; + readonly ArgumentEntityTypeTranslations = ArgumentEntityTypeTranslations; readonly ArgumentType = ArgumentType; readonly DataKeyType = DataKeyType; @@ -103,20 +134,16 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI private fb: FormBuilder, private cd: ChangeDetectorRef, private popover: TbPopoverComponent, - private store: Store + private store: Store, + private destroyRef: DestroyRef ) { this.observeEntityFilterChanges(); - this.observeEntityTypeChanges(); + this.observeArgumentTypeChanges(); this.observeEntityKeyChanges(); - this.observeUpdatePosition(); } get entityType(): ArgumentEntityType { - return this.argumentFormGroup.get('refEntityId').get('entityType').value; - } - - get refEntityIdFormGroup(): FormGroup { - return this.argumentFormGroup.get('refEntityId') as FormGroup; + return this.argumentType.value; } get refEntityKeyFormGroup(): FormGroup { @@ -130,14 +157,31 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI } ngOnInit(): void { + this.updatedFormValidators(); + this.updatedArgumentType(); this.argumentFormGroup.patchValue(this.argument, {emitEvent: false}); this.currentEntityFilter = getCalculatedFieldCurrentEntityFilter(this.entityName, this.entityId); - this.updateEntityFilter(this.argument.refEntityId?.entityType, true); + this.updateEntityFilter(this.entityType, true); + this.updatedRefEntityIdState(this.entityType, false); this.toggleByEntityKeyType(this.argument.refEntityKey?.type); this.setInitialEntityKeyType(); + this.setInitialEntityType(); + if (this.watchKeyChange) { + this.setWatchKeyChange(); + } + + if (this.defaultValueRequired) { + this.argumentFormGroup.get('defaultValue').addValidators(Validators.required); + this.argumentFormGroup.get('defaultValue').updateValueAndValidity({onlySelf: true}); + } this.argumentTypes = Object.values(ArgumentType) - .filter(type => type !== ArgumentType.Rolling || this.calculatedFieldType === CalculatedFieldType.SCRIPT); + .filter(type => type !== ArgumentType.Rolling || this.isScript); + + if (this.readonly) { + this.argumentType.disable({emitEvent: false}); + this.argumentFormGroup.disable({emitEvent: false}); + } } ngAfterViewInit(): void { @@ -147,12 +191,13 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI } saveArgument(): void { - const { refEntityId, ...restConfig } = this.argumentFormGroup.value; - const value = (refEntityId.entityType === ArgumentEntityType.Current ? restConfig : { refEntityId, ...restConfig }) as CalculatedFieldArgumentValue; - if (refEntityId.entityType === ArgumentEntityType.Tenant) { - refEntityId.id = this.tenantId; + const value = this.argumentFormGroup.value as CalculatedFieldArgumentValue; + if (this.entityType === ArgumentEntityType.Owner) { + value.refDynamicSourceConfiguration = {type: ArgumentEntityType.Owner}; + } else if (this.entityType === ArgumentEntityType.Tenant) { + value.refEntityId = new TenantId(this.tenantId) as any; } - if (refEntityId.entityType !== ArgumentEntityType.Current && refEntityId.entityType !== ArgumentEntityType.Tenant) { + if (this.entityType !== ArgumentEntityType.Current && this.entityType !== ArgumentEntityType.Tenant) { value.entityName = this.entityNameSubject.value; } if (value.defaultValue) { @@ -166,6 +211,23 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI this.popover.hide(); } + private updatedFormValidators(): void { + this.argumentFormGroup.get('argumentName').addValidators( + [uniqueNameValidator(this.usedArgumentNames), forbiddenNamesValidator(this.forbiddenNames)]); + this.argumentFormGroup.get('argumentName').updateValueAndValidity({emitEvent: false}); + } + + private updatedArgumentType(): void { + let argumentType = ArgumentEntityType.Current; + if (this.argument.refDynamicSourceConfiguration?.type === ArgumentEntityType.Owner) { + this.enableAutocomplete = (this.entityId.entityType === EntityType.DEVICE_PROFILE || this.entityId.entityType === EntityType.ASSET_PROFILE); + argumentType = ArgumentEntityType.Owner; + } else if (this.argument.refEntityId?.entityType) { + argumentType = this.argument.refEntityId.entityType; + } + this.argumentType.setValue(argumentType, {emitEvent: false}); + } + private toggleByEntityKeyType(type: ArgumentType): void { const isAttribute = type === ArgumentType.Attribute; const isRolling = type === ArgumentType.Rolling; @@ -181,6 +243,12 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI case ArgumentEntityType.Current: entityFilter = this.currentEntityFilter; break; + case ArgumentEntityType.Owner: + entityFilter = { + type: AliasFilterType.singleEntity, + singleEntity: this.ownerId + }; + break; case ArgumentEntityType.Tenant: entityFilter = { type: AliasFilterType.singleEntity, @@ -197,7 +265,13 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI }; } if (!onInit) { - this.argumentFormGroup.get('refEntityKey').get('key').setValue(''); + this.argumentFormGroup.get('refEntityKey').get('key').setValue('', {emitEvents: !this.watchKeyChange}); + if (this.watchKeyChange && this.argumentFormGroup.get('argumentName').pristine) { + this.argumentFormGroup.get('argumentName').markAsUntouched({emitEvent: false}); + this.argumentFormGroup.get('argumentName').setValue('', {emitEvent: false}); + } + } else if (this.predefinedEntityFilter) { + entityFilter = this.predefinedEntityFilter; } this.entityFilter = entityFilter; this.cd.markForCheck(); @@ -205,41 +279,28 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI private observeEntityFilterChanges(): void { merge( - this.refEntityIdFormGroup.get('entityType').valueChanges, + this.argumentType.valueChanges, this.refEntityKeyFormGroup.get('type').valueChanges, - this.refEntityIdFormGroup.get('id').valueChanges.pipe(filter(Boolean)), + this.argumentFormGroup.get('refEntityId').valueChanges.pipe(filter(Boolean)), this.refEntityKeyFormGroup.get('scope').valueChanges, ) .pipe(debounceTime(50), takeUntilDestroyed()) .subscribe(() => this.updateEntityFilter(this.entityType)); } - private observeEntityTypeChanges(): void { - this.refEntityIdFormGroup.get('entityType').valueChanges + private observeArgumentTypeChanges(): void { + this.argumentType.valueChanges .pipe(distinctUntilChanged(), takeUntilDestroyed()) .subscribe(type => { - this.argumentFormGroup.get('refEntityId').get('id').setValue(''); - const isEntityWithId = type !== ArgumentEntityType.Tenant && type !== ArgumentEntityType.Current; - this.argumentFormGroup.get('refEntityId') - .get('id')[isEntityWithId ? 'enable' : 'disable'](); - if (!isEntityWithId) { - this.entityNameSubject.next(null); - } + this.argumentFormGroup.get('refEntityId').setValue(null); + this.enableAutocomplete = (this.entityId.entityType === EntityType.DEVICE_PROFILE || this.entityId.entityType === EntityType.ASSET_PROFILE) && type === ArgumentEntityType.Owner; + this.updatedRefEntityIdState(type); if (!this.enableAttributeScopeSelection) { this.refEntityKeyFormGroup.get('scope').setValue(AttributeScope.SERVER_SCOPE); } }); } - private uniqNameRequired(): ValidatorFn { - return (control: FormControl) => { - const newName = control.value.trim().toLowerCase(); - const isDuplicate = this.usedArgumentNames?.some(name => name.toLowerCase() === newName); - - return isDuplicate ? { duplicateName: true } : null; - }; - } - private observeEntityKeyChanges(): void { this.argumentFormGroup.get('refEntityKey').get('type').valueChanges .pipe(takeUntilDestroyed()) @@ -247,29 +308,36 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI } private setInitialEntityKeyType(): void { - if (this.calculatedFieldType === CalculatedFieldType.SIMPLE && this.argument.refEntityKey?.type === ArgumentType.Rolling) { + if (!this.isScript && this.argument.refEntityKey?.type === ArgumentType.Rolling) { const typeControl = this.argumentFormGroup.get('refEntityKey').get('type'); typeControl.setValue(null); typeControl.markAsTouched(); } } - private forbiddenArgumentNameValidator(): ValidatorFn { - return (control: FormControl) => { - const trimmedValue = control.value.trim().toLowerCase(); - const forbiddenArgumentNames = ['ctx', 'e', 'pi']; - return forbiddenArgumentNames.includes(trimmedValue) ? { forbiddenName: true } : null; - }; + private setInitialEntityType() { + if (!this.argumentEntityTypes.includes(this.entityType)) { + this.argumentType.setValue(null); + this.argumentType.markAsTouched(); + } } - private observeUpdatePosition(): void { - merge( - this.refEntityIdFormGroup.get('entityType').valueChanges, - this.refEntityKeyFormGroup.get('type').valueChanges, - this.argumentFormGroup.get('timeWindow').valueChanges, - this.refEntityIdFormGroup.get('id').valueChanges.pipe(filter(Boolean)), - ) - .pipe(delay(50), takeUntilDestroyed()) - .subscribe(() => this.popover.updatePosition()); + private setWatchKeyChange(): void { + this.refEntityKeyFormGroup.get('key').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((key) => { + if (this.argumentFormGroup.get('argumentName').pristine) { + this.argumentFormGroup.get('argumentName').setValue(key); + this.argumentFormGroup.get('argumentName').markAsTouched({emitEvent: false}); + } + }); + } + + private updatedRefEntityIdState(type: ArgumentEntityType, emitEvent = true): void { + const isEntityWithId = !!type && ![ArgumentEntityType.Tenant, ArgumentEntityType.Current, ArgumentEntityType.Owner].includes(type); + this.argumentFormGroup.get('refEntityId')[isEntityWithId ? 'enable' : 'disable']({emitEvent}); + if (!isEntityWithId) { + this.entityNameSubject.next(null); + } } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.html similarity index 69% rename from ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.html rename to ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.html index 93f1d58d53..fc3c6ac7b3 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.html @@ -15,13 +15,15 @@ limitations under the License. --> -
-
+
+
-
{{ 'common.name' | translate }}
+
{{ argumentNameColumn | translate }}
@@ -29,7 +31,7 @@ @@ -37,13 +39,15 @@ - + {{ 'entity.entity-type' | translate }}
@if (argument.refEntityId?.entityType === ArgumentEntityType.Tenant) { {{ 'calculated-fields.argument-current-tenant' | translate }} + } @else if (argument.refDynamicSourceConfiguration?.type === ArgumentEntityType.Owner) { + {{ 'calculated-fields.argument-owner' | translate }} } @else if (argument.refEntityId?.id) { {{ entityTypeTranslations.get(argument.refEntityId.entityType).type | translate }} } @else { @@ -59,7 +63,7 @@
@if (argument.refEntityId?.id && argument.refEntityId?.entityType !== ArgumentEntityType.Tenant) { - {{ entityNameMap.get(argument.refEntityId.id) ?? '' }} @@ -88,25 +92,37 @@ -
- +
+ @if (!isEditValue) { + + } @else { + + }
- - + +
-
- {{ 'calculated-fields.no-arguments' | translate }} -
- @if (errorText) { + @if (errorText || (dataSource.isEmpty() | async)) { }
+
+ {{ 'calculated-fields.no-arguments' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.scss similarity index 89% rename from ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss rename to ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.scss index 04e72eee84..7a2b96f497 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.scss @@ -41,6 +41,10 @@ } } + .no-data-found { + font-size: 16px; + } + .max-args-warning { .mat-icon { color: #FAA405; @@ -51,6 +55,9 @@ --mat-badge-legacy-small-size-container-size: 8px; --mat-badge-small-size-container-overlap-offset: -5px; --mat-badge-small-size-text-size: 0; + width: 100%; + display: flex; + justify-content: end; } } @@ -62,7 +69,7 @@ } .arguments-table { - .mat-mdc-header-row.mat-row-select .mat-mdc-header-cell.entity-type-header { + .mat-mdc-header-row.mat-row-select .mat-mdc-header-cell:nth-child(2) { padding: 0 28px 0 0; } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts similarity index 80% rename from ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts rename to ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts index 5446aaf14e..bd8292d113 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts @@ -16,6 +16,7 @@ import { AfterViewInit, + booleanAttribute, ChangeDetectorRef, Component, DestroyRef, @@ -43,17 +44,19 @@ import { CalculatedFieldArgumentValue, CalculatedFieldType, } from '@shared/models/calculated-field.models'; -import { CalculatedFieldArgumentPanelComponent } from '@home/components/calculated-fields/components/public-api'; +import { + CalculatedFieldArgumentPanelComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { EntityId } from '@shared/models/id/entity-id'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; -import { getEntityDetailsPageURL, isEqual } from '@core/utils'; +import { getEntityDetailsPageURL, isDefined, isEqual } from '@core/utils'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; import { EntityService } from '@core/http/entity.service'; -import { MatSort } from '@angular/material/sort'; +import { MatSort, SortDirection } from '@angular/material/sort'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -83,16 +86,26 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces @Input() entityId: EntityId; @Input() tenantId: string; @Input() entityName: string; - @Input() calculatedFieldType: CalculatedFieldType; + @Input() ownerId: EntityId; + @Input() isScript: boolean; + @Input({transform: booleanAttribute}) disable = false; + @Input({transform: booleanAttribute}) isEditValue = true; + @Input() watchKeyChange = false; @ViewChild(MatSort, { static: true }) sort: MatSort; errorText = ''; argumentsFormArray = this.fb.array([]); entityNameMap = new Map(); - sortOrder = { direction: 'asc', property: '' }; + sortOrder: { direction: SortDirection; property: string } = {direction: 'asc', property: ''}; dataSource = new CalculatedFieldArgumentDatasource(); + argumentNameColumn = 'common.name'; + argumentNameColumnCopy = 'calculated-fields.copy-argument-name'; + displayColumns = ['name', 'entityType', 'target', 'type', 'key', 'actions']; + + protected panelAdditionalCtx: Record + readonly entityTypeTranslations = entityTypeTranslations; readonly ArgumentTypeTranslations = ArgumentTypeTranslations; readonly ArgumentEntityType = ArgumentEntityType; @@ -105,14 +118,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces private propagateChange: (argumentsObj: Record) => void = () => {}; constructor( - private fb: FormBuilder, - private popoverService: TbPopoverService, - private viewContainerRef: ViewContainerRef, - private cd: ChangeDetectorRef, - private renderer: Renderer2, - private entityService: EntityService, - private destroyRef: DestroyRef, - private store: Store + protected fb: FormBuilder, + protected popoverService: TbPopoverService, + protected viewContainerRef: ViewContainerRef, + protected cd: ChangeDetectorRef, + protected renderer: Renderer2, + protected entityService: EntityService, + protected destroyRef: DestroyRef, + protected store: Store ) { this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => { this.updateDataSource(value); @@ -121,9 +134,8 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces } ngOnChanges(changes: SimpleChanges): void { - if (changes.calculatedFieldType?.previousValue - && changes.calculatedFieldType.currentValue !== changes.calculatedFieldType.previousValue) { - this.argumentsFormArray.updateValueAndValidity(); + if (isDefined(changes.isScript?.previousValue) && changes.isScript.currentValue !== changes.isScript.previousValue) { + this.changeIsScriptMode(); } } @@ -139,11 +151,15 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces this.propagateChange = fn; } - registerOnTouched(_): void {} + registerOnTouched(_: any): void {} validate(): ValidationErrors | null { this.updateErrorText(); - return this.errorText ? { argumentsFormArray: false } : null; + return this.errorText || !this.argumentsFormArray.controls.length ? { argumentsFormArray: false } : null; + } + + setDisabledState(isDisabled: boolean): void { + this.disable = isDisabled; } onDelete($event: Event, argument: CalculatedFieldArgumentValue): void { @@ -153,7 +169,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces this.argumentsFormArray.markAsDirty(); } - manageArgument($event: Event, matButton: MatButton, argument = {} as CalculatedFieldArgumentValue): void { + manageArgument($event: Event, matButton: MatButton, argument = {} as CalculatedFieldArgumentValue, readonly: boolean = false): void { $event?.stopPropagation(); if (this.popoverComponent && !this.popoverComponent.tbHidden) { this.popoverComponent.hide(); @@ -168,19 +184,22 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces index, argument, entityId: this.entityId, - calculatedFieldType: this.calculatedFieldType, + isScript: this.isScript, buttonTitle: isExists ? 'action.apply' : 'action.add', tenantId: this.tenantId, entityName: this.entityName, + ownerId: this.ownerId, + watchKeyChange: this.watchKeyChange, usedArgumentNames: this.argumentsFormArray.value.map(({ argumentName }) => argumentName).filter(name => name !== argument.argumentName), + readonly }; this.popoverComponent = this.popoverService.displayPopover({ trigger, renderer: this.renderer, componentType: CalculatedFieldArgumentPanelComponent, hostView: this.viewContainerRef, - preferredPlacement: isExists ? ['left', 'leftTop', 'leftBottom'] : ['topRight', 'right', 'rightTop'], - context: ctx, + preferredPlacement: isExists ? ['leftOnly', 'leftTopOnly', 'leftBottomOnly'] : ['rightOnly', 'rightTopOnly', 'rightBottomOnly'], + context: Object.assign(ctx, this.panelAdditionalCtx), isModal: true }); this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ entityName, ...value }) => { @@ -203,14 +222,11 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces this.dataSource.loadData(sortedValue); } - private updateErrorText(): void { - if (this.calculatedFieldType === CalculatedFieldType.SIMPLE - && this.argumentsFormArray.controls.some(control => control.value.refEntityKey.type === ArgumentType.Rolling)) { + protected updateErrorText(): void { + if (!this.isScript && this.argumentsFormArray.controls.some(control => control.value.refEntityKey.type === ArgumentType.Rolling)) { this.errorText = 'calculated-fields.hint.arguments-simple-with-rolling'; } else if (this.argumentsFormArray.controls.some(control => control.value.refEntityId?.id === NULL_UUID)) { this.errorText = 'calculated-fields.hint.arguments-entity-not-found'; - } else if (!this.argumentsFormArray.controls.length) { - this.errorText = 'calculated-fields.hint.arguments-empty'; } else { this.errorText = ''; } @@ -225,7 +241,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces } writeValue(argumentsObj: Record): void { - this.argumentsFormArray.clear(); + this.argumentsFormArray.clear({emitEvent: false}); this.populateArgumentsFormArray(argumentsObj); this.updateEntityNameMap(this.argumentsFormArray.value); } @@ -234,6 +250,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces return getEntityDetailsPageURL(id, type); } + protected changeIsScriptMode(): void { + this.argumentsFormArray.updateValueAndValidity({emitEvent: !this.disable}); + } + + protected isEditButtonShowBadge(argument: CalculatedFieldArgumentValue): boolean { + return !(argument.refEntityKey.type === ArgumentType.Rolling && !this.isScript) && argument.refEntityId?.id !== NULL_UUID + } + private populateArgumentsFormArray(argumentsObj: Record): void { Object.keys(argumentsObj).forEach(key => { const value: CalculatedFieldArgumentValue = { @@ -242,7 +266,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces }; this.argumentsFormArray.push(this.fb.control(value), { emitEvent: false }); }); - this.argumentsFormArray.updateValueAndValidity(); + this.updateDataSource(this.argumentsFormArray.value); } private updateEntityNameMap(values: CalculatedFieldArgumentValue[]): void { @@ -255,7 +279,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces return acc; }, {} as Record); const tasks = Object.entries(entitiesByType).map(([entityType, ids]) => - this.entityService.getEntities(entityType as EntityType, ids) + this.entityService.getEntities(entityType as EntityType, ids, {ignoreLoading: true}) ); if (!tasks.length) { return; diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module.ts new file mode 100644 index 0000000000..2b392564f9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module.ts @@ -0,0 +1,55 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldArgumentPanelComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component'; +import { + CalculatedFieldArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component'; +import { + PropagateArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/propagate-arguments-table.component'; +import { + RelatedAggregationArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/related-aggregation-arguments-table.component'; +import { + EntityAggregationArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/entity-aggregation-arguments-table.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + ], + declarations: [ + CalculatedFieldArgumentPanelComponent, + CalculatedFieldArgumentsTableComponent, + PropagateArgumentsTableComponent, + RelatedAggregationArgumentsTableComponent, + EntityAggregationArgumentsTableComponent, + ], + exports: [ + CalculatedFieldArgumentsTableComponent, + PropagateArgumentsTableComponent, + RelatedAggregationArgumentsTableComponent, + EntityAggregationArgumentsTableComponent, + ] +}) +export class CalculatedFieldArgumentsTableModule {} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/entity-aggregation-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/entity-aggregation-arguments-table.component.ts new file mode 100644 index 0000000000..b371653b18 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/entity-aggregation-arguments-table.component.ts @@ -0,0 +1,72 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Renderer2, ViewContainerRef, } from '@angular/core'; +import { FormBuilder, NG_VALIDATORS, NG_VALUE_ACCESSOR, } from '@angular/forms'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { EntityService } from '@core/http/entity.service'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + CalculatedFieldArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component'; +import { ArgumentEntityType } from '@shared/models/calculated-field.models'; + +@Component({ + selector: 'tb-entity-aggregation-arguments-table', + templateUrl: './calculated-field-arguments-table.component.html', + styleUrls: [`calculated-field-arguments-table.component.scss`], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => EntityAggregationArgumentsTableComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => EntityAggregationArgumentsTableComponent), + multi: true + } + ], +}) +export class EntityAggregationArgumentsTableComponent extends CalculatedFieldArgumentsTableComponent { + + constructor( + protected fb: FormBuilder, + protected popoverService: TbPopoverService, + protected viewContainerRef: ViewContainerRef, + protected cd: ChangeDetectorRef, + protected renderer: Renderer2, + protected entityService: EntityService, + protected destroyRef: DestroyRef, + protected store: Store + ) { + super(fb, popoverService, viewContainerRef, cd, renderer, entityService, destroyRef, store); + + this.argumentNameColumn = 'calculated-fields.argument-name'; + this.displayColumns = ['name', 'type', 'key', 'actions']; + this.panelAdditionalCtx = { + hiddenEntityTypes: true, + argumentEntityTypes: [ArgumentEntityType.Current], + hint: 'calculated-fields.entity-aggregation.argument-setting-hint', + hiddenDefaultValue: true, + hiddenEntityKeyTypes: true, + watchKeyChange: true, + }; + + this.isScript = false; + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/propagate-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/propagate-arguments-table.component.ts new file mode 100644 index 0000000000..b84ea2ca9f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/propagate-arguments-table.component.ts @@ -0,0 +1,132 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + DestroyRef, + forwardRef, + OnInit, + Renderer2, + ViewContainerRef, +} from '@angular/core'; +import { FormBuilder, NG_VALIDATORS, NG_VALUE_ACCESSOR, } from '@angular/forms'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { EntityService } from '@core/http/entity.service'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + CalculatedFieldArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component'; +import { + ArgumentEntityType, + ArgumentType, + CalculatedFieldArgumentValue, + FORBIDDEN_NAMES +} from '@shared/models/calculated-field.models'; +import { isDefined, isUndefinedOrNull } from '@core/utils'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; + +@Component({ + selector: 'tb-propagate-arguments-table', + templateUrl: './calculated-field-arguments-table.component.html', + styleUrls: [`calculated-field-arguments-table.component.scss`], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => PropagateArgumentsTableComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => PropagateArgumentsTableComponent), + multi: true + } + ], +}) +export class PropagateArgumentsTableComponent extends CalculatedFieldArgumentsTableComponent implements OnInit { + + constructor( + protected fb: FormBuilder, + protected popoverService: TbPopoverService, + protected viewContainerRef: ViewContainerRef, + protected cd: ChangeDetectorRef, + protected renderer: Renderer2, + protected entityService: EntityService, + protected destroyRef: DestroyRef, + protected store: Store + ) { + super(fb, popoverService, viewContainerRef, cd, renderer, entityService, destroyRef, store) + } + + ngOnInit() { + this.updatedValue(); + } + + protected changeIsScriptMode(): void { + this.updatedValue(); + super.changeIsScriptMode(); + } + + private updatedValue() { + if (this.isScript) { + this.argumentNameColumn = 'common.name'; + this.argumentNameColumnCopy = 'calculated-fields.copy-argument-name'; + this.displayColumns = ['name', 'entityType', 'target', 'type', 'key', 'actions']; + this.panelAdditionalCtx = null; + } else { + this.argumentNameColumn = 'calculated-fields.output-key'; + this.argumentNameColumnCopy = 'calculated-fields.copy-output-key'; + this.displayColumns = ['name', 'type', 'key', 'actions']; + this.panelAdditionalCtx = { + argumentEntityTypes: [ArgumentEntityType.Current], + argumentNameContext: { + label: 'calculated-fields.output-key', + required: 'calculated-fields.hint.output-key-required', + duplicate: 'calculated-fields.hint.output-key-duplicate', + pattern: 'calculated-fields.hint.output-key-pattern', + maxlength: 'calculated-fields.hint.output-key-max-length', + forbidden: 'calculated-fields.hint.output-key-forbidden' + }, + watchKeyChange: true, + forbiddenNames: [...FORBIDDEN_NAMES, 'propagationCtx'], + }; + } + } + + protected isEditButtonShowBadge(argument: CalculatedFieldArgumentValue): boolean { + if (!this.isScript && (isDefined(argument?.refEntityId) || isDefined(argument?.refDynamicSourceConfiguration))) { + return false; + } + return super.isEditButtonShowBadge(argument); + } + + protected updateErrorText(): void { + if (!this.isScript && this.argumentsFormArray.controls.some(control => isDefined(control.value?.refEntityId) || isDefined(control.value.refDynamicSourceConfiguration))) { + this.errorText = 'calculated-fields.hint.arguments-propagate-argument-entity-type'; + } else if (!this.isScript && this.argumentsFormArray.controls.some(control => control.value.refEntityKey.type === ArgumentType.Rolling)) { + this.errorText = 'calculated-fields.hint.arguments-propagate-arguments-with-rolling'; + } else if (this.argumentsFormArray.controls.some(control => control.value.refEntityId?.id === NULL_UUID)) { + this.errorText = 'calculated-fields.hint.arguments-entity-not-found'; + } else if (!this.argumentsFormArray.controls.length) { + this.errorText = 'calculated-fields.hint.arguments-empty'; + } else if (this.isScript && !this.argumentsFormArray.controls.some(control => isUndefinedOrNull(control.value?.refEntityId) && isUndefinedOrNull(control.value.refDynamicSourceConfiguration))) { + this.errorText = 'calculated-fields.hint.arguments-propagate-argument-must-current-entity'; + } else { + this.errorText = ''; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/related-aggregation-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/related-aggregation-arguments-table.component.ts new file mode 100644 index 0000000000..0a50a82729 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/related-aggregation-arguments-table.component.ts @@ -0,0 +1,96 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + DestroyRef, + forwardRef, + Input, + Renderer2, + ViewContainerRef, +} from '@angular/core'; +import { FormBuilder, NG_VALIDATORS, NG_VALUE_ACCESSOR, } from '@angular/forms'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { EntityService } from '@core/http/entity.service'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + CalculatedFieldArgumentsTableComponent +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component'; +import { ArgumentEntityType, RelationPathLevel } from '@shared/models/calculated-field.models'; +import { AliasFilterType } from '@shared/models/alias.models'; +import { EntityType } from '@shared/models/entity-type.models'; + +@Component({ + selector: 'tb-related-aggregation-arguments-table', + templateUrl: './calculated-field-arguments-table.component.html', + styleUrls: [`calculated-field-arguments-table.component.scss`], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => RelatedAggregationArgumentsTableComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => RelatedAggregationArgumentsTableComponent), + multi: true + } + ], +}) +export class RelatedAggregationArgumentsTableComponent extends CalculatedFieldArgumentsTableComponent { + + @Input({required: true}) + set relation(value: RelationPathLevel) { + this.panelAdditionalCtx.predefinedEntityFilter = { + type: AliasFilterType.relationsQuery, + rootStateEntity: false, + rootEntity: this.entityId, + direction: value.direction, + filters: [{ + relationType: value.relationType, + entityTypes: [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.TENANT] + }], + maxLevel: 1, + }; + } + + constructor( + protected fb: FormBuilder, + protected popoverService: TbPopoverService, + protected viewContainerRef: ViewContainerRef, + protected cd: ChangeDetectorRef, + protected renderer: Renderer2, + protected entityService: EntityService, + protected destroyRef: DestroyRef, + protected store: Store + ) { + super(fb, popoverService, viewContainerRef, cd, renderer, entityService, destroyRef, store); + + this.argumentNameColumn = 'calculated-fields.argument-name'; + this.displayColumns = ['name', 'type', 'key', 'actions']; + this.panelAdditionalCtx = { + hiddenEntityTypes: true, + defaultValueRequired: true, + argumentEntityTypes: [ArgumentEntityType.Current], + hint: 'calculated-fields.hint.setting-arguments-aggregation', + watchKeyChange: true, + }; + + this.isScript = false; + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/common/calculated-field-panel.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/common/calculated-field-panel.scss new file mode 100644 index 0000000000..f3218dbab7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/common/calculated-field-panel.scss @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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-config-panel { + width: 520px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + max-width: fit-content; + } + @media #{$mat-xs} { + width: 90vw; + } + + .tb-config-panel-title { + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + font-weight: 500; + font-size: 16px; + } + + .tb-config-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + + .fixed-title-width { + @media #{$mat-xs} { + min-width: 120px; + } + } + } + + .tb-config-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/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component.ts deleted file mode 100644 index e0f67a87cd..0000000000 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -/// -/// Copyright © 2016-2026 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT 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, Inject, 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 { Router } from '@angular/router'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { CalculatedFieldEventBody, DebugEventType, Event, EventType } from '@shared/models/event.models'; -import { EventTableComponent } from '@home/components/event/event-table.component'; -import { - CalculatedField, - CalculatedFieldTestScriptFn, - CalculatedFieldType -} from '@shared/models/calculated-field.models'; - -export interface CalculatedFieldDebugDialogData { - tenantId: string; - value: CalculatedField; - getTestScriptDialogFn: CalculatedFieldTestScriptFn; -} - -@Component({ - selector: 'tb-calculated-field-debug-dialog', - styleUrls: ['calculated-field-debug-dialog.component.scss'], - templateUrl: './calculated-field-debug-dialog.component.html', -}) -export class CalculatedFieldDebugDialogComponent extends DialogComponent implements AfterViewInit { - - @ViewChild(EventTableComponent, {static: true}) eventsTable: EventTableComponent; - - readonly DebugEventType = DebugEventType; - readonly debugEventTypes = DebugEventType; - readonly EventType = EventType; - - constructor(protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: CalculatedFieldDebugDialogData, - protected dialogRef: MatDialogRef) { - super(store, router, dialogRef); - } - - ngAfterViewInit(): void { - this.eventsTable.entitiesTable.cellActionDescriptors[0].isEnabled = (event => this.data.value.type === CalculatedFieldType.SCRIPT && !!(event as Event).body.arguments); - this.eventsTable.entitiesTable.updateData(); - } - - cancel(): void { - this.dialogRef.close(null); - } - - onDebugEventSelected(event: CalculatedFieldEventBody): void { - this.data.getTestScriptDialogFn(this.data.value, JSON.parse(event.arguments)) - .subscribe(expression => this.dialogRef.close(expression)); - } -} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html index f5f009b320..b463930c09 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html @@ -15,12 +15,13 @@ limitations under the License. --> -
+

{{ 'entity.type-calculated-field' | translate}}

- -
- -
-
-
-
-
{{ 'calculated-fields.output' | translate }}
-
- - {{ 'calculated-fields.output-type' | translate }} - - @for (type of outputTypes; track type) { - {{ OutputTypeTranslations.get(type) | translate}} - } - - - @if (outputFormGroup.get('type').value === OutputType.Attribute - && (data.entityId.entityType === EntityType.DEVICE || data.entityId.entityType === EntityType.DEVICE_PROFILE)) { - - {{ 'calculated-fields.attribute-scope' | translate }} - - - {{ 'calculated-fields.server-attributes' | translate }} - - - {{ 'calculated-fields.shared-attributes' | translate }} - - - - } -
- @if (fieldFormGroup.get('type').value === CalculatedFieldType.SIMPLE) { -
- - - {{ (outputFormGroup.get('type').value === OutputType.Timeseries - ? 'calculated-fields.timeseries-key' - : 'calculated-fields.attribute-key') - | translate }} - - - @if (outputFormGroup.get('name').errors && outputFormGroup.get('name').touched) { - - @if (outputFormGroup.get('name').hasError('required')) { - {{ 'common.hint.key-required' | translate }} - } @else if (outputFormGroup.get('name').hasError('pattern')) { - {{ 'common.hint.key-pattern' | translate }} - } @else if (outputFormGroup.get('name').hasError('maxlength')) { - {{ 'common.hint.key-max-length' | translate }} - } - - } - - - {{ 'calculated-fields.decimals-by-default' | translate }} - - @if (outputFormGroup.get('decimalsByDefault').errors && outputFormGroup.get('decimalsByDefault').touched) { - {{ 'calculated-fields.hint.decimals-range' | translate }} - } - -
-
- -
- calculated-fields.use-latest-timestamp -
-
-
- } -
- + @switch (fieldFormGroup.get('type').value) { + @case (CalculatedFieldType.GEOFENCING) { + + } + @case (CalculatedFieldType.PROPAGATION) { + + } + @case (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) { + + } + @case (CalculatedFieldType.ENTITY_AGGREGATION) { + + } + @default { + + } + }
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss index ab9638b7f5..fb02408d02 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss @@ -16,6 +16,11 @@ .calculated-field-dialog-container { width: 869px; max-width: 100%; + display: grid; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12); + --mdc-outlined-text-field-container-shape: 6px; + --mat-form-field-trailing-icon-color: rgba(0, 0, 0, 0.56); } .tbel-script-lang-chip { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts index 1dbdf59f13..6e8b7fb93f 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts @@ -18,31 +18,30 @@ import { Component, DestroyRef, Inject, ViewEncapsulation } 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'; import { CalculatedField, CalculatedFieldConfiguration, - calculatedFieldDefaultScript, + calculatedFieldsEntityTypeList, CalculatedFieldTestScriptFn, CalculatedFieldType, - CalculatedFieldTypeTranslations, - getCalculatedFieldArgumentsEditorCompleter, - getCalculatedFieldArgumentsHighlights, - OutputType, - OutputTypeTranslations + calculatedFieldTypes, + CalculatedFieldTypeTranslations } from '@shared/models/calculated-field.models'; -import { digitsRegex, oneSpaceInsideRegex } from '@shared/models/regex.constants'; -import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { EntityType } from '@shared/models/entity-type.models'; -import { map, startWith, switchMap } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { ScriptLanguage } from '@shared/models/rule-node.models'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { Observable } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model'; +import { deepTrim } from '@core/utils'; +import { BaseData } from '@shared/models/base-data'; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; +import { FormGroup } from '@angular/forms'; +import { AssetInfo } from '@shared/models/asset.models'; +import { DeviceInfo } from '@shared/models/device.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; export interface CalculatedFieldDialogData { value?: CalculatedField; @@ -50,9 +49,11 @@ export interface CalculatedFieldDialogData { entityId: EntityId; tenantId: string; entityName?: string; + ownerId: EntityId; additionalDebugActionConfig: AdditionalDebugActionConfig<(calculatedField: CalculatedField) => void>; getTestScriptDialogFn: CalculatedFieldTestScriptFn; isDirty?: boolean; + disabledSelectType?: boolean; } @Component({ @@ -63,55 +64,23 @@ export interface CalculatedFieldDialogData { }) export class CalculatedFieldDialogComponent extends DialogComponent { - fieldFormGroup = this.fb.group({ - name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - type: [CalculatedFieldType.SIMPLE], - debugSettings: [], - configuration: this.fb.group({ - arguments: this.fb.control({}), - expressionSIMPLE: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - expressionSCRIPT: [calculatedFieldDefaultScript], - output: this.fb.group({ - name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - scope: [{ value: AttributeScope.SERVER_SCOPE, disabled: true }], - type: [OutputType.Timeseries], - decimalsByDefault: [null as number, [Validators.min(0), Validators.max(15), Validators.pattern(digitsRegex)]], - }), - useLatestTs: [false] - }), - }); - - functionArgs$ = this.configFormGroup.get('arguments').valueChanges - .pipe( - startWith(this.data.value?.configuration?.arguments ?? {}), - map(argumentsObj => ['ctx', ...Object.keys(argumentsObj)]) - ); - - argumentsEditorCompleter$ = this.configFormGroup.get('arguments').valueChanges - .pipe( - startWith(this.data.value?.configuration?.arguments ?? {}), - map(argumentsObj => getCalculatedFieldArgumentsEditorCompleter(argumentsObj)) - ); - - argumentsHighlightRules$ = this.configFormGroup.get('arguments').valueChanges - .pipe( - startWith(this.data.value?.configuration?.arguments ?? {}), - map(argumentsObj => getCalculatedFieldArgumentsHighlights(argumentsObj)) - ); + fieldFormGroup: FormGroup; additionalDebugActionConfig = this.data.value?.id ? { ...this.data.additionalDebugActionConfig, action: () => this.data.additionalDebugActionConfig.action({ id: this.data.value.id, ...this.fromGroupValue }), } : null; - readonly OutputTypeTranslations = OutputTypeTranslations; - readonly OutputType = OutputType; - readonly AttributeScope = AttributeScope; + entityName = this.data.entityName; + ownerId = this.data.ownerId; + + disabledConfiguration = false; + isLoading = false; + readonly EntityType = EntityType; + readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList; readonly CalculatedFieldType = CalculatedFieldType; - readonly ScriptLanguage = ScriptLanguage; - readonly fieldTypes = Object.values(CalculatedFieldType) as CalculatedFieldType[]; - readonly outputTypes = Object.values(OutputType) as OutputType[]; + readonly fieldTypes = calculatedFieldTypes; readonly CalculatedFieldTypeTranslations = CalculatedFieldTypeTranslations; constructor(protected store: Store, @@ -120,34 +89,37 @@ export class CalculatedFieldDialogComponent extends DialogComponent, private calculatedFieldsService: CalculatedFieldsService, private destroyRef: DestroyRef, - private fb: FormBuilder) { + private cfFormService: CalculatedFieldFormService) { super(store, router, dialogRef); - this.observeIsLoading(); + this.fieldFormGroup = this.cfFormService.buildForm(); + this.cfFormService.setupTypeChange(this.fieldFormGroup, this.destroyRef); this.applyDialogData(); - this.observeTypeChanges(); - } - get configFormGroup(): FormGroup { - return this.fieldFormGroup.get('configuration') as FormGroup; - } + if (this.data.isDirty) { + this.fieldFormGroup.markAsDirty(); + } + + if (!this.data.entityId) { + this.fieldFormGroup.get('entityId').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((entityId) => { + this.disabledConfiguration = !entityId; + if (this.disabledConfiguration) { + this.fieldFormGroup.get('configuration').disable({emitEvent: false}); + } else { + this.fieldFormGroup.get('configuration').enable({emitEvent: false}); + this.fieldFormGroup.get('configuration').updateValueAndValidity({emitEvent: false}); + } + }); + } - get outputFormGroup(): FormGroup { - return this.fieldFormGroup.get('configuration').get('output') as FormGroup; + if (this.data.disabledSelectType) { + this.fieldFormGroup.get('type').disable({emitEvent: false}); + } } get fromGroupValue(): CalculatedField { - const { configuration, type, name, ...rest } = this.fieldFormGroup.value; - const { expressionSIMPLE, expressionSCRIPT, output, ...restConfig } = configuration; - return { - configuration: { - ...restConfig, - type, expression: configuration['expression'+type].trim(), - output: { ...output, name: output.name?.trim() ?? '' } - }, - name: name.trim(), - type, - ...rest, - } as CalculatedField; + return deepTrim(this.fieldFormGroup.getRawValue() as CalculatedField); } cancel(): void { @@ -156,101 +128,51 @@ export class CalculatedFieldDialogComponent extends DialogComponent this.dialogRef.close(calculatedField)); - } - } - - onTestScript(): void { - const calculatedFieldId = this.data.value?.id?.id; - let testScriptDialogResult$: Observable; - - if (calculatedFieldId) { - testScriptDialogResult$ = this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.data.getTestScriptDialogFn(this.fromGroupValue, args, false); - }), - takeUntilDestroyed(this.destroyRef) - ) + .subscribe({ + next: calculatedField => this.dialogRef.close(calculatedField), + error: () => this.isLoading = false + }); } else { - testScriptDialogResult$ = this.data.getTestScriptDialogFn(this.fromGroupValue, null, false); + this.fieldFormGroup.get('name').markAsTouched(); } - - testScriptDialogResult$.subscribe(expression => { - this.configFormGroup.get('expressionSCRIPT').setValue(expression); - this.configFormGroup.get('expressionSCRIPT').markAsDirty(); - }); } - private applyDialogData(): void { - const { configuration = {}, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, ...value } = this.data.value ?? {}; - const { expression, ...restConfig } = configuration as CalculatedFieldConfiguration; - const updatedConfig = { ...restConfig , ['expression'+type]: expression }; - this.fieldFormGroup.patchValue({ configuration: updatedConfig, type, debugSettings, ...value }, {emitEvent: false}); + onTestScript(expression?: string): Observable { + return this.cfFormService.testScript( + this.data.value?.id?.id, + this.fromGroupValue, + this.data.getTestScriptDialogFn, + this.destroyRef, + expression + ); } - private observeTypeChanges(): void { - this.toggleKeyByCalculatedFieldType(this.fieldFormGroup.get('type').value); - this.toggleScopeByOutputType(this.outputFormGroup.get('type').value); - - this.outputFormGroup.get('type').valueChanges - .pipe(takeUntilDestroyed()) - .subscribe(type => this.toggleScopeByOutputType(type)); - this.fieldFormGroup.get('type').valueChanges - .pipe(takeUntilDestroyed()) - .subscribe(type => this.toggleKeyByCalculatedFieldType(type)); + changeEntity(entity: BaseData): void { + this.entityName = entity.name; + if (this.isAssignedToCustomer(entity as AssetInfo | DeviceInfo)) { + this.ownerId = (entity as AssetInfo | DeviceInfo).customerId; + } } - private toggleScopeByOutputType(type: OutputType): void { - if (type === OutputType.Attribute) { - this.outputFormGroup.get('scope').enable({emitEvent: false}); - } else { - this.outputFormGroup.get('scope').disable({emitEvent: false}); - } - if (this.fieldFormGroup.get('type').value === CalculatedFieldType.SIMPLE) { - if (type === OutputType.Attribute) { - this.configFormGroup.get('useLatestTs').disable({emitEvent: false}); - } else { - this.configFormGroup.get('useLatestTs').enable({emitEvent: false}); - } - } else { - this.configFormGroup.get('useLatestTs').disable({emitEvent: false}); - } + get entityId(): EntityId { + return this.data.entityId || this.fieldFormGroup.get('entityId').value; } - private toggleKeyByCalculatedFieldType(type: CalculatedFieldType): void { - if (type === CalculatedFieldType.SIMPLE) { - this.outputFormGroup.get('name').enable({emitEvent: false}); - this.configFormGroup.get('expressionSIMPLE').enable({emitEvent: false}); - this.configFormGroup.get('expressionSCRIPT').disable({emitEvent: false}); - if (this.outputFormGroup.get('type').value === OutputType.Attribute) { - this.configFormGroup.get('useLatestTs').disable({emitEvent: false}); - } else { - this.configFormGroup.get('useLatestTs').enable({emitEvent: false}); - } - } else { - this.outputFormGroup.get('name').disable({emitEvent: false}); - this.configFormGroup.get('useLatestTs').disable({emitEvent: false}); - this.configFormGroup.get('expressionSIMPLE').disable({emitEvent: false}); - this.configFormGroup.get('expressionSCRIPT').enable({emitEvent: false}); + private applyDialogData(): void { + const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.data.entityId, ...value } = this.data.value ?? {}; + const preparedConfig = this.cfFormService.prepareConfig(configuration); + this.fieldFormGroup.patchValue({ configuration: preparedConfig, type, debugSettings, entityId, ...value }, {emitEvent: false}); + setTimeout(() => this.fieldFormGroup.get('type').updateValueAndValidity({onlySelf: true})); + if (!this.data.entityId) { + this.fieldFormGroup.get('configuration').disable({emitEvent: false}); + this.disabledConfiguration = true; } } - private observeIsLoading(): void { - this.isLoading$.pipe(takeUntilDestroyed()).subscribe(loading => { - if (loading) { - this.fieldFormGroup.disable({emitEvent: false}); - } else { - this.fieldFormGroup.enable({emitEvent: false}); - this.toggleScopeByOutputType(this.outputFormGroup.get('type').value); - this.toggleKeyByCalculatedFieldType(this.fieldFormGroup.get('type').value); - if (this.data.isDirty) { - this.fieldFormGroup.markAsDirty(); - } - } - }); + private isAssignedToCustomer(entity: AssetInfo | DeviceInfo): boolean { + return entity && entity.customerId && entity.customerId.id !== NULL_UUID; } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.html new file mode 100644 index 0000000000..ab7987c340 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.html @@ -0,0 +1,144 @@ + +
+
+
+ {{ 'calculated-fields.arguments' | translate }} +
+
+ {{ 'calculated-fields.entity-aggregation.argument-hint' | translate }} +
+ +
+
+
+ {{ 'calculated-fields.metrics.metrics' | translate }} +
+ +
+
+
+ {{ 'calculated-fields.entity-aggregation.aggregation-interval' | translate }} +
+ +
+ + calculated-fields.aggregate-interval-type + + + {{ AggIntervalTypeTranslations.get(type) | translate }} + + + + + +
+ @if (entityAggregationConfiguration.get('interval.type').value === AggIntervalType.CUSTOM) { + + + } +
+ +
+ {{ 'calculated-fields.entity-aggregation.apply-offset' | translate }} +
+
+ @if (entityAggregationConfiguration.get('interval.allowOffsetSec').value) { + + +
+ {{ hint }} +
+ } +
+
+
+ +
+ {{ 'calculated-fields.entity-aggregation.wait-delay' | translate }} +
+
+ @if (entityAggregationConfiguration.get('allowWatermark').value) { + + + + + } +
+
+ +
+ {{ 'calculated-fields.entity-aggregation.produce-intermediate-result' | translate }} +
+
+
+
+ +
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts new file mode 100644 index 0000000000..11c95352d3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts @@ -0,0 +1,461 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { booleanAttribute, Component, forwardRef, Input } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { EntityId } from '@shared/models/id/entity-id'; +import { + AggInterval, + AggIntervalType, + AggIntervalTypeTranslations, + CalculatedFieldEntityAggregationConfiguration, + CalculatedFieldOutput, + CalculatedFieldType, + defaultCalculatedFieldOutput, + notEmptyObjectValidator, +} from '@shared/models/calculated-field.models'; +import { filter, map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { AVG_MONTH, AVG_QUARTER, DAY, HOUR, MINUTE, SECOND, WEEK, YEAR } from '@shared/models/time/time.models'; +import { deepClone, isDefinedAndNotNull } from '@core/utils'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { merge, Observable } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import _moment from 'moment'; + +interface CalculatedFieldEntityAggregationConfigurationValue extends CalculatedFieldEntityAggregationConfiguration { + interval: AggInterval & {allowOffsetSec?: boolean}; + allowWatermark: boolean; +} + +enum TimeCategory { + SECONDS = 'SECONDS', + MINUTES = 'MINUTES', + HOURS = 'HOURS', + DAYS = 'DAYS' +} + +@Component({ + selector: 'tb-entity-aggregation-component', + templateUrl: './entity-aggregation-component.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => EntityAggregationComponentComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => EntityAggregationComponentComponent), + multi: true + } + ], +}) +export class EntityAggregationComponentComponent implements ControlValueAccessor, Validator { + + @Input({required: true}) + entityId: EntityId; + + @Input({required: true}) + tenantId: string; + + @Input({required: true}) + entityName: string; + + @Input() testScript: (expression?: string) => Observable; + + @Input({transform: booleanAttribute}) isEditValue = true; + + readonly minAllowedAggregationIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedAggregationIntervalInSecForCF; + readonly intermediateAggregationIntervalInSecForCF = getCurrentAuthState(this.store).intermediateAggregationIntervalInSecForCF; + readonly DayInSec = DAY / SECOND; + + entityAggregationConfiguration = this.fb.group({ + arguments: this.fb.control({}, notEmptyObjectValidator()), + metrics: this.fb.control({}, notEmptyObjectValidator()), + interval: this.fb.group({ + type: [AggIntervalType.HOUR], + tz: ['', Validators.required], + durationSec: [this.minAllowedAggregationIntervalInSecForCF, Validators.required], + allowOffsetSec: [false], + offsetSec: [this.minAllowedAggregationIntervalInSecForCF > 60 ? MINUTE / SECOND : 1, Validators.required], + }), + allowWatermark: [false], + watermark: this.fb.group({ + duration: [HOUR/SECOND, Validators.required], + }), + produceIntermediateResult: [false], + output: this.fb.control(defaultCalculatedFieldOutput), + }); + + arguments$ = this.entityAggregationConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => Object.keys(argumentsObj)) + ); + + AggIntervalType = AggIntervalType; + AggIntervalTypes = Object.values(AggIntervalType) as AggIntervalType[]; + AggIntervalTypeTranslations = AggIntervalTypeTranslations; + + hint: string; + + private propagateChange: (config: CalculatedFieldEntityAggregationConfiguration) => void = () => { }; + + constructor(private fb: FormBuilder, + private store: Store, + private translate: TranslateService,) { + + this.entityAggregationConfiguration.get('interval.type').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((type: AggIntervalType) => { + this.checkAggIntervalType(type); + }); + + this.entityAggregationConfiguration.get('interval.allowOffsetSec').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((allow: boolean) => { + this.checkIntervalDuration(allow); + }); + + this.entityAggregationConfiguration.get('allowWatermark').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((allow: boolean) => { + this.checkWatermark(allow); + }); + + merge( + this.entityAggregationConfiguration.get('interval.type').valueChanges, + this.entityAggregationConfiguration.get('interval.durationSec').valueChanges, + this.entityAggregationConfiguration.get('interval.offsetSec').valueChanges, + this.entityAggregationConfiguration.get('interval.allowOffsetSec').valueChanges, + ).pipe( + filter(() => this.entityAggregationConfiguration.get('interval.allowOffsetSec').value), + takeUntilDestroyed() + ).subscribe(() => { + this.updatedOffsetHint(); + }); + + merge( + this.entityAggregationConfiguration.get('interval.type').valueChanges, + this.entityAggregationConfiguration.get('interval.durationSec').valueChanges + ).pipe( + takeUntilDestroyed() + ).subscribe(() => { + this.checkProduceIntermediate(); + }); + + this.entityAggregationConfiguration.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value: CalculatedFieldEntityAggregationConfigurationValue) => { + this.updatedModel(deepClone(value)); + }); + } + + validate(): ValidationErrors | null { + return this.entityAggregationConfiguration.valid || this.entityAggregationConfiguration.disabled ? null : {invalidPropagateConfig: false}; + } + + writeValue(value: CalculatedFieldEntityAggregationConfiguration): void { + const data: CalculatedFieldEntityAggregationConfigurationValue = { + ...value, + allowWatermark: isDefinedAndNotNull(value.watermark), + interval: {...value.interval, allowOffsetSec: isDefinedAndNotNull(value?.interval?.offsetSec)} + } + this.entityAggregationConfiguration.patchValue(data, {emitEvent: false}); + if (this.entityAggregationConfiguration.enabled) { + this.checkAggIntervalType(this.entityAggregationConfiguration.get('interval.type').value); + this.checkIntervalDuration(this.entityAggregationConfiguration.get('interval.allowOffsetSec').value); + this.checkWatermark(this.entityAggregationConfiguration.get('allowWatermark').value); + this.checkProduceIntermediate(); + } + this.updatedOffsetHint(); + setTimeout(() => { + this.entityAggregationConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); + }); + } + + registerOnChange(fn: (config: CalculatedFieldEntityAggregationConfiguration) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: any): void { } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.entityAggregationConfiguration.disable({emitEvent: false}); + } else { + this.entityAggregationConfiguration.enable({emitEvent: false}); + this.checkAggIntervalType(this.entityAggregationConfiguration.get('interval.type').value); + this.checkIntervalDuration(this.entityAggregationConfiguration.get('interval.allowOffsetSec').value); + this.checkWatermark(this.entityAggregationConfiguration.get('allowWatermark').value); + this.checkProduceIntermediate(); + } + } + + get maxOffsetTime(): number { + switch (this.entityAggregationConfiguration.get('interval.type').value as AggIntervalType) { + case AggIntervalType.HOUR: + return HOUR / SECOND - 1; + case AggIntervalType.DAY: + return DAY / SECOND - 1; + case AggIntervalType.WEEK: + case AggIntervalType.WEEK_SUN_SAT: + return 7 * DAY / SECOND - 1; + case AggIntervalType.MONTH: + return AVG_MONTH / SECOND; + case AggIntervalType.QUARTER: + return AVG_QUARTER / SECOND - 1; + case AggIntervalType.YEAR: + return YEAR / SECOND - 1; + case AggIntervalType.CUSTOM: + return this.entityAggregationConfiguration.get('interval.durationSec').value - 1; + } + } + + private updatedModel(value: CalculatedFieldEntityAggregationConfigurationValue): void { + value.type = CalculatedFieldType.ENTITY_AGGREGATION; + if (!value.interval.allowOffsetSec) { + delete value.interval.offsetSec; + } + delete value.interval.allowOffsetSec; + if (!value.allowWatermark) { + delete value.watermark; + } + delete value.allowWatermark; + this.propagateChange(value); + } + + private checkAggIntervalType(type: AggIntervalType) { + if (type === AggIntervalType.CUSTOM) { + this.entityAggregationConfiguration.get('interval.durationSec').enable({emitEvent: false}); + } else { + this.entityAggregationConfiguration.get('interval.durationSec').disable({emitEvent: false}); + } + } + + private checkIntervalDuration(allow: boolean) { + if (allow) { + this.entityAggregationConfiguration.get('interval.offsetSec').enable({emitEvent: false}); + } else { + this.entityAggregationConfiguration.get('interval.offsetSec').disable({emitEvent: false}); + this.hint = ''; + } + } + + private checkWatermark(allow: boolean) { + if (allow) { + this.entityAggregationConfiguration.get('watermark').enable({emitEvent: false}); + } else { + this.entityAggregationConfiguration.get('watermark').disable({emitEvent: false}); + } + } + + private checkProduceIntermediate() { + const intervalType = this.entityAggregationConfiguration.get('interval.type').value as AggIntervalType; + let durationSec = 0; + switch (intervalType) { + case AggIntervalType.CUSTOM: + durationSec = this.entityAggregationConfiguration.get('interval.durationSec').value; + break + case AggIntervalType.HOUR: + durationSec = HOUR / SECOND; + break + case AggIntervalType.DAY: + durationSec = DAY / SECOND; + break + case AggIntervalType.WEEK: + case AggIntervalType.WEEK_SUN_SAT: + durationSec = WEEK / SECOND; + break + case AggIntervalType.MONTH: + durationSec = AVG_MONTH / SECOND; + break + case AggIntervalType.QUARTER: + durationSec = AVG_QUARTER / SECOND; + break + case AggIntervalType.YEAR: + durationSec = YEAR / SECOND; + break + } + if (durationSec > this.intermediateAggregationIntervalInSecForCF) { + this.entityAggregationConfiguration.get('produceIntermediateResult').enable({emitEvent: false}); + } else { + this.entityAggregationConfiguration.get('produceIntermediateResult').disable({emitEvent: false}); + } + } + + private updatedOffsetHint(): void { + const offset = this.entityAggregationConfiguration.get('interval.offsetSec').value; + const intervalType = this.entityAggregationConfiguration.get('interval.type').value as AggIntervalType; + const durationSec = this.entityAggregationConfiguration.get('interval.durationSec').value; + const offsetCategory = this.getTimeCategory(offset); + const now = _moment.utc(); + let interval: string; + if (intervalType === AggIntervalType.CUSTOM) { + const formatString = this.getCustomFormatString(offsetCategory, this.getTimeCategory(durationSec)); + const intervals: string[] = []; + let allInterval = durationSec >= HOUR*6/SECOND && durationSec < DAY/SECOND; + now.startOf('year').add(offset, 'seconds'); + + let repeat = 2; + if (allInterval) { + repeat = Math.floor(DAY/SECOND/durationSec); + if (repeat > 4) { + repeat = 2; + allInterval = false; + } + } + + for (let i = 0; i < repeat; i++) { + const s1 = now.clone().add(i * durationSec, 'seconds').format(formatString); + const s2 = now.clone().add((i + 1) * durationSec, 'seconds').format(formatString); + intervals.push(`${s1} - ${s2}`); + } + interval = intervals.join('; '); + + if (allInterval) { + this.hint = this.translate.instant('calculated-fields.aggregate-period-hint-offset', {interval}); + } else { + interval += '…' + this.hint = this.translate.instant('calculated-fields.aggregate-period-hint-offset-and-so-on', {interval}); + } + } else { + interval = this.buildStandardIntervalString(now, intervalType, offset, offsetCategory); + this.hint = this.translate.instant('calculated-fields.aggregate-period-hint-offset-and-so-on', { interval }); + } + } + + private getTimeCategory(seconds: number): TimeCategory { + if (seconds % (DAY / SECOND) === 0) { + return TimeCategory.DAYS; + } + if (seconds % (HOUR / SECOND) === 0) { + return TimeCategory.HOURS; + } + if (seconds % (MINUTE / SECOND) === 0) { + return TimeCategory.MINUTES; + } + return TimeCategory.SECONDS; + } + + private getCustomFormatString(offsetCat: TimeCategory, durationCat: TimeCategory): string { + if (durationCat === TimeCategory.DAYS) { + if (offsetCat === TimeCategory.SECONDS) { + return '[Day] D, HH:mm:ss'; + } + if (offsetCat === TimeCategory.MINUTES || offsetCat === TimeCategory.HOURS) { + return '[Day] D, HH:mm'; + } + return '[Day] D'; + } else { + if (offsetCat === TimeCategory.SECONDS) { + return 'HH:mm:ss'; + } + return 'HH:mm'; + } + } + + private formatAdditiveInterval(now: _moment.Moment, addUnit: 'hour' | 'day' | 'month' | 'quarter', offsetCat: TimeCategory, + formats: { [key in TimeCategory]?: { s1: string, s2: string, s3: string } }): string { + const formatTs = formats[offsetCat] || formats[TimeCategory.SECONDS]; + + if (!formatTs) { + return ''; + } + + const s1 = now.format(formatTs.s1); + const s2 = now.clone().add(1, addUnit).format(formatTs.s2); + const s3 = now.clone().add(2, addUnit).format(formatTs.s3); + + return `${s1} - ${s2}; ${s2} - ${s3}…`; + } + + private formatNextInterval(now: _moment.Moment, offsetCat: TimeCategory, secFmt: string, minHourFmt: string, dayFmt: string): string { + let s1: string; + if (offsetCat === TimeCategory.SECONDS) { + s1 = now.format(secFmt); + } else if (offsetCat === TimeCategory.MINUTES || offsetCat === TimeCategory.HOURS) { + s1 = now.format(minHourFmt); + } else { + s1 = now.format(dayFmt); + } + + const s2 = `Next ${s1}`; + const s3 = `Following ${s1}`; + return `${s1} - ${s2}; ${s2} - ${s3}… `; + } + + private buildStandardIntervalString(now: _moment.Moment, type: AggIntervalType, offset: number, offsetCat: TimeCategory): string { + switch (type) { + case AggIntervalType.HOUR: + now.startOf('day').add(offset, 'seconds'); + return this.formatAdditiveInterval(now, 'hour', offsetCat, { + [TimeCategory.SECONDS]: { s1: 'HH:mm:ss', s2: 'HH:mm:ss', s3: 'HH:mm:ss' }, + [TimeCategory.MINUTES]: { s1: 'HH:mm:ss', s2: 'HH:mm', s3: 'HH:mm' } + }); + + case AggIntervalType.DAY: + now.startOf('month').add(offset, 'seconds'); + return this.formatAdditiveInterval(now, 'day', offsetCat, { + [TimeCategory.SECONDS]: { s1: '[Day] D, HH:mm:ss', s2: '[Day] D, HH:mm:ss', s3: '[Day] D, HH:mm:ss' }, + [TimeCategory.MINUTES]: { s1: '[Day] D, HH:mm:ss', s2: '[Day] D, HH:mm', s3: '[Day] D, HH:mm' }, + [TimeCategory.HOURS]: { s1: 'HH:mm:ss', s2: '[Day] D, HH:mm', s3: '[Day] D, HH:mm' } // Note: Original logic, s1 format is different + }); + + case AggIntervalType.WEEK: + now.isoWeekday(1).startOf('isoWeek').add(offset, 'seconds'); + return this.formatNextInterval(now, offsetCat, 'ddd, HH:mm:ss', 'ddd, HH:mm', 'ddd'); + + case AggIntervalType.WEEK_SUN_SAT: + now.startOf('week').add(offset, 'seconds'); + return this.formatNextInterval(now, offsetCat, 'ddd, HH:mm:ss', 'ddd, HH:mm', 'ddd'); + + case AggIntervalType.MONTH: + now.startOf('year').add(offset, 'seconds'); + return this.formatAdditiveInterval(now, 'month', offsetCat, { + [TimeCategory.SECONDS]: { s1: 'Do [of month], HH:mm:ss', s2: '[Next] Do, HH:mm:ss', s3: '[Following] Do, HH:mm:ss' }, + [TimeCategory.MINUTES]: { s1: 'Do [of month], HH:mm', s2: '[Next] Do, HH:mm', s3: '[Following] Do, HH:mm' }, + [TimeCategory.HOURS]: { s1: 'Do [of month], HH:mm', s2: '[Next] Do, HH:mm', s3: '[Following] Do, HH:mm' }, + [TimeCategory.DAYS]: { s1: 'Do [of month]', s2: '[Next] Do', s3: '[Following] Do' } + }); + + case AggIntervalType.QUARTER: + now.startOf('year').add(offset, 'seconds'); + return this.formatAdditiveInterval(now, 'quarter', offsetCat, { + [TimeCategory.SECONDS]: { s1: 'MMM Do, HH:mm:ss', s2: 'MMM Do, HH:mm:ss', s3: 'MMM Do, HH:mm:ss' }, + [TimeCategory.MINUTES]: { s1: 'MMM Do, HH:mm', s2: 'MMM Do, HH:mm', s3: 'MMM Do, HH:mm' }, + [TimeCategory.HOURS]: { s1: 'MMM Do, HH:mm', s2: 'MMM Do, HH:mm', s3: 'MMM Do, HH:mm' }, + [TimeCategory.DAYS]: { s1: 'MMM Do', s2: 'MMM Do', s3: 'MMM Do' } + }); + + case AggIntervalType.YEAR: + now.startOf('year').add(offset, 'seconds'); + return this.formatNextInterval(now, offsetCat, 'MMM Do, HH:mm:ss', 'MMM Do, HH:mm', 'MMM Do'); + + default: + return ''; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.module.ts new file mode 100644 index 0000000000..347d99ed15 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.module.ts @@ -0,0 +1,49 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldOutputModule +} from '@home/components/calculated-fields/components/output/calculated-field-output.module'; +import { + CalculatedFieldArgumentsTableModule +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module'; +import { + EntityAggregationComponentComponent +} from '@home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component'; +import { + CalculatedFieldMetricsTableModule +} from '@home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + CalculatedFieldOutputModule, + CalculatedFieldArgumentsTableModule, + CalculatedFieldMetricsTableModule, + ], + declarations: [ + EntityAggregationComponentComponent, + ], + exports: [ + EntityAggregationComponentComponent, + ] +}) +export class EntityAggregationComponentModule { +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html new file mode 100644 index 0000000000..bb191e8f26 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html @@ -0,0 +1,274 @@ + +
+
{{ 'calculated-fields.geofencing-zone-groups-settings' | translate }}
+
+
+
{{ 'calculated-fields.name' | translate }}
+ + + @if (geofencingFormGroup.get('name').touched && geofencingFormGroup.get('name').hasError('required')) { + + warning + + } @else if (geofencingFormGroup.get('name').touched && geofencingFormGroup.get('name').hasError('duplicateName')) { + + warning + + } @else if (geofencingFormGroup.get('name').touched && geofencingFormGroup.get('name').hasError('pattern')) { + + warning + + } @else if (geofencingFormGroup.get('name').touched && geofencingFormGroup.get('name').hasError('maxlength')) { + + warning + + } @else if (geofencingFormGroup.get('name').touched && geofencingFormGroup.get('name').hasError('forbiddenName')) { + + warning + + } + +
+ +
+
{{ 'entity.entity-type' | translate }}
+ + + @for (type of argumentEntityTypes; track type) { + {{ ArgumentEntityTypeTranslations.get(type) | translate }} + } + + +
+ @if (ArgumentEntityTypeParamsMap.has(entityType)) { +
+
{{ ArgumentEntityTypeParamsMap.get(entityType).title | translate }}
+ +
+ } +
+ +
+ + + {{ 'calculated-fields.entity-zone-relationship' | translate }} + +
+
+
+
calculated-fields.level
+
calculated-fields.direction-level
+
calculated-fields.relation-type
+
+
+ @if (levelsFormArray()?.controls?.length) { +
+ @for (keyControl of levelsFormArray().controls; track trackByKey; ) { +
+
+ +
+
+
{{ $index + 1 }}
+ + + @for (direction of GeofencingDirectionList; track direction) { + {{ GeofencingDirectionLevelTranslations.get(direction) | translate }} + } + + + + +
+
+ +
+
+ } +
+ } @else { + {{ 'calculated-fields.no-level' | translate }} + } + @if (levelsFormArray().errors) { + + } +
+
+ @if (maxRelationLevelPerCfArgument && levelsFormArray().length >= maxRelationLevelPerCfArgument) { +
+ warning + {{ 'calculated-fields.max-allowed-levels-error' | translate }} +
+ } @else { + + } +
+
+
+
+ + @if (entityFilter.singleEntity?.id || entityType === ArgumentEntityType.RelationQuery || entityType === ArgumentEntityType.Current || entityType === ArgumentEntityType.Owner) { +
+
+ {{ 'calculated-fields.perimeter-attribute-key' | translate }} +
+ @if (entityType === ArgumentEntityType.RelationQuery) { + + + @if (geofencingFormGroup.get('perimeterKeyName').touched && geofencingFormGroup.get('perimeterKeyName').hasError('required')) { + + warning + + } @else if (geofencingFormGroup.get('perimeterKeyName').touched && geofencingFormGroup.get('perimeterKeyName').hasError('pattern')) { + + warning + + } + + } @else { + + } +
+ } +
+
{{ 'calculated-fields.report-strategy' | translate }}
+ + + @for (strategy of GeofencingReportStrategyList; track strategy) { + {{ GeofencingReportStrategyTranslations.get(strategy) | translate }} + } + + +
+
+
+ +
+ {{ 'calculated-fields.create-relation-with-matched-zones' | translate }} +
+
+
+
{{ 'calculated-fields.direction' | translate }}
+ + + @for (direction of GeofencingDirectionList; track direction) { + {{ GeofencingDirectionTranslations.get(direction) | translate }} + } + + +
+
+
{{ 'calculated-fields.relation-type' | translate }}
+ + +
+
+
+
+ + @if (!readonly) { + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.scss new file mode 100644 index 0000000000..6b3652865d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.scss @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .level-text { + display: flex; + justify-content: center; + width: 25px; + color: rgba(0, 0, 0, 0.54); + } + + .tb-form-table { + .tb-form-row { + gap: 12px; + } + + .tb-form-table-body { + gap: unset; + } + + .tb-form-table-header { + padding: 0; + } + + .tb-form-table-header-cell { + &.tb-actions-header { + width: 40px; + min-width: 40px; + } + } + } + + .max-args-warning { + .mat-icon { + color: #FAA405; + } + } + + .limit-field-row { + @media screen and (max-width: 520px) { + display: flex; + flex-direction: column; + + .fixed-title-width { + align-self: flex-start; + padding-top: 8px; + } + } + } +} + +:host ::ng-deep { + tb-entity-autocomplete { + .mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper { + padding-right: 0 !important; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.ts new file mode 100644 index 0000000000..fc70058f8b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.ts @@ -0,0 +1,331 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, OnInit, output, ViewChild } from '@angular/core'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { + AbstractControl, + FormBuilder, + FormControl, + FormGroup, + UntypedFormArray, + ValidatorFn, + Validators +} from '@angular/forms'; +import { charsWithNumRegex, oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { + ArgumentEntityType, + ArgumentEntityTypeParamsMap, + ArgumentEntityTypeTranslations, + CalculatedFieldGeofencing, + CalculatedFieldGeofencingValue, + FORBIDDEN_NAMES, + forbiddenNamesValidator, + GeofencingDirectionLevelTranslations, + GeofencingDirectionTranslations, + GeofencingReportStrategy, + GeofencingReportStrategyTranslations, + getCalculatedFieldCurrentEntityFilter, + uniqueNameValidator +} from '@shared/models/calculated-field.models'; +import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; +import { EntityType } from '@shared/models/entity-type.models'; +import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { EntityId } from '@shared/models/id/entity-id'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityFilter } from '@shared/models/query/query.models'; +import { AliasFilterType } from '@shared/models/alias.models'; +import { BehaviorSubject, merge, Observable, of } from 'rxjs'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { AppState } from '@core/core.state'; +import { Store } from '@ngrx/store'; +import { EntityAutocompleteComponent } from '@shared/components/entity/entity-autocomplete.component'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { EntitySearchDirection } from '@shared/models/relation.models'; +import { CdkDragDrop } from "@angular/cdk/drag-drop"; + +@Component({ + selector: 'tb-calculated-field-geofencing-zone-groups-panel', + templateUrl: './calculated-field-geofencing-zone-groups-panel.component.html', + styleUrls: ['../common/calculated-field-panel.scss', './calculated-field-geofencing-zone-groups-panel.component.scss'] +}) +export class CalculatedFieldGeofencingZoneGroupsPanelComponent implements OnInit, AfterViewInit { + + @Input() buttonTitle: string; + @Input() zone: CalculatedFieldGeofencing; + @Input() entityId: EntityId; + @Input() tenantId: string; + @Input() entityName: string; + @Input() ownerId: EntityId; + @Input() usedNames: string[]; + @Input() readonly = false; + + @ViewChild('entityAutocomplete') entityAutocomplete: EntityAutocompleteComponent; + + geofencingDataApplied = output(); + + readonly maxRelationLevelPerCfArgument = getCurrentAuthState(this.store).maxRelationLevelPerCfArgument; + + geofencingFormGroup = this.fb.group({ + name: ['', [Validators.required, forbiddenNamesValidator(FORBIDDEN_NAMES), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]], + refEntityId: this.fb.group({ + entityType: [ArgumentEntityType.Current], + id: [''] + }), + refDynamicSourceConfiguration: this.fb.group({ + levels: this.fb.array([], [this.levelsRequired()]) + }), + perimeterKeyName: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex)]], + reportStrategy: [GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS], + createRelationsWithMatchedZones: [false], + direction: [EntitySearchDirection.TO], + relationType: ['', [Validators.required]] + }); + + entityFilter: EntityFilter; + entityNameSubject = new BehaviorSubject(null); + + enableAutocomplete = false; + + readonly ArgumentEntityType = ArgumentEntityType; + readonly argumentEntityTypes = Object.values(ArgumentEntityType) as ArgumentEntityType[]; + readonly ArgumentEntityTypeTranslations = ArgumentEntityTypeTranslations; + readonly DataKeyType = DataKeyType; + readonly ArgumentEntityTypeParamsMap = ArgumentEntityTypeParamsMap; + readonly GeofencingReportStrategyList = Object.values(GeofencingReportStrategy) as Array; + readonly GeofencingReportStrategyTranslations = GeofencingReportStrategyTranslations; + readonly GeofencingDirectionList = Object.values(EntitySearchDirection) as Array; + readonly GeofencingDirectionTranslations = GeofencingDirectionTranslations; + readonly GeofencingDirectionLevelTranslations = GeofencingDirectionLevelTranslations; + readonly AttributeScope = AttributeScope; + + private currentEntityFilter: EntityFilter; + + constructor( + private fb: FormBuilder, + private cd: ChangeDetectorRef, + private popover: TbPopoverComponent, + private store: Store + ) { + + this.observeEntityFilterChanges(); + this.observeEntityTypeChanges(); + this.observeCreateRelationZonesChanges(); + } + + get entityType(): ArgumentEntityType { + return this.geofencingFormGroup.get('refEntityId').get('entityType').value; + } + + get refEntityIdFormGroup(): FormGroup { + return this.geofencingFormGroup.get('refEntityId') as FormGroup; + } + + get refDynamicSourceFormGroup(): FormGroup { + return this.geofencingFormGroup.get('refDynamicSourceConfiguration') as FormGroup; + } + + ngOnInit(): void { + this.updatedFormValidators(); + this.geofencingFormGroup.patchValue(this.zone, {emitEvent: false}); + if (this.zone.refDynamicSourceConfiguration?.type) { + this.refEntityIdFormGroup.get('entityType').setValue(this.zone.refDynamicSourceConfiguration.type, {emitEvent: false}); + } + if (this.zone?.refDynamicSourceConfiguration?.levels?.length > 0) { + this.zone.refDynamicSourceConfiguration.levels.forEach(level => { + this.levelsFormArray().push(this.fb.group({ + direction: [level.direction], + relationType: [level.relationType, [Validators.required]] + })); + }) + } else { + this.addKey(); + } + this.enableAutocomplete = (this.entityId.entityType === EntityType.DEVICE_PROFILE || this.entityId.entityType === EntityType.ASSET_PROFILE) && + this.refEntityIdFormGroup.get('entityType').value === ArgumentEntityType.Owner; + this.validateDirectionAndRelationType(this.zone?.createRelationsWithMatchedZones); + this.validateRefDynamicSourceConfiguration(this.zone?.refEntityId?.entityType || this.zone?.refDynamicSourceConfiguration?.type); + + this.currentEntityFilter = getCalculatedFieldCurrentEntityFilter(this.entityName, this.entityId); + this.updateEntityFilter(this.zone.refEntityId?.entityType); + + if (this.readonly) { + this.geofencingFormGroup.disable({emitEvent: false}); + } + } + + fetchOptions(searchText: string): Observable> { + const search = searchText ? searchText?.toLowerCase() : ''; + return of(['Contains', 'Manages']).pipe(map(name => name?.filter(option => option.toLowerCase().includes(search)))); + } + + private updatedFormValidators(): void { + this.geofencingFormGroup.get('name').addValidators(uniqueNameValidator(this.usedNames)); + this.geofencingFormGroup.get('name').updateValueAndValidity({emitEvent: false}); + } + + private observeCreateRelationZonesChanges(): void { + this.geofencingFormGroup.get('createRelationsWithMatchedZones').valueChanges + .pipe(takeUntilDestroyed()) + .subscribe(value => this.validateDirectionAndRelationType(value)); + } + + private validateDirectionAndRelationType(createRelation = false): void { + if (createRelation) { + this.geofencingFormGroup.get('direction').enable({emitEvent: false}); + this.geofencingFormGroup.get('relationType').enable({emitEvent: false}); + } else { + this.geofencingFormGroup.get('direction').disable({emitEvent: false}); + this.geofencingFormGroup.get('relationType').disable({emitEvent: false}); + } + } + + private validateRefDynamicSourceConfiguration(type: ArgumentEntityType = ArgumentEntityType.Current): void { + if (type === ArgumentEntityType.RelationQuery) { + this.refDynamicSourceFormGroup.enable({emitEvent: false}); + } else { + this.refDynamicSourceFormGroup.disable({emitEvent: false}); + } + } + + ngAfterViewInit(): void { + if (this.zone.refEntityId?.id === NULL_UUID) { + this.entityAutocomplete.selectEntityFormGroup.get('entity').markAsTouched(); + } + } + + saveZone(): void { + const value = this.geofencingFormGroup.value as CalculatedFieldGeofencingValue; + const argumentType = value.refEntityId.entityType; + switch (argumentType) { + case ArgumentEntityType.Current: + delete value.refEntityId; + break; + case ArgumentEntityType.Owner: + delete value.refEntityId; + value.refDynamicSourceConfiguration = {type: ArgumentEntityType.Owner}; + break; + case ArgumentEntityType.RelationQuery: + delete value.refEntityId; + value.refDynamicSourceConfiguration.type = ArgumentEntityType.RelationQuery; + break; + case ArgumentEntityType.Tenant: + value.refEntityId.id = this.tenantId; + break + default: + value.entityName = this.entityNameSubject.value; + } + this.geofencingDataApplied.emit(value); + } + + cancel(): void { + this.popover.hide(); + } + + private updateEntityFilter(entityType: ArgumentEntityType = ArgumentEntityType.Current): void { + let entityFilter: EntityFilter; + switch (entityType) { + case ArgumentEntityType.Current: + case ArgumentEntityType.RelationQuery: + entityFilter = this.currentEntityFilter; + break; + case ArgumentEntityType.Owner: + entityFilter = { + type: AliasFilterType.singleEntity, + singleEntity: this.ownerId + }; + break; + case ArgumentEntityType.Tenant: + entityFilter = { + type: AliasFilterType.singleEntity, + singleEntity: { + id: this.tenantId, + entityType: EntityType.TENANT + }, + }; + break; + default: + entityFilter = { + type: AliasFilterType.singleEntity, + singleEntity: this.geofencingFormGroup.get('refEntityId').value as unknown as EntityId, + }; + } + this.entityFilter = entityFilter; + this.cd.markForCheck(); + } + + private observeEntityFilterChanges(): void { + merge( + this.refEntityIdFormGroup.get('entityType').valueChanges, + this.refEntityIdFormGroup.get('id').valueChanges, + ) + .pipe(debounceTime(50), takeUntilDestroyed()) + .subscribe(() => this.updateEntityFilter(this.entityType)); + } + + private observeEntityTypeChanges(): void { + this.refEntityIdFormGroup.get('entityType').valueChanges + .pipe(distinctUntilChanged(), takeUntilDestroyed()) + .subscribe(type => { + this.enableAutocomplete = (this.entityId.entityType === EntityType.DEVICE_PROFILE || this.entityId.entityType === EntityType.ASSET_PROFILE) && type === ArgumentEntityType.Owner; + this.geofencingFormGroup.get('refEntityId').get('id').setValue(null); + this.geofencingFormGroup.get('perimeterKeyName').reset(''); + const isEntityWithId = !!type && ![ArgumentEntityType.Tenant, ArgumentEntityType.Current, ArgumentEntityType.Owner, ArgumentEntityType.RelationQuery].includes(type); + this.geofencingFormGroup.get('refEntityId').get('id')[isEntityWithId ? 'enable' : 'disable'](); + if (!isEntityWithId) { + this.entityNameSubject.next(null); + } + this.validateRefDynamicSourceConfiguration(type); + }); + } + + private levelsRequired(): ValidatorFn { + return (control: FormControl) => { + return control.value.length ? null : { levelsRequired: true }; + }; + } + + levelsFormArray(): UntypedFormArray { + return this.refDynamicSourceFormGroup.get('levels') as UntypedFormArray; + } + + trackByKey(_index: number, keyControl: AbstractControl): any { + return keyControl; + } + + removeKey(index: number) { + this.levelsFormArray().removeAt(index); + this.levelsFormArray().markAsDirty(); + } + + addKey() { + this.levelsFormArray().push(this.fb.group({ + direction: [EntitySearchDirection.TO], + relationType: ['', [Validators.required]] + })); + } + + keyDrop(event: CdkDragDrop) { + const keysArray = this.levelsFormArray(); + const key = keysArray.at(event.previousIndex); + keysArray.removeAt(event.previousIndex); + keysArray.insert(event.currentIndex, key); + } + + get dragEnabled(): boolean { + return this.levelsFormArray().controls.length > 1 && !this.readonly; + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.html new file mode 100644 index 0000000000..92fded8a99 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.html @@ -0,0 +1,163 @@ + +
+
+ + + +
{{ 'common.name' | translate }}
+
+ +
+
{{ geofenceZone.name }}
+ +
+
+
+ + + {{ 'entity.entity-type' | translate }} + + +
+ @if (geofenceZone.refEntityId?.entityType === ArgumentEntityType.Tenant) { + {{ 'calculated-fields.argument-current-tenant' | translate }} + } @else if (geofenceZone.refDynamicSourceConfiguration?.type === ArgumentEntityType.Owner) { + {{ 'calculated-fields.argument-owner' | translate }} + } @else if (geofenceZone.refDynamicSourceConfiguration?.type === ArgumentEntityType.RelationQuery) { + {{ 'calculated-fields.argument-relation-query' | translate }} + } @else if (geofenceZone.refEntityId?.id) { + {{ entityTypeTranslations.get(geofenceZone.refEntityId.entityType).type | translate }} + } @else { + {{ 'calculated-fields.argument-current' | translate }} + } +
+
+
+ + + {{ 'calculated-fields.target-zone' | translate }} + + +
+ @if (geofenceZone.refEntityId?.id && geofenceZone.refEntityId?.entityType !== ArgumentEntityType.Tenant) { + + {{ entityNameMap.get(geofenceZone.refEntityId.id) ?? '' }} + + } +
+
+
+ + + + {{ 'calculated-fields.perimeter-key' | translate }} + + + +
{{ geofenceZone.perimeterKeyName }}
+
+
+
+ + + + {{ 'calculated-fields.report-strategy' | translate }} + + +
{{ GeofencingReportStrategyTranslations.get(geofenceZone.reportStrategy) | translate }}
+
+
+ + + + +
+ @if (!isEditValue) { + + } @else { + + } + +
+
+
+ + +
+ @if (errorText) { + + } +
+
+ {{ 'calculated-fields.no-zone-configured' | translate }} +
+
+ + @if (maxArgumentsPerCF && zoneGroupsFormArray.length >= maxArgumentsPerCF) { +
+ warning + {{ 'calculated-fields.hint.max-geofencing-zone' | translate }} +
+ } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts new file mode 100644 index 0000000000..341b898265 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts @@ -0,0 +1,313 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + booleanAttribute, + ChangeDetectorRef, + Component, + DestroyRef, + forwardRef, + Input, + Renderer2, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, +} from '@angular/forms'; +import { + ArgumentEntityType, + CalculatedFieldGeofencing, + CalculatedFieldGeofencingValue, + GeofencingReportStrategyTranslations, +} from '@shared/models/calculated-field.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityId } from '@shared/models/id/entity-id'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { getEntityDetailsPageURL, isEqual } from '@core/utils'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; +import { EntityService } from '@core/http/entity.service'; +import { MatSort } from '@angular/material/sort'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { forkJoin, Observable } from 'rxjs'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { BaseData } from '@shared/models/base-data'; +import { + CalculatedFieldGeofencingZoneGroupsPanelComponent +} from '@home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component'; + +@Component({ + selector: 'tb-calculated-field-geofencing-zone-groups-table', + templateUrl: './calculated-field-geofencing-zone-groups-table.component.html', + styleUrls: [`../calculated-field-arguments/calculated-field-arguments-table.component.scss`], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CalculatedFieldGeofencingZoneGroupsTableComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CalculatedFieldGeofencingZoneGroupsTableComponent), + multi: true + } + ], +}) +export class CalculatedFieldGeofencingZoneGroupsTableComponent implements ControlValueAccessor, Validator, AfterViewInit { + + @Input({required: true}) entityId: EntityId; + @Input({required: true}) tenantId: string; + @Input({required: true}) entityName: string; + @Input({required: true}) ownerId: EntityId; + @Input({transform: booleanAttribute}) isEditValue = true; + + @ViewChild(MatSort, { static: true }) sort: MatSort; + + errorText = ''; + zoneGroupsFormArray = this.fb.array([]); + entityNameMap = new Map(); + sortOrder = { direction: 'asc', property: '' }; + dataSource = new CalculatedFieldZoneDatasource(); + disable = false; + + readonly GeofencingReportStrategyTranslations = GeofencingReportStrategyTranslations; + readonly entityTypeTranslations = entityTypeTranslations; + readonly ArgumentEntityType = ArgumentEntityType; + readonly maxArgumentsPerCF = getCurrentAuthState(this.store).maxArgumentsPerCF - 2; + readonly NULL_UUID = NULL_UUID; + + private popoverComponent: TbPopoverComponent; + private propagateChange: (zonesObj: Record) => void = () => {}; + + constructor( + private fb: FormBuilder, + private popoverService: TbPopoverService, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef, + private renderer: Renderer2, + private entityService: EntityService, + private destroyRef: DestroyRef, + private store: Store + ) { + this.zoneGroupsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => { + this.updateDataSource(value); + this.propagateChange(this.getZonesObject(value)); + }); + } + + ngAfterViewInit(): void { + this.sort.sortChange.asObservable().pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { + this.sortOrder.property = this.sort.active; + this.sortOrder.direction = this.sort.direction; + this.updateDataSource(this.zoneGroupsFormArray.value); + }); + } + + registerOnChange(fn: (zonesObj: Record) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_): void {} + + validate(): ValidationErrors | null { + this.updateErrorText(); + return this.errorText ? { zonesFormArray: false } : null; + } + + setDisabledState(isDisabled: boolean): void { + this.disable = isDisabled; + } + + onDelete($event: Event, zone: CalculatedFieldGeofencingValue): void { + $event.stopPropagation(); + const index = this.zoneGroupsFormArray.controls.findIndex(control => isEqual(control.value, zone)); + this.zoneGroupsFormArray.removeAt(index); + this.zoneGroupsFormArray.markAsDirty(); + } + + manageZone($event: Event, matButton: MatButton, zone = {} as CalculatedFieldGeofencingValue, readonly: boolean = false): void { + $event?.stopPropagation(); + if (this.popoverComponent && !this.popoverComponent.tbHidden) { + this.popoverComponent.hide(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const index = this.zoneGroupsFormArray.controls.findIndex(control => isEqual(control.value, zone)); + const isExists = index !== -1; + const ctx = { + index, + zone, + entityId: this.entityId, + buttonTitle: isExists ? 'action.apply' : 'action.add', + tenantId: this.tenantId, + entityName: this.entityName, + ownerId: this.ownerId, + usedNames: this.zoneGroupsFormArray.value.map(({ name }) => name).filter(name => name !== zone.name), + readonly + }; + this.popoverComponent = this.popoverService.displayPopover({ + trigger, + renderer: this.renderer, + componentType: CalculatedFieldGeofencingZoneGroupsPanelComponent, + hostView: this.viewContainerRef, + preferredPlacement: isExists ? ['leftOnly', 'leftTopOnly', 'leftBottomOnly'] : ['rightOnly', 'rightTopOnly', 'rightBottomOnly'], + context: ctx, + isModal: true + }); + this.popoverComponent.tbComponentRef.instance.geofencingDataApplied.subscribe(({ entityName, ...value }) => { + this.popoverComponent.hide(); + if (entityName) { + this.entityNameMap.set(value.refEntityId.id, entityName); + } + if (isExists) { + this.zoneGroupsFormArray.at(index).setValue(value); + } else { + this.zoneGroupsFormArray.push(this.fb.control(value)); + } + this.cd.markForCheck(); + }); + } + } + + private updateDataSource(value: CalculatedFieldGeofencingValue[]): void { + const sortedValue = this.sortData(value); + this.dataSource.loadData(sortedValue); + } + + private updateErrorText(): void { + if (this.zoneGroupsFormArray.controls.some(control => control.value.refEntityId?.id === NULL_UUID)) { + this.errorText = 'calculated-fields.hint.geofencing-entity-not-found'; + } else { + this.errorText = ''; + } + } + + private getZonesObject(value: CalculatedFieldGeofencingValue[]): Record { + return value.reduce((acc, zoneValue) => { + const { name, ...zone } = zoneValue as CalculatedFieldGeofencingValue; + acc[name] = zone; + return acc; + }, {} as Record); + } + + writeValue(zonesObj: Record): void { + this.zoneGroupsFormArray.clear({emitEvent: false}); + this.populateZonesFormArray(zonesObj); + this.updateEntityNameMap(this.zoneGroupsFormArray.value); + } + + getEntityDetailsPageURL(id: string, type: EntityType): string { + return getEntityDetailsPageURL(id, type); + } + + private populateZonesFormArray(zonesObj: Record): void { + Object.keys(zonesObj).forEach(key => { + const value: CalculatedFieldGeofencingValue = { + ...zonesObj[key], + name: key + }; + this.zoneGroupsFormArray.push(this.fb.control(value), { emitEvent: false }); + }); + this.updateDataSource(this.zoneGroupsFormArray.value); + } + + private updateEntityNameMap(values: CalculatedFieldGeofencingValue[]): void { + const entitiesByType = values.reduce((acc, { refEntityId = {}}) => { + if (refEntityId.id && refEntityId.entityType !== ArgumentEntityType.Tenant) { + const { id, entityType } = refEntityId as EntityId; + acc[entityType] = acc[entityType] ?? []; + acc[entityType].push(id); + } + return acc; + }, {} as Record); + const tasks = Object.entries(entitiesByType).map(([entityType, ids]) => + this.entityService.getEntities(entityType as EntityType, ids) + ); + if (!tasks.length) { + return; + } + this.fetchEntityNames(tasks, values); + } + + private fetchEntityNames(tasks: Observable[]>[], values: CalculatedFieldGeofencingValue[]): void { + forkJoin(tasks as Observable[]>[]) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((result: Array>[]) => { + result.forEach((entities: BaseData[]) => entities.forEach((entity: BaseData) => this.entityNameMap.set(entity.id.id, entity.name))); + let updateTable = false; + values.forEach(({ refEntityId }) => { + if (refEntityId?.id && !this.entityNameMap.has(refEntityId.id) && refEntityId.entityType !== ArgumentEntityType.Tenant) { + updateTable = true; + const control = this.zoneGroupsFormArray.controls.find(control => control.value.refEntityId?.id === refEntityId.id); + const value = control.value; + value.refEntityId.id = NULL_UUID; + control.setValue(value, { emitEvent: false }); + } + }); + if (updateTable) { + this.zoneGroupsFormArray.updateValueAndValidity(); + } + }); + } + + private getSortValue(zone: CalculatedFieldGeofencingValue, column: string): string { + switch (column) { + case 'entityType': + if (zone.refEntityId?.entityType === ArgumentEntityType.Tenant) { + return 'calculated-fields.argument-current-tenant'; + } else if (zone.refDynamicSourceConfiguration.type === ArgumentEntityType.RelationQuery) { + return 'calculated-fields.argument-relation-query'; + } else if (zone.refEntityId?.id) { + return entityTypeTranslations.get((zone.refEntityId)?.entityType as unknown as EntityType).type; + } else { + return 'calculated-fields.argument-current'; + } + case 'key': + return zone.perimeterKeyName; + case 'reportStrategy': + return GeofencingReportStrategyTranslations.get(zone.reportStrategy); + default: + return zone.name; + } + } + + private sortData(data: CalculatedFieldGeofencingValue[]): CalculatedFieldGeofencingValue[] { + return data.sort((a, b) => { + const valA = this.getSortValue(a, this.sortOrder.property) ?? ''; + const valB = this.getSortValue(b, this.sortOrder.property) ?? ''; + return (this.sortOrder.direction === 'asc' ? 1 : -1) * valA.localeCompare(valB); + }); + } +} + +class CalculatedFieldZoneDatasource extends TbTableDatasource { + constructor() { + super(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.html new file mode 100644 index 0000000000..25f736a222 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.html @@ -0,0 +1,70 @@ + +
+
+
+ {{ 'calculated-fields.entity-coordinates' | translate }} +
+
+ + +
+
+ +
+
+ {{ 'calculated-fields.geofencing-zone-groups' | translate }} +
+ +
+ +
+ {{ 'calculated-fields.zone-group-refresh-interval' | translate }} +
+
+
+ + +
+
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts new file mode 100644 index 0000000000..02d02241b4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts @@ -0,0 +1,171 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { booleanAttribute, Component, forwardRef, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { + ArgumentEntityType, + CalculatedFieldGeofencing, + CalculatedFieldGeofencingConfiguration, + CalculatedFieldOutput, + CalculatedFieldType, + defaultCalculatedFieldOutput, + getCalculatedFieldCurrentEntityFilter, + notEmptyObjectValidator +} from '@shared/models/calculated-field.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityFilter } from '@shared/models/query/query.models'; +import { EntityId } from '@shared/models/id/entity-id'; + +@Component({ + selector: 'tb-geofencing-configuration', + templateUrl: './geofencing-configuration.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => GeofencingConfigurationComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => GeofencingConfigurationComponent), + multi: true + } + ], +}) +export class GeofencingConfigurationComponent implements ControlValueAccessor, Validator, OnChanges { + + @Input({required: true}) + entityId: EntityId; + + @Input({required: true}) + tenantId: string; + + @Input({required: true}) + entityName: string; + + @Input({required: true}) + ownerId: EntityId; + + @Input({transform: booleanAttribute}) isEditValue = true; + + readonly minAllowedScheduledUpdateIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedScheduledUpdateIntervalInSecForCF; + readonly DataKeyType = DataKeyType; + + geofencingConfiguration = this.fb.group({ + entityCoordinates: this.fb.group({ + latitudeKeyName: [null, [Validators.required]], + longitudeKeyName: [null, [Validators.required]], + }), + zoneGroups: this.fb.control>({}, notEmptyObjectValidator()), + scheduledUpdateEnabled: [true], + scheduledUpdateInterval: [this.minAllowedScheduledUpdateIntervalInSecForCF], + output: this.fb.control(defaultCalculatedFieldOutput) + }); + + currentEntityFilter: EntityFilter; + isRelatedEntity: boolean; + + private propagateChange: (config: CalculatedFieldGeofencingConfiguration) => void = () => { }; + + constructor(private fb: FormBuilder, + private store: Store) { + + this.geofencingConfiguration.get('zoneGroups').valueChanges + .pipe(takeUntilDestroyed()) + .subscribe((zoneGroups: Record) => + this.checkRelatedEntity(zoneGroups) + ); + + this.geofencingConfiguration.get('scheduledUpdateEnabled').valueChanges + .pipe(takeUntilDestroyed()) + .subscribe((value: boolean) => + this.checkScheduledUpdateEnabled(value) + ); + + this.geofencingConfiguration.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(() => { + this.updatedModel(this.geofencingConfiguration.getRawValue() as any); + }) + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.entityName || changes.entityId) { + const entityNameChanges = changes.entityName; + const entityIdChanges = changes.entityId; + if ((entityNameChanges?.currentValue !== entityNameChanges?.previousValue) || (entityIdChanges?.currentValue !== entityIdChanges?.previousValue)) { + this.currentEntityFilter = getCalculatedFieldCurrentEntityFilter(this.entityName, this.entityId); + } + } + } + + validate(): ValidationErrors | null { + return this.geofencingConfiguration.valid || this.geofencingConfiguration.disabled ? null : { geofencingConfigError: false }; + } + + writeValue(config: CalculatedFieldGeofencingConfiguration): void { + this.geofencingConfiguration.patchValue(config, {emitEvent: false}); + this.checkRelatedEntity(this.geofencingConfiguration.get('zoneGroups').value); + if (this.geofencingConfiguration.enabled) { + this.checkScheduledUpdateEnabled(this.geofencingConfiguration.get('scheduledUpdateEnabled').value); + } + } + + registerOnChange(fn: (config: CalculatedFieldGeofencingConfiguration) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: any): void { } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.geofencingConfiguration.disable({emitEvent: false}); + } else { + this.geofencingConfiguration.enable({emitEvent: false}); + this.checkScheduledUpdateEnabled(this.geofencingConfiguration.get('scheduledUpdateEnabled').value); + } + } + + private updatedModel(value: CalculatedFieldGeofencingConfiguration) { + value.type = CalculatedFieldType.GEOFENCING; + this.propagateChange(value) + } + + private checkScheduledUpdateEnabled(value: boolean) { + if (value) { + this.geofencingConfiguration.get('scheduledUpdateInterval').enable({emitEvent: false}); + } else { + this.geofencingConfiguration.get('scheduledUpdateInterval').disable({emitEvent: false}); + } + } + + private checkRelatedEntity(zoneGroups: Record) { + this.isRelatedEntity = Object.values(zoneGroups).some(zone => zone.refDynamicSourceConfiguration?.type === ArgumentEntityType.RelationQuery); + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.module.ts new file mode 100644 index 0000000000..a7b9b49e1c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.module.ts @@ -0,0 +1,52 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldGeofencingZoneGroupsTableComponent +} from '@home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component'; +import { + CalculatedFieldGeofencingZoneGroupsPanelComponent +} from '@home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component'; +import { SharedModule } from '@shared/shared.module'; +import { + GeofencingConfigurationComponent +} from '@home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component'; +import { + CalculatedFieldOutputModule +} from '@home/components/calculated-fields/components/output/calculated-field-output.module'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + CalculatedFieldOutputModule + ], + declarations: [ + CalculatedFieldGeofencingZoneGroupsTableComponent, + CalculatedFieldGeofencingZoneGroupsPanelComponent, + GeofencingConfigurationComponent + ], + exports: [ + CalculatedFieldGeofencingZoneGroupsTableComponent, + CalculatedFieldGeofencingZoneGroupsPanelComponent, + GeofencingConfigurationComponent + ] +}) +export class GeofencingConfigurationModule { + +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html new file mode 100644 index 0000000000..00ae7685e3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html @@ -0,0 +1,226 @@ + +
+
{{ 'calculated-fields.metrics.metric-settings' | translate }}
+
+
+
{{ 'calculated-fields.metrics.metric-name' | translate }}
+ + + @if (metricForm.get('name').touched && metricForm.get('name').hasError('required')) { + + warning + + } @else if (metricForm.get('name').touched && metricForm.get('name').hasError('duplicateName')) { + + warning + + } @else if (metricForm.get('name').touched && metricForm.get('name').hasError('pattern')) { + + warning + + } @else if (metricForm.get('name').touched && metricForm.get('name').hasError('maxlength')) { + + warning + + } @else if (metricForm.get('name').touched && metricForm.get('name').hasError('forbiddenName')) { + + warning + + } + +
+
+
{{ 'calculated-fields.metrics.aggregation' | translate }}
+ + + @for (aggFunction of AggFunctions; track aggFunction) { + {{ AggFunctionTranslations.get(aggFunction) | translate }} + } + + +
+ + @if (!simpleMode) { +
+ + + + +
+ {{ 'calculated-fields.metrics.filter' | translate }} +
+
+
+
+ + +
{{ 'api-usage.tbel' | translate }} +
+ +
+
+ +
+
+
+
+ } + + @if (!simpleMode) { +
+
+ {{ 'calculated-fields.metrics.value-source' | translate }} +
+ + + @for (inputType of AggInputTypes; track inputType) { + {{ AggInputTypeTranslations.get(inputType) | translate }} + } + + +
+ } + @if (this.metricForm.get('input.type').value === AggInputType.key) { +
+
{{ 'calculated-fields.argument-name' | translate }}
+ + + @for (argument of arguments; track argument) { + {{ argument }} + } + + @if (metricForm.get('input.key').touched && metricForm.get('input.key').hasError('required')) { + + warning + + } + +
+ } @else { + +
{{ 'api-usage.tbel' | translate }} +
+ +
+
+ +
+ } +
+ @if (simpleMode) { +
+
{{ 'calculated-fields.default-value' | translate }}
+ + + +
+ } +
+
+ + @if (!readonly) { + + } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts new file mode 100644 index 0000000000..4f98a7225f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts @@ -0,0 +1,186 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, OnInit, output } from '@angular/core'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { FormBuilder, Validators } from '@angular/forms'; +import { charsWithNumRegex } from '@shared/models/regex.constants'; +import { + AggFunction, + AggFunctionTranslations, + AggInputType, + AggInputTypeTranslations, + CalculatedFieldAggMetricValue, + calculatedFieldMetricFilterDefaultScript, + calculatedFieldMetricMapDefaultScript, + FORBIDDEN_NAMES, + forbiddenNamesValidator, + uniqueNameValidator +} from '@shared/models/calculated-field.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityFilter } from '@shared/models/query/query.models'; +import { ScriptLanguage } from '@shared/models/rule-node.models'; +import { TbEditorCompleter } from '@shared/models/ace/completion.models'; +import { AceHighlightRules } from '@shared/models/ace/ace.models'; +import { Observable } from "rxjs"; + +interface CalculatedFieldAggMetricValuePanel extends CalculatedFieldAggMetricValue { + allowFilter: boolean; +} + +@Component({ + selector: 'tb-calculated-field-metrics-panel', + templateUrl: './calculated-field-metrics-panel.component.html', + styleUrls: ['../common/calculated-field-panel.scss', '../../calculated-field.component.scss'], +}) +export class CalculatedFieldMetricsPanelComponent implements OnInit { + + @Input() buttonTitle: string; + @Input() metric: CalculatedFieldAggMetricValue; + @Input() usedNames: string[]; + @Input() arguments: Array; + @Input() simpleMode: boolean; + @Input() editorCompleter: TbEditorCompleter; + @Input() highlightRules: AceHighlightRules; + @Input({required: true}) testScript: (expression?: string) => Observable; + @Input() readonly = false; + + metricDataApplied = output(); + filterExpanded = false; + functionArgs: Array + + metricForm = this.fb.group({ + name: ['', [Validators.required, forbiddenNamesValidator(FORBIDDEN_NAMES), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]], + function: [AggFunction.AVG], + allowFilter: [false], + filter: [calculatedFieldMetricFilterDefaultScript, Validators.required], + input: this.fb.group({ + type: [AggInputType.key], + key: ['', Validators.required], + function: [calculatedFieldMetricMapDefaultScript, Validators.required], + }), + defaultValue: [null] + }); + + entityFilter: EntityFilter; + + AggFunctions = Object.values(AggFunction) as AggFunction[]; + readonly AggFunctionTranslations = AggFunctionTranslations; + readonly ScriptLanguage = ScriptLanguage; + readonly AggInputType = AggInputType; + readonly AggInputTypes = Object.values(AggInputType) as AggInputType[]; + readonly AggInputTypeTranslations = AggInputTypeTranslations; + + constructor( + private fb: FormBuilder, + private popover: TbPopoverComponent, + ) { + this.observeFilterAllowChange(); + this.observeInputTypeChange(); + } + + ngOnInit(): void { + this.updatedForm(); + + const data: CalculatedFieldAggMetricValuePanel = { + ...this.metric, + allowFilter: !!this.metric.filter, + } + this.metricForm.patchValue(data, {emitEvent: false}); + + this.validateFilter(data.allowFilter); + this.validateInputTypeFilter(data.input?.type ?? AggInputType.key); + this.validateInputKey(); + + this.functionArgs = ['ctx', ...this.arguments]; + + if (this.simpleMode) { + this.AggFunctions = this.AggFunctions.filter(aggFunc => aggFunc !== AggFunction.COUNT_UNIQUE); + } + + if (this.readonly) { + this.metricForm.disable({emitEvent: false}); + } + } + + saveMetric(): void { + const value = this.metricForm.value as CalculatedFieldAggMetricValuePanel; + if (!value.allowFilter) { + delete value.filter; + } + delete value.allowFilter; + this.metricDataApplied.emit(value); + } + + cancel(): void { + this.popover.hide(); + } + + private updatedForm(): void { + this.metricForm.get('name').addValidators(uniqueNameValidator(this.usedNames)); + this.metricForm.get('name').updateValueAndValidity({emitEvent: false}); + + if (!this.simpleMode) { + this.metricForm.removeControl('defaultValue', {emitEvent: false}); + } + } + + private observeFilterAllowChange(): void { + this.metricForm.get('allowFilter').valueChanges + .pipe(takeUntilDestroyed()) + .subscribe(value => this.validateFilter(value)); + } + + private observeInputTypeChange(): void { + this.metricForm.get('input.type').valueChanges + .pipe(takeUntilDestroyed()) + .subscribe(value => this.validateInputTypeFilter(value)); + } + + private validateFilter(allowFilter = false): void { + if (allowFilter) { + this.metricForm.get('filter').enable({emitEvent: false}); + } else { + this.metricForm.get('filter').disable({emitEvent: false}); + } + this.filterExpanded = allowFilter; + } + + private validateInputTypeFilter(value: AggInputType): void { + const inputForm = this.metricForm.get('input'); + if (value === AggInputType.key) { + inputForm.get('key').enable({emitEvent: false}); + inputForm.get('function').disable({emitEvent: false}); + } else { + inputForm.get('key').disable({emitEvent: false}); + inputForm.get('function').enable({emitEvent: false}); + } + } + + private validateInputKey() { + if (this.metric.input?.type === AggInputType.key && !this.arguments.includes(this.metric.input.key)) { + this.metricForm.get('input.key').setValue(null); + this.metricForm.get('input.key').markAsTouched(); + } + } + + onTestScript(scriptFunc: 'filter' | 'input.function') { + this.testScript(this.metricForm.get(scriptFunc).value).subscribe(expression => { + this.metricForm.get(scriptFunc).setValue(expression); + this.metricForm.get(scriptFunc).markAsDirty(); + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.html new file mode 100644 index 0000000000..f26387d5da --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.html @@ -0,0 +1,151 @@ + +
+
+ + + +
{{ 'calculated-fields.metrics.metric-name' | translate }}
+
+ +
+
{{ metric.name }}
+ +
+
+
+ + + {{ 'calculated-fields.metrics.aggregation' | translate }} + + +
{{ AggFunctionTranslations.get(metric.function) | translate }}
+
+
+ + + {{ 'calculated-fields.metrics.argument-name' | translate }} + + +
{{ metric.input.key }}
+
+
+ + + {{ 'calculated-fields.metrics.filtered' | translate }} + + +
+ {{ metric.filter ? 'check_box' : 'check_box_outline_blank' }} +
+
+
+ + + {{ 'calculated-fields.metrics.value-source' | translate }} + + +
{{ AggInputTypeTranslations.get(metric.input.type) | translate }}
+
+
+ + + + +
+ @if (disable) { + + } @else { + + } + +
+
+
+ + +
+
+
+ {{ 'calculated-fields.metrics.no-metrics-configured' | translate }} +
+
+ + @if (maxArgumentsPerCF && metricsFormArray.length >= maxArgumentsPerCF) { +
+ warning + {{ 'calculated-fields.metrics.max-metrics' | translate }} +
+ } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts new file mode 100644 index 0000000000..7862de3a33 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts @@ -0,0 +1,263 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + booleanAttribute, + ChangeDetectorRef, + Component, + DestroyRef, + forwardRef, + Input, + OnInit, + Renderer2, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, +} from '@angular/forms'; +import { + AggFunctionTranslations, + AggInputTypeTranslations, + CalculatedFieldAggMetric, + CalculatedFieldAggMetricValue, +} from '@shared/models/calculated-field.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { isDefinedAndNotNull, isEqual } from '@core/utils'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; +import { MatSort, SortDirection } from '@angular/material/sort'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + CalculatedFieldMetricsPanelComponent +} from '@home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component'; +import { TbEditorCompleter } from '@shared/models/ace/completion.models'; +import { AceHighlightRules } from '@shared/models/ace/ace.models'; +import { Observable } from "rxjs"; + +@Component({ + selector: 'tb-calculated-field-metrics-table', + templateUrl: './calculated-field-metrics-table.component.html', + styleUrls: [`../calculated-field-arguments/calculated-field-arguments-table.component.scss`], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CalculatedFieldMetricsTableComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CalculatedFieldMetricsTableComponent), + multi: true + } + ], +}) +export class CalculatedFieldMetricsTableComponent implements OnInit, ControlValueAccessor, Validator, AfterViewInit { + + @Input() arguments: Array; + @Input() editorCompleter: TbEditorCompleter; + @Input() highlightRules: AceHighlightRules; + @Input({transform: booleanAttribute}) simpleMode: boolean = false; + @Input({required: true}) testScript: (expression?: string) => Observable; + + @ViewChild(MatSort, { static: true }) sort: MatSort; + + errorText = ''; + metricsFormArray = this.fb.array([]); + sortOrder = { direction: 'asc' as SortDirection, property: '' }; + dataSource = new CalculatedFieldMetricsDatasource(); + disable = false; + + displayColumns = ['name', 'function', 'filter', 'valueSource', 'actions']; + + readonly AggFunctionTranslations = AggFunctionTranslations; + readonly AggInputTypeTranslations = AggInputTypeTranslations; + readonly maxArgumentsPerCF = getCurrentAuthState(this.store).maxArgumentsPerCF - 2; + + private popoverComponent: TbPopoverComponent; + private propagateChange: (zonesObj: Record) => void = () => {}; + + constructor( + private fb: FormBuilder, + private popoverService: TbPopoverService, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef, + private renderer: Renderer2, + private destroyRef: DestroyRef, + private store: Store + ) { + this.metricsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => { + this.updateDataSource(value); + this.propagateChange(this.getMetricsObject(value)); + }); + } + + ngOnInit() { + if (this.simpleMode) { + this.displayColumns = ['name', 'function', 'argumentName', 'actions']; + } + } + + ngAfterViewInit(): void { + this.sort.sortChange.asObservable().pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.sortOrder.property = this.sort.active; + this.sortOrder.direction = this.sort.direction; + this.updateDataSource(this.metricsFormArray.value); + }); + } + + registerOnChange(fn: (zonesObj: Record) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void {} + + validate(): ValidationErrors | null { + this.updateErrorText(); + return this.errorText ? { metricsFormArray: false } : null; + } + + setDisabledState(isDisabled: boolean): void { + this.disable = isDisabled; + } + + onDelete($event: Event, metric: CalculatedFieldAggMetricValue): void { + $event.stopPropagation(); + const index = this.metricsFormArray.controls.findIndex(control => isEqual(control.value, metric)); + this.metricsFormArray.removeAt(index); + this.metricsFormArray.markAsDirty(); + } + + manageMetrics($event: Event, matButton: MatButton, metric = {} as CalculatedFieldAggMetricValue, readonly: boolean = false): void { + $event?.stopPropagation(); + if (this.popoverComponent && !this.popoverComponent.tbHidden) { + this.popoverComponent.hide(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const index = this.metricsFormArray.controls.findIndex(control => isEqual(control.value, metric)); + const isExists = index !== -1; + const ctx = { + index, + metric, + buttonTitle: isExists ? 'action.apply' : 'action.add', + usedNames: this.metricsFormArray.value.map(({ name }) => name).filter(name => name !== metric.name), + arguments: this.arguments, + editorCompleter: this.editorCompleter, + highlightRules: this.highlightRules, + simpleMode: this.simpleMode, + testScript: this.testScript, + readonly + }; + this.popoverComponent = this.popoverService.displayPopover({ + trigger, + renderer: this.renderer, + componentType: CalculatedFieldMetricsPanelComponent, + hostView: this.viewContainerRef, + preferredPlacement: isExists ? ['leftOnly', 'leftTopOnly', 'leftBottomOnly'] : ['rightOnly', 'rightTopOnly', 'rightBottomOnly'], + context: ctx, + isModal: true + }); + this.popoverComponent.tbComponentRef.instance.metricDataApplied.subscribe((value) => { + this.popoverComponent.hide(); + if (isExists) { + this.metricsFormArray.at(index).setValue(value); + } else { + this.metricsFormArray.push(this.fb.control(value)); + } + this.cd.markForCheck(); + }); + } + } + + private updateDataSource(value: CalculatedFieldAggMetricValue[]): void { + const sortedValue = this.sortData(value); + this.dataSource.loadData(sortedValue); + } + + private updateErrorText(): void { + if (!this.metricsFormArray.controls.length) { + this.errorText = 'calculated-fields.metrics.metrics-empty'; + } else { + this.errorText = ''; + } + } + + private getMetricsObject(value: CalculatedFieldAggMetricValue[]): Record { + return value.reduce((acc, metricValue) => { + const { name, ...metric } = metricValue; + acc[name] = metric; + return acc; + }, {} as Record); + } + + writeValue(metrics: Record): void { + this.metricsFormArray.clear({emitEvent: false}); + this.populateZonesFormArray(metrics); + } + + private populateZonesFormArray(metrics: Record): void { + Object.keys(metrics).forEach(key => { + const value: CalculatedFieldAggMetricValue = { + ...metrics[key], + name: key + }; + this.metricsFormArray.push(this.fb.control(value), { emitEvent: false }); + }); + this.updateDataSource(this.metricsFormArray.value); + } + + private getSortValue(metric: CalculatedFieldAggMetricValue, column: string): string { + switch (column) { + case 'function': + return metric.function; + case 'valueSource': + return metric.input?.type; + case 'filter': + return isDefinedAndNotNull(metric.filter).toString(); + default: + return metric.name; + } + } + + private sortData(data: CalculatedFieldAggMetricValue[]): CalculatedFieldAggMetricValue[] { + return data.sort((a, b) => { + const valA = this.getSortValue(a, this.sortOrder.property) ?? ''; + const valB = this.getSortValue(b, this.sortOrder.property) ?? ''; + return (this.sortOrder.direction === 'asc' ? 1 : -1) * valA.localeCompare(valB); + }); + } +} + +class CalculatedFieldMetricsDatasource extends TbTableDatasource { + constructor() { + super(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts new file mode 100644 index 0000000000..ee72828bc8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts @@ -0,0 +1,42 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldMetricsTableComponent +} from '@home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component'; +import { + CalculatedFieldMetricsPanelComponent +} from '@home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + ], + declarations: [ + CalculatedFieldMetricsTableComponent, + CalculatedFieldMetricsPanelComponent + ], + exports: [ + CalculatedFieldMetricsTableComponent + ] +}) +export class CalculatedFieldMetricsTableModule { + +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.html new file mode 100644 index 0000000000..8a6bc2abe8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.html @@ -0,0 +1,179 @@ + +
+
+ {{ 'calculated-fields.output' | translate }} +
+
+
+ + {{ 'calculated-fields.output-type' | translate }} + + @for (type of outputTypes; track type) { + {{ OutputTypeTranslations.get(type) | translate }} + } + + + @if (outputForm.get('type').value === OutputType.Attribute + && (entityId?.entityType === EntityType.DEVICE || entityId?.entityType === EntityType.DEVICE_PROFILE)) { + + {{ 'calculated-fields.attribute-scope' | translate }} + + + {{ 'calculated-fields.server-attributes' | translate }} + + + {{ 'calculated-fields.shared-attributes' | translate }} + + + + } +
+ @if (simpleMode) { + @if (hiddenName) { +
+ + +
+ } @else { +
+ + {{ (outputForm.get('type').value === OutputType.Timeseries + ? 'calculated-fields.timeseries-key' + : 'calculated-fields.attribute-key') | translate }} + + @if (outputForm.get('name').errors && outputForm.get('name').touched) { + + @if (outputForm.get('name').hasError('required')) { + {{ 'common.hint.key-required' | translate }} + } @else if (outputForm.get('name').hasError('pattern')) { + {{ 'common.hint.key-pattern' | translate }} + } @else if (outputForm.get('name').hasError('maxlength')) { + {{ 'common.hint.key-max-length' | translate }} + } + + } + + +
+ + } + } +
+
+ + + +
+
+ {{ 'calculated-fields.output-strategy.strategy' | translate }} +
+ + @for (outputStrategyType of OutputStrategyTypes; track outputStrategyType) { + {{ OutputStrategyTypeTranslations.get(outputStrategyType) | translate }} + } + +
+
+
+ @if (outputForm.get('strategy.type').value === OutputStrategyType.IMMEDIATE) { +
+
calculated-fields.output-strategy.processing-parameters
+ @if (outputForm.get('type').value === OutputType.Timeseries) { + +
+
calculated-fields.output-strategy.save-time-series
+
+
+ +
+
calculated-fields.output-strategy.save-latest-values
+
+
+ } @else { + +
+
calculated-fields.output-strategy.save-database
+
+
+ } + +
+
calculated-fields.output-strategy.send-web-sockets
+
+
+ +
+
calculated-fields.output-strategy.save-calculated-fields
+
+
+
+ @if (outputForm.get('type').value === OutputType.Attribute) { +
+ +
+
calculated-fields.output-strategy.update-attribute-only-on-value-change
+
+
+
+
+ +
+
calculated-fields.output-strategy.send-attributes-updated-notification
+
+
+
+ } @else { +
+ +
+
calculated-fields.output-strategy.ttl
+
+
+ + +
+ } + } +
+
+
+ + + {{ 'calculated-fields.decimals-by-default' | translate }} + + @if (outputForm.get('decimalsByDefault').errors && outputForm.get('decimalsByDefault').touched) { + {{ 'calculated-fields.hint.decimals-range' | translate }} + } + + + + + diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.scss new file mode 100644 index 0000000000..b0d282b7f4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.scss @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .mat-mdc-chip-disabled { + .mdc-evolution-chip__action { + cursor: default + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts new file mode 100644 index 0000000000..a9924909b7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts @@ -0,0 +1,265 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, inject, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { + CalculatedFieldOutput, + CalculatedFieldSimpleOutput, + OutputStrategyType, + OutputStrategyTypeTranslations, + OutputType, + OutputTypeTranslations +} from '@shared/models/calculated-field.models'; +import { digitsRegex, oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityId } from '@shared/models/id/entity-id'; +import { EntityType } from '@shared/models/entity-type.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { merge } from 'rxjs'; +import { deepClone } from '@core/utils'; + +@Component({ + selector: 'tb-calculate-field-output', + templateUrl: './calculated-field-output.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CalculatedFieldOutputComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CalculatedFieldOutputComponent), + multi: true + } + ], + styleUrls: ['./calculated-field-output.component.scss'], +}) +export class CalculatedFieldOutputComponent implements ControlValueAccessor, Validator, OnInit, OnChanges { + + @Input() + @coerceBoolean() + simpleMode = false; + + @Input() + @coerceBoolean() + hiddenName = false; + + @Input() + @coerceBoolean() + disableType = false; + + @Input() + containerInputClass: string | string[] | Record = 'flex flex-col gap-3'; + + @Input({required: true}) + entityId: EntityId; + + disabled = false; + + readonly outputTypes = Object.values(OutputType) as OutputType[]; + readonly OutputType = OutputType; + readonly AttributeScope = AttributeScope; + readonly OutputTypeTranslations = OutputTypeTranslations; + readonly EntityType = EntityType; + + readonly OutputStrategyType = OutputStrategyType; + readonly OutputStrategyTypes = Object.values(OutputStrategyType) as OutputStrategyType[]; + readonly OutputStrategyTypeTranslations = OutputStrategyTypeTranslations; + + private fb = inject(FormBuilder); + private destroyRef = inject(DestroyRef); + + outputForm = this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + scope: [{value: AttributeScope.SERVER_SCOPE, disabled: true}], + type: [OutputType.Timeseries], + decimalsByDefault: [null as number, [Validators.min(0), Validators.max(15), Validators.pattern(digitsRegex)]], + strategy: this.fb.group({ + type: [OutputStrategyType.IMMEDIATE], + saveTimeSeries: [true], + saveLatest: [true], + saveAttribute: [true], + sendWsUpdate: [true], + processCfs: [true], + updateAttributesOnlyOnValueChange: [true], + sendAttributesUpdatedNotification: [false], + useCustomTtl: [false], + ttl: [0] + }) + }); + + private propagateChange: (config: CalculatedFieldOutput | CalculatedFieldSimpleOutput) => void = () => { }; + + ngOnInit() { + this.outputForm.get('type').valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(type => { + this.toggleScopeByOutputType(type); + this.updatedStrategy(); + }); + + this.outputForm.get('strategy.saveTimeSeries').valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(value => this.updateTimeSeriesTtl(value)); + + merge( + this.outputForm.get('strategy.type').valueChanges, + this.outputForm.get('strategy.useCustomTtl').valueChanges + ) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => { + this.updatedStrategy(); + }); + + this.updatedFormWithMode(); + + this.outputForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value: CalculatedFieldOutput | CalculatedFieldSimpleOutput) => { + this.updatedModel(value); + }) + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (change.currentValue !== change.previousValue) { + if (propName === 'simpleMode' && !this.disabled) { + this.updatedFormWithMode(); + if (!change.firstChange) { + this.outputForm.updateValueAndValidity(); + } + } + } + } + } + + validate(): ValidationErrors | null { + return this.outputForm.valid || this.outputForm.disabled ? null : {outputConfig: false}; + } + + writeValue(value: CalculatedFieldOutput | CalculatedFieldSimpleOutput): void { + this.outputForm.patchValue(value, {emitEvent: false}); + if (value.type === OutputType.Timeseries && value.strategy?.type === OutputStrategyType.IMMEDIATE && value.strategy?.ttl) { + this.outputForm.get('strategy.useCustomTtl').setValue(true, {emitEvent: false}); + } + this.outputForm.get('type').updateValueAndValidity({onlySelf: true, emitEvent: false}); + } + + registerOnChange(fn: (config: CalculatedFieldOutput | CalculatedFieldSimpleOutput) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: any): void { } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.outputForm.disable({emitEvent: false}); + } else { + this.outputForm.enable({emitEvent: false}); + this.updatedFormWithMode(); + this.toggleScopeByOutputType(this.outputForm.get('type').value); + this.updatedStrategy(); + this.updateTimeSeriesTtl(this.outputForm.get('strategy.saveTimeSeries').value); + if (this.outputForm.invalid) { + this.outputForm.updateValueAndValidity(); + } + } + } + + private updatedModel(value: CalculatedFieldOutput | CalculatedFieldSimpleOutput) { + if (this.simpleMode && 'name' in value) { + value.name = value.name?.trim() ?? ''; + } + if (this.disableType) { + value.type = this.outputForm.get('type').value; + } + if (value.type === OutputType.Timeseries && value.strategy.type === OutputStrategyType.IMMEDIATE) { + value = deepClone(value); + delete (value.strategy as any).useCustomTtl + } + this.propagateChange(value); + } + + private toggleScopeByOutputType(type: OutputType): void { + if (type === OutputType.Attribute) { + this.outputForm.get('scope').enable({emitEvent: false}); + } else { + this.outputForm.get('scope').disable({emitEvent: false}); + } + } + + private updatedFormWithMode(): void { + if (this.simpleMode && !this.hiddenName) { + this.outputForm.get('name').enable({emitEvent: false}); + } else { + this.outputForm.get('name').disable({emitEvent: false}); + } + if (this.simpleMode) { + this.outputForm.get('decimalsByDefault').enable({emitEvent: false}); + } else { + this.outputForm.get('decimalsByDefault').disable({emitEvent: false}); + } + if (this.disableType) { + this.outputForm.get('type').disable({emitEvent: false}); + } + } + + private updatedStrategy(): void { + const strategyType = this.outputForm.get('strategy.type').value; + this.outputForm.get('strategy').disable({emitEvent: false}); + this.outputForm.get('strategy.type').enable({emitEvent: false}); + + if (strategyType === OutputStrategyType.IMMEDIATE) { + const outputType = this.outputForm.get('type').value; + this.outputForm.get('strategy.sendWsUpdate').enable({emitEvent: false}); + this.outputForm.get('strategy.processCfs').enable({emitEvent: false}); + if (outputType === OutputType.Attribute) { + this.outputForm.get('strategy.saveAttribute').enable({emitEvent: false}); + this.outputForm.get('strategy.updateAttributesOnlyOnValueChange').enable({emitEvent: false}); + this.outputForm.get('strategy.sendAttributesUpdatedNotification').enable({emitEvent: false}); + } else { + this.outputForm.get('strategy.saveTimeSeries').enable({emitEvent: false}); + this.outputForm.get('strategy.saveLatest').enable({emitEvent: false}); + this.outputForm.get('strategy.useCustomTtl').enable({emitEvent: false}); + if (this.outputForm.get('strategy.useCustomTtl').value) { + this.outputForm.get('strategy.ttl').enable({emitEvent: false}); + } + } + } + } + + private updateTimeSeriesTtl(value: boolean) { + if (value) { + this.outputForm.get('strategy.useCustomTtl').enable({emitEvent: false}); + } else { + this.outputForm.get('strategy.useCustomTtl').disable({emitEvent: false}); + this.outputForm.get('strategy.ttl').disable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.module.ts new file mode 100644 index 0000000000..848aa1d5de --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.module.ts @@ -0,0 +1,36 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldOutputComponent +} from '@home/components/calculated-fields/components/output/calculated-field-output.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + ], + declarations: [ + CalculatedFieldOutputComponent + ], + exports: [ + CalculatedFieldOutputComponent + ] +}) +export class CalculatedFieldOutputModule { } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html deleted file mode 100644 index 08f660b05f..0000000000 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html +++ /dev/null @@ -1,209 +0,0 @@ - -
-
-
{{ 'calculated-fields.argument-settings' | translate }}
-
-
-
{{ 'calculated-fields.argument-name' | translate }}
- - - @if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('required')) { - - warning - - } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('duplicateName')) { - - warning - - } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('pattern')) { - - warning - - } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('maxlength')) { - - warning - - } @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('forbiddenName')) { - - warning - - } - -
- -
-
{{ 'entity.entity-type' | translate }}
- - - @for (type of argumentEntityTypes; track type) { - {{ ArgumentEntityTypeTranslations.get(type) | translate }} - } - - -
- @if (ArgumentEntityTypeParamsMap.has(entityType)) { -
-
{{ ArgumentEntityTypeParamsMap.get(entityType).title | translate }}
- -
- } -
- -
-
{{ 'calculated-fields.argument-type' | translate }}
- - - @for (type of argumentTypes; track type) { - {{ ArgumentTypeTranslations.get(type) | translate }} - } - - @if (refEntityKeyFormGroup.get('type').hasError('required') && refEntityKeyFormGroup.get('type').touched) { - - warning - - } - -
- @if (entityFilter.singleEntity?.id || entityType === ArgumentEntityType.Current || entityType === ArgumentEntityType.Tenant) { - @if (refEntityKeyFormGroup.get('type').value !== ArgumentType.Attribute) { -
-
{{ 'calculated-fields.timeseries-key' | translate }}
- @if (refEntityKeyFormGroup.get('type').value === ArgumentType.LatestTelemetry) { - - } @else { - - } - - - -
- } @else { - @if (enableAttributeScopeSelection) { -
-
{{ 'calculated-fields.attribute-scope' | translate }}
- - - - {{ 'calculated-fields.server-attributes' | translate }} - - - {{ 'calculated-fields.client-attributes' | translate }} - - - {{ 'calculated-fields.shared-attributes' | translate }} - - - -
- } -
-
{{ 'calculated-fields.attribute-key' | translate }}
- -
- } - } -
- @if (refEntityKeyFormGroup.get('type').value !== ArgumentType.Rolling) { -
-
{{ 'calculated-fields.default-value' | translate }}
- - - -
- } @else { -
-
{{ 'calculated-fields.time-window' | translate }}
- -
- @if (maxDataPointsPerRollingArg) { -
-
{{ 'calculated-fields.limit' | translate }}
-
- - - - - - -
-
- } - } -
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.html new file mode 100644 index 0000000000..475f956b07 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.html @@ -0,0 +1,100 @@ + +
+
+
+ {{ 'calculated-fields.propagation-path-related-entities' | translate }} +
+
+ + {{ 'calculated-fields.direction' | translate }} + + @for (direction of Directions; track direction) { + {{ PropagationDirectionTranslations.get(direction) | translate }} + } + + + + +
+
+
+
+
+ {{ 'calculated-fields.data-propagate' | translate }} +
+ + {{ 'calculated-fields.propagate-type.arguments-only' | translate }} + {{ 'calculated-fields.propagate-type.expression-result' | translate }} + +
+ +
+
+
{{ 'calculated-fields.script' | translate }}
+
+ +
{{ 'api-usage.tbel' | translate }} +
+ +
+
+ +
+
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts new file mode 100644 index 0000000000..1cf2476c2e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts @@ -0,0 +1,190 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { booleanAttribute, Component, forwardRef, Input } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { EntityId } from '@shared/models/id/entity-id'; +import { Observable, of } from 'rxjs'; +import { + calculatedFieldDefaultScript, + CalculatedFieldOutput, + CalculatedFieldPropagationConfiguration, + CalculatedFieldType, + defaultCalculatedFieldOutput, + getCalculatedFieldArgumentsEditorCompleter, + getCalculatedFieldArgumentsHighlights, + notEmptyObjectValidator, + OutputType, + PropagationDirectionTranslations, + PropagationWithExpression +} from '@shared/models/calculated-field.models'; +import { map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ScriptLanguage } from '@app/shared/models/rule-node.models'; +import { EntitySearchDirection } from '@shared/models/relation.models'; +import {Store} from "@ngrx/store"; +import {AppState} from "@core/core.state"; +import {getCurrentAuthState} from "@core/auth/auth.selectors"; + +@Component({ + selector: 'tb-propagation-configuration', + templateUrl: './propagation-configuration.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => PropagationConfigurationComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => PropagationConfigurationComponent), + multi: true + } + ], +}) +export class PropagationConfigurationComponent implements ControlValueAccessor, Validator { + + @Input({required: true}) + entityId: EntityId; + + @Input({required: true}) + tenantId: string; + + @Input({required: true}) + entityName: string; + + @Input({required: true}) + ownerId: EntityId; + + @Input({required: true}) + testScript: () => Observable; + + @Input({transform: booleanAttribute}) isEditValue = true; + + readonly maxRelatedEntitiesToReturnPerCfArgument = getCurrentAuthState(this.store).maxRelatedEntitiesToReturnPerCfArgument; + + propagateConfiguration = this.fb.group({ + arguments: this.fb.control({}, notEmptyObjectValidator()), + applyExpressionToResolvedArguments: [false], + relation: this.fb.group({ + direction: [EntitySearchDirection.TO, Validators.required], + relationType: ['Contains', Validators.required], + }), + expression: [calculatedFieldDefaultScript], + output: this.fb.control(defaultCalculatedFieldOutput), + }); + + disabled = false; + + readonly ScriptLanguage = ScriptLanguage; + readonly CalculatedFieldType = CalculatedFieldType; + readonly OutputType = OutputType; + readonly Directions = Object.values(EntitySearchDirection) as Array; + readonly PropagationDirectionTranslations = PropagationDirectionTranslations; + + functionArgs$ = this.propagateConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => ['ctx', ...Object.keys(argumentsObj)]) + ); + + argumentsEditorCompleter$ = this.propagateConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => getCalculatedFieldArgumentsEditorCompleter(argumentsObj ?? {})) + ); + + argumentsHighlightRules$ = this.propagateConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => getCalculatedFieldArgumentsHighlights(argumentsObj)) + ); + + private propagateChange: (config: CalculatedFieldPropagationConfiguration) => void = () => { }; + + constructor(private fb: FormBuilder, + private store: Store) { + this.propagateConfiguration.get('applyExpressionToResolvedArguments').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(() => { + this.updatedFormWithScript(); + }) + + this.propagateConfiguration.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value: CalculatedFieldPropagationConfiguration) => { + this.updatedModel(value); + }) + } + + validate(): ValidationErrors | null { + return this.propagateConfiguration.valid || this.propagateConfiguration.disabled ? null : {invalidPropagateConfig: false}; + } + + writeValue(value: PropagationWithExpression): void { + value.expression = value.expression ?? calculatedFieldDefaultScript; + this.propagateConfiguration.patchValue(value, {emitEvent: false}); + if (!this.disabled) { + this.updatedFormWithScript(); + } + setTimeout(() => { + this.propagateConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); + }); + } + + registerOnChange(fn: (config: CalculatedFieldPropagationConfiguration) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: any): void { } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.propagateConfiguration.disable({emitEvent: false}); + } else { + this.propagateConfiguration.enable({emitEvent: false}); + this.updatedFormWithScript(); + } + } + + onTestScript() { + this.testScript().subscribe((expression) => { + this.propagateConfiguration.get('expression').setValue(expression); + this.propagateConfiguration.get('expression').markAsDirty(); + }) + } + + fetchOptions(searchText: string): Observable> { + const search = searchText ? searchText?.toLowerCase() : ''; + return of(['Contains', 'Manages']).pipe(map(name => name?.filter(option => option.toLowerCase().includes(search)))); + } + + private updatedModel(value: CalculatedFieldPropagationConfiguration): void { + value.type = CalculatedFieldType.PROPAGATION; + this.propagateChange(value); + } + + private updatedFormWithScript() { + if (this.propagateConfiguration.get('applyExpressionToResolvedArguments').value) { + this.propagateConfiguration.get('expression').enable({emitEvent: false}); + } else { + this.propagateConfiguration.get('expression').disable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.module.ts new file mode 100644 index 0000000000..209f27eec1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.module.ts @@ -0,0 +1,44 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldOutputModule +} from '@home/components/calculated-fields/components/output/calculated-field-output.module'; +import { + CalculatedFieldArgumentsTableModule +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module'; +import { + PropagationConfigurationComponent +} from '@home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + CalculatedFieldOutputModule, + CalculatedFieldArgumentsTableModule, + ], + declarations: [ + PropagationConfigurationComponent, + ], + exports: [ + PropagationConfigurationComponent, + ] +}) +export class PropagationConfigurationModule { } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/public-api.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/public-api.ts index 62a8eb603f..476714e3cb 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/public-api.ts @@ -15,7 +15,4 @@ /// export * from './dialog/calculated-field-dialog.component'; -export * from './arguments-table/calculated-field-arguments-table.component'; -export * from './panel/calculated-field-argument-panel.component'; -export * from './debug-dialog/calculated-field-debug-dialog.component'; export * from './test-dialog/calculated-field-script-test-dialog.component'; diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html new file mode 100644 index 0000000000..f3ac0eefd8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html @@ -0,0 +1,86 @@ + +
+
+
+ {{ 'calculated-fields.aggregation-path-related-entities' | translate }} +
+
+ + {{ 'calculated-fields.direction' | translate }} + + @for (direction of Directions; track direction) { + {{ PropagationDirectionTranslations.get(direction) | translate }} + } + + + + +
+
+
+
+ {{ 'calculated-fields.arguments' | translate }} +
+ +
+
+
+ {{ 'calculated-fields.metrics.metrics' | translate }} +
+ + + +
+ + @if (relatedAggregationConfiguration.get('output').value?.type === OutputType.Timeseries) { +
+ +
+
calculated-fields.use-latest-timestamp
+
+
+
+ } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.scss new file mode 100644 index 0000000000..d012d20732 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.scss @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + .simpleMode { + min-width: 0; + + .mat-slide { + overflow: hidden; + + .mdc-form-field { + width: 100%; + + .mdc-label { + min-width: 0; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts new file mode 100644 index 0000000000..fcbe46be03 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts @@ -0,0 +1,174 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { booleanAttribute, Component, forwardRef, Input } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { EntityId } from '@shared/models/id/entity-id'; +import { Observable, of } from 'rxjs'; +import { + CalculatedFieldOutput, + CalculatedFieldRelatedAggregationConfiguration, + CalculatedFieldType, + defaultCalculatedFieldOutput, + getCalculatedFieldArgumentsEditorCompleter, + getCalculatedFieldArgumentsHighlights, + notEmptyObjectValidator, + OutputType, + PropagationDirectionTranslations +} from '@shared/models/calculated-field.models'; +import { map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ScriptLanguage } from '@app/shared/models/rule-node.models'; +import { EntitySearchDirection } from '@shared/models/relation.models'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-related-entities-aggregation-component', + templateUrl: './related-entities-aggregation-component.component.html', + styleUrl: './related-entities-aggregation-component.component.scss', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => RelatedEntitiesAggregationComponentComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => RelatedEntitiesAggregationComponentComponent), + multi: true + } + ], +}) +export class RelatedEntitiesAggregationComponentComponent implements ControlValueAccessor, Validator { + + @Input({required: true}) + entityId: EntityId; + + @Input({required: true}) + tenantId: string; + + @Input({required: true}) + entityName: string; + + @Input({required: true}) + testScript: (expression?: string) => Observable; + + @Input({transform: booleanAttribute}) isEditValue = true; + + readonly ScriptLanguage = ScriptLanguage; + readonly CalculatedFieldType = CalculatedFieldType; + readonly OutputType = OutputType; + readonly Directions = Object.values(EntitySearchDirection) as Array; + readonly PropagationDirectionTranslations = PropagationDirectionTranslations; + readonly minAllowedDeduplicationIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedDeduplicationIntervalInSecForCF; + readonly maxRelatedEntitiesToReturnPerCfArgument = getCurrentAuthState(this.store).maxRelatedEntitiesToReturnPerCfArgument; + + relatedAggregationConfiguration = this.fb.group({ + relation: this.fb.group({ + direction: [EntitySearchDirection.FROM, Validators.required], + relationType: ['Contains', Validators.required], + }), + arguments: this.fb.control({}, notEmptyObjectValidator()), + metrics: this.fb.control({}, notEmptyObjectValidator()), + deduplicationIntervalInSec: [this.minAllowedDeduplicationIntervalInSecForCF], + output: this.fb.control(defaultCalculatedFieldOutput), + useLatestTs: [false] + }); + + arguments$ = this.relatedAggregationConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => Object.keys(argumentsObj)) + ); + + argumentsEditorCompleter$ = this.relatedAggregationConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => getCalculatedFieldArgumentsEditorCompleter(argumentsObj ?? {})) + ); + + argumentsHighlightRules$ = this.relatedAggregationConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => getCalculatedFieldArgumentsHighlights(argumentsObj)) + ); + + private readonly minAllowedScheduledUpdateIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedScheduledUpdateIntervalInSecForCF; + private propagateChange: (config: CalculatedFieldRelatedAggregationConfiguration) => void = () => { }; + + constructor(private fb: FormBuilder, + private store: Store) { + + this.relatedAggregationConfiguration.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value: CalculatedFieldRelatedAggregationConfiguration) => { + this.updatedModel(value); + }); + this.relatedAggregationConfiguration.get('output').valueChanges.pipe( + takeUntilDestroyed(), + ).subscribe(() => { + this.toggleScopeByOutputType(); + }); + } + + validate(): ValidationErrors | null { + return this.relatedAggregationConfiguration.valid || this.relatedAggregationConfiguration.disabled ? null : {invalidPropagateConfig: false}; + } + + writeValue(value: CalculatedFieldRelatedAggregationConfiguration): void { + this.relatedAggregationConfiguration.patchValue(value, {emitEvent: false}); + setTimeout(() => { + this.relatedAggregationConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); + }); + } + + registerOnChange(fn: (config: CalculatedFieldRelatedAggregationConfiguration) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: any): void { } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.relatedAggregationConfiguration.disable({emitEvent: false}); + } else { + this.relatedAggregationConfiguration.enable({emitEvent: false}); + } + } + + fetchOptions(searchText: string): Observable> { + const search = searchText ? searchText?.toLowerCase() : ''; + return of(['Contains', 'Manages']).pipe(map(name => name?.filter(option => option.toLowerCase().includes(search)))); + } + + private updatedModel(value: CalculatedFieldRelatedAggregationConfiguration): void { + value.type = CalculatedFieldType.RELATED_ENTITIES_AGGREGATION; + value.scheduledUpdateInterval = this.minAllowedScheduledUpdateIntervalInSecForCF; + this.propagateChange(value); + } + + private toggleScopeByOutputType(): void { + if (this.relatedAggregationConfiguration.get('output').value.type === OutputType.Attribute) { + this.relatedAggregationConfiguration.get('useLatestTs').disable({emitEvent: false}); + } else { + this.relatedAggregationConfiguration.get('useLatestTs').enable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.module.ts new file mode 100644 index 0000000000..aaa7905845 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.module.ts @@ -0,0 +1,49 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + CalculatedFieldOutputModule +} from '@home/components/calculated-fields/components/output/calculated-field-output.module'; +import { + CalculatedFieldArgumentsTableModule +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module'; +import { + RelatedEntitiesAggregationComponentComponent +} from '@home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component'; +import { + CalculatedFieldMetricsTableModule +} from '@home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + CalculatedFieldOutputModule, + CalculatedFieldArgumentsTableModule, + CalculatedFieldMetricsTableModule, + ], + declarations: [ + RelatedEntitiesAggregationComponentComponent, + ], + exports: [ + RelatedEntitiesAggregationComponentComponent, + ] +}) +export class RelatedEntitiesAggregationComponentModule { +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.html new file mode 100644 index 0000000000..161d2dcc25 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.html @@ -0,0 +1,105 @@ + +
+
+
+ {{ 'calculated-fields.arguments' | translate }} +
+ +
+
+
+ {{ (isScript ? 'calculated-fields.type.script' : 'calculated-fields.expression') | translate }} +
+ + +
+
+ @if (simpleConfiguration.get('expressionSIMPLE').errors && simpleConfiguration.get('expressionSIMPLE').touched) { + + @if (simpleConfiguration.get('expressionSIMPLE').hasError('required')) { + {{ 'calculated-fields.hint.expression-required' | translate }} + } @else if (simpleConfiguration.get('expressionSIMPLE').hasError('pattern')) { + {{ 'calculated-fields.hint.expression-invalid' | translate }} + } @else if (simpleConfiguration.get('expressionSIMPLE').hasError('maxLength')) { + {{ 'calculated-fields.hint.expression-max-length' | translate }} + } + + } @else { + {{ 'calculated-fields.hint.expression' | translate }} + } +
+
+ +
{{ 'api-usage.tbel' | translate }} +
+ +
+
+ +
+
+
+ +
+ +
+ calculated-fields.use-latest-timestamp +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts new file mode 100644 index 0000000000..7292775055 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts @@ -0,0 +1,209 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { booleanAttribute, Component, forwardRef, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { + calculatedFieldDefaultScript, + CalculatedFieldScriptConfiguration, + CalculatedFieldSimpleConfiguration, + CalculatedFieldSimpleOutput, + CalculatedFieldType, + defaultSimpleCalculatedFieldOutput, + getCalculatedFieldArgumentsEditorCompleter, + getCalculatedFieldArgumentsHighlights, + OutputType +} from '@shared/models/calculated-field.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { deepClone } from '@core/utils'; +import { EntityId } from '@shared/models/id/entity-id'; +import { Observable } from 'rxjs'; +import { ScriptLanguage } from '@shared/models/rule-node.models'; +import { map } from 'rxjs/operators'; + +type SimpeConfiguration = CalculatedFieldSimpleConfiguration | CalculatedFieldScriptConfiguration; + +@Component({ + selector: 'tb-simple-configuration', + templateUrl: './simple-configuration.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SimpleConfigurationComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SimpleConfigurationComponent), + multi: true + } + ], +}) +export class SimpleConfigurationComponent implements ControlValueAccessor, Validator, OnChanges { + + @Input() + isScript: boolean; + + @Input({required: true}) + entityId: EntityId; + + @Input({required: true}) + tenantId: string; + + @Input({required: true}) + entityName: string; + + @Input({required: true}) + ownerId: EntityId; + + @Input({required: true}) + testScript: () => Observable; + + @Input({transform: booleanAttribute}) isEditValue = true; + + simpleConfiguration = this.fb.group({ + arguments: this.fb.control({}), + expressionSIMPLE: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + expressionSCRIPT: [calculatedFieldDefaultScript], + output: this.fb.control(defaultSimpleCalculatedFieldOutput), + useLatestTs: [false] + }); + + readonly ScriptLanguage = ScriptLanguage; + readonly OutputType = OutputType; + + functionArgs$ = this.simpleConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => ['ctx', ...Object.keys(argumentsObj)]) + ); + + argumentsEditorCompleter$ = this.simpleConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => getCalculatedFieldArgumentsEditorCompleter(argumentsObj ?? {})) + ); + + argumentsHighlightRules$ = this.simpleConfiguration.get('arguments').valueChanges.pipe( + map(argumentsObj => getCalculatedFieldArgumentsHighlights(argumentsObj)) + ); + + disabled = false; + + private propagateChange: (config: SimpeConfiguration) => void = () => { }; + + constructor(private fb: FormBuilder) { + this.simpleConfiguration.get('output').valueChanges.pipe( + takeUntilDestroyed(), + ).subscribe(() => { + this.toggleScopeByOutputType(); + }); + + this.simpleConfiguration.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value) => { + const { expressionSIMPLE, expressionSCRIPT, ...config } = value; + const cfConfig = config as SimpeConfiguration; + cfConfig.expression = this.isScript ? expressionSCRIPT : expressionSIMPLE; + this.updatedModel(cfConfig); + }) + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (change.currentValue !== change.previousValue) { + if (propName === 'isScript' && !this.disabled) { + this.updatedFormWithScript(); + if (!change.firstChange) { + this.simpleConfiguration.updateValueAndValidity(); + } + } + } + } + } + + validate(): ValidationErrors | null { + return this.simpleConfiguration.valid || this.simpleConfiguration.disabled ? null : {invalidSimpleConfig: false}; + } + + writeValue(value: SimpeConfiguration): void { + const formValue: any = deepClone(value); + if (this.isScript) { + formValue.expressionSCRIPT = formValue.expression ?? calculatedFieldDefaultScript; + } else if (value.type === CalculatedFieldType.SIMPLE) { + formValue.expressionSIMPLE = formValue.expression; + } + this.simpleConfiguration.patchValue(formValue, {emitEvent: false}); + setTimeout(() => { + this.simpleConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); + this.simpleConfiguration.get('output').updateValueAndValidity({onlySelf: true}); + }); + } + + registerOnChange(fn: (config: SimpeConfiguration) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.simpleConfiguration.disable({emitEvent: false}); + } else { + this.simpleConfiguration.enable({emitEvent: false}); + this.updatedFormWithScript(); + } + } + + onTestScript() { + this.testScript().subscribe((expression) => { + this.simpleConfiguration.get('expressionSCRIPT').setValue(expression); + this.simpleConfiguration.get('expressionSCRIPT').markAsDirty(); + }) + } + + private updatedModel(value: SimpeConfiguration): void { + value.type = this.isScript ? CalculatedFieldType.SCRIPT : CalculatedFieldType.SIMPLE; + this.propagateChange(value); + } + + private updatedFormWithScript() { + if (this.isScript) { + this.simpleConfiguration.get('expressionSIMPLE').disable({emitEvent: false}); + this.simpleConfiguration.get('expressionSCRIPT').enable({emitEvent: false}); + } else { + this.simpleConfiguration.get('expressionSIMPLE').enable({emitEvent: false}); + this.simpleConfiguration.get('expressionSCRIPT').disable({emitEvent: false}); + } + this.toggleScopeByOutputType(); + } + + private toggleScopeByOutputType(): void { + if (this.isScript || this.simpleConfiguration.get('output').value.type === OutputType.Attribute || this.disabled) { + this.simpleConfiguration.get('useLatestTs').disable({emitEvent: false}); + } else { + this.simpleConfiguration.get('useLatestTs').enable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.module.ts new file mode 100644 index 0000000000..11c6899ae6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.module.ts @@ -0,0 +1,44 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + SimpleConfigurationComponent +} from '@home/components/calculated-fields/components/simple-configuration/simple-configuration.component'; +import { + CalculatedFieldOutputModule +} from '@home/components/calculated-fields/components/output/calculated-field-output.module'; +import { + CalculatedFieldArgumentsTableModule +} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.module'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + CalculatedFieldOutputModule, + CalculatedFieldArgumentsTableModule, + ], + declarations: [ + SimpleConfigurationComponent, + ], + exports: [ + SimpleConfigurationComponent + ] +}) +export class SimpleConfigurationModule {} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.html new file mode 100644 index 0000000000..a1ccb56560 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.html @@ -0,0 +1,96 @@ + + + + + + + + + + +
+ +
+ + + + +
+
+
+ +
+
+
calculated-fields.calculated-field-types
+ + +
+
+
entity.entity-type
+ + + {{ 'alarm-rule.any-type' | translate }} + + {{ entityTypeTranslations.get(type)?.type | translate }} + + + +
+ @if (cfFilterForm.get('entityType').value) { +
+
entity.entities
+ + +
+ } +
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.scss new file mode 100644 index 0000000000..beb6c4a343 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.scss @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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: flex; + max-width: 100%; + .mdc-button { + max-width: 100%; + } +} + +:host ::ng-deep { + .mdc-button { + .mat-icon { + min-width: 24px; + } + .mdc-button__label { + overflow: hidden; + text-overflow: ellipsis; + } + } +} + +::ng-deep { + .tb-calculated-fields-config-component { + max-width: 100%; + width: 600px; + min-width: 100%; + flex: 1; + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts new file mode 100644 index 0000000000..333e74d195 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts @@ -0,0 +1,294 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + DestroyRef, + ElementRef, + forwardRef, + Inject, + InjectionToken, + Input, + OnInit, + Optional, + TemplateRef, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { deepClone, isArraysEqualIgnoreUndefined, isDefinedAndNotNull, isEmpty, isUndefinedOrNull } from '@core/utils'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { fromEvent, Subscription } from 'rxjs'; +import { POSITION_MAP } from '@shared/models/overlay.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + calculatedFieldsEntityTypeList, + CalculatedFieldsQuery, + calculatedFieldTypes, + CalculatedFieldTypeTranslations +} from '@shared/models/calculated-field.models'; +import { StringItemsOption } from '@shared/components/string-items-list.component'; +import { TranslateService } from '@ngx-translate/core'; + +export const CALCULATED_FIELDS_CONFIG_DATA = new InjectionToken('CalculatedFieldsFilterConfigData'); + +export interface CalculatedFieldsFilterConfigData { + panelMode: boolean; + userMode: boolean; + filterConfig: CalculatedFieldsQuery; + initialFilterConfig?: CalculatedFieldsQuery; +} + +@Component({ + selector: 'tb-calculated-fields-filter-config', + templateUrl: './calculated-fields-filter-config.component.html', + styleUrls: ['./calculated-fields-filter-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CalculatedFieldsFilterConfigComponent), + multi: true + } + ] +}) +export class CalculatedFieldsFilterConfigComponent implements OnInit, ControlValueAccessor { + + @ViewChild('calculatedFieldsFilterPanel') + calculatedFieldsFilterPanel: TemplateRef; + + @Input() + disabled: boolean; + + @coerceBoolean() + @Input() + buttonMode = true; + + @Input() + initialCfFilterConfig: CalculatedFieldsQuery = { + types: [], + entityType: null, + entities: [] + }; + + panelMode = false; + + buttonDisplayValue = this.translate.instant('calculated-fields.calculated-field-filter-title'); + + cfFilterForm: FormGroup; + + panelResult: CalculatedFieldsQuery = null; + + entityType = EntityType; + + listEntityTypes = calculatedFieldsEntityTypeList; + entityTypeTranslations = entityTypeTranslations; + + readonly types: StringItemsOption[] = calculatedFieldTypes.map(item => ({ + name: this.translate.instant(CalculatedFieldTypeTranslations.get(item).name), + value: item + })); + + private cfFilterOverlayRef: OverlayRef; + private cfFilterConfig: CalculatedFieldsQuery; + private resizeWindows: Subscription; + + private propagateChange = (_: any) => {}; + + constructor(@Optional() @Inject(CALCULATED_FIELDS_CONFIG_DATA) + private data: CalculatedFieldsFilterConfigData | undefined, + @Optional() private overlayRef: OverlayRef, + private fb: FormBuilder, + private overlay: Overlay, + private nativeElement: ElementRef, + private viewContainerRef: ViewContainerRef, + private destroyRef: DestroyRef, + private translate: TranslateService) { + } + + ngOnInit(): void { + if (this.data) { + this.panelMode = this.data.panelMode; + this.cfFilterConfig = this.data.filterConfig; + this.initialCfFilterConfig = this.data.initialFilterConfig; + if (this.panelMode && !this.initialCfFilterConfig) { + this.initialCfFilterConfig = deepClone(this.cfFilterConfig); + } + } + this.cfFilterForm = this.fb.group({ + types: [null, []], + entityType: [null, []], + entities: [null, []] + }); + this.cfFilterForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + if (!this.buttonMode) { + this.cfConfigUpdated(this.cfFilterForm.value); + } + } + ); + if (this.panelMode) { + this.updateCfConfigForm(this.cfFilterConfig); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.cfFilterForm.disable({emitEvent: false}); + } else { + this.cfFilterForm.enable({emitEvent: false}); + } + } + + writeValue(cfFilterConfig?: CalculatedFieldsQuery): void { + this.cfFilterConfig = cfFilterConfig; + if (!this.initialCfFilterConfig && cfFilterConfig) { + this.initialCfFilterConfig = deepClone(cfFilterConfig); + } + this.updateButtonDisplayValue(); + this.updateCfConfigForm(cfFilterConfig); + } + + toggleCfFilterPanel($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const config = new OverlayConfig({ + panelClass: 'tb-filter-panel', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + maxHeight: '80vh', + height: 'min-content', + minWidth: '' + }); + config.hasBackdrop = true; + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(this.nativeElement) + .withPositions([POSITION_MAP.bottomLeft]); + + this.cfFilterOverlayRef = this.overlay.create(config); + this.cfFilterOverlayRef.backdropClick().subscribe(() => { + this.cfFilterOverlayRef.dispose(); + }); + this.cfFilterOverlayRef.attach(new TemplatePortal(this.calculatedFieldsFilterPanel, + this.viewContainerRef)); + this.resizeWindows = fromEvent(window, 'resize').subscribe(() => { + this.cfFilterOverlayRef.updatePosition(); + }); + } + + cancel() { + this.updateCfConfigForm(this.cfFilterConfig); + this.cfFilterForm.markAsPristine(); + if (this.overlayRef) { + this.overlayRef.dispose(); + } else { + this.resizeWindows.unsubscribe(); + this.cfFilterOverlayRef.dispose(); + } + } + + update() { + this.cfConfigUpdated(this.cfFilterForm.value); + this.cfFilterForm.markAsPristine(); + if (this.panelMode) { + this.panelResult = this.cfFilterConfig; + } + if (this.overlayRef) { + this.overlayRef.dispose(); + } else { + this.resizeWindows.unsubscribe(); + this.cfFilterOverlayRef.dispose(); + } + } + + reset() { + const cfFilterConfig = this.cfFilterFromFormValue(this.cfFilterForm.value); + if (!this.cfFilterConfigEquals(cfFilterConfig, this.initialCfFilterConfig)) { + this.updateCfConfigForm(this.initialCfFilterConfig); + this.cfFilterForm.markAsDirty(); + } + } + + private cfFilterConfigEquals = (filter1?: CalculatedFieldsQuery, filter2?: CalculatedFieldsQuery): 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.types, filter2.types)) { + return false; + } + if (!isArraysEqualIgnoreUndefined(filter1.entities, filter2.entities)) { + return false; + } + return filter1.entityType === filter2.entityType; + } + return false; + }; + + private updateCfConfigForm(cfFilterConfig?: CalculatedFieldsQuery) { + this.cfFilterForm.patchValue({ + types: cfFilterConfig?.types ?? [], + entityType: cfFilterConfig?.entityType ?? null, + entities: cfFilterConfig?.entities ?? [], + }, {emitEvent: false}); + } + + private cfConfigUpdated(formValue: any) { + this.cfFilterConfig = this.cfFilterFromFormValue(formValue); + this.updateButtonDisplayValue(); + this.propagateChange(this.cfFilterConfig); + } + + private updateButtonDisplayValue() { + if (this.buttonMode) { + const filterTextParts: string[] = []; + if (this.cfFilterConfig?.types?.length) { + filterTextParts.push(this.cfFilterConfig.types.map((type) => this.translate.instant(CalculatedFieldTypeTranslations.get(type).name)).join(', ')); + } + if (this.cfFilterConfig?.entityType) { + filterTextParts.push(this.translate.instant( entityTypeTranslations.get(this.cfFilterConfig.entityType).type)); + } + if (!filterTextParts.length) { + this.buttonDisplayValue = this.translate.instant('calculated-fields.calculated-field-filter-title'); + } else { + this.buttonDisplayValue = this.translate.instant('calculated-fields.filter-title') + `: ${filterTextParts.join(', ')}`; + } + } + } + + private cfFilterFromFormValue(formValue: any): CalculatedFieldsQuery { + return { + types: formValue?.types ?? [], + entityType: formValue?.entityType ?? null, + entities: formValue?.entities ?? [], + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.html new file mode 100644 index 0000000000..f24c112f27 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.html @@ -0,0 +1,20 @@ + + + diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.scss new file mode 100644 index 0000000000..c6571e8450 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.scss @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + padding-right: 8px; + overflow: hidden; + max-width: 100%; +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.ts new file mode 100644 index 0000000000..b6bc46371c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-header.component.ts @@ -0,0 +1,43 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component'; +import { CalculatedField, CalculatedFieldsQuery } from "@shared/models/calculated-field.models"; +import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config'; + +@Component({ + selector: 'tb-calculated-fields-table-header', + templateUrl: './calculated-fields-header.component.html', + styleUrls: ['./calculated-fields-header.component.scss'] +}) +export class CalculatedFieldsHeaderComponent extends EntityTableHeaderComponent { + + get calculatedFieldsTableConfig(): CalculatedFieldsTableConfig { + return this.entitiesTableConfig as CalculatedFieldsTableConfig; + } + + constructor(protected store: Store) { + super(store); + } + + calculatedFieldsFilterChanged(calculatedFieldFilterConfig: CalculatedFieldsQuery) { + this.calculatedFieldsTableConfig.calculatedFieldFilterConfig = calculatedFieldFilterConfig; + this.calculatedFieldsTableConfig.getTable().resetSortAndFilter(true, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html index ce1f0d1a7d..e971c3da39 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html @@ -73,9 +73,8 @@ layers {{'dashboard.states-short' | translate}} - - + +
@@ -103,5 +109,11 @@
+ diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts index a73ac37795..5c602f1189 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts @@ -14,20 +14,21 @@ /// limitations under the License. /// -import { Component, forwardRef, OnDestroy } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; import { ControlValueAccessor, - UntypedFormBuilder, - UntypedFormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, ValidationErrors, Validator, Validators } from '@angular/forms'; import { getDefaultClientSecurityConfig, - getDefaultServerSecurityConfig, Lwm2mClientKeyTooltipTranslationsMap, + getDefaultServerSecurityConfig, + Lwm2mClientKeyTooltipTranslationsMap, Lwm2mSecurityConfigModels, Lwm2mSecurityType, Lwm2mSecurityTypeTranslationMap @@ -35,6 +36,11 @@ import { import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { isDefinedAndNotNull } from '@core/utils'; +import { DeviceId } from "@shared/models/id/device-id"; +import { DeviceService } from "@core/http/device.service"; +import { ActionNotificationShow } from "@core/notification/notification.actions"; +import { Store } from "@ngrx/store"; +import { AppState } from "@core/core.state"; @Component({ selector: 'tb-device-credentials-lwm2m', @@ -65,7 +71,12 @@ export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Va private destroy$ = new Subject(); private propagateChange = (v: any) => {}; - constructor(private fb: UntypedFormBuilder) { + @Input() + deviceId: DeviceId; + + constructor(protected store: Store, + private fb: UntypedFormBuilder, + private deviceService: DeviceService) { this.lwm2mConfigFormGroup = this.initLwm2mConfigForm(); } @@ -101,6 +112,41 @@ export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Va this.destroy$.complete(); } + /** + * AbstractRpcController -> rpcController + * - API + * "/api/plugins/rpc/twoway/${this.deviceId.id}" + * - DiscoveryAll + * requestBody = "{\"method\":\"DiscoverAll\"}"; + * - "Registration Update Trigger", + * requestBody = "{\"method\": \"Execute\", \"params\": {\"id\": \"/1/0/8\"}} + * - "Bootstrap-Request Trigger" + * requestBody = "{\"method\": \"Execute\", \"params\": {\"id\": \"/1/0/9\"}} + */ + + public rebootDevice(isBootstrapServer: boolean): void { + this.deviceService.rebootDevice(this.deviceId.id, isBootstrapServer).subscribe(responseReboot => { + if (responseReboot.result === 'SUCCESS') { + this.store.dispatch(new ActionNotificationShow( + { + message: responseReboot.msg, + type: 'success', + duration: 1500, + verticalPosition: 'top', + horizontalPosition: 'left' + })); + } else { + this.store.dispatch(new ActionNotificationShow( + { + message: responseReboot.msg, + type: 'error', + verticalPosition: 'top', + horizontalPosition: 'left' + })); + } + }); + } + private initClientSecurityConfig(config: Lwm2mSecurityConfigModels): void { this.lwm2mConfigFormGroup.patchValue(config, {emitEvent: false}); this.securityConfigClientUpdateValidators(config.client.securityConfigClientMode); 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 a28e894583..516aca01e2 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 @@ -81,7 +81,8 @@ - +
diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts index 9e394b613d..11e82fafdf 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts @@ -36,6 +36,7 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { generateSecret, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { DeviceId } from "@shared/models/id/device-id"; @Component({ selector: 'tb-device-credentials', @@ -88,6 +89,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, credentialTypeNamesMap = credentialTypeNames; + deviceId: DeviceId; + private propagateChange = null; private propagateChangePending = false; @@ -126,6 +129,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, writeValue(value: DeviceCredentials | null): void { if (isDefinedAndNotNull(value)) { + this.deviceId = value.deviceId; const credentialsType = this.credentialsTypes.includes(value.credentialsType) ? value.credentialsType : this.credentialsTypes[0]; this.deviceCredentialsFormGroup.patchValue({ credentialsType, diff --git a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.html b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.html index 68128cfd34..f2617c7fc8 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.html +++ b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.html @@ -33,6 +33,13 @@
+ + -
-
+
+
diff --git a/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss b/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss index 6de9cfd398..e0441fd914 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss @@ -13,11 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import '../../../../../scss/constants'; + :host { + display: grid; + height: 100%; + max-width: 100%; + max-height: 100vh; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + + @media #{$mat-gt-xs} { + .mat-mdc-dialog-content { + max-height: 80vh; + } + } + .tb-event-content { - width: 100%; min-width: 400px; - height: 100%; - min-height: 50px; + &.border { + border: 1px solid #c0c0c0; + } + @media #{$mat-xs} { + min-width: 100%; + } } } diff --git a/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts b/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts index 770ebb9c9b..90192e7fa0 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts @@ -23,7 +23,7 @@ import { Ace } from 'ace-builds'; import { DialogComponent } from '@shared/components/dialog.component'; import { Router } from '@angular/router'; import { ContentType, contentTypesMap } from '@shared/models/constants'; -import { getAce } from '@shared/models/ace/ace.models'; +import { getAce, updateEditorSize } from '@shared/models/ace/ace.models'; import { Observable } from 'rxjs/internal/Observable'; import { beautifyJs } from '@shared/models/beautify.models'; import { of } from 'rxjs'; @@ -40,40 +40,38 @@ export interface EventContentDialogData { templateUrl: './event-content-dialog.component.html', styleUrls: ['./event-content-dialog.component.scss'] }) -export class EventContentDialogComponent extends DialogComponent implements OnInit, OnDestroy { +export class EventContentDialogComponent extends DialogComponent implements OnInit, OnDestroy { @ViewChild('eventContentEditor', {static: true}) eventContentEditorElmRef: ElementRef; - content: string; title: string; - contentType: ContentType; - aceEditor: Ace.Editor; + showBorder = false; + + private contentType: ContentType; + private aceEditor: Ace.Editor; constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: EventContentDialogData, - public dialogRef: MatDialogRef, + protected dialogRef: MatDialogRef, private renderer: Renderer2) { super(store, router, dialogRef); } ngOnInit(): void { - this.content = this.data.content; this.title = this.data.title; this.contentType = this.data.contentType; - this.createEditor(this.eventContentEditorElmRef, this.content); + this.createEditor(this.eventContentEditorElmRef, this.data.content); } ngOnDestroy(): void { - if (this.aceEditor) { - this.aceEditor.destroy(); - } + this.aceEditor?.destroy(); super.ngOnDestroy(); } - isJson(str) { + private isJson(str: string) { try { return isLiteralObject(JSON.parse(str)); } catch (e) { @@ -81,78 +79,53 @@ export class EventContentDialogComponent extends DialogComponent = null; if (this.contentType) { mode = contentTypesMap.get(this.contentType).code; if (this.contentType === ContentType.JSON && content) { - content$ = beautifyJs(content, {indent_size: 4}); + content$ = beautifyJs(content, {indent_size: 2}); } else if (this.contentType === ContentType.BINARY && content) { try { const decodedData = base64toString(content); if (this.isJson(decodedData)) { mode = 'json'; - content$ = beautifyJs(decodedData, {indent_size: 4}); + content$ = beautifyJs(decodedData, {indent_size: 2}); } else { content$ = of(decodedData); } - } catch (e) {} + } catch (e) {/**/} } } if (!content$) { content$ = of(content); } - content$.subscribe( - (processedContent) => { - let editorOptions: Partial = { - mode: `ace/mode/${mode}`, - theme: 'ace/theme/github', - showGutter: false, - showPrintMargin: false, - readOnly: true - }; - - const advancedOptions = { - enableSnippets: false, - enableBasicAutocompletion: false, - enableLiveAutocompletion: false - }; - - editorOptions = {...editorOptions, ...advancedOptions}; - getAce().subscribe( - (ace) => { - this.aceEditor = ace.edit(editorElement, editorOptions); - this.aceEditor.session.setUseWrapMode(false); - this.aceEditor.setValue(processedContent, -1); - this.updateEditorSize(editorElement, processedContent, this.aceEditor); - } - ); - } - ); - } - - updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { - let newHeight = 400; - let newWidth = 600; - if (content && content.length > 0) { - const lines = content.split('\n'); - newHeight = 17 * lines.length + 16; - let maxLineLength = 0; - lines.forEach((row) => { - const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); - const lineLength = line.length; - maxLineLength = Math.max(maxLineLength, lineLength); - }); - newWidth = 9 * maxLineLength + 16; - } - // newHeight = Math.min(400, newHeight); - this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px'); - this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); - this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px'); - editor.resize(); + content$.subscribe((processedContent) => { + const isJSON = mode === 'json' && processedContent !== '{}'; + this.showBorder = isJSON; + const editorOptions: Partial = { + mode: `ace/mode/${mode}`, + theme: 'ace/theme/github', + showGutter: isJSON, + showFoldWidgets: true, + foldStyle: 'markbeginend', + showPrintMargin: false, + readOnly: true, + enableSnippets: false, + enableBasicAutocompletion: false, + enableLiveAutocompletion: false, + }; + getAce().subscribe( + (ace) => { + this.aceEditor = ace.edit(editorElement, editorOptions); + this.aceEditor.session.setUseWrapMode(false); + this.aceEditor.setValue(processedContent, -1); + updateEditorSize(editorElement, processedContent, this.aceEditor, this.renderer, {showGutter: isJSON}); + } + ) + }); } - } diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts index c206eb93a5..9f97196aa3 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -22,7 +22,14 @@ import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { DebugEventType, Event, EventBody, EventType, FilterEventBody } from '@shared/models/event.models'; +import { + DebugEventType, + Event as DebugEvent, + Event, + EventBody, + EventType, + FilterEventBody +} from '@shared/models/event.models'; import { TimePageLink } from '@shared/models/page/page-link'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; @@ -95,7 +102,8 @@ export class EventTableConfig extends EntityTableConfig { private store: Store, public testButtonLabel?: string, private debugEventSelected?: EventEmitter, - public hideClearEventAction = false) { + public hideClearEventAction = false, + public disableDebugEventAction = false) { super(); this.loadDataOnInit = false; this.tableTitle = ''; @@ -453,7 +461,7 @@ export class EventTableConfig extends EntityTableConfig { this.cellActionDescriptors.push({ name: this.translate.instant('rulenode.test-with-this-message', {test: this.translate.instant(this.testButtonLabel)}), icon: 'bug_report', - isEnabled: (entity) => entity.body.type === 'IN' || entity.body.error !== undefined, + isEnabled: (entity) => (entity.body.type === 'IN' || entity.body.error !== undefined) && !this.disableDebugEventAction, onAction: ($event, entity) => { this.debugEventSelected.next(entity.body); } @@ -464,7 +472,7 @@ export class EventTableConfig extends EntityTableConfig { this.cellActionDescriptors.push({ name: this.translate.instant('calculated-fields.test-with-this-message'), icon: 'bug_report', - isEnabled: () => true, + isEnabled: (event) => !this.disableDebugEventAction && !!(event as DebugEvent).body.arguments, onAction: (_, entity) => this.debugEventSelected.next(entity.body) }); break; diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts index 77fbb7d0bc..13b888d26b 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts @@ -17,10 +17,12 @@ import { AfterViewInit, ChangeDetectorRef, - Component, EventEmitter, + Component, + EventEmitter, Input, OnDestroy, - OnInit, Output, + OnInit, + Output, ViewChild, ViewContainerRef } from '@angular/core'; @@ -61,6 +63,21 @@ export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { @Input() hideClearEventAction: boolean = false; + private disableDebugEventActionValue = false; + + get disableDebugEventAction() { + return this.disableDebugEventActionValue; + } + + @Input() + set disableDebugEventAction(value) { + this.disableDebugEventActionValue = value; + if (this.eventTableConfig) { + this.eventTableConfig.disableDebugEventAction = this.disableDebugEventAction; + this.eventTableConfig.updateCellAction(); + } + }; + activeValue = false; dirtyValue = false; entityIdValue: EntityId; @@ -151,7 +168,8 @@ export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { this.store, this.functionTestButtonLabel, this.debugEventSelected, - this.hideClearEventAction + this.hideClearEventAction, + this.disableDebugEventAction ); } 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 00c0d3e5e1..a726e4baf7 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 @@ -23,6 +23,7 @@ import { DetailsPanelComponent } from '@home/components/details-panel.component' import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; import { AuditLogDetailsDialogComponent } from '@home/components/audit-log/audit-log-details-dialog.component'; import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component'; +import { AuditLogHeaderComponent } from '@home/components/audit-log/audit-log-header.component'; import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component'; import { EventTableComponent } from '@home/components/event/event-table.component'; import { EventFilterPanelComponent } from '@home/components/event/event-filter-panel.component'; @@ -183,30 +184,24 @@ import { } from '@home/components/dashboard-page/layout/select-dashboard-breakpoint.component'; import { EntityChipsComponent } from '@home/components/entity/entity-chips.component'; import { DashboardViewComponent } from '@home/components/dashboard-view/dashboard-view.component'; -import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; -import { CalculatedFieldDialogComponent } from '@home/components/calculated-fields/components/dialog/calculated-field-dialog.component'; import { EntityDebugSettingsButtonComponent } from '@home/components/entity/debug/entity-debug-settings-button.component'; -import { - CalculatedFieldArgumentsTableComponent -} from '@home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component'; -import { - CalculatedFieldArgumentPanelComponent -} from '@home/components/calculated-fields/components/panel/calculated-field-argument-panel.component'; -import { - CalculatedFieldDebugDialogComponent -} from '@home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component'; -import { - CalculatedFieldScriptTestDialogComponent -} from '@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component'; -import { - CalculatedFieldTestArgumentsComponent -} from '@home/components/calculated-fields/components/test-arguments/calculated-field-test-arguments.component'; import { CheckConnectivityDialogComponent } from '@home/components/ai-model/check-connectivity-dialog.component'; import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialog.component'; import { ResourcesDialogComponent } from "@home/components/resources/resources-dialog.component"; import { ResourcesLibraryComponent } from "@home/components/resources/resources-library.component"; +import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; +import { CalculatedFieldsModule } from '@home/components/calculated-fields/calculated-field.module'; +import { AlarmRuleModule } from "@home/components/alarm-rules/alarm-rule.module"; +import { AlarmRulesTableComponent } from "@home/components/alarm-rules/alarm-rules-table.component"; +import { ApiKeysTableComponent } from '@home/components/api-key/api-keys-table.component'; +import { AddApiKeyDialogComponent } from '@home/components/api-key/add-api-key-dialog.component'; +import { EditApiKeyDescriptionPanelComponent } from '@home/components/api-key/edit-api-key-description-panel.component'; +import { ApiKeyGeneratedDialogComponent } from '@home/components/api-key/api-key-generated-dialog.component'; +import { ApiKeysTableDialogComponent } from '@home/components/api-key/api-keys-table-dialog.component'; +import { AuditLogFilterComponent } from "@home/components/audit-log/audit-log-filter.component"; +import { EventsDialogComponent } from '@home/dialogs/events-dialog.component'; @NgModule({ declarations: @@ -219,6 +214,8 @@ import { ResourcesLibraryComponent } from "@home/components/resources/resources- EntityDetailsPageComponent, AuditLogTableComponent, AuditLogDetailsDialogComponent, + CalculatedFieldsTableComponent, + AlarmRulesTableComponent, EventContentDialogComponent, EventTableHeaderComponent, EventTableComponent, @@ -351,22 +348,25 @@ import { ResourcesLibraryComponent } from "@home/components/resources/resources- SendNotificationButtonComponent, EntityChipsComponent, DashboardViewComponent, - CalculatedFieldsTableComponent, - CalculatedFieldDialogComponent, - CalculatedFieldArgumentsTableComponent, - CalculatedFieldArgumentPanelComponent, - CalculatedFieldDebugDialogComponent, - CalculatedFieldScriptTestDialogComponent, - CalculatedFieldTestArgumentsComponent, CheckConnectivityDialogComponent, AIModelDialogComponent, ResourcesDialogComponent, ResourcesLibraryComponent, + ApiKeysTableComponent, + ApiKeysTableDialogComponent, + AddApiKeyDialogComponent, + EditApiKeyDescriptionPanelComponent, + ApiKeyGeneratedDialogComponent, + AuditLogHeaderComponent, + AuditLogFilterComponent, + EventsDialogComponent ], imports: [ CommonModule, SharedModule, SharedHomeComponentsModule, + CalculatedFieldsModule, + AlarmRuleModule, WidgetConfigComponentsModule, BasicWidgetConfigModule, Lwm2mProfileComponentsModule, @@ -384,6 +384,8 @@ import { ResourcesLibraryComponent } from "@home/components/resources/resources- EntityDetailsPanelComponent, EntityDetailsPageComponent, AuditLogTableComponent, + CalculatedFieldsTableComponent, + AlarmRulesTableComponent, EventTableComponent, EdgeDownlinkTableHeaderComponent, EdgeDownlinkTableComponent, @@ -500,17 +502,13 @@ import { ResourcesLibraryComponent } from "@home/components/resources/resources- SendNotificationButtonComponent, EntityChipsComponent, DashboardViewComponent, - CalculatedFieldsTableComponent, - CalculatedFieldDialogComponent, - CalculatedFieldArgumentsTableComponent, - CalculatedFieldArgumentPanelComponent, - CalculatedFieldDebugDialogComponent, - CalculatedFieldScriptTestDialogComponent, - CalculatedFieldTestArgumentsComponent, CheckConnectivityDialogComponent, AIModelDialogComponent, ResourcesDialogComponent, ResourcesLibraryComponent, + ApiKeysTableComponent, + ApiKeysTableDialogComponent, + EventsDialogComponent ], providers: [ WidgetComponentService, 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 689691848e..cc48c99c2c 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 @@ -112,17 +112,6 @@ - -
- {{'device-profile.alarm-rules-with-count' | translate: - {count: alarmRulesFormGroup.get('alarms').value ? - alarmRulesFormGroup.get('alarms').value.length : 0} }} - - -
-
{{ 'device-profile.device-provisioning' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts index 480e4b74eb..05b8517e55 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts @@ -85,8 +85,6 @@ export class AddDeviceProfileDialogComponent extends transportConfigFormGroup: UntypedFormGroup; - alarmRulesFormGroup: UntypedFormGroup; - provisionConfigFormGroup: UntypedFormGroup; serviceType = ServiceType.TB_RULE_ENGINE; @@ -132,12 +130,6 @@ export class AddDeviceProfileDialogComponent extends this.deviceProfileTransportTypeChanged(); }); - this.alarmRulesFormGroup = this.fb.group( - { - alarms: [null] - } - ); - this.provisionConfigFormGroup = this.fb.group( { provisionConfiguration: [{ @@ -176,8 +168,6 @@ export class AddDeviceProfileDialogComponent extends case 1: return this.transportConfigFormGroup; case 2: - return this.alarmRulesFormGroup; - case 3: return this.provisionConfigFormGroup; } } @@ -199,7 +189,6 @@ export class AddDeviceProfileDialogComponent extends profileData: { configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, - alarms: this.alarmRulesFormGroup.get('alarms').value, provisionConfiguration: deviceProvisionConfiguration } }; 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 5ed849e0e2..183f4319d4 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 @@ -15,8 +15,13 @@ limitations under the License. --> - - {{ 'device-profile.device-profile' | translate }} + + {{ 'device-profile.device-profile' | translate }} (); 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 863c902853..00ddc65358 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 @@ -154,21 +154,23 @@ - - - -
{{'device-profile.alarm-rules-with-count' | translate: - {count: entityForm.get('profileData.alarms').value ? - entityForm.get('profileData.alarms').value.length : 0} }}
-
-
- - - - -
+ @if (entityForm.get('profileData.alarms').value?.length) { + + + +
{{'device-profile.alarm-rules-with-count' | translate: + {count: entityForm.get('profileData.alarms').value ? + entityForm.get('profileData.alarms').value.length : 0} }}
+
+
+ + + + +
+ } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index 3da66e4ee4..3f38a5c33a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -24,7 +24,7 @@ 'device-profile.lwm2m.bootstrap-server' : 'device-profile.lwm2m.lwm2m-server') | translate }}
{{ ('device-profile.lwm2m.short-id' | translate) + ': ' }} - {{ serverFormGroup.get('shortServerId').value }} + {{ serverFormGroup.get('shortServerId').value ? serverFormGroup.get('shortServerId').value : '' }}
{{ ('device-profile.lwm2m.mode' | translate) + ': ' }} {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[serverFormGroup.get('securityMode').value]) }} @@ -54,16 +54,18 @@ - + {{ 'device-profile.lwm2m.short-id' | translate }} help - + {{ 'device-profile.lwm2m.short-id-required' | translate }} - {{ 'device-profile.lwm2m.short-id-pattern' | translate }} + {{ (isBootstrap ? 'device-profile.lwm2m.short-id-pattern-bs' : 'device-profile.lwm2m.short-id-pattern') | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index dc523e1b87..823c1bb5b7 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -74,8 +74,8 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc currentSecurityMode = null; bootstrapDisabled = false; - shortServerIdMin = 1; - shortServerIdMax = 65534; + readonly shortServerIdMin = 1; + readonly shortServerIdMax = 65534; @Input() @coerceBoolean() @@ -94,18 +94,15 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } ngOnInit(): void { - if (this.isBootstrap) { - this.shortServerIdMin = 0; - this.shortServerIdMax = 65535; - } this.serverFormGroup = this.fb.group({ host: ['', Validators.required], port: ['', [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]], securityMode: [Lwm2mSecurityType.NO_SEC], serverPublicKey: [''], clientHoldOffTime: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], - shortServerId: ['', - [Validators.required, Validators.min(this.shortServerIdMin), Validators.max(this.shortServerIdMax), Validators.pattern('[0-9]*')]], + shortServerId: ['', this.isBootstrap ? + [] : [Validators.required, Validators.pattern('[0-9]*'), Validators.min(this.shortServerIdMin), Validators.max(this.shortServerIdMax)] + ], bootstrapServerAccountTimeout: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], binding: [''], lifetime: [null, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], @@ -129,6 +126,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc this.serverFormGroup.get('serverPublicKey').patchValue(serverSecurityConfig.serverCertificate, {emitEvent: false}); } }); + this.serverFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(value => { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index eac4fdd17e..f61ad0bf68 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -25,6 +25,11 @@ (removeList)="removeObjectsList($event)" formControlName="objectIds"> + + + {{ 'device-profile.lwm2m.init-attr-tel-as-obs-strategy' | translate }} + + device-profile.lwm2m.observe-strategy.observe-strategy diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 18eb3581bd..06c19c88c0 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -108,6 +108,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServerUpdateEnable: [false], bootstrap: [[]], observeStrategy: [null, []], + initAttrTelAsObsStrategy: [false], clientLwM2mSettings: this.fb.group({ clientOnlyObserveAfterConnect: [1, []], useObject19ForOtaInfo: [false], @@ -183,6 +184,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro takeUntil(this.destroy$) ).subscribe(value => this.updateObserveStrategy(value)); + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((value) => { @@ -272,6 +274,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrap: this.configurationValue.bootstrap, bootstrapServerUpdateEnable: this.configurationValue.bootstrapServerUpdateEnable || false, observeStrategy: this.configurationValue.observeAttr.observeStrategy || ObserveStrategy.SINGLE, + initAttrTelAsObsStrategy: this.configurationValue.observeAttr.initAttrTelAsObsStrategy ?? false, clientLwM2mSettings: { clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, useObject19ForOtaInfo: this.configurationValue.clientLwM2mSettings.useObject19ForOtaInfo ?? false, @@ -440,6 +443,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const attributes: any = {}; const keyNameNew = {}; const observeStrategyValue = this.lwm2mDeviceProfileFormGroup.get('observeStrategy').value; + const initAttrTelAsObsStrategyValue = this.lwm2mDeviceProfileFormGroup.get('initAttrTelAsObsStrategy').value; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); observeJson.forEach(obj => { if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { @@ -481,6 +485,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro telemetry: telemetryArray, keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), attributeLwm2m: attributes, + initAttrTelAsObsStrategy: initAttrTelAsObsStrategyValue, observeStrategy: observeStrategyValue }; } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 3c97193480..ac445a3407 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -223,6 +223,7 @@ export interface ObservableAttributes { keyName: {}; attributeLwm2m: AttributesNameValueMap; observeStrategy: ObserveStrategy; + initAttrTelAsObsStrategy?: boolean; } export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { 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 e618e02560..19bd852f0a 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 @@ -15,7 +15,7 @@ limitations under the License. --> -
+
{{ 'tenant-profile.entities' | translate }} tenant-profile.unlimited @@ -26,10 +26,10 @@ - + {{ 'tenant-profile.maximum-devices-required' | translate}} - + {{ 'tenant-profile.maximum-devices-range' | translate}} @@ -39,10 +39,10 @@ - + {{ 'tenant-profile.maximum-dashboards-required' | translate}} - + {{ 'tenant-profile.maximum-dashboards-range' | translate}} @@ -54,10 +54,10 @@ - + {{ 'tenant-profile.maximum-assets-required' | translate}} - + {{ 'tenant-profile.maximum-assets-range' | translate}} @@ -67,10 +67,10 @@ - + {{ 'tenant-profile.maximum-users-required' | translate}} - + {{ 'tenant-profile.maximum-users-range' | translate}} @@ -89,10 +89,10 @@ - + {{ 'tenant-profile.maximum-customers-required' | translate}} - + {{ 'tenant-profile.maximum-customers-range' | translate}} @@ -102,10 +102,10 @@ - + {{ 'tenant-profile.maximum-rule-chains-required' | translate}} - + {{ 'tenant-profile.maximum-rule-chains-range' | translate}} @@ -117,10 +117,10 @@ - + {{ 'tenant-profile.maximum-edges-required' | translate }} - + {{ 'tenant-profile.maximum-edges-range' | translate }} @@ -141,10 +141,10 @@ - + {{ 'tenant-profile.max-r-e-executions-required' | translate}} - + {{ 'tenant-profile.max-r-e-executions-range' | translate}} @@ -154,10 +154,10 @@ - + {{ 'tenant-profile.max-transport-messages-required' | translate}} - + {{ 'tenant-profile.max-transport-messages-range' | translate}} @@ -176,10 +176,10 @@ - + {{ 'tenant-profile.max-j-s-executions-required' | translate}} - + {{ 'tenant-profile.max-j-s-executions-range' | translate}} @@ -189,10 +189,10 @@ - + {{ 'tenant-profile.max-tbel-executions-required' | translate}} - + {{ 'tenant-profile.max-tbel-executions-range' | translate}} @@ -204,10 +204,10 @@ - + {{ 'tenant-profile.max-rule-node-executions-per-message-required' | translate}} - + {{ 'tenant-profile.max-rule-node-executions-per-message-range' | translate}} @@ -217,10 +217,10 @@ - + {{ 'tenant-profile.max-transport-data-points-required' | translate}} - + {{ 'tenant-profile.max-transport-data-points-range' | translate}} @@ -239,10 +239,10 @@ - + {{ 'tenant-profile.max-calculated-fields-required' | translate}} - + {{ 'tenant-profile.max-calculated-fields-range' | translate}} @@ -252,10 +252,10 @@ - + {{ 'tenant-profile.max-data-points-per-rolling-arg-required' | translate}} - + {{ 'tenant-profile.max-data-points-per-rolling-arg-range' | translate}} @@ -267,10 +267,10 @@ - + {{ 'tenant-profile.max-arguments-per-cf-required' | translate}} - + {{ 'tenant-profile.max-arguments-per-cf-range' | translate}} @@ -290,10 +290,10 @@ - + {{ 'tenant-profile.max-state-size-required' | translate}} - + {{ 'tenant-profile.max-state-size-range' | translate}} @@ -303,15 +303,115 @@ - + {{ 'tenant-profile.max-value-argument-size-required' | translate}} - + {{ 'tenant-profile.max-value-argument-size-range' | translate}}
+
+ + tenant-profile.max-related-level-per-argument + + + {{ 'tenant-profile.max-related-level-per-argument-required' | translate}} + + + {{ 'tenant-profile.max-related-level-per-argument-range' | translate}} + + + + + tenant-profile.min-allowed-scheduled-update-interval + + + {{ 'tenant-profile.min-allowed-scheduled-update-interval-required' | translate}} + + + {{ 'tenant-profile.min-allowed-scheduled-update-interval-range' | translate}} + + + +
+
+ + tenant-profile.min-allowed-aggregation-interval + + + {{ 'tenant-profile.min-allowed-aggregation-interval-required' | translate}} + + + {{ 'tenant-profile.min-allowed-aggregation-interval-range' | translate}} + + + + + tenant-profile.min-allowed-deduplication-interval + + + {{ 'tenant-profile.min-allowed-deduplication-interval-required' | translate}} + + + {{ 'tenant-profile.min-allowed-deduplication-interval-range' | translate}} + + + +
+
+ + tenant-profile.intermediate-aggregation-interval + + + {{ 'tenant-profile.intermediate-aggregation-interval-required' | translate}} + + + {{ 'tenant-profile.intermediate-aggregation-interval-range' | translate}} + + + + + tenant-profile.reevaluation-check-interval + + + {{ 'tenant-profile.reevaluation-check-interval-required' | translate}} + + + {{ 'tenant-profile.reevaluation-check-interval-range' | translate}} + + + +
+
+ + tenant-profile.relation-search-entity-limit + + + {{ 'tenant-profile.relation-search-entity-limit-required' | translate}} + + + {{ 'tenant-profile.relation-search-entity-limit-range' | translate}} + + tenant-profile.relation-search-entity-limit-hint + +
+
@@ -326,10 +426,10 @@ - + {{ 'tenant-profile.max-d-p-storage-days-required' | translate}} - + {{ 'tenant-profile.max-d-p-storage-days-range' | translate}} @@ -339,10 +439,10 @@ - + {{ 'tenant-profile.alarms-ttl-days-required' | translate}} - + {{ 'tenant-profile.alarms-ttl-days-days-range' | translate}} @@ -354,10 +454,10 @@ - + {{ 'tenant-profile.default-storage-ttl-days-required' | translate}} - + {{ 'tenant-profile.default-storage-ttl-days-range' | translate}} @@ -367,10 +467,10 @@ - + {{ 'tenant-profile.rpc-ttl-days-required' | translate}} - + {{ 'tenant-profile.rpc-ttl-days-days-range' | translate}} @@ -382,10 +482,10 @@ - + {{ 'tenant-profile.queue-stats-ttl-days-required' | translate}} - + {{ 'tenant-profile.queue-stats-ttl-days-range' | translate}} @@ -395,10 +495,10 @@ - + {{ 'tenant-profile.rule-engine-exceptions-ttl-days-required' | translate}} - + {{ 'tenant-profile.rule-engine-exceptions-ttl-days-range' | translate}} @@ -413,16 +513,16 @@ {{ 'tenant-profile.sms-enabled' | translate }} - tenant-profile.max-sms - + {{ 'tenant-profile.max-sms-required' | translate}} - + {{ 'tenant-profile.max-sms-range' | translate}} @@ -432,10 +532,10 @@ - + {{ 'tenant-profile.max-emails-required' | translate}} - + {{ 'tenant-profile.max-emails-range' | translate}} @@ -445,15 +545,31 @@ - + {{ 'tenant-profile.max-created-alarms-required' | translate}} - + {{ 'tenant-profile.max-created-alarms-range' | translate}}
+
+ + tenant-profile.alarms-reevaluation-interval + + + {{ 'tenant-profile.alarms-reevaluation-interval-required' | translate}} + + + {{ 'tenant-profile.alarms-reevaluation-interval-range' | translate}} + + + +
+
@@ -466,7 +582,7 @@ - + {{ 'tenant-profile.maximum-debug-duration-min-range' | translate }} @@ -485,10 +601,10 @@ - + {{ 'tenant-profile.maximum-resources-sum-data-size-required' | translate}} - + {{ 'tenant-profile.maximum-resources-sum-data-size-range' | translate}} @@ -498,10 +614,10 @@ - + {{ 'tenant-profile.maximum-ota-package-sum-data-size-required' | translate}} - + {{ 'tenant-profile.maximum-ota-package-sum-data-size-range' | translate}} @@ -513,10 +629,10 @@ - + {{ 'tenant-profile.maximum-resource-size-required' | translate}} - + {{ 'tenant-profile.maximum-resource-size-range' | translate}} @@ -533,14 +649,14 @@ tenant-profile.ws-limit-max-sessions-per-tenant - + {{ 'tenant-profile.too-small-value-zero' | translate}} tenant-profile.ws-limit-max-subscriptions-per-tenant - + {{ 'tenant-profile.too-small-value-zero' | translate}} @@ -549,14 +665,14 @@ tenant-profile.ws-limit-max-sessions-per-customer - + {{ 'tenant-profile.too-small-value-zero' | translate}} tenant-profile.ws-limit-max-subscriptions-per-customer - + {{ 'tenant-profile.too-small-value-zero' | translate}} @@ -572,14 +688,14 @@ tenant-profile.ws-limit-max-sessions-per-public-user - + {{ 'tenant-profile.too-small-value-zero' | translate}} tenant-profile.ws-limit-max-subscriptions-per-public-user - + {{ 'tenant-profile.too-small-value-zero' | translate}} @@ -588,14 +704,14 @@ tenant-profile.ws-limit-max-sessions-per-regular-user - + {{ 'tenant-profile.too-small-value-zero' | translate}} tenant-profile.ws-limit-max-subscriptions-per-regular-user - + {{ 'tenant-profile.too-small-value-zero' | translate}} @@ -604,7 +720,7 @@ tenant-profile.ws-limit-queue-per-session - + {{ 'tenant-profile.too-small-value-one' | 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 4f1efa32d2..be674b78fc 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 @@ -14,16 +14,13 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@app/core/core.state'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { DefaultTenantProfileConfiguration, TenantProfileConfiguration } from '@shared/models/tenant.model'; +import { Component, forwardRef, Input } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { DefaultTenantProfileConfiguration, FormControlsFrom } from '@shared/models/tenant.model'; import { isDefinedAndNotNull, isUndefinedOrNull} from '@core/utils'; import { RateLimitsType } from './rate-limits/rate-limits.models'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-default-tenant-profile-configuration', @@ -35,110 +32,112 @@ import { Subject } from 'rxjs'; multi: true }] }) -export class DefaultTenantProfileConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { +export class DefaultTenantProfileConfigurationComponent implements ControlValueAccessor { - defaultTenantProfileConfigurationFormGroup: UntypedFormGroup; + tenantProfileConfigurationForm: FormGroup>; - private requiredValue: boolean; - private destroy$ = new Subject(); - get required(): boolean { - return this.requiredValue; - } @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } + @coerceBoolean() + required: boolean; @Input() + @coerceBoolean() disabled: boolean; rateLimitsType = RateLimitsType; - private propagateChange = (v: any) => { }; - - constructor(private store: Store, - private fb: UntypedFormBuilder) { - this.defaultTenantProfileConfigurationFormGroup = this.fb.group({ - maxDevices: [null, [Validators.required, Validators.min(0)]], - maxAssets: [null, [Validators.required, Validators.min(0)]], - maxCustomers: [null, [Validators.required, Validators.min(0)]], - maxUsers: [null, [Validators.required, Validators.min(0)]], - maxDashboards: [null, [Validators.required, Validators.min(0)]], - maxRuleChains: [null, [Validators.required, Validators.min(0)]], - maxEdges: [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, []], - transportDeviceMsgRateLimit: [null, []], - transportDeviceTelemetryMsgRateLimit: [null, []], - transportDeviceTelemetryDataPointsRateLimit: [null, []], - transportGatewayMsgRateLimit: [null, []], - transportGatewayTelemetryMsgRateLimit: [null, []], - transportGatewayTelemetryDataPointsRateLimit: [null, []], - transportGatewayDeviceMsgRateLimit: [null, []], - transportGatewayDeviceTelemetryMsgRateLimit: [null, []], - transportGatewayDeviceTelemetryDataPointsRateLimit: [null, []], - tenantEntityExportRateLimit: [null, []], - tenantEntityImportRateLimit: [null, []], - tenantNotificationRequestsRateLimit: [null, []], - tenantNotificationRequestsPerRuleRateLimit: [null, []], - maxTransportMessages: [null, [Validators.required, Validators.min(0)]], - 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)]], - maxSms: [null, []], - smsEnabled: [null, []], - maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], - maxDebugModeDurationMinutes: [null, [Validators.min(0)]], - 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)]], - maxWsSessionsPerCustomer: [null, [Validators.min(0)]], - maxWsSessionsPerRegularUser: [null, [Validators.min(0)]], - maxWsSessionsPerPublicUser: [null, [Validators.min(0)]], - wsMsgQueueLimitPerSession: [null, [Validators.min(0)]], - maxWsSubscriptionsPerTenant: [null, [Validators.min(0)]], - maxWsSubscriptionsPerCustomer: [null, [Validators.min(0)]], - maxWsSubscriptionsPerRegularUser: [null, [Validators.min(0)]], - maxWsSubscriptionsPerPublicUser: [null, [Validators.min(0)]], - wsUpdatesPerSessionRateLimit: [null, []], - cassandraWriteQueryTenantCoreRateLimits: [null, []], - cassandraReadQueryTenantCoreRateLimits: [null, []], - cassandraWriteQueryTenantRuleEngineRateLimits: [null, []], - cassandraReadQueryTenantRuleEngineRateLimits: [null, []], - edgeEventRateLimits: [null, []], - edgeEventRateLimitsPerEdge: [null, []], - edgeUplinkMessagesRateLimits: [null, []], - edgeUplinkMessagesRateLimitsPerEdge: [null, []], - maxCalculatedFieldsPerEntity: [null, [Validators.required, Validators.min(0)]], - maxArgumentsPerCF: [null, [Validators.required, Validators.min(0)]], - maxDataPointsPerRollingArg: [null, [Validators.required, Validators.min(0)]], - maxStateSizeInKBytes: [null, [Validators.required, Validators.min(0)]], - calculatedFieldDebugEventsRateLimit: [null, []], - maxSingleValueArgumentSizeInKBytes: [null, [Validators.required, Validators.min(0)]], + private propagateChange = (_v: any) => { }; + + constructor(private fb: FormBuilder) { + this.tenantProfileConfigurationForm = this.fb.group({ + maxDevices: [0, [Validators.required, Validators.min(0)]], + maxAssets: [0, [Validators.required, Validators.min(0)]], + maxCustomers: [0, [Validators.required, Validators.min(0)]], + maxUsers: [0, [Validators.required, Validators.min(0)]], + maxDashboards: [0, [Validators.required, Validators.min(0)]], + maxRuleChains: [0, [Validators.required, Validators.min(0)]], + maxEdges: [0, [Validators.required, Validators.min(0)]], + maxResourcesInBytes: [0, [Validators.required, Validators.min(0)]], + maxOtaPackagesInBytes: [0, [Validators.required, Validators.min(0)]], + maxResourceSize: [0, [Validators.required, Validators.min(0)]], + transportTenantMsgRateLimit: [''], + transportTenantTelemetryMsgRateLimit: [''], + transportTenantTelemetryDataPointsRateLimit: [''], + transportDeviceMsgRateLimit: [''], + transportDeviceTelemetryMsgRateLimit: [''], + transportDeviceTelemetryDataPointsRateLimit: [''], + transportGatewayMsgRateLimit: [''], + transportGatewayTelemetryMsgRateLimit: [''], + transportGatewayTelemetryDataPointsRateLimit: [''], + transportGatewayDeviceMsgRateLimit: [''], + transportGatewayDeviceTelemetryMsgRateLimit: [''], + transportGatewayDeviceTelemetryDataPointsRateLimit: [''], + tenantEntityExportRateLimit: [''], + tenantEntityImportRateLimit: [''], + tenantNotificationRequestsRateLimit: [''], + tenantNotificationRequestsPerRuleRateLimit: [''], + maxTransportMessages: [0, [Validators.required, Validators.min(0)]], + maxTransportDataPoints: [0, [Validators.required, Validators.min(0)]], + maxREExecutions: [0, [Validators.required, Validators.min(0)]], + maxJSExecutions: [0, [Validators.required, Validators.min(0)]], + maxTbelExecutions: [0, [Validators.required, Validators.min(0)]], + maxDPStorageDays: [0, [Validators.required, Validators.min(0)]], + maxRuleNodeExecutionsPerMessage: [0, [Validators.required, Validators.min(0)]], + maxEmails: [0, [Validators.required, Validators.min(0)]], + maxSms: [0], + smsEnabled: [false], + maxCreatedAlarms: [0, [Validators.required, Validators.min(0)]], + maxDebugModeDurationMinutes: [0, [Validators.min(0)]], + defaultStorageTtlDays: [0, [Validators.required, Validators.min(0)]], + alarmsTtlDays: [0, [Validators.required, Validators.min(0)]], + rpcTtlDays: [0, [Validators.required, Validators.min(0)]], + queueStatsTtlDays: [0, [Validators.required, Validators.min(0)]], + ruleEngineExceptionsTtlDays: [0, [Validators.required, Validators.min(0)]], + tenantServerRestLimitsConfiguration: [''], + customerServerRestLimitsConfiguration: [''], + maxWsSessionsPerTenant: [0, [Validators.min(0)]], + maxWsSessionsPerCustomer: [0, [Validators.min(0)]], + maxWsSessionsPerRegularUser: [0, [Validators.min(0)]], + maxWsSessionsPerPublicUser: [0, [Validators.min(0)]], + wsMsgQueueLimitPerSession: [0, [Validators.min(0)]], + maxWsSubscriptionsPerTenant: [0, [Validators.min(0)]], + maxWsSubscriptionsPerCustomer: [0, [Validators.min(0)]], + maxWsSubscriptionsPerRegularUser: [0, [Validators.min(0)]], + maxWsSubscriptionsPerPublicUser: [0, [Validators.min(0)]], + wsUpdatesPerSessionRateLimit: [''], + cassandraWriteQueryTenantCoreRateLimits: [''], + cassandraReadQueryTenantCoreRateLimits: [''], + cassandraWriteQueryTenantRuleEngineRateLimits: [''], + cassandraReadQueryTenantRuleEngineRateLimits: [''], + edgeEventRateLimits: [''], + edgeEventRateLimitsPerEdge: [''], + edgeUplinkMessagesRateLimits: [''], + edgeUplinkMessagesRateLimitsPerEdge: [''], + maxCalculatedFieldsPerEntity: [0, [Validators.required, Validators.min(0)]], + maxArgumentsPerCF: [0, [Validators.required, Validators.min(0)]], + maxRelationLevelPerCfArgument: [1, [Validators.required, Validators.min(1)]], + minAllowedDeduplicationIntervalInSecForCF: [0, [Validators.required, Validators.min(0)]], + minAllowedAggregationIntervalInSecForCF: [0, [Validators.required, Validators.min(0)]], + maxRelatedEntitiesToReturnPerCfArgument: [1, [Validators.required, Validators.min(1)]], + minAllowedScheduledUpdateIntervalInSecForCF: [0, [Validators.required, Validators.min(0)]], + intermediateAggregationIntervalInSecForCF: [0, [Validators.required, Validators.min(1)]], + cfReevaluationCheckInterval: [0, [Validators.required, Validators.min(1)]], + alarmsReevaluationInterval: [0, [Validators.required, Validators.min(1)]], + maxDataPointsPerRollingArg: [0, [Validators.required, Validators.min(0)]], + maxStateSizeInKBytes: [0, [Validators.required, Validators.min(0)]], + calculatedFieldDebugEventsRateLimit: [''], + maxSingleValueArgumentSizeInKBytes: [0, [Validators.required, Validators.min(0)]], }); - this.defaultTenantProfileConfigurationFormGroup.get('smsEnabled').valueChanges.pipe( - takeUntil(this.destroy$) + this.tenantProfileConfigurationForm.get('smsEnabled').valueChanges.pipe( + takeUntilDestroyed() ).subscribe((value: boolean) => { this.maxSmsValidation(value); } ); - this.defaultTenantProfileConfigurationFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) + this.tenantProfileConfigurationForm.valueChanges.pipe( + takeUntilDestroyed() ).subscribe(() => { this.updateModel(); }); @@ -146,34 +145,26 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA private maxSmsValidation(smsEnabled: boolean) { if (smsEnabled) { - this.defaultTenantProfileConfigurationFormGroup.get('maxSms').addValidators([Validators.required, Validators.min(0)]); + this.tenantProfileConfigurationForm.get('maxSms').addValidators([Validators.required, Validators.min(0)]); } else { - this.defaultTenantProfileConfigurationFormGroup.get('maxSms').clearValidators(); + this.tenantProfileConfigurationForm.get('maxSms').clearValidators(); } - this.defaultTenantProfileConfigurationFormGroup.get('maxSms').updateValueAndValidity({emitEvent: false}); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); + this.tenantProfileConfigurationForm.get('maxSms').updateValueAndValidity({emitEvent: false}); } registerOnChange(fn: any): void { this.propagateChange = fn; } - registerOnTouched(fn: any): void { - } - - ngOnInit() { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { - this.defaultTenantProfileConfigurationFormGroup.disable({emitEvent: false}); + this.tenantProfileConfigurationForm.disable({emitEvent: false}); } else { - this.defaultTenantProfileConfigurationFormGroup.enable({emitEvent: false}); + this.tenantProfileConfigurationForm.enable({emitEvent: false}); } } @@ -183,14 +174,14 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA value.smsEnabled = true; } this.maxSmsValidation(value.smsEnabled); - this.defaultTenantProfileConfigurationFormGroup.patchValue(value, {emitEvent: false}); + this.tenantProfileConfigurationForm.patchValue(value, {emitEvent: false}); } } private updateModel() { - let configuration: TenantProfileConfiguration = null; - if (this.defaultTenantProfileConfigurationFormGroup.valid) { - configuration = this.defaultTenantProfileConfigurationFormGroup.getRawValue(); + let configuration: DefaultTenantProfileConfiguration = null; + if (this.tenantProfileConfigurationForm.valid) { + configuration = this.tenantProfileConfigurationForm.getRawValue(); } this.propagateChange(configuration); } 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 d4f773a8c2..5335f36ff4 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 @@ -104,8 +104,20 @@ {{ 'relation.type' | translate }} - - {{ relation.type }} + +
+ {{ relation.type }} + + +
@@ -117,9 +129,13 @@ {{ 'relation.to-entity-name' | translate }} - - {{ relation.toName | customTranslate }} - + @if(relation.entityURL){ + + {{ relation.toName | customTranslate }} + + } @else { + {{ relation.toName | customTranslate }} + } @@ -131,9 +147,13 @@ {{ 'relation.from-entity-name' | translate }} - + @if(relation.entityURL){ + + {{ relation.fromName | customTranslate }} + + } @else { {{ relation.fromName | customTranslate }} - + } diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss index ec44e6987b..13ca6b5dd3 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.scss @@ -59,6 +59,17 @@ overflow: hidden; text-overflow: ellipsis; } + + .type-copy { + visibility: hidden; + transition: visibility 0.1s; + } + + .type:hover { + .type-copy { + visibility: visible; + } + } } } diff --git a/ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.ts b/ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.ts index fb20a0fe26..5456fd7f88 100644 --- a/ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/resources/resources-dialog.component.ts @@ -98,18 +98,17 @@ export class ResourcesDialogComponent extends DialogComponent response[0]) ).subscribe(result => this.dialogRef.close(result)); } else { if (resource.resourceType !== ResourceType.GENERAL) { delete resource.descriptor; } - this.resourceService.saveResource(resource).subscribe(result => this.dialogRef.close(result)); + this.resourceService.uploadResource(resource).subscribe(result => this.dialogRef.close(result)); } } } diff --git a/ui-ngx/src/app/modules/home/components/resources/resources-library.component.html b/ui-ngx/src/app/modules/home/components/resources/resources-library.component.html index 893dbf52f9..efc49b9d82 100644 --- a/ui-ngx/src/app/modules/home/components/resources/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/components/resources/resources-library.component.html @@ -70,7 +70,7 @@ impleme return this.fb.group({ title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, Validators.required], - fileName: [entity ? entity.fileName : null, Validators.required], + fileName: [entity ? entity.fileName : null], data: [entity ? entity.data : null, this.isAdd ? [Validators.required] : []], descriptor: this.fb.group({ mediaType: [''] diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-processing-setting.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-processing-setting.component.html index ab70346ff7..9f51ca5189 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-processing-setting.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-processing-setting.component.html @@ -39,6 +39,6 @@ > diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts index 6f72c7ef90..e94f12dd69 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts @@ -33,7 +33,6 @@ import { RelationsQueryConfigOldComponent } from './relations-query-config-old.c import { SelectAttributesComponent } from './select-attributes.component'; import { AlarmStatusSelectComponent } from './alarm-status-select.component'; import { ExampleHintComponent } from './example-hint.component'; -import { TimeUnitInputComponent } from './time-unit-input.component'; @NgModule({ declarations: [ @@ -52,7 +51,6 @@ import { TimeUnitInputComponent } from './time-unit-input.component'; SelectAttributesComponent, AlarmStatusSelectComponent, ExampleHintComponent, - TimeUnitInputComponent ], imports: [ CommonModule, @@ -75,7 +73,6 @@ import { TimeUnitInputComponent } from './time-unit-input.component'; SelectAttributesComponent, AlarmStatusSelectComponent, ExampleHintComponent, - TimeUnitInputComponent ] }) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html index 3cf88a2463..73605e0767 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html @@ -29,7 +29,7 @@ labelText="rule-node-config.ai.model" (entityChanged)="onEntityChange($event)" [entityType]="entityType.AI_MODEL" - (createNew)="createModelAi('modelId')" + (createNew)="createModelAi($event, 'modelId')" formControlName="modelId"> diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts index de1a1085f8..a01f95549b 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts @@ -108,12 +108,13 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent { return this.translate.instant(`rule-node-config.ai.response-format-hint-${this.aiConfigForm.get('responseFormat.type').value}`); } - createModelAi(formControl: string) { + createModelAi(name: string, formControl: string) { this.dialog.open(AIModelDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - isAdd: true + isAdd: true, + name } }).afterClosed() .subscribe((model) => { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts index 3b5b67cfbc..f326e7e9ce 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts @@ -269,7 +269,8 @@ export enum HttpRequestType { GET = 'GET', POST = 'POST', PUT = 'PUT', - DELETE = 'DELETE' + DELETE = 'DELETE', + PATCH = 'PATCH' } export const ToByteStandartCharsetTypes = [ diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html index 134d42ef39..40b47e70b2 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html @@ -15,76 +15,79 @@ limitations under the License. --> -
- -

{{ 'version-control.create-entities-version' | translate }}

- -
- - - -
- - - - version-control.version-name - - - {{ 'version-control.version-name-required' | translate }} - +@if (!versionCreateResult$) { +
+ +

{{ 'version-control.create-entities-version' | translate }}

+ +
+ + + +
+ + + + version-control.version-name + + + {{ 'version-control.version-name-required' | translate }} + + +
+ + version-control.default-sync-strategy + + @for (strategy of syncStrategies; track strategy) { + + {{syncStrategyTranslations.get(strategy) | translate}} + + } + + + + + +
+ +
- - version-control.default-sync-strategy - - - {{syncStrategyTranslations.get(strategy) | translate}} - - - - - - - -
- - -
-
-
-
-
-
- -
- +} @else { +
+ @if ((versionCreateResult$ | async)?.done || hasError) { +
+ +
+ } @else {
version-control.creating-version
-
-
+ } +} diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html index 96b1927040..a5ced53c03 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html @@ -15,59 +15,63 @@ limitations under the License. --> -
- -

{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}

- -
- - -
- - -
-
- - {{ 'version-control.rollback-on-error' | translate }} - -
-
- - -
-
-
-
+@if (!versionLoadResult$) { +
+ +

{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}

+ +
+ + +
+ + +
+
+ + {{ 'version-control.rollback-on-error' | translate }} + +
+
+ + +
+
+} @else { +
{{ 'version-control.no-entities-restored' | translate }}
-
-
{{ entityTypeLoadResultMessage(entityTypeLoadResult) }}
-
- -
- +
+ @for (entityTypeLoadResult of entityTypeLoadResults; track entityTypeLoadResult.entityType) { +
{{ entityTypeLoadResultMessage(entityTypeLoadResult) }}
+ } + @if ((versionLoadResult$ | async)?.done || hasError) { +
+ +
+ } @else {
version-control.restoring-entities-from-version
-
-
+ } +} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html index 464dd3cded..4adeb47d69 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html @@ -18,86 +18,90 @@
version-control.entities-to-export
-
- - -
- -
-
{{ entityTypeText(entityTypeFormGroup) }}
-
-
- - -
-
-
- -
- - -
- - version-control.sync-strategy - - - {{ 'version-control.default' | translate }} - - - {{syncStrategyTranslations.get(strategy) | translate}} - - - -
- - {{ 'version-control.export-credentials' | translate }} - - - {{ 'version-control.export-attributes' | translate }} - - - {{ 'version-control.export-relations' | translate }} - - - {{ 'version-control.export-calculated-fields' | translate }} - + @for (entityTypeFormGroup of entityTypesFormGroupArray(); track entityTypeFormGroup; let index = $index, isLast = $last) { +
+ + +
+ +
+
{{ entityTypeText(entityTypeFormGroup) }}
+
+
+ + +
+
+
+ +
+ + +
+ + version-control.sync-strategy + + + {{ 'version-control.default' | translate }} + + @for (strategy of syncStrategies; track strategy) { + + {{syncStrategyTranslations.get(strategy) | translate}} + + } + + +
+ + {{ 'version-control.export-credentials' | translate }} + + + {{ 'version-control.export-attributes' | translate }} + + + {{ 'version-control.export-relations' | translate }} + + + {{ (entityTypeFormGroup.get('entityType').value === entityTypes.CUSTOMER ? 'version-control.export-alarm-rules' : 'version-control.export-calculated-fields') | translate }} + +
+
+ + {{ 'version-control.all-entities' | translate }} + + @if (!entityTypeFormGroup.get('config').get('allEntities').value) { + + + } +
-
- - {{ 'version-control.all-entities' | translate }} - - - -
-
- -
-
- version-control.no-entities-to-export-prompt -
+ +
+ } @empty { + version-control.no-entities-to-export-prompt + }
-
- -
- -
- - -
-
- - {{ 'version-control.remove-other-entities' | translate }} - - - {{ 'version-control.find-existing-entity-by-name' | translate }} - -
-
- - {{ 'version-control.load-credentials' | translate }} - - - {{ 'version-control.load-attributes' | translate }} - - - {{ 'version-control.load-relations' | translate }} - - - {{ 'version-control.load-calculated-fields' | translate }} - + @for (entityTypeFormGroup of entityTypesFormGroupArray(); track entityTypeFormGroup; let index = $index, isLast = $last) { +
+ + +
+ +
+
{{ entityTypeText(entityTypeFormGroup) }}
+
+
+ + +
+
+
+ +
+ + +
+
+ + {{ 'version-control.remove-other-entities' | translate }} + + + {{ 'version-control.find-existing-entity-by-name' | translate }} + +
+
+ + {{ 'version-control.load-credentials' | translate }} + + + {{ 'version-control.load-attributes' | translate }} + + + {{ 'version-control.load-relations' | translate }} + + + {{ (entityTypeFormGroup.get('entityType').value === entityTypes.CUSTOMER ? 'version-control.load-alarm-rules' : 'version-control.load-calculated-fields') | translate }} + +
-
- -
-
- version-control.no-entities-to-restore-prompt -
+ +
+ } @empty { + version-control.no-entities-to-restore-prompt + }
-
-
-
-
+} @else { + @if ((versionCreateResult$ | async)?.done || resultMessage) { +
{{ resultMessage }}
-
- + } @else {
version-control.creating-version
-
-
+ } +} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html index 6057126270..53672bf812 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html @@ -15,51 +15,53 @@ limitations under the License. --> -
- -

{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}

- -
- - - - -
-
- - {{ 'version-control.load-credentials' | translate }} - - - {{ 'version-control.load-attributes' | translate }} - - - {{ 'version-control.load-relations' | translate }} - - - {{ 'version-control.load-calculated-fields' | translate }} - -
-
- -
- - -
-
-
-
-
+@if (!versionLoadResult$) { + @if (entityDataInfo) { + +

{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}

+ +
+ + +
+
+
+ + {{ 'version-control.load-credentials' | translate }} + + + {{ 'version-control.load-attributes' | translate }} + + + {{ 'version-control.load-relations' | translate }} + + + {{ ( externalEntityId?.entityType === EntityType.CUSTOMER ? 'version-control.load-alarm-rules' : 'version-control.load-calculated-fields') | translate }} + +
+
+
+
+ + +
+ } @else { + + } +} @else { + @if ((versionLoadResult$ | async)?.done || errorMessage) { +
-
- + } @else {
version-control.restoring-entity-version
-
-
+ } +} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts index 89915593a8..c48648dacf 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts @@ -33,6 +33,7 @@ import { delay, share } from 'rxjs/operators'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { Observable, Subscription } from 'rxjs'; import { parseHttpErrorMessage } from '@core/utils'; +import { EntityType } from '@shared/models/entity-type.models'; @Component({ selector: 'tb-entity-version-restore', @@ -62,6 +63,8 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn errorMessage: SafeHtml; + EntityType = EntityType; + versionLoadResult$: Observable; private versionLoadResultSubscription: Subscription; diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html index e58a93cf30..cc3fb23da5 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html @@ -114,7 +114,9 @@ {{ 'version-control.version-name' | translate }} - {{ entityVersion.name }} +
+ {{ entityVersion.name?.length > 256 ? entityVersion.name.substring(0, 256) + '…' : entityVersion.name }} +
diff --git a/ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.html b/ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.html index f6ceab690b..60278cbe9b 100644 --- a/ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/remove-other-entities-confirm.component.html @@ -19,7 +19,7 @@
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html index 12e2ca72ce..4ff90e43aa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html @@ -57,7 +57,7 @@ - {{ getCellClickColumnInfo($index, column) }} + {{ getCellClickColumnInfo($index, column) | customTranslate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html index 3370ef4186..51ebab7a92 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html @@ -82,9 +82,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html index 0c9ddd233e..f748c13f81 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html @@ -120,9 +120,15 @@ {{ 'widgets.button.label' | translate }}
- - - + @@ -151,9 +157,15 @@ {{ 'widgets.button.label' | translate }}
- - - + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts index 12542ac5be..895848a933 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts @@ -20,7 +20,7 @@ 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 { Datasource, TargetDevice, } from '@shared/models/widget.models'; +import { Datasource, TargetDevice, widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { ValueType } from '@shared/models/constants'; import { getTargetDeviceFromDatasources } from '@shared/models/widget-settings.models'; @@ -48,7 +48,7 @@ export class SegmentedButtonBasicConfigComponent extends BasicWidgetConfigCompon const datasources: Datasource[] = this.segmentedButtonWidgetConfigForm.get('datasources').value; return getTargetDeviceFromDatasources(datasources); } - + predefinedValues = widgetTitleAutocompleteValues; segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'first'; segmentedButtonColorStylesType: SegmentedButtonColorStylesType = 'selected'; @@ -142,4 +142,52 @@ export class SegmentedButtonBasicConfigComponent extends BasicWidgetConfigCompon return this.widgetConfig; } + + protected validatorTriggers(): string[] { + return ['appearance.leftAppearance.showLabel', 'appearance.leftAppearance.showIcon', 'appearance.rightAppearance.showLabel', 'appearance.rightAppearance.showIcon',]; + } + + protected updateValidators(emitEvent: boolean) { + const showLeftLabel: boolean = this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.showLabel').value; + const showRightLabel: boolean = this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.showLabel').value; + + const showLeftIcon: boolean = this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.showIcon').value; + const showRightIcon: boolean = this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.showIcon').value; + + if (showLeftLabel) { + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.label').enable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.labelFont').enable({emitEvent}); + } else { + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.label').disable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.labelFont').disable({emitEvent}); + } + + if (showRightLabel) { + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.label').enable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.labelFont').enable({emitEvent}); + } else { + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.label').disable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.labelFont').disable({emitEvent}); + } + + if (showLeftIcon) { + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.icon').enable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.iconSize').enable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.iconSizeUnit').enable({emitEvent}); + } else { + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.icon').disable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.iconSize').disable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.leftAppearance.iconSizeUnit').disable({emitEvent}); + } + + if (showRightIcon) { + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.icon').enable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.iconSize').enable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.iconSizeUnit').enable({emitEvent}); + } else { + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.icon').disable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.iconSize').disable({emitEvent}); + this.segmentedButtonWidgetConfigForm.get('appearance.rightAppearance.iconSizeUnit').disable({emitEvent}); + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/toggle-button-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/toggle-button-basic-config.component.html index 7c4b699c60..d506fe4e8b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/toggle-button-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/toggle-button-basic-config.component.html @@ -92,9 +92,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { 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 index 52e1375d8d..f0897ae55c 100644 --- 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 @@ -39,9 +39,16 @@ {{ 'widget-config.title' | 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 index e31ea66342..91666396b9 100644 --- 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 @@ -20,7 +20,7 @@ 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 { ComparisonResultType, DataKey, Datasource, WidgetConfig, widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -80,6 +80,8 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo datePreviewFn = this._datePreviewFn.bind(this); + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private cd: ChangeDetectorRef, @@ -101,10 +103,10 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo history: { historyType: HistoryWindowType.INTERVAL, quickInterval: QuickTimeInterval.CURRENT_MONTH_SO_FAR, + interval: 12 * HOUR, }, aggregation: { type: AggregationType.AVG, - interval: 12 * HOUR, limit: 5000 } }; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.html index 54b3da30c8..90cb150339 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.html @@ -41,9 +41,16 @@ {{ 'widgets.label-card.label' | translate }}
- - - + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts index 5fdc60083d..e587269b15 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/label-value-card-basic-config.component.ts @@ -25,6 +25,7 @@ import { datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig, + widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { formatValue, isUndefined } from '@core/utils'; @@ -60,6 +61,8 @@ export class LabelValueCardBasicConfigComponent extends BasicWidgetConfigCompone valuePreviewFn = this._valuePreviewFn.bind(this); + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { 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 index 83490e9a2d..120ccf6457 100644 --- 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 @@ -52,9 +52,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { 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 07874696f8..1e28c9eb85 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 @@ -32,9 +32,15 @@
widget-config.appearance
widgets.simple-card.label
- - - +
widgets.simple-card.label-position
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts index de7d66d9d9..3594c8dc98 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts @@ -25,6 +25,7 @@ import { datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig, + widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -54,6 +55,8 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent { simpleCardWidgetConfigForm: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html index f6b67f72b6..43777d596e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html @@ -44,9 +44,16 @@ {{ 'widget-config.card-title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { 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 index 095b26e11d..9a4a6196c8 100644 --- 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 @@ -53,9 +53,16 @@ {{ 'widgets.value-card.label' | 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 index 975f8a9655..ef3fbabbb0 100644 --- 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 @@ -25,6 +25,7 @@ import { datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig, + widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -84,6 +85,8 @@ export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent { return layout !== ValueCardLayout.simplified; } + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private cd: ChangeDetectorRef, 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 index 5e45c9baa6..c7943ec0a7 100644 --- 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 @@ -51,9 +51,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts index db6afe3404..dccbb62f7d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts @@ -19,7 +19,7 @@ import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { DataKey } from '@shared/models/widget.models'; +import { DataKey, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -40,6 +40,8 @@ export class BarChartBasicConfigComponent extends LatestChartBasicConfigComponen @ViewChild('barChart') barChartConfigTemplate: TemplateRef; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, protected fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index f28d0432ca..1aa4c2be3d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -49,9 +49,16 @@ {{ 'widget-config.title' | translate }}
- - - + widgets.time-series-chart.axis.y-axis
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts index d7603fe76f..1436275c7d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts @@ -26,6 +26,7 @@ import { legendPositions, legendPositionTranslationMap, WidgetConfig, + widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -73,6 +74,8 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, 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 index f1e5501343..c9d0af5dfd 100644 --- 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 @@ -19,7 +19,7 @@ import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { DataKey } from '@shared/models/widget.models'; +import { DataKey, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -41,6 +41,8 @@ export class DoughnutBasicConfigComponent extends LatestChartBasicConfigComponen @ViewChild('doughnutChart') doughnutChartConfigTemplate: TemplateRef; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, protected fb: UntypedFormBuilder) { 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 index ca383c0d88..029a1a8cb3 100644 --- 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 @@ -44,9 +44,16 @@ {{ 'widget-config.card-title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html index cded03d843..b0f108ba58 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html @@ -50,9 +50,16 @@ {{ 'widget-config.card-title' | translate }}
- - - + ; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, protected fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/polar-area-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/polar-area-chart-basic-config.component.ts index 0e5abe194d..3050b0550d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/polar-area-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/polar-area-chart-basic-config.component.ts @@ -19,7 +19,7 @@ import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { DataKey } from '@shared/models/widget.models'; +import { DataKey, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -40,6 +40,8 @@ export class PolarAreaChartBasicConfigComponent extends LatestChartBasicConfigCo @ViewChild('polarAreaChart') polarAreaChartConfigTemplate: TemplateRef; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, protected fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts index 56cdf66fb6..6af8b7de34 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts @@ -19,7 +19,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { DataKey } from '@shared/models/widget.models'; +import { DataKey, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -40,6 +40,8 @@ export class RadarChartBasicConfigComponent extends LatestChartBasicConfigCompon @ViewChild('radarChart') radarChartConfigTemplate: TemplateRef; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, protected fb: UntypedFormBuilder) { 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 index c761ccdcc6..7d3195e7f4 100644 --- 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 @@ -33,9 +33,16 @@ {{ 'widget-config.title' | translate }}
- - - + widgets.time-series-chart.axis.y-axis
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 index 6681ad09a6..d93a77e879 100644 --- 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 @@ -26,6 +26,7 @@ import { legendPositions, legendPositionTranslationMap, WidgetConfig, + widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -102,6 +103,8 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html index 52992518ee..85ef776718 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html @@ -103,6 +103,9 @@ formControlName="states"> @@ -123,9 +126,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entities-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entities-table-basic-config.component.html index 4bca260f37..27ad183dfa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entities-table-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entities-table-basic-config.component.html @@ -44,9 +44,16 @@ {{ 'widget-config.card-title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.html index 65e4faf42e..fae864ba15 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.html @@ -26,7 +26,7 @@
widget-config.appearance
- +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.ts index 53712619a1..b7f6ca68ee 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/entity/entity-count-basic-config.component.ts @@ -20,7 +20,7 @@ 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, } from '@shared/models/widget.models'; +import { DatasourceType, WidgetConfig, widgetTitleAutocompleteValues, } 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'; @@ -36,6 +36,8 @@ export class EntityCountBasicConfigComponent extends BasicWidgetConfigComponent entityCountWidgetConfigForm: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private utils: UtilsService, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html index 39c516d706..e36e315b3e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html @@ -53,9 +53,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html index d60673f00c..473bdb0b4b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html @@ -35,9 +35,16 @@ {{ 'widgets.liquid-level-card.title' | translate }}
- - - + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts index af11303c46..c232d966e2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts @@ -27,6 +27,7 @@ import { datasourcesHasOnlyComparisonAggregation, DatasourceType, WidgetConfig, + widgetTitleAutocompleteValues, } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -126,6 +127,8 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon datePreviewFn = this._datePreviewFn.bind(this); + predefinedValues = widgetTitleAutocompleteValues; + private keySearchText: string; private latestKeySearchTextResult: Array; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/signal-strength-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/signal-strength-basic-config.component.html index 6298e5902e..ed034aaf89 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/signal-strength-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/signal-strength-basic-config.component.html @@ -47,9 +47,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html index fa4401c8af..3d1f05fb4b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html @@ -16,7 +16,9 @@ --> - +
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, @@ -85,6 +88,8 @@ export class MapBasicConfigComponent extends BasicWidgetConfigComponent { this.mapWidgetConfigForm = this.fb.group({ mapSettings: [settings, []], + timewindowConfig: [getTimewindowConfig(configData.config), []], + showTitle: [configData.config.showTitle, []], title: [configData.config.title, []], titleFont: [configData.config.titleFont, []], @@ -104,15 +109,10 @@ export class MapBasicConfigComponent extends BasicWidgetConfigComponent { actions: [configData.config.actions || {}, []] }); - if (this.trip) { - this.mapWidgetConfigForm.addControl('timewindowConfig', this.fb.control(getTimewindowConfig(configData.config))) - } } protected prepareOutputConfig(config: any): WidgetConfigComponentData { - if (this.trip) { - setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); - } + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); this.widgetConfig.config.settings = config.mapSettings || {}; this.widgetConfig.config.showTitle = config.showTitle; @@ -184,4 +184,20 @@ export class MapBasicConfigComponent extends BasicWidgetConfigComponent { config.enableFullscreen = buttons.includes('fullscreen'); } + public get displayTimewindowConfig(): boolean { + if (this.trip) { + return true; + } else { + return this.widget ? MapModelDefinition.hasTimewindow(this.widget) : false; + } + } + + public get onlyHistoryTimewindow(): boolean { + if (this.trip) { + return false; + } else { + return this.widget ? MapModelDefinition.datasourcesHasOnlyComparisonAggregation(this.widget) : false; + } + } + } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts index 1d8a741c91..f464cdb66c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts @@ -20,7 +20,7 @@ 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 { TargetDevice, WidgetConfig, } from '@shared/models/widget.models'; +import { TargetDevice, WidgetConfig } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { isUndefined } from '@core/utils'; import { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html index 2eaaacc124..b6566c181e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html @@ -76,9 +76,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/scada/scada-symbol-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/scada/scada-symbol-basic-config.component.html index 17c33b9f45..03cdee6b95 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/scada/scada-symbol-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/scada/scada-symbol-basic-config.component.html @@ -35,9 +35,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/weather/wind-speed-direction-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/weather/wind-speed-direction-basic-config.component.html index 0ac16e2e64..3727cd0952 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/weather/wind-speed-direction-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/weather/wind-speed-direction-basic-config.component.html @@ -95,9 +95,16 @@ {{ 'widget-config.title' | translate }}
- - - + , protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index a5ecd10817..1dacf0cd22 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -37,7 +37,6 @@ datasourceFormGroup.get('type').value === datasourceType.entityCount || datasourceFormGroup.get('type').value === datasourceType.alarmCount ? datasourceFormGroup.get('type').value : ''"> 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 124df36eb1..29bdc08692 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 @@ -23,11 +23,19 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { DataKey, DatasourceType, Widget, WidgetConfigMode, widgetType } from '@shared/models/widget.models'; +import { + DataKey, + DatasourceType, + Widget, + widgetTypeCanHaveTimewindow, + WidgetConfigMode, + widgetType +} from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; -import { isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { initModelFromDefaultTimewindow } from '@shared/models/time/time.models'; export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks; @@ -107,6 +115,11 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement if (this.isAdd) { this.setupDefaults(widgetConfig); } + if (widgetTypeCanHaveTimewindow(widgetConfig.widgetType) && isUndefinedOrNull(widgetConfig.config.timewindow)) { + widgetConfig.config.timewindow = initModelFromDefaultTimewindow(null, + widgetConfig.widgetType === widgetType.latest, false, this.widgetConfigComponent.timeService, + widgetConfig.widgetType === widgetType.timeseries); + } this.onConfigSet(widgetConfig); this.updateValidators(false); for (const trigger of this.validatorTriggers()) { 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 index c90cc28a22..932624125d 100644 --- 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 @@ -45,14 +45,14 @@ -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss index b30534d167..de45bcc5c9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss @@ -51,7 +51,7 @@ flex-direction: row-reverse; } .tb-latest-chart-shape { - flex: 1; + flex: 1 1 100%; min-width: 10px; min-height: 10px; display: flex; @@ -95,8 +95,14 @@ &.legend-right, &.legend-left { gap: 24px; .tb-latest-chart-legend { + flex: 0 1 auto; + overflow-y: auto; + max-height: 100%; + flex-wrap: nowrap; + min-width: fit-content; + max-width: 100%; flex-direction: column; - justify-content: center; + justify-content: stretch; align-items: stretch; .tb-latest-chart-legend-item { flex-direction: row; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts index facd9f0cad..634c6f5a1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts @@ -181,17 +181,13 @@ export class LatestChartComponent implements OnInit, OnDestroy, AfterViewInit { private onResize() { if (this.legendHorizontal) { - this.renderer.setStyle(this.chartShape.nativeElement, 'max-width', null); this.renderer.setStyle(this.chartShape.nativeElement, 'min-width', null); - this.renderer.setStyle(this.chartLegend.nativeElement, 'flex', null); } const shapeWidth = this.chartShape.nativeElement.getBoundingClientRect().width; const shapeHeight = this.chartShape.nativeElement.getBoundingClientRect().height; const size = Math.min(shapeWidth, shapeHeight); if (this.legendHorizontal) { - this.renderer.setStyle(this.chartShape.nativeElement, 'max-width', `${size}px`); this.renderer.setStyle(this.chartShape.nativeElement, 'min-width', `${size}px`); - this.renderer.setStyle(this.chartLegend.nativeElement, 'flex', '1'); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index 0574344628..4816852768 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -381,8 +381,8 @@ export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSetting decimals?: number; interval?: number; splitNumber?: number; - min?: number | string; - max?: number | string; + min?: number | string | ValueSourceConfig; + max?: number | string | ValueSourceConfig; ticksGenerator?: TimeSeriesChartTicksGenerator | string; ticksFormatter?: TimeSeriesChartTicksFormatter | string; } @@ -867,6 +867,9 @@ export interface TimeSeriesChartAxis { id: string; settings: TimeSeriesChartAxisSettings; option: CartesianAxisOption; + minLatestDataKey?: DataKey; + maxLatestDataKey?: DataKey; + unitConvertor?: (value: number) => number; } export interface TimeSeriesChartYAxis extends TimeSeriesChartAxis { @@ -926,6 +929,29 @@ export const createTimeSeriesYAxis = (units: string, return ticks?.filter(tick => tick.value >= extent[0] && tick.value <= extent[1]); }; } + + let initialMin: number | string | undefined; + if (isDefinedAndNotNull(settings.min)) { + if (typeof settings.min === 'object' && 'type' in settings.min) { + initialMin = undefined; + } else if (typeof settings.min === 'number') { + initialMin = unitConvertor ? unitConvertor(settings.min) : settings.min; + } else if (typeof settings.min === 'string') { + initialMin = settings.min; + } + } + + let initialMax: number | string | undefined; + if (isDefinedAndNotNull(settings.max)) { + if (typeof settings.max === 'object' && 'type' in settings.max) { + initialMax = undefined; + } else if (typeof settings.max === 'number') { + initialMax = unitConvertor ? unitConvertor(settings.max) : settings.max; + } else if (typeof settings.max === 'string') { + initialMax = settings.max; + } + } + return { id: settings.id, decimals, @@ -939,8 +965,8 @@ export const createTimeSeriesYAxis = (units: string, offset: 0, alignTicks: true, scale: true, - min: isDefinedAndNotNull(settings.min) ? unitConvertor(Number(settings.min)) : settings.min, - max: isDefinedAndNotNull(settings.max) ? unitConvertor(Number(settings.max)) : settings.max, + min: initialMin, + max: initialMax, minInterval, splitNumber, interval, @@ -1411,6 +1437,13 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, } } seriesOption.data = item.data; + if (seriesOption.type === 'line') { + const settings: TimeSeriesChartKeySettings = item.dataKey.settings; + const lineSettings = settings.lineSettings; + if (!lineSettings.showPoints) { + seriesOption.showSymbol = item.data.length === 1; + } + } return seriesOption; }; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index ca2456e109..21f5f2d68c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -54,13 +54,15 @@ import { getFocusedSeriesIndex, measureAxisNameSize } from '@home/components/widget/lib/chart/echarts-widget.models'; -import { DateFormatProcessor, ValueSourceType } from '@shared/models/widget-settings.models'; +import { DateFormatProcessor, ValueSourceConfig, ValueSourceType } from '@shared/models/widget-settings.models'; import { formattedDataFormDatasourceData, formatValue, isDefined, isDefinedAndNotNull, isEqual, + isNumber, + isString, mergeDeep } from '@core/utils'; import { DataKey, Datasource, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; @@ -286,9 +288,36 @@ export class TbTimeSeriesChart { } } } + + for (const yAxis of this.yAxisList) { + const minType = (yAxis.settings.min as ValueSourceConfig).type; + const maxType = (yAxis.settings.max as ValueSourceConfig).type; + if (minType === ValueSourceType.latestKey && yAxis.minLatestDataKey) { + const data = this.ctx.latestData.find(d => d.dataKey === yAxis.minLatestDataKey); + if (data?.data[0]) { + const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor); + if (yAxis.option.min !== value) { + yAxis.option.min = value; + update = true; + } + } + } + + if (maxType === ValueSourceType.latestKey && yAxis.maxLatestDataKey) { + const data = this.ctx.latestData.find(d => d.dataKey === yAxis.maxLatestDataKey); + if (data?.data[0]) { + const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor); + if (yAxis.option.max !== value) { + yAxis.option.max = value; + update = true; + } + } + } + } } if (this.timeSeriesChart && update) { this.updateSeriesData(); + this.updateAxisLimits(); } } @@ -550,7 +579,10 @@ export class TbTimeSeriesChart { private setupYAxes(): void { const yAxisSettingsList = Object.values(this.settings.yAxes); yAxisSettingsList.sort((a1, a2) => a1.order - a2.order); + const axisLimitDatasources: Datasource[] = []; for (const yAxisSettings of yAxisSettingsList) { + yAxisSettings.min = this.normalizeAxisLimit(yAxisSettings.min); + yAxisSettings.max = this.normalizeAxisLimit(yAxisSettings.max); const axisSettings = mergeDeep({} as TimeSeriesChartYAxisSettings, defaultTimeSeriesChartYAxisSettings, yAxisSettings); const units = isNotEmptyTbUnits(axisSettings.units) ? axisSettings.units : this.ctx.units; @@ -563,8 +595,81 @@ export class TbTimeSeriesChart { axisSettings.ticksFormatter = this.stateValueConverter.ticksFormatter; } const yAxis = createTimeSeriesYAxis(unitSymbol, decimals, axisSettings, this.ctx.utilsService, this.darkMode, unitConvertor); + if (isDefinedAndNotNull(axisSettings.min)) { + this.processYAxisLimit(axisSettings.min as ValueSourceConfig, 'min', yAxis, axisLimitDatasources, unitConvertor); + } + if (isDefinedAndNotNull(axisSettings.max)) { + this.processYAxisLimit(axisSettings.max as ValueSourceConfig, 'max', yAxis, axisLimitDatasources, unitConvertor); + } this.yAxisList.push(yAxis); } + this.subscribeForAxisLimits(axisLimitDatasources); + } + + private processYAxisLimit( + limit: ValueSourceConfig, + limitType: 'min' | 'max', + yAxis: TimeSeriesChartYAxis, + axisLimitDatasources: Datasource[], + unitConvertor?: (value: number) => number + ): void { + if (limit && typeof limit === 'object' && 'type' in limit) { + if (limit.type === ValueSourceType.latestKey) { + let latestDataKey: DataKey = null; + if (this.ctx.datasources.length) { + for (const datasource of this.ctx.datasources) { + latestDataKey = datasource.latestDataKeys?.find(d => + (d.type === DataKeyType.function && d.label === limit.latestKey) || + (d.type !== DataKeyType.function && d.name === limit.latestKey && + d.type === limit.latestKeyType)); + if (latestDataKey) { + break; + } + } + } + if (latestDataKey) { + if (limitType === 'min') { + yAxis.minLatestDataKey = latestDataKey; + } else { + yAxis.maxLatestDataKey = latestDataKey; + } + } + } else if (limit.type === ValueSourceType.entity) { + const entityAliasId = this.ctx.aliasController.getEntityAliasId(limit.entityAlias); + if (entityAliasId) { + let datasource = axisLimitDatasources.find(d => d.entityAliasId === entityAliasId); + const entityDataKey: DataKey = { + type: limit.entityKeyType, + name: limit.entityKey, + label: limit.entityKey, + settings: { + yAxisId: yAxis.id, + axisLimit: limitType + } + }; + if (datasource) { + datasource.dataKeys.push(entityDataKey); + } else { + datasource = { + type: DatasourceType.entity, + name: limit.entityAlias, + aliasName: limit.entityAlias, + entityAliasId, + dataKeys: [entityDataKey] + }; + axisLimitDatasources.push(datasource); + } + } + } else if (limit.type === ValueSourceType.constant) { + const value = unitConvertor && isDefinedAndNotNull(limit.value) ? unitConvertor(limit.value) : limit.value; + if (limitType === 'min') { + yAxis.option.min = value; + } else { + yAxis.option.max = value; + } + } + return; + } } private setupVisualMap(): void { @@ -622,6 +727,50 @@ export class TbTimeSeriesChart { } } + private subscribeForAxisLimits(datasources: Datasource[]) { + if (datasources.length) { + const axisLimitsSubscriptionOptions: WidgetSubscriptionOptions = { + datasources, + useDashboardTimewindow: false, + type: widgetType.latest, + callbacks: { + onDataUpdated: (subscription) => { + let update = false; + if (subscription.data) { + for (const yAxis of this.yAxisList) { + for (const data of subscription.data) { + if (data.dataKey.settings?.yAxisId === yAxis.id) { + const limitType = data.dataKey.settings.axisLimit as ('min' | 'max'); + if (data.data[0]) { + const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor); + if (isDefinedAndNotNull(value)) { + if (limitType === 'min') { + if (yAxis.option.min !== value) { + yAxis.option.min = value; + update = true; + } + } else { + if (yAxis.option.max !== value) { + yAxis.option.max = value; + update = true; + } + } + } + } + } + } + } + } + if (this.timeSeriesChart && update) { + this.updateAxisLimits(); + } + } + } + }; + this.ctx.subscriptionApi.createSubscription(axisLimitsSubscriptionOptions, true).subscribe(); + } + } + private drawChart() { echartsModule.init(); this.renderer.setStyle(this.chartElement, 'letterSpacing', 'normal'); @@ -714,6 +863,24 @@ export class TbTimeSeriesChart { } } + private parseAxisLimitData = (data: any, unitConvertor?: (value: number) => number): number => { + let value: number; + if (isDefinedAndNotNull(data)) { + if (isNumber(data)) { + value = data; + } else if (isString(data)) { + value = Number(data); + } + } + if (isDefinedAndNotNull(value) && !isNaN(value)) { + if (unitConvertor) { + return unitConvertor(value); + } + return value; + } + return null; + }; + private updateSeries(): void { this.timeSeriesChartOptions.series = generateChartData(this.dataItems, this.thresholdItems, this.stackMode, @@ -836,6 +1003,16 @@ export class TbTimeSeriesChart { return changed; } + private updateAxisLimits(): void { + if (this.timeSeriesChart && !this.timeSeriesChart.isDisposed()) { + this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions, { + replaceMerge: ['yAxis'] + }); + this.updateAxes(); + } + } + private scaleYAxis(yAxis: TimeSeriesChartYAxis): boolean { if (!this.stateData) { const axisBarDataItems = this.dataItems.filter(d => d.yAxisId === yAxis.id && d.enabled && @@ -903,4 +1080,21 @@ export class TbTimeSeriesChart { this.timeSeriesChart.setOption(this.timeSeriesChartOptions); } } + + private normalizeAxisLimit(limit: string | number | ValueSourceConfig): string | number | ValueSourceConfig { + if (!limit) { + return { + type: ValueSourceType.constant, + value: null, + entityAlias: null + }; + } else if (typeof limit === 'number' || typeof limit === 'string') { + return { + type: ValueSourceType.constant, + value: Number(limit), + entityAlias: null + }; + } + return limit; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index a31139de20..75eb1e403d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -143,7 +143,6 @@ export class TbCanvasDigitalGauge { ticks: this.localSettings.ticks, title: this.localSettings.title, - fontTitleSize: this.localSettings.titleFont.size, fontTitleStyle: this.localSettings.titleFont.style, fontTitleWeight: this.localSettings.titleFont.weight, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.html index c99af1490b..39ebbaf249 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.html @@ -17,8 +17,16 @@ -->
- - {{ column.title }} + + {{'entity.show-all-columns' | translate}} + + @for (column of columns; track column.def) { + + {{ column.title }} + + }
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.ts index e868919b92..d9e94ee7bd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.ts @@ -15,6 +15,8 @@ /// import { Component, Inject, InjectionToken } from '@angular/core'; +import { isDefinedAndNotNull } from '@core/utils'; +import { SelectableColumnsPipe } from '@shared/public-api'; import { DisplayColumn } from '@home/components/widget/lib/table-widget.models'; export const DISPLAY_COLUMNS_PANEL_DATA = new InjectionToken('DisplayColumnsPanelData'); @@ -33,8 +35,28 @@ export class DisplayColumnsPanelComponent { columns: DisplayColumn[]; - constructor(@Inject(DISPLAY_COLUMNS_PANEL_DATA) public data: DisplayColumnsPanelData) { - this.columns = this.data.columns; + constructor(@Inject(DISPLAY_COLUMNS_PANEL_DATA) public data: DisplayColumnsPanelData, + private selectableColumnsPipe: SelectableColumnsPipe ) { + this.columns = this.selectableColumnsPipe.transform(this.data.columns); + } + + get allColumnsVisible(): boolean { + return isDefinedAndNotNull(this.columns) && this.columns.every(column => column.display); + } + + get someColumnsVisible(): boolean { + const filtredColumns = this.columns.filter(item => item.display); + return filtredColumns.length !== 0 && this.columns.length !== filtredColumns.length; + } + + public toggleAllColumns(event: any): void { + const isChecked = event.checked; + + this.columns.forEach(column => { + column.display = isChecked; + }); + + this.update(); } public update() { 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 index bf5b76fce6..5a99f2d733 100644 --- 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 @@ -65,7 +65,7 @@ [style.min-width]="(entityDatasource.countCellButtonAction * 48) + 'px'"> - +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-data-key-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-data-key-row.component.scss new file mode 100644 index 0000000000..8d295765f8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-data-key-row.component.scss @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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-form-table-row.tb-api-usage-data-key-row { + + .tb-source-field { + flex: 1 1 25%; + display: flex; + gap: 12px; + .tb-label-field { + flex: 1; + } + } + + .tb-data-key-fields { + display: flex; + gap: 12px; + min-width: 0; + flex: 1 1 50%; + } + + .tb-data-key-field { + flex: 1; + min-width: 0; + } + + .tb-remove-button { + width: 40px; + min-width: 40px; + } + + @media #{$mat-lt-lg} { + .tb-source-field { + flex-direction: column; + flex: 1 1 50%; + } + .tb-data-key-fields{ + flex-direction: column; + flex: 1 1 50%; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-data-key-row.component.ts new file mode 100644 index 0000000000..aed354bb69 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-data-key-row.component.ts @@ -0,0 +1,156 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + ApiUsageDataKeysSettings, + ApiUsageSettingsContext +} from "@home/components/widget/lib/settings/cards/api-usage-settings.component.models"; +import { Observable, of } from "rxjs"; + +@Component({ + selector: 'tb-api-usage-data-key-row', + templateUrl: './api-usage-data-key-row.component.html', + styleUrls: ['./api-usage-data-key-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ApiUsageDataKeyRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class ApiUsageDataKeyRowComponent implements ControlValueAccessor, OnInit { + + DatasourceType = DatasourceType; + DataKeyType = DataKeyType; + + widgetType = widgetType; + + @Input() + disabled: boolean; + + @Input() + dsEntityAliasId: string; + + @Input() + context: ApiUsageSettingsContext; + + @Output() + dataKeyRemoved = new EventEmitter(); + + dataKeyFormGroup: UntypedFormGroup; + + modelValue: ApiUsageDataKeysSettings; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.dataKeyFormGroup = this.fb.group({ + label: [null, [Validators.required]], + state: [null, []], + status: [null, [Validators.required]], + maxLimit: [null, [Validators.required]], + current: [null, [Validators.required]] + }); + this.dataKeyFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => this.updateModel() + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.dataKeyFormGroup.disable({emitEvent: false}); + } else { + this.dataKeyFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: ApiUsageDataKeysSettings): void { + this.modelValue = value; + this.dataKeyFormGroup.patchValue( + { + label: value?.label, + state: value?.state, + status: value?.status, + maxLimit: value?.maxLimit, + current: value?.current + }, {emitEvent: false} + ); + this.updateValidators(); + this.cd.markForCheck(); + } + + editKey(keyType: 'status' | 'maxLimit' | 'current') { + const targetDataKey: DataKey = this.dataKeyFormGroup.get(keyType).value; + this.context.editKey(targetDataKey, this.dsEntityAliasId).subscribe( + (updatedDataKey) => { + if (updatedDataKey) { + this.dataKeyFormGroup.get(keyType).patchValue(updatedDataKey); + } + } + ); + } + + private updateValidators() { + } + + private updateModel() { + this.modelValue = {...this.modelValue, ...this.dataKeyFormGroup.value}; + this.propagateChange(this.modelValue); + } + + fetchDashboardStates(searchText?: string): Observable> { + return of(this.context.callbacks.fetchDashboardStates(searchText)); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-settings.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-settings.component.models.ts new file mode 100644 index 0000000000..09d1d73743 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-settings.component.models.ts @@ -0,0 +1,118 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { IAliasController } from '@core/api/widget-api.models'; +import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; +import { DataKey, Widget, widgetType } from '@shared/models/widget.models'; +import { Observable } from 'rxjs'; +import { BackgroundSettings, BackgroundType } from '@shared/models/widget-settings.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { materialColors } from '@shared/models/material.models'; + +export interface ApiUsageSettingsContext { + aliasController: IAliasController; + callbacks: WidgetConfigCallbacks; + widget: Widget; + editKey: (key: DataKey, entityAliasId: string, WidgetType?: widgetType) => Observable; + generateDataKey: (key: DataKey) => DataKey; +} + + +export interface ApiUsageWidgetSettings { + dsEntityAliasId: string; + apiUsageDataKeys: ApiUsageDataKeysSettings[]; + targetDashboardState: string; + background: BackgroundSettings; + padding: string; +} + +export interface ApiUsageDataKeysSettings { + label: string; + state: string; + status: DataKey; + maxLimit: DataKey; + current: DataKey; +} + +const generateDataKey = (label: string, status: string, maxLimit: string, current: string) => { + return { + label, + state: '', + status: { + name: status, + label: status, + type: DataKeyType.timeseries, + funcBody: undefined, + settings: {}, + color: materialColors[0].value + }, + maxLimit: { + name: maxLimit, + label: maxLimit, + type: DataKeyType.timeseries, + funcBody: undefined, + settings: {}, + color: materialColors[0].value + }, + current: { + name: current, + label: current, + type: DataKeyType.timeseries, + funcBody: undefined, + settings: {}, + color: materialColors[0].value + } + } +} + +export const apiUsageDefaultSettings: ApiUsageWidgetSettings = { + dsEntityAliasId: '', + apiUsageDataKeys: [ + generateDataKey('{i18n:api-usage.transport-messages}', 'transportApiState', 'transportMsgLimit', 'transportMsgCount'), + generateDataKey('{i18n:api-usage.transport-data-points}', 'transportApiState', 'transportDataPointsLimit', 'transportDataPointsCount'), + generateDataKey('{i18n:api-usage.rule-engine-executions}', 'ruleEngineApiState', 'ruleEngineExecutionLimit', 'ruleEngineExecutionCount'), + generateDataKey('{i18n:api-usage.javascript-function-executions}', 'jsExecutionApiState', 'jsExecutionLimit', 'jsExecutionCount'), + generateDataKey('{i18n:api-usage.tbel-function-executions}', 'tbelExecutionApiState', 'tbelExecutionLimit', 'tbelExecutionCount'), + generateDataKey('{i18n:api-usage.data-points-storage-days}', 'dbApiState', 'storageDataPointsLimit', 'storageDataPointsCount'), + generateDataKey('{i18n:api-usage.alarms-created}', 'alarmApiState', 'createdAlarmsLimit', 'createdAlarmsCount'), + generateDataKey('{i18n:api-usage.emails}', 'emailApiState', 'emailLimit', 'emailCount'), + generateDataKey('{i18n:api-usage.sms}', 'smsApiState', 'smsLimit', 'smsCount'), + ], + targetDashboardState: 'default', + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + }, + padding: '0' +}; + +export const getUniqueDataKeys = (data: ApiUsageDataKeysSettings[]): DataKey[] => { + const seenNames = new Set(); + return data + .flatMap(item => [item.status, item.maxLimit, item.current]) + .filter(key => { + if (seenNames.has(key.name)) { + return false; + } + seenNames.add(key.name); + return true; + }); +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.html new file mode 100644 index 0000000000..046d76d351 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.html @@ -0,0 +1,100 @@ + + +
+
widget-config.datasource
+ + + +
+
+
+
widgets.api-usage.label
+
widgets.api-usage.state-name
+
widgets.api-usage.status
+
widgets.api-usage.limit
+
widgets.api-usage.current-number
+
+
+
+
+
+ + +
+ +
+
+
+
+
+
+ +
+
+ + {{ 'widgets.api-usage.no-key' | translate }} + + + +
+ +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.scss new file mode 100644 index 0000000000..6767ec1e7b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.scss @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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-map-data-layers { + .tb-form-table-header-cell { + &.tb-source-header { + flex: 1 1 50%; + } + &.tb-x-pos-header { + flex: 1 1 25%; + } + &.tb-y-pos-header { + flex: 1 1 25%; + } + &.tb-key-header { + flex: 1 1 50%; + } + &.tb-actions-header { + width: 80px; + min-width: 80px; + } + @media #{$mat-lt-lg} { + &.tb-source-header { + flex: 1 1 30%; + } + &.tb-x-pos-header, &.tb-y-pos-header { + flex: 1 1 35%; + } + &.tb-key-header { + flex: 1 1 70%; + } + } + @media #{$mat-xs} { + &.tb-x-pos-header, &.tb-y-pos-header { + display: none; + } + &.tb-key-header { + display: none; + } + } + } + + .tb-form-table-body { + tb-api-usage-data-key-row { + overflow: hidden; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.ts new file mode 100644 index 0000000000..b89679de5b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/api-usage-widget-settings.component.ts @@ -0,0 +1,212 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 } from '@angular/core'; +import { + DataKey, + DataKeyConfigMode, + WidgetSettings, + WidgetSettingsComponent, + widgetType +} from '@shared/models/widget.models'; +import { + AbstractControl, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup, + ValidationErrors, + ValidatorFn, Validators +} from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + ApiUsageDataKeysSettings, + apiUsageDefaultSettings, + ApiUsageSettingsContext +} from '@home/components/widget/lib/settings/cards/api-usage-settings.component.models'; +import { deepClone } from '@core/utils'; +import { Observable, of } from 'rxjs'; +import { + DataKeyConfigDialogComponent, + DataKeyConfigDialogData +} from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; + +@Component({ + selector: 'tb-api-usage-widget-settings', + templateUrl: './api-usage-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss', 'api-usage-widget-settings.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ApiUsageWidgetSettingsComponent), + multi: true + } + ], +}) +export class ApiUsageWidgetSettingsComponent extends WidgetSettingsComponent { + + apiUsageWidgetSettingsForm: UntypedFormGroup; + + context: ApiUsageSettingsContext; + + constructor(protected store: Store, + private dialog: MatDialog, + private fb: UntypedFormBuilder) { + super(store); + } + + ngOnInit() { + this.context = { + aliasController: this.aliasController, + callbacks: this.callbacks, + widget: this.widget, + editKey: this.editKey.bind(this), + generateDataKey: this.generateDataKey.bind(this) + }; + } + + protected doUpdateSettings(settingsForm: UntypedFormGroup, settings: WidgetSettings) { + settingsForm.setControl('apiUsageDataKeys', this.prepareDataKeysFormArray(settings?.apiUsageDataKeys), {emitEvent: false}); + } + + dataKeysFormArray(): UntypedFormArray { + return this.apiUsageWidgetSettingsForm.get('apiUsageDataKeys') as UntypedFormArray; + } + + trackByDataKey(index: number): any { + return index; + } + + get dragEnabled(): boolean { + return this.dataKeysFormArray().controls.length > 1; + } + + layerDrop(event: CdkDragDrop) { + const layer = this.dataKeysFormArray().at(event.previousIndex); + this.dataKeysFormArray().removeAt(event.previousIndex); + this.dataKeysFormArray().insert(event.currentIndex, layer); + } + + removeDataKey(index: number) { + (this.apiUsageWidgetSettingsForm.get('apiUsageDataKeys') as UntypedFormArray).removeAt(index); + } + + addDataKey() { + const dataKey = { + label: '', + state: '', + status: null, + maxLimit: null, + current: null + }; + const dataKeysArray = this.apiUsageWidgetSettingsForm.get('apiUsageDataKeys') as UntypedFormArray; + const dataKeyControl = this.fb.control(dataKey, [this.apiUsageDataKeyValidator()]); + dataKeysArray.push(dataKeyControl); + } + + protected settingsForm(): UntypedFormGroup { + return this.apiUsageWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return apiUsageDefaultSettings; + } + + protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { + return { + dsEntityAliasId: settings?.dsEntityAliasId, + apiUsageDataKeys: settings?.apiUsageDataKeys, + targetDashboardState: settings?.targetDashboardState, + background: settings?.background, + padding: settings.padding + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.apiUsageWidgetSettingsForm = this.fb.group({ + dsEntityAliasId: [settings?.dsEntityAliasId, Validators.required], + apiUsageDataKeys: this.prepareDataKeysFormArray(settings?.apiUsageDataKeys), + targetDashboardState: [settings?.targetDashboardState], + background: [settings?.background, []], + padding: [settings.padding, []] + }); + } + + private prepareDataKeysFormArray(dataKeys: ApiUsageDataKeysSettings[]): UntypedFormArray { + const dataKeysControls: Array = []; + if (dataKeys) { + dataKeys.forEach((dataLayer) => { + dataKeysControls.push(this.fb.control(dataLayer, [this.apiUsageDataKeyValidator()])); + }); + } + return this.fb.array(dataKeysControls); + } + + protected validatorTriggers(): string[] { + return []; + } + + protected updateValidators() { + } + + apiUsageDataKeyValidator = (): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const value: ApiUsageDataKeysSettings = control.value; + if (!value?.label || !value?.current || !value?.maxLimit || !value?.status) { + return { + dataKey: true + } + } + return null; + }; + }; + + private editKey(key: DataKey, entityAliasId: string, _widgetType = widgetType.latest): Observable { + return this.dialog.open(DataKeyConfigDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + dataKey: deepClone(key), + dataKeyConfigMode: DataKeyConfigMode.general, + aliasController: this.aliasController, + widgetType: _widgetType, + entityAliasId, + showPostProcessing: true, + callbacks: this.callbacks, + hideDataKeyColor: true, + hideDataKeyDecimals: true, + hideDataKeyUnits: true, + hideDataKeyAggregation: true, + widget: this.widget, + dashboard: null, + dataKeySettingsForm: null, + dataKeySettingsDirective: null + } + }).afterClosed(); + } + + private generateDataKey(key: DataKey): DataKey { + return this.callbacks.generateDataKey(key.name, key.type, null, false, null); + } + + fetchDashboardStates(searchText?: string): Observable> { + return of(this.callbacks.fetchDashboardStates(searchText)); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html index 97437ba48a..b4520590b6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.html @@ -28,9 +28,16 @@ {{ 'widgets.label-card.label' | translate }}
- - - + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts index b0aa8139dc..d90bf9571b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-value-card-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import {WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues} from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -34,6 +34,8 @@ export class LabelValueCardWidgetSettingsComponent extends WidgetSettingsCompone valuePreviewFn = this._valuePreviewFn.bind(this); + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); 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 5339c501e1..375c50dbc3 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 @@ -115,6 +115,23 @@ {{ 'widgets.table.use-entity-label-tab-name' | translate }} +
+
widgets.table.sort-by
+
+ + + {{ entityFields.createdTime.name | translate }} + {{ entityFields.name.name | translate }} + + + + + {{ 'common.sort-asc' | translate }} + {{ 'common.sort-desc' | translate }} + + +
+
widgets.table.rows
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 0ed98e1758..a2b86cab55 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 @@ -20,6 +20,8 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models'; +import { Direction } from '@shared/models/page/sort-order'; +import { entityFields } from '@shared/models/entity.models'; @Component({ selector: 'tb-timeseries-table-widget-settings', @@ -28,6 +30,9 @@ import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widge }) export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsComponent { + entityFields = entityFields; + Direction = Direction; + timeseriesTableWidgetSettingsForm: UntypedFormGroup; pageStepSizeValues = []; @@ -58,12 +63,20 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon hideEmptyLines: false, disableStickyHeader: false, useRowStyleFunction: false, - rowStyleFunction: '' + rowStyleFunction: '', + sortOrder: { + property: this.entityFields.createdTime.keyName, + direction: Direction.DESC + } }; } protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { settings.pageStepIncrement = settings.pageStepIncrement ?? settings.defaultPageSize; + settings.sortOrder = { + property: settings.sortOrder?.property || this.entityFields.createdTime.keyName, + direction: settings.sortOrder?.direction || Direction.DESC + }; this.pageStepSizeValues = buildPageStepSizeValues(settings.pageStepCount, settings.pageStepIncrement); return settings; } @@ -93,7 +106,17 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon hideEmptyLines: [settings.hideEmptyLines, []], disableStickyHeader: [settings.disableStickyHeader, []], useRowStyleFunction: [settings.useRowStyleFunction, []], - rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] + rowStyleFunction: [settings.rowStyleFunction, [Validators.required]], + sortOrder: this.fb.group({ + property: [ + settings.sortOrder.property, + Validators.required + ], + direction: [ + settings.sortOrder.direction, + Validators.required + ] + }) }); } @@ -101,6 +124,14 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon return ['useRowStyleFunction', 'displayPagination', 'pageStepCount', 'pageStepIncrement']; } + protected prepareOutputSettings(settings: WidgetSettings): WidgetSettings { + settings.sortOrder = { + property: settings.sortOrder?.property || this.entityFields.createdTime.keyName, + direction: settings.sortOrder?.direction || Direction.DESC + }; + return settings; + } + protected updateValidators(emitEvent: boolean, trigger: string) { if (trigger === 'pageStepCount' || trigger === 'pageStepIncrement') { this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').reset(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index 07ca827630..8ab61ea509 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -91,6 +91,9 @@
widgets.time-series-chart.axis.y-axis
widgets.time-series-chart.axis.y-axis
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html index f9123bb723..36bf2aea89 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html @@ -62,6 +62,9 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.html index 1777702254..3d9b4434a1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.html @@ -76,6 +76,20 @@
} + @case (MapItemType.polyline) { +
+
widget-action.map-item-tooltip.start-draw-polyline
+ + + +
+
+
widget-action.map-item-tooltip.finish-draw-polyline
+ + + +
+ } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html index 1110455dc9..cca04a69b1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html @@ -35,117 +35,73 @@
- - - - - - - - - - - - - - - - - - - - - - + + @if (mobileActionFormGroup.get('type').value === mobileActionType.takePhoto || + mobileActionFormGroup.get('type').value === mobileActionType.takePictureFromGallery || + mobileActionFormGroup.get('type').value === mobileActionType.takeScreenshot) { +
+ + {{ 'widget-action.mobile.save-to-gallery' | translate }} + +
+ } + @if (mobileActionFormGroup.get('type').value === mobileActionType.deviceProvision) { +
+
{{ 'widget-action.mobile.provision-type' | translate }}*
+ + + + {{ provisionTypeTranslationMap.get(type) | translate }} + + + +
+ } + + @for (config of actionConfig; track config.formControlName) { +
+ + + + {{ config.title | translate }} + + + + +
+ }
- - + + @if(mobileActionFormGroup.get('type').value) { + @for (config of commonActionConfig; track config.formControlName) { +
+ + + + {{ config.title | translate }} + + + + +
+ } + }
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts index a30e7a8adc..553e3c38a1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts @@ -24,6 +24,9 @@ import { } from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { + ActionConfig, + ProvisionType, + provisionTypeTranslationMap, WidgetActionType, WidgetMobileActionDescriptor, WidgetMobileActionType, @@ -35,6 +38,7 @@ import { getDefaultGetPhoneNumberFunction, getDefaultHandleEmptyResultFunction, getDefaultHandleErrorFunction, + getDefaultHandleNonMobileFallBackFunction, getDefaultProcessImageFunction, getDefaultProcessLaunchResultFunction, getDefaultProcessLocationFunction, @@ -68,6 +72,12 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit functionScopeVariables: string[]; + actionConfig: ActionConfig[]; + commonActionConfig: ActionConfig[]; + + provisionTypes: string[] = Object.keys(ProvisionType); + provisionTypeTranslationMap = provisionTypeTranslationMap; + private requiredValue: boolean; get required(): boolean { return this.requiredValue; @@ -99,8 +109,10 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit this.mobileActionFormGroup = this.fb.group({ type: [null, Validators.required], handleEmptyResultFunction: [null], - handleErrorFunction: [null] + handleErrorFunction: [null], + handleNonMobileFallbackFunction: [null] }); + this.getCommonActionConfigs(); this.mobileActionFormGroup.get('type').valueChanges.pipe( takeUntilDestroyed(this.destroyRef) ).subscribe((type: WidgetMobileActionType) => { @@ -109,6 +121,7 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit action = {...action, ...this.mobileActionTypeFormGroup.value}; } this.updateMobileActionType(type, action); + this.getActionConfigs(); }); this.mobileActionFormGroup.valueChanges.pipe( takeUntilDestroyed(this.destroyRef) @@ -133,10 +146,14 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit } writeValue(value: WidgetMobileActionDescriptor | null): void { - this.mobileActionFormGroup.patchValue({type: value?.type, - handleEmptyResultFunction: value?.handleEmptyResultFunction, - handleErrorFunction: value?.handleErrorFunction}, {emitEvent: false}); + this.mobileActionFormGroup.patchValue({ + type: value?.type, + handleEmptyResultFunction: value?.handleEmptyResultFunction, + handleErrorFunction: value?.handleErrorFunction, + handleNonMobileFallbackFunction: value?.handleNonMobileFallbackFunction + }, {emitEvent: false}); this.updateMobileActionType(value?.type, value); + this.getActionConfigs(); } private updateModel() { @@ -164,6 +181,12 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit handleErrorFunction = getDefaultHandleErrorFunction(type); this.mobileActionFormGroup.patchValue({handleErrorFunction}, {emitEvent: false}); } + let handleNonMobileFallbackFunction = action?.handleNonMobileFallbackFunction; + const defaultHandleNonMobileFallbackFunction = getDefaultHandleNonMobileFallBackFunction(); + if (defaultHandleNonMobileFallbackFunction !== handleNonMobileFallbackFunction) { + handleNonMobileFallbackFunction = getDefaultHandleNonMobileFallBackFunction(); + this.mobileActionFormGroup.patchValue({handleNonMobileFallbackFunction}, {emitEvent: false}); + } } this.mobileActionTypeFormGroup = this.fb.group({}); if (type) { @@ -183,6 +206,10 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit 'processImageFunction', this.fb.control(processImageFunction, []) ); + this.mobileActionTypeFormGroup.addControl( + 'saveToGallery', + this.fb.control(action?.saveToGallery || false, []) + ); break; case WidgetMobileActionType.mapDirection: case WidgetMobileActionType.mapLocation: @@ -267,6 +294,10 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit 'handleProvisionSuccessFunction', this.fb.control(handleProvisionSuccessFunction, [Validators.required]) ); + this.mobileActionTypeFormGroup.addControl( + 'provisionType', + this.fb.control(action?.provisionType || ProvisionType.auto, []) + ); } } this.mobileActionTypeFormGroup.valueChanges.pipe( @@ -276,5 +307,108 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit }); } + getActionConfigs() { + const type = this.mobileActionFormGroup.get('type').value; + this.actionConfig = []; + switch (type) { + case this.mobileActionType.deviceProvision: + this.actionConfig.push({ + title: 'widget-action.mobile.handle-provision-success-function', + formControlName: 'handleProvisionSuccessFunction', + functionName: 'handleProvisionSuccess', + functionArgs: ['deviceName', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'] + }); + break; + case this.mobileActionType.mapDirection: + case this.mobileActionType.mapLocation: + this.actionConfig.push({ + title: 'widget-action.mobile.get-location-function', + formControlName: 'getLocationFunction', + functionName: 'getLocation', + functionArgs: ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_get_location_fn' + }); + this.actionConfig.push({ + title: 'widget-action.mobile.process-launch-result-function', + formControlName: 'processLaunchResultFunction', + functionName: 'processLaunchResult', + functionArgs: ['launched', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_process_launch_result_fn' + }); + break; + case this.mobileActionType.makePhoneCall: + this.actionConfig.push({ + title: 'widget-action.mobile.get-phone-number-function', + formControlName: 'getPhoneNumberFunction', + functionName: 'getPhoneNumber', + functionArgs: ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_get_phone_number_fn' + }); + this.actionConfig.push({ + title: 'widget-action.mobile.process-launch-result-function', + formControlName: 'processLaunchResultFunction', + functionName: 'processLaunchResult', + functionArgs: ['launched', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_process_launch_result_fn' + }); + break; + case this.mobileActionType.takePhoto: + case this.mobileActionType.takePictureFromGallery: + case this.mobileActionType.takeScreenshot: + this.actionConfig.push({ + title: 'widget-action.mobile.process-image-function', + formControlName: 'processImageFunction', + functionName: 'processImage', + functionArgs: ['imageUrl', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_process_image_fn' + }); + break; + case this.mobileActionType.scanQrCode: + this.actionConfig.push({ + title: 'widget-action.mobile.process-qr-code-function', + formControlName: 'processQrCodeFunction', + functionName: 'processQrCode', + functionArgs: ['code', 'format', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_process_qr_code_fn' + }); + break; + case this.mobileActionType.getLocation: + this.actionConfig.push({ + title: 'widget-action.mobile.process-location-function', + formControlName: 'processLocationFunction', + functionName: 'processLocation', + functionArgs: ['latitude', 'longitude', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_process_location_fn' + }); + break; + } + } + + getCommonActionConfigs() { + this.commonActionConfig = [ + { + title: 'widget-action.mobile.handle-empty-result-function', + formControlName: 'handleEmptyResultFunction', + functionName: 'handleEmptyResult', + functionArgs: ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_handle_empty_result_fn' + }, + { + title: 'widget-action.mobile.handle-error-function', + formControlName: 'handleErrorFunction', + functionName: 'handleError', + functionArgs: ['error', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'], + helpId: 'widget/action/mobile_handle_error_fn' + }, + { + title: 'widget-action.mobile.handle-non-mobile-fallback-function', + formControlName: 'handleNonMobileFallbackFunction', + functionName: 'handleNonMobileFallback', + functionArgs: ['$event', 'widgetContext'], + helpId: 'widget/action/mobile_handle_non_mobile_fallback_fn' + } + ]; + } + protected readonly WidgetActionType = WidgetActionType; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts index b991a83576..c48407cd1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts @@ -172,6 +172,14 @@ const handleErrorFunctionTemplate: TbFunction = ' }, 100);\n' + '}\n'; +const handleNonMobileFallbackFunctionTemplate: TbFunction = + '// Optional function body to handle non-mobile fallback \n' + + 'showFallbackToast();\n' + + '\n' + + 'function showFallbackToast(title, error) {\n' + + ' widgetContext.showWarnToast(\'This action is only available in the mobile application.\');\n' + + '}\n'; + const getLocationFunctionTemplate: TbFunction = '// Function body that should return location as array of two numbers (latitude, longitude) for further processing by mobile action.\n' + '// Usually location can be obtained from entity attributes/telemetry. \n\n' + @@ -326,3 +334,5 @@ export const getDefaultHandleErrorFunction = (type: WidgetMobileActionType): TbF } return handleErrorFunctionTemplate.replace('--TITLE--', title); }; + +export const getDefaultHandleNonMobileFallBackFunction = () => handleNonMobileFallbackFunctionTemplate; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/place-map-item-sample-js.raw b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/place-map-item-sample-js.raw index cb8c23faee..05e06542a5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/place-map-item-sample-js.raw +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/place-map-item-sample-js.raw @@ -79,7 +79,7 @@ function AddEntityDialogController(instance) { const mapType = widgetContext.mapInstance.type(); attributes.push({key: mapType === 'image' ? 'xPos' : 'latitude', value: additionalParams.coordinates.x}); attributes.push({key: mapType === 'image' ? 'yPos' : 'longitude', value: additionalParams.coordinates.y}); - } else if (mapItemType === 'Rectangle' || mapItemType === 'Polygon') { + } else if (mapItemType === 'Rectangle' || mapItemType === 'Polygon' || mapItemType === 'Line' ) { attributes.push({key: 'perimeter', value: additionalParams.coordinates}); } else if (mapItemType === 'Circle') { attributes.push({key: 'circle', value: additionalParams.coordinates}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts index 7c374e9654..0836ad8e15 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts @@ -64,6 +64,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, callbacks: EntityAliasSelectCallbacks; @Input() + @coerceBoolean() showLabel: boolean; @ViewChild('entityAliasAutocomplete') entityAliasAutocomplete: MatAutocomplete; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.html new file mode 100644 index 0000000000..986975b874 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.html @@ -0,0 +1,71 @@ + +
+
+ {{ labelKey | translate}} +
+
+ + + + {{ ValueSourceTypeTranslation.get(type) | translate }} + + + + + +
+
+ + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts new file mode 100644 index 0000000000..88b01c19c5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts @@ -0,0 +1,209 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, UntypedFormControl, + UntypedFormGroup, ValidationErrors, Validator, + Validators, +} from '@angular/forms'; +import { + ValueSourceConfig, + ValueSourceType, + ValueSourceTypes, + ValueSourceTypeTranslation +} from '@shared/models/widget-settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { DataKey, Datasource, DatasourceType, } from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; +import { merge } from 'rxjs'; + +@Component({ + selector: 'tb-axis-scale-row', + templateUrl: './axis-scale-row.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AxisScaleRowComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AxisScaleRowComponent), + multi: true + }, + ] +}) +export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + isPanelView = false; + + @Input() + aliasController: IAliasController; + + @Input() + callbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Input() + labelKey: string; + + ValueSourceType = ValueSourceType; + + DataKeyType = DataKeyType; + + DatasourceType = DatasourceType; + + ValueSourceTypeTranslation = ValueSourceTypeTranslation; + + ValueSourceTypes = ValueSourceTypes; + + limitForm: UntypedFormGroup; + + latestKeyFormControl: UntypedFormControl; + + entityKeyFormControl: UntypedFormControl; + + private propagateChanges: (value: any) => void = () => {}; + + private modelValue: ValueSourceConfig | null = null; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.limitForm = this.fb.group({ + type: [ValueSourceType.constant], + value: [null], + entityAlias: [null] + }); + this.latestKeyFormControl = this.fb.control(null, [Validators.required]); + this.entityKeyFormControl = this.fb.control(null, [Validators.required]); + merge( + this.latestKeyFormControl.valueChanges, + this.entityKeyFormControl.valueChanges, + this.limitForm.valueChanges + ).pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => { + this.updateValidators(); + this.updateModel(); + }); + } + + writeValue(value: ValueSourceConfig) { + this.modelValue = value; + this.limitForm.patchValue( + { + type: value.type || ValueSourceType.constant, + value: value.value, + entityAlias: value.entityAlias, + }, {emitEvent: false} + ); + if (value.type === ValueSourceType.latestKey) { + this.latestKeyFormControl.patchValue({ + type: value.latestKeyType, + name: value.latestKey + }, {emitEvent: false}); + } else if (value.type === ValueSourceType.entity) { + this.entityKeyFormControl.patchValue({ + type: value.entityKeyType, + name: value.entityKey + }, {emitEvent: false}); + } + + this.updateValidators(); + this.limitForm.markAllAsTouched(); + } + + registerOnChange(fn: any) { + this.propagateChanges = fn; + } + + registerOnTouched(fn: any) { + } + + validate(): ValidationErrors | null { + const type = this.limitForm.get('type')?.value; + const errors: any = {}; + + if (this.limitForm.invalid) { + errors.form = false; + } + + if (type === ValueSourceType.latestKey) { + if (!this.latestKeyFormControl.value || this.latestKeyFormControl.invalid) { + errors.latestKey = false; + } + } else if (type === ValueSourceType.entity) { + if (!this.limitForm.get('entityAlias')?.value) { + errors.entityAlias = false; + } + if (!this.entityKeyFormControl.value || this.entityKeyFormControl.invalid) { + errors.entityKey = false; + } + } + + return Object.keys(errors).length ? { axisLimitForm: errors } : null; + } + + private updateValidators() { + const axisTypeControl = this.limitForm.get('type'); + if (axisTypeControl && this.entityKeyFormControl && this.latestKeyFormControl) { + const type = axisTypeControl.value; + if (type === ValueSourceType.latestKey) { + this.latestKeyFormControl.setValidators([Validators.required]); + this.entityKeyFormControl.clearValidators(); + } else if (type === ValueSourceType.entity) { + this.latestKeyFormControl.clearValidators(); + this.limitForm.get('entityAlias').setValidators([Validators.required]); + this.entityKeyFormControl.setValidators([Validators.required]); + } else { + this.latestKeyFormControl.clearValidators(); + this.entityKeyFormControl.clearValidators(); + } + this.latestKeyFormControl.updateValueAndValidity({ emitEvent: false }); + this.entityKeyFormControl.updateValueAndValidity({ emitEvent: false }); + } + } + + private updateModel() { + const value = this.limitForm.value; + const type = value.type; + let updates: Partial = { type }; + if (type === ValueSourceType.latestKey) { + const latestKey: DataKey = this.latestKeyFormControl.value; + updates.latestKey = latestKey?.name; + updates.latestKeyType = latestKey?.type as any; + } else if (type === ValueSourceType.entity) { + const entityKey: DataKey = this.entityKeyFormControl.value; + updates.entityKey = entityKey?.name; + updates.entityKeyType = entityKey?.type as any; + updates.entityAlias = value?.entityAlias; + } else { + updates.value = value?.value; + } + this.modelValue = updates as ValueSourceConfig; + this.propagateChanges(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html index 5c2ceca5f1..a1d60da276 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html @@ -37,9 +37,16 @@ {{ 'widgets.button.label' | translate }} - - - +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts index 61f84d8029..c3bd4e3579 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts @@ -26,6 +26,7 @@ import { import { merge } from 'rxjs'; import { coerceBoolean } from '@shared/decorators/coercion'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { widgetTitleAutocompleteValues } from '@shared/models/widget.models'; @Component({ selector: 'tb-widget-button-appearance', @@ -71,6 +72,8 @@ export class WidgetButtonAppearanceComponent implements OnInit, ControlValueAcce appearanceFormGroup: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component.html index 380ffcd604..e500157c64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component.html @@ -19,6 +19,9 @@
{{ panelTitle }}
+
+
+
widgets.chart.chart-axis.scale-limits
+
+
+
widgets.chart.chart-axis.limit
+
widgets.chart.chart-axis.source
+
widgets.chart.chart-axis.key-value
+
+
+ + + + + +
+
+
+
-
-
-
widgets.chart.chart-axis.scale
-
-
widgets.chart.chart-axis.scale-min
- - - -
widgets.chart.chart-axis.scale-max
- - - -
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts index b713a6335f..f31775f22f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts @@ -17,9 +17,11 @@ import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, + NG_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup, + ValidationErrors, Validator, Validators } from '@angular/forms'; import { @@ -32,6 +34,9 @@ import { merge } from 'rxjs'; import { coerceBoolean } from '@shared/decorators/coercion'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { IAliasController } from '@app/core/public-api'; +import { Datasource } from '@app/shared/public-api'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; @Component({ selector: 'tb-time-series-chart-axis-settings', @@ -42,10 +47,15 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TimeSeriesChartAxisSettingsComponent), multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TimeSeriesChartAxisSettingsComponent), + multi: true } ] }) -export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValueAccessor { +export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValueAccessor, Validator { @Input() @coerceBoolean() @@ -61,6 +71,15 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu defaultXAxisTicksFormat = defaultXAxisTicksFormat; + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + @Input() disabled: boolean; @@ -147,6 +166,12 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu registerOnTouched(_fn: any): void { } + validate(): ValidationErrors | null { + return this.axisSettingsFormGroup.valid ? null : { + axisSettings: false + }; + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts index 8a229d0a7b..1d4b93c712 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts @@ -38,7 +38,8 @@ import { import { defaultTimeSeriesChartYAxisSettings, getNextTimeSeriesYAxisId, - TimeSeriesChartYAxes, TimeSeriesChartYAxisId, + TimeSeriesChartYAxes, + TimeSeriesChartYAxisId, TimeSeriesChartYAxisSettings, timeSeriesChartYAxisValid, timeSeriesChartYAxisValidator @@ -47,6 +48,9 @@ import { mergeDeep } from '@core/utils'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { coerceBoolean } from '@shared/decorators/coercion'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { IAliasController } from '@app/core/public-api'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; +import { DataKey, DataKeyType, Datasource, ValueSourceConfig, ValueSourceType } from '@app/shared/public-api'; @Component({ selector: 'tb-time-series-chart-y-axes-panel', @@ -68,6 +72,15 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; }) export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, OnInit, Validator { + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + @Input() disabled: boolean; @@ -113,6 +126,7 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, for (const axis of axes) { yAxes[axis.id] = axis; } + this.updateLatestDataKeys(Object.values(yAxes)); this.propagateChange(yAxes); } ); @@ -135,7 +149,7 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, } writeValue(value: TimeSeriesChartYAxes | undefined): void { - const yAxes: TimeSeriesChartYAxes = value || {}; + const yAxes: TimeSeriesChartYAxes = this.checkLatestDataKeys(value || {}); if (!yAxes.default) { yAxes.default = mergeDeep({} as TimeSeriesChartYAxisSettings, defaultTimeSeriesChartYAxisSettings, {id: 'default', order: 0} as TimeSeriesChartYAxisSettings); @@ -182,6 +196,8 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, const axes: TimeSeriesChartYAxisSettings[] = this.yAxesFormGroup.get('axes').value; axis.id = getNextTimeSeriesYAxisId(axes); axis.order = axes.length; + axis.min = this.normalizeAxisLimit(axis.min); + axis.max = this.normalizeAxisLimit(axis.max); const axesArray = this.yAxesFormGroup.get('axes') as UntypedFormArray; const axisControl = this.fb.control(axis, [timeSeriesChartYAxisValidator]); axesArray.push(axisControl); @@ -194,4 +210,101 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, }); return this.fb.array(axesControls); } + + private checkLatestDataKeys(yAxes: TimeSeriesChartYAxes): TimeSeriesChartYAxes { + const latestKeys = this.datasource?.latestDataKeys || []; + const result: TimeSeriesChartYAxes = {}; + + for (const [id, axis] of Object.entries(yAxes)) { + axis.min = this.normalizeAxisLimit(axis.min); + axis.max = this.normalizeAxisLimit(axis.max); + const minCfg = axis.min; + const maxCfg = axis.max; + + const minValid = !!minCfg && ( + minCfg.type !== ValueSourceType.latestKey || + latestKeys.some(k => this.isYAxisKey(k, minCfg)) + ); + + const maxValid = !!maxCfg && ( + maxCfg.type !== ValueSourceType.latestKey || + latestKeys.some(k => this.isYAxisKey(k, maxCfg)) + ); + + if (minValid && maxValid) { + result[id] = axis; + } + } + + return result; + } + + private updateLatestDataKeys(yAxes: TimeSeriesChartYAxisSettings[]) { + if (this.datasource) { + let latestKeys = this.datasource.latestDataKeys; + if (!latestKeys) { + latestKeys = []; + this.datasource.latestDataKeys = latestKeys; + } + const existingYAxisKeys = latestKeys.filter(k => k.settings?.__yAxisMinKey === true || k.settings?.__yAxisMaxKey === true); + const foundYAxisKeys: DataKey[] = []; + + for(const yAxis of yAxes) { + const min = yAxis.min as ValueSourceConfig; + const max = yAxis.max as ValueSourceConfig; + if (min.type === ValueSourceType.latestKey) { + const found = existingYAxisKeys.find(k => this.isYAxisKey(k, min)); + if (!found) { + const newKey = this.dataKeyCallbacks.generateDataKey(min.latestKey, min.latestKeyType, + null, true, null); + newKey.settings.__yAxisMinKey = true; + latestKeys.push(newKey); + } else if (foundYAxisKeys.indexOf(found) === -1) { + foundYAxisKeys.push(found); + } + } + if (max.type === ValueSourceType.latestKey) { + const found = existingYAxisKeys.find(k => this.isYAxisKey(k, max)); + if (!found) { + const newKey = this.dataKeyCallbacks.generateDataKey(max.latestKey, max.latestKeyType, + null, true, null); + newKey.settings.__yAxisMaxKey = true; + latestKeys.push(newKey); + } else if (foundYAxisKeys.indexOf(found) === -1) { + foundYAxisKeys.push(found); + } + } + } + const toRemove = existingYAxisKeys.filter(k => foundYAxisKeys.indexOf(k) === -1); + for (const key of toRemove) { + const index = latestKeys.indexOf(key); + if (index > -1) { + latestKeys.splice(index, 1); + } + } + } + } + + private isYAxisKey(d: DataKey, limit: ValueSourceConfig): boolean { + return (d.type === DataKeyType.function && d.label === limit.latestKey) || + (d.type !== DataKeyType.function && d.name === limit.latestKey && + d.type === limit.latestKeyType); + } + + private normalizeAxisLimit(limit: string | number | ValueSourceConfig): ValueSourceConfig { + if (!limit) { + return { + type: ValueSourceType.constant, + value: null, + entityAlias: null + }; + } else if (typeof limit === 'number' || typeof limit === 'string') { + return { + type: ValueSourceType.constant, + value: Number(limit), + entityAlias: null + }; + } + return limit; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html index 7194fd36a9..6d6f492619 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html @@ -30,14 +30,14 @@ -
+
- +
-
+
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts index 7c7daea095..54d54d1a98 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts @@ -42,6 +42,8 @@ import { import { deepClone } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { TimeSeriesChartYAxesPanelComponent } from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component'; +import { ValueSourceType } from '@shared/models/widget-settings.models'; @Component({ selector: 'tb-time-series-chart-y-axis-row', @@ -85,6 +87,7 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O constructor(private fb: UntypedFormBuilder, private translate: TranslateService, private popoverService: TbPopoverService, + private timeSeriesChartYAxesPanel: TimeSeriesChartYAxesPanelComponent, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, private cd: ChangeDetectorRef, @@ -97,8 +100,8 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O position: [null, []], units: [null, []], decimals: [null, []], - min: [null, []], - max: [null, []], + min: this.createLimitFormGroup(), + max: this.createLimitFormGroup(), show: [null, []] }); this.axisFormGroup.valueChanges.pipe( @@ -132,17 +135,19 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O writeValue(value: TimeSeriesChartYAxisSettings): void { this.modelValue = value; - this.axisFormGroup.patchValue( - { - label: value.label, - position: value.position, - units: value.units, - decimals: value.decimals, - min: value.min, - max: value.max, - show: value.show, - }, {emitEvent: false} - ); + const min = this.normalizeLimit(value.min); + const max = this.normalizeLimit(value.max); + + this.axisFormGroup.patchValue({ + label: value.label, + position: value.position, + units: value.units, + decimals: value.decimals, + min, + max, + show: value.show, + }, { emitEvent: false }); + this.updateValidators(); this.cd.markForCheck(); } @@ -165,7 +170,10 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O axisType: 'yAxis', panelTitle: this.translate.instant('widgets.time-series-chart.axis.y-axis-settings'), axisSettings: deepClone(this.modelValue), - advanced: this.advanced + advanced: this.advanced, + aliasController: this.timeSeriesChartYAxesPanel.aliasController, + dataKeyCallbacks: this.timeSeriesChartYAxesPanel.dataKeyCallbacks, + datasource: this.timeSeriesChartYAxesPanel.datasource }, isModal: true }); @@ -190,6 +198,10 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O } } + checkIsConstantLimit(limit: 'min' | 'max') { + return this.axisFormGroup.get(`${limit}.type`)?.value === ValueSourceType.constant; + } + private updateValidators() { const show: boolean = this.axisFormGroup.get('show').value; if (show) { @@ -203,10 +215,21 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O this.axisFormGroup.get('units').disable({emitEvent: false}); this.axisFormGroup.get('decimals').disable({emitEvent: false}); } + if(!this.checkIsConstantLimit('min')){ + this.axisFormGroup.get('min').disable({emitEvent: false}); + } else { + this.axisFormGroup.get('min').enable({emitEvent: false}); + } + if(!this.checkIsConstantLimit('max')){ + this.axisFormGroup.get('max').disable({emitEvent: false}); + } else { + this.axisFormGroup.get('max').enable({emitEvent: false}); + } + } private updateModel() { - const value = this.axisFormGroup.value; + const value = this.axisFormGroup.getRawValue(); this.modelValue.label = value.label; this.modelValue.position = value.position; this.modelValue.units = value.units; @@ -216,4 +239,39 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O this.modelValue.show = value.show; this.propagateChange(this.modelValue); } + + private createLimitFormGroup() { + return this.fb.group({ + type: [ValueSourceType.constant, []], + value: [null, []], + latestKey: [null, []], + latestKeyType: [null, []], + entityAlias: [null, []], + entityKey: [null, []], + entityKeyType: [null, []] + }); + } + + private normalizeLimit(limit: any) { + const base = { + type: ValueSourceType.constant, + value: null, + latestKey: null, + latestKeyType: null, + entityAlias: null, + entityKey: null, + entityKeyType: null + }; + + if (limit == null) return base; + + if (typeof limit === 'number' || typeof limit === 'string') { + return { ...base, type: ValueSourceType.constant, value: Number(limit) }; + } + + return { + ...base, + ...limit, + }; + } } 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 index 45acb33b31..2e76fffa3a 100644 --- 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 @@ -113,9 +113,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On this.colorRangeListFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => this.updateModel()); - this.colorRangeListFormGroup.get('advancedMode').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe(() => setTimeout(() => {this.popover?.updatePosition();}, 0)); } ngOnDestroy() { @@ -179,7 +176,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On public removeAdvancedRange(index: number) { (this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray).removeAt(index); - setTimeout(() => {this.popover?.updatePosition();}, 0); } get advancedRangeFormArray(): UntypedFormArray { @@ -193,7 +189,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On removeRange(index: number) { this.rangeListFormArray.removeAt(index); this.colorRangeListFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } rangeDrop(event: CdkDragDrop, range: string) { @@ -216,7 +211,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On const advancedRangeColorsArray = this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray; const advancedRangeColorControl = this.fb.control(advancedRange, [advancedRangeValidator]); advancedRangeColorsArray.push(advancedRangeColorControl); - setTimeout(() => {this.popover?.updatePosition();}, 0); } addRange() { @@ -228,7 +222,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On }; this.rangeListFormArray.push(this.colorRangeControl(newRange)); this.colorRangeListFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } } 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 index cf7d3d051f..f48319af75 100644 --- 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 @@ -94,7 +94,7 @@
-
+
{ - this.updateValidators(); - setTimeout(() => {this.popover?.updatePosition();}, 0); - }); this.updateValidators(); } 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 index f67b647c48..811a0a4f60 100644 --- 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 @@ -164,7 +164,7 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD renderer: this.renderer, componentType: ColorSettingsPanelComponent, hostView: this.viewContainerRef, - preferredPlacement: 'left', + preferredPlacement: ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], context: { colorSettings: this.modelValue, settingsComponents: this.colorSettingsComponentService.getOtherColorSettingsComponents(this), 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 index 40db32d978..08a0246217 100644 --- 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 @@ -38,9 +38,22 @@ {{ 'widgets.count.label' | translate }}
- - - + @if (!predefinedValues) { + + + + }@else { + + } 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 index 7221c54061..dfa39e58c5 100644 --- 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 @@ -61,6 +61,9 @@ export class CountWidgetSettingsComponent extends PageComponent implements OnIni @Input() alarmElseEntity: boolean; + @Input() + predefinedValues: string[]; + private propagateChange = null; countCardLayouts = countCardLayouts; @@ -69,7 +72,6 @@ export class CountWidgetSettingsComponent extends PageComponent implements OnIni countCardLayoutImageMap: Map; countWidgetConfigForm: UntypedFormGroup; - constructor(protected store: Store, private fb: UntypedFormBuilder, private destroyRef: DestroyRef) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts index ba778cf607..633ae1b89d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts @@ -117,9 +117,6 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro this.gradientFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => this.updateModel()); - this.gradientFormGroup.get('advancedMode').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe(() => setTimeout(() => {this.popover?.updatePosition();}, 0)); } ngOnDestroy() { @@ -236,7 +233,6 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro this.gradientListFormArray.removeAt(index); } this.gradientFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } gradientDrop(event: CdkDragDrop, advanced = false) { @@ -255,7 +251,6 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro this.gradientListFormArray.push(this.colorGradientControl('rgba(0,0,0,0.87)')); } this.gradientFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } updateModel() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.html index fa8a152d31..485f6d0af4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.html @@ -21,9 +21,16 @@ {{ 'widgets.status-widget.label' | translate }}
- - - + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts index a810896685..5e8f7e2ce6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts @@ -22,6 +22,7 @@ import { StatusWidgetStateSettings } from '@home/components/widget/lib/indicator/status-widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { widgetTitleAutocompleteValues } from '@app/shared/public-api'; @Component({ selector: 'tb-status-widget-state-settings', @@ -51,6 +52,8 @@ export class StatusWidgetStateSettingsComponent implements OnInit, OnChanges, Co public stateSettingsFormGroup: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + constructor(private fb: UntypedFormBuilder, private destroyRef: DestroyRef) { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html index 6635982677..5b91e07224 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html @@ -50,6 +50,7 @@ [hideDataKeyColor]="data.hideDataKeyColor" [hideDataKeyUnits]="data.hideDataKeyUnits" [hideDataKeyDecimals]="data.hideDataKeyDecimals" + [hideDataKeyAggregation]="data.hideDataKeyAggregation" [supportsUnitConversion]="data.supportsUnitConversion" formControlName="dataKey"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts index 6ed15ce4f5..72329d208e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts @@ -56,6 +56,7 @@ export interface DataKeyConfigDialogData { hideDataKeyColor?: boolean; hideDataKeyUnits?: boolean; hideDataKeyDecimals?: boolean; + hideDataKeyAggregation?: boolean; supportsUnitConversion?: boolean } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html index 52eb5aabb3..85acdab5cf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html @@ -79,7 +79,7 @@ formControlName="funcBody">
- +
{{ 'datakey.aggregation' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts index dafcce57b6..ac6596576c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts @@ -153,6 +153,10 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con @coerceBoolean() hideDataKeyDecimals = false; + @Input() + @coerceBoolean() + hideDataKeyAggregation = false; + @Input() @coerceBoolean() supportsUnitConversion = false; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html index 8fb0194d59..886c9fb681 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html @@ -163,6 +163,26 @@ (keyEdit)="editKey('circleKey')" formControlName="circleKey"> + +
+
+
widgets.maps.data-layer.shape.stroke
@@ -455,7 +477,9 @@ [dsType]="dataLayerFormGroup.get('dsType').value" [dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value" [dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value" - helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_stroke_color_fn' : 'widget/lib/map/circle_stroke_color_fn' }}" formControlName="strokeColor"> + helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_stroke_color_fn' : + (dataLayerType === 'circles' ? 'widget/lib/map/polygon_stroke_color_fn' : 'widget/lib/map/polyline_stroke_color_fn') }}" + formControlName="strokeColor">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts index 8c5e37d148..62a46165dd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts @@ -29,7 +29,7 @@ import { MarkerType, pathDecoratorSymbols, pathDecoratorSymbolTranslationMap, - PolygonsDataLayerSettings, + PolygonsDataLayerSettings, PolylinesDataLayerSettings, ShapeDataLayerSettings, ShapeFillType, TripsDataLayerSettings, updateDataKeyToNewDsType @@ -113,6 +113,8 @@ export class MapDataLayerDialogComponent extends DialogComponent this.updateValidators() @@ -291,6 +298,15 @@ export class MapDataLayerDialogComponent extends DialogComponent { if (updatedDataKey) { this.dataLayerFormGroup.get(keyType).patchValue(updatedDataKey); @@ -511,6 +533,12 @@ export class MapDataLayerDialogComponent extends DialogComponent + + +
widgets.maps.data-layer.polygon.polygon-key
widgets.maps.data-layer.circle.circle-key
+
widgets.maps.data-layer.polyline.polyline-key
{{ 'widgets.maps.overlays.markers' | translate }} {{ 'widgets.maps.overlays.polygons' | translate }} {{ 'widgets.maps.overlays.circles' | translate }} + //todo translation + Polylines
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts index ee1b7d5b78..2dd6f701a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts @@ -24,6 +24,7 @@ export interface MapSettingsContext { aliasController: IAliasController; callbacks: WidgetConfigCallbacks; widget: Widget; - editKey: (key: DataKey, deviceId: string, entityAliasId: string, WidgetType?: widgetType) => Observable; + editKey: (key: DataKey, deviceId: string, entityAliasId: string, WidgetType?: widgetType, + hideDataKeyAggregation?: boolean) => Observable; generateDataKey: (key: DataKey) => DataKey; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts index 985e5df1f9..322fbdbde0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts @@ -145,6 +145,7 @@ export class MapSettingsComponent implements OnInit, ControlValueAccessor, Valid markers: [null, []], polygons: [null, []], circles: [null, []], + polylines: [null, []], additionalDataSources: [null, []], controlsPosition: [null, []], zoomActions: [null, []], @@ -180,7 +181,8 @@ export class MapSettingsComponent implements OnInit, ControlValueAccessor, Valid }); merge(this.mapSettingsFormGroup.get('markers').valueChanges, this.mapSettingsFormGroup.get('polygons').valueChanges, - this.mapSettingsFormGroup.get('circles').valueChanges + this.mapSettingsFormGroup.get('circles').valueChanges, + this.mapSettingsFormGroup.get('polylines').valueChanges ).pipe( takeUntilDestroyed(this.destroyRef) ).subscribe(() => { @@ -281,6 +283,10 @@ export class MapSettingsComponent implements OnInit, ControlValueAccessor, Valid const polygons: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('polygons').value; dragModeButtonSettingsEnabled = polygons.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move)); } + if (!dragModeButtonSettingsEnabled) { + const polylines: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('polylines').value; + dragModeButtonSettingsEnabled = polylines.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move)); + } if (!dragModeButtonSettingsEnabled) { const circles: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('circles').value; dragModeButtonSettingsEnabled = circles.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move)); @@ -298,7 +304,8 @@ export class MapSettingsComponent implements OnInit, ControlValueAccessor, Valid this.propagateChange(this.modelValue); } - private editKey(key: DataKey, deviceId: string, entityAliasId: string, _widgetType = widgetType.latest): Observable { + private editKey(key: DataKey, deviceId: string, entityAliasId: string, _widgetType = widgetType.latest, + hideDataKeyAggregation = true): Observable { return this.dialog.open(DataKeyConfigDialogComponent, { disableClose: true, @@ -315,6 +322,7 @@ export class MapSettingsComponent implements OnInit, ControlValueAccessor, Valid hideDataKeyColor: true, hideDataKeyDecimals: true, hideDataKeyUnits: true, + hideDataKeyAggregation: hideDataKeyAggregation, widget: this.widget, dashboard: null, dataKeySettingsForm: null, 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 index 9004928078..2a9d05b57b 100644 --- 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 @@ -267,6 +267,7 @@ import { import { ShapeFillStripeSettingsPanelComponent } from '@home/components/widget/lib/settings/common/map/shape-fill-stripe-settings-panel.component'; +import { AxisScaleRowComponent } from './axis-scale-row.component'; @NgModule({ declarations: [ @@ -372,7 +373,8 @@ import { DataKeysComponent, DataKeyConfigDialogComponent, DataKeyConfigComponent, - WidgetSettingsComponent + WidgetSettingsComponent, + AxisScaleRowComponent ], imports: [ CommonModule, @@ -453,7 +455,8 @@ import { DataKeysComponent, DataKeyConfigDialogComponent, DataKeyConfigComponent, - WidgetSettingsComponent + WidgetSettingsComponent, + AxisScaleRowComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html index 776b773428..330433141e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html @@ -86,9 +86,16 @@ {{ 'widgets.single-switch.label' | translate }}
- - - + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts index c3e768ae5b..d841317283 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues, widgetType } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -51,6 +51,8 @@ export class SingleSwitchWidgetSettingsComponent extends WidgetSettingsComponent singleSwitchWidgetSettingsForm: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.html index 476e2e07ce..51af51985d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.html @@ -18,10 +18,14 @@
widgets.rpc.common-settings - - widgets.rpc.slide-toggle-label - - +
widgets.rpc.label-position diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts index 888a016d91..5090e8c94f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { TargetDevice, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -31,6 +31,8 @@ export class SlideToggleWidgetSettingsComponent extends WidgetSettingsComponent slideToggleWidgetSettingsForm: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.html index ae4e74e6d8..192e5a7be7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.html @@ -18,10 +18,14 @@
widgets.rpc.common-settings - - widgets.rpc.widget-title - - + widgets.rpc.button-label diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.ts index a9fedf335c..765890a101 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/update-device-attribute-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -32,6 +32,8 @@ export class UpdateDeviceAttributeWidgetSettingsComponent extends WidgetSettings contentTypes = ContentType; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); 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 index 92c39ec75f..eb36eafb06 100644 --- 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 @@ -20,9 +20,15 @@
widgets.table.table-header
{{ 'widgets.table.entities-table-title' | translate }}
- - - +
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 index 5c19810eb4..e4238a13d2 100644 --- 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 @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -31,6 +31,8 @@ export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponen entitiesTableWidgetSettingsForm: UntypedFormGroup; pageStepSizeValues = []; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); 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 index ffd81460fb..147b7defb1 100644 --- 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 @@ -18,6 +18,6 @@
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 index 956158aafc..e8bc5f9bad 100644 --- 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 @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -29,6 +29,8 @@ import { countDefaultSettings } from '@home/components/widget/lib/count/count-wi export class EntityCountWidgetSettingsComponent extends WidgetSettingsComponent { entityCountWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.html index bc377800fd..f51d3784b0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.html @@ -16,10 +16,15 @@ -->
- - widgets.gateway.gateway-title - - + {{ 'widgets.gateway.read-only' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.ts index dcc8f15d2b..60035457d1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-single-device-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class GatewayConfigSingleDeviceWidgetSettingsComponent extends WidgetSettingsComponent { gatewayConfigSingleDeviceWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.html index 0e32d98266..c525c67b56 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.html @@ -18,10 +18,15 @@
widgets.gateway.general-settings - - widgets.gateway.widget-title - - + widgets.gateway.default-archive-file-name diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.ts index c63e19d4ec..d2d921db68 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-config-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class GatewayConfigWidgetSettingsComponent extends WidgetSettingsComponent { gatewayConfigWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.html index 65311f6e94..1bebce0b7d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.html @@ -16,10 +16,15 @@ -->
- - widgets.gateway.events-title - - + widgets.gateway.events-filter diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.ts index b0f335726b..ef7e8c7574 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-events-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -32,6 +32,8 @@ export class GatewayEventsWidgetSettingsComponent extends WidgetSettingsComponen separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; gatewayEventsWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.html index f621fdbc97..96b7d8419a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.html @@ -15,19 +15,39 @@ limitations under the License. --> -
-
- widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - -
-
- widgets.input-widgets.image-settings -
- - widgets.input-widgets.image-format +
+
+
widgets.input-widgets.general-settings
+
+
widgets.input-widgets.widget-title
+ +
+
+
+
widgets.input-widgets.save-image
+ + {{ 'widgets.input-widgets.save-to-gallery' | translate }} + + @if (photoCameraInputWidgetSettingsForm.get('saveToGallery').value) { + + {{ 'widgets.input-widgets.public-image' | translate }} + + } +
+
+
widgets.input-widgets.image-settings
+
+
widgets.input-widgets.image-format
+ {{ 'widgets.input-widgets.image-format-jpeg' | translate }} @@ -40,20 +60,33 @@ - - widgets.input-widgets.image-quality - - -
-
- - widgets.input-widgets.max-image-width - - - - widgets.input-widgets.max-image-height - - -
-
+
+ + @if (photoCameraInputWidgetSettingsForm.get('imageFormat').value !== 'image/png') { +
+
widgets.input-widgets.image-quality
+ + + % + +
+ } + +
+
Size
+
+
widgets.input-widgets.max-image-width
+ + + px + + +
widgets.input-widgets.max-image-height
+ + + px + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.ts index 7cd51b5722..097d09ad9b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/photo-camera-input-widget-settings.component.ts @@ -15,10 +15,10 @@ /// 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 { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { AppState } from '@core/core.state'; +import { Store } from '@ngrx/store'; @Component({ selector: 'tb-photo-camera-input-widget-settings', @@ -29,6 +29,8 @@ export class PhotoCameraInputWidgetSettingsComponent extends WidgetSettingsCompo photoCameraInputWidgetSettingsForm: UntypedFormGroup; + predefinedValues = widgetTitleAutocompleteValues; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); @@ -42,6 +44,8 @@ export class PhotoCameraInputWidgetSettingsComponent extends WidgetSettingsCompo return { widgetTitle: '', + saveToGallery: false, + usePublicGalleryLink: true, imageFormat: 'image/png', imageQuality: 0.92, maxWidth: 640, @@ -57,11 +61,28 @@ export class PhotoCameraInputWidgetSettingsComponent extends WidgetSettingsCompo widgetTitle: [settings.widgetTitle, []], // Image settings - + saveToGallery: [settings.saveToGallery], + usePublicGalleryLink: [settings.usePublicGalleryLink], imageFormat: [settings.imageFormat, []], - imageQuality: [settings.imageQuality, [Validators.min(0), Validators.max(1)]], + imageQuality: [settings.imageQuality, [Validators.min(0), Validators.max(100)]], maxWidth: [settings.maxWidth, [Validators.min(1)]], maxHeight: [settings.maxHeight, [Validators.min(1)]] }); } + + protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { + return { + ...settings, + saveToGallery: settings.saveToGallery ?? false, + usePublicGalleryLink: settings.usePublicGalleryLink ?? false, + imageQuality: settings.imageQuality * 100 + } + } + + protected prepareOutputSettings(settings: WidgetSettings): WidgetSettings { + return { + ...settings, + imageQuality: settings.imageQuality / 100 + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.html index d2d1b32eff..efa5b6cd3a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.html @@ -18,10 +18,15 @@
widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - +
{{ 'widgets.input-widgets.show-label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts index 5d9703e95b..acfc9a8b59 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts @@ -29,6 +29,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { widgetTitleAutocompleteValues } from '@app/shared/public-api'; export interface UpdateAttributeGeneralSettings { widgetTitle: string; @@ -77,6 +78,8 @@ export class UpdateAttributeGeneralSettingsComponent extends PageComponent imple @Input() hasLabelValue = true; + + predefinedValues = widgetTitleAutocompleteValues; private modelValue: UpdateAttributeGeneralSettings; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.html index 9825593f07..056209f77a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.html @@ -18,10 +18,15 @@
widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - + {{ 'widgets.input-widgets.show-result-message' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.ts index fc212f5ae3..3b4b6e682b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-boolean-attribute-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class UpdateBooleanAttributeWidgetSettingsComponent extends WidgetSettingsComponent { updateBooleanAttributeWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.html index 7b1080fd24..2c811a6ce1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.html @@ -18,10 +18,15 @@
widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - + {{ 'widgets.input-widgets.show-result-message' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.ts index 7839e48cfc..0bb0e0af1d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-image-attribute-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class UpdateImageAttributeWidgetSettingsComponent extends WidgetSettingsComponent { updateImageAttributeWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.html index 507ec97a5f..2294fac186 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.html @@ -18,10 +18,15 @@
widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - +
{{ 'widgets.input-widgets.show-label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.ts index 84be46e289..70d4c42350 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-json-attribute-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class UpdateJsonAttributeWidgetSettingsComponent extends WidgetSettingsComponent { updateJsonAttributeWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.html index 5867aef0aa..83cd0fd5ba 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.html @@ -18,10 +18,15 @@
widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - + {{ 'widgets.input-widgets.show-result-message' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.ts index 7756a465c8..020bb13358 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-location-attribute-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class UpdateLocationAttributeWidgetSettingsComponent extends WidgetSettingsComponent { updateLocationAttributeWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { 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 eedd0ec063..b9def80dfd 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 @@ -18,10 +18,15 @@
widgets.input-widgets.general-settings - - widgets.input-widgets.widget-title - - + {{ 'widgets.input-widgets.show-result-message' | translate }} 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 880bce24c8..aa31db2b0b 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 @@ -15,7 +15,7 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { WidgetSettings, WidgetSettingsComponent, widgetTitleAutocompleteValues } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSettingsComponent { updateMultipleAttributesWidgetSettingsForm: UntypedFormGroup; + + predefinedValues = widgetTitleAutocompleteValues; constructor(protected store: Store, private fb: UntypedFormBuilder) { 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 af8694d24c..3d8c564b67 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 @@ -375,6 +375,12 @@ import { ValueStepperWidgetSettingsComponent } from '@home/components/widget/lib/settings/control/value-stepper-widget-settings.component'; import { MapWidgetSettingsComponent } from '@home/components/widget/lib/settings/map/map-widget-settings.component'; +import { + ApiUsageWidgetSettingsComponent +} from "@home/components/widget/lib/settings/cards/api-usage-widget-settings.component"; +import { + ApiUsageDataKeyRowComponent +} from "@home/components/widget/lib/settings/cards/api-usage-data-key-row.component"; @NgModule({ declarations: [ @@ -508,7 +514,9 @@ import { MapWidgetSettingsComponent } from '@home/components/widget/lib/settings LabelValueCardWidgetSettingsComponent, UnreadNotificationWidgetSettingsComponent, ScadaSymbolWidgetSettingsComponent, - MapWidgetSettingsComponent + MapWidgetSettingsComponent, + ApiUsageWidgetSettingsComponent, + ApiUsageDataKeyRowComponent ], imports: [ CommonModule, @@ -647,7 +655,8 @@ import { MapWidgetSettingsComponent } from '@home/components/widget/lib/settings LabelValueCardWidgetSettingsComponent, UnreadNotificationWidgetSettingsComponent, ScadaSymbolWidgetSettingsComponent, - MapWidgetSettingsComponent + MapWidgetSettingsComponent, + ApiUsageWidgetSettingsComponent ] }) export class WidgetSettingsModule { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts index 34995f2396..0826b0a5e7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts @@ -451,112 +451,41 @@ export function constructTableCssString(widgetConfig: WidgetConfig): string { const mdDarkDisabled = defaultColor.setAlpha(0.26).toRgbString(); const mdDarkDisabled2 = defaultColor.setAlpha(0.38).toRgbString(); const mdDarkDivider = defaultColor.setAlpha(0.12).toRgbString(); - - const cssString = - '.mat-mdc-input-element::placeholder {\n' + - ' color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-input-element::-moz-placeholder {\n' + - ' color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-input-element::-webkit-input-placeholder {\n' + - ' color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-input-element:-ms-input-placeholder {\n' + - ' color: ' + mdDarkSecondary + ';\n' + - '}\n' + - 'mat-toolbar.mat-mdc-table-toolbar {\n' + - 'color: ' + mdDark + ';\n' + - '}\n' + - 'mat-toolbar.mat-mdc-table-toolbar:not([color="primary"]) button.mat-mdc-icon-button mat-icon {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-tab .mdc-tab__text-label {\n' + - 'color: ' + mdDark + ';\n' + - '}\n' + - '.mat-mdc-tab-header-pagination-chevron {\n' + - 'border-color: ' + mdDark + ';\n' + - '}\n' + - '.mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron {\n' + - 'border-color: ' + mdDarkDisabled2 + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-header-row {\n' + - 'background-color: ' + origBackgroundColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-header-cell {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-cell, .mat-mdc-table .mat-mdc-header-cell {\n' + - 'border-bottom-color: ' + mdDarkDivider + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-cell .mat-mdc-checkbox ' + - '.mdc-checkbox__native-control:focus:enabled:not(:checked):not(:indeterminate):not([data-indeterminate=true])'+ - '~.mdc-checkbox__background, ' + - '.mat-table .mat-header-cell .mat-mdc-checkbox ' + - '.mdc-checkbox__native-control:focus:enabled:not(:checked):not(:indeterminate):not([data-indeterminate=true])'+ - '~.mdc-checkbox__background {\n' + - 'border-color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row .mat-mdc-cell.mat-mdc-table-sticky {\n' + - 'transition: background-color .2s;\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row.tb-current-entity {\n' + - 'background-color: ' + currentEntityColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row.tb-current-entity .mat-mdc-cell.mat-mdc-table-sticky {\n' + - 'background-color: ' + currentEntityStickyColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row:hover:not(.tb-current-entity) {\n' + - 'background-color: ' + hoverColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row:hover:not(.tb-current-entity) .mat-mdc-cell.mat-mdc-table-sticky {\n' + - 'background-color: ' + hoverStickyColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row.mat-row-select.mat-selected:not(.tb-current-entity) {\n' + - 'background-color: ' + selectedColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row.mat-row-select.mat-selected:not(.tb-current-entity) .mat-mdc-cell.mat-mdc-table-sticky {\n' + - 'background-color: ' + selectedStickyColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row .mat-mdc-cell.mat-mdc-table-sticky, .mat-mdc-table .mat-mdc-header-cell.mat-mdc-table-sticky {\n' + - 'background-color: ' + origBackgroundColor + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-row {\n' + - 'color: ' + mdDark + ';\n' + - 'background-color: rgba(0, 0, 0, 0);\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button mat-icon {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button[disabled][disabled] mat-icon {\n' + - 'color: ' + mdDarkDisabled + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button tb-icon {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button[disabled][disabled] tb-icon {\n' + - 'color: ' + mdDarkDisabled + ';\n' + - '}\n' + - '.mat-divider {\n' + - 'border-top-color: ' + mdDarkDivider + ';\n' + - '}\n' + - '.mat-mdc-paginator {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-paginator button.mat-mdc-icon-button {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}\n' + - '.mat-mdc-paginator button.mat-mdc-icon-button[disabled][disabled] {\n' + - 'color: ' + mdDarkDisabled + ';\n' + - '}\n' + - '.mat-mdc-paginator .mat-mdc-select-value {\n' + - 'color: ' + mdDarkSecondary + ';\n' + - '}'; + + const cssString = ` { + --mat-toolbar-container-text-color: ${mdDark}; + --mat-tab-header-active-label-text-color: ${mdDark}; + --mat-tab-header-inactive-label-text-color: ${mdDark}; + --mat-tab-header-pagination-icon-color: ${mdDark}; + --mat-tab-header-pagination-disabled-icon-color: ${mdDarkDisabled2}; + --mat-table-header-headline-color: ${mdDarkSecondary}; + --mat-table-row-item-label-text-color: ${mdDark}; + --mat-icon-color: ${mdDarkSecondary}; + --mdc-icon-button-disabled-icon-color: ${mdDarkDisabled}; + --mat-divider-color: ${mdDarkDivider}; + --mat-paginator-container-text-color: ${mdDarkSecondary}; + --mdc-icon-button-icon-color: ${mdDarkSecondary}; + --mat-paginator-enabled-icon-color: ${mdDarkSecondary}; + --mat-paginator-disabled-icon-color: ${mdDarkDisabled}; + --mat-select-enabled-trigger-text-color: ${mdDarkSecondary}; + --mat-select-disabled-trigger-text-color: ${mdDarkDisabled}; + --mat-table-row-item-outline-color: ${mdDarkDivider}; + --mdc-checkbox-unselected-focus-icon-color: ${mdDarkSecondary}; + + --tb-orig-background-color: ${origBackgroundColor}; + --tb-current-entity-color: ${currentEntityColor}; + --tb-current-entity-sticky-color: ${currentEntityStickyColor}; + --tb-hover-color: ${hoverColor}; + --tb-hover-sticky-color: ${hoverStickyColor}; + --tb-selected-color: ${selectedColor}; + --tb-selected-sticky-color: ${selectedStickyColor}; + } + `; return cssString; } -export function getHeaderTitle(dataKey: DataKey, keySettings: TableWidgetDataKeySettings, utils: UtilsService) { - if (isDefined(keySettings.customTitle) && isNotEmptyStr(keySettings.customTitle)) { +export function getHeaderTitle(dataKey: DataKey, keySettings: TableWidgetDataKeySettings | undefined, utils: UtilsService) { + if (isNotEmptyStr(keySettings?.customTitle)) { return utils.customTranslation(keySettings.customTitle, keySettings.customTitle); } return dataKey.label; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss index 87ed15ed6c..c75500651e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss @@ -44,7 +44,53 @@ padding: 0 5px; } } + + .mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron { + border-color: var(--mat-tab-header-pagination-disabled-icon-color); + } + } + + .mat-mdc-input-element::placeholder { + color: var(--mat-table-header-headline-color); } + + .mat-mdc-table { + .mat-mdc-header-row { + background-color: var(--tb-orig-background-color); + } + + .mat-mdc-cell, .mat-mdc-header-cell { + &.mat-mdc-table-sticky { + background-color:var(--tb-orig-background-color); + } + } + + .mat-mdc-row { + background-color: rgba(0,0,0,0); + &.tb-current-entity { + background-color: var(--tb-current-entity-color); + .mat-mdc-cell.mat-mdc-table-sticky { + background-color: var(--tb-current-entity-sticky-color); + } + } + &:hover:not(.tb-current-entity){ + background-color: var(--tb-hover-color); + .mat-mdc-cell.mat-mdc-table-sticky { + background-color: var(--tb-hover-sticky-color); + } + } + &.mat-row-select.mat-selected:not(.tb-current-entity){ + background-color: var(--tb-selected-color); + .mat-mdc-cell.mat-mdc-table-sticky { + background-color: var(--tb-selected-sticky-color); + } + } + &, .mat-mdc-cell.mat-mdc-table-sticky { + transition: background-color .2s; + background-color:var(--tb-orig-background-color); + } + } + } } :host-context(.tb-has-timewindow) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html index c6b4f3d8ed..99a31331fd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html @@ -41,13 +41,13 @@ class="flex-1" mat-stretch-tabs="false" [(selectedIndex)]="sourceIndex" (selectedIndexChange)="onSourceIndexChanged()"> - +
- Timestamp + {{ 'widgets.table.timestamp-column-name' | translate }} @@ -78,7 +78,7 @@ diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss index feb312c0f2..9107f5947d 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss @@ -71,13 +71,6 @@ } :host ::ng-deep { - - .mat-mdc-form-field { - .mat-mdc-form-field-infix { - width: 100%; - } - } - .mat-expansion-panel { .mat-expansion-panel-content { font-size: 16px; diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 3d8cc5efde..6995d41879 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'; +import { Component, DestroyRef, OnInit, QueryList, ViewChildren } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; import { Store } from '@ngrx/store'; @@ -28,32 +28,40 @@ import { TwoFactorAuthSettings, TwoFactorAuthSettingsForm } from '@shared/models/two-factor-auth.models'; -import { isNotEmptyStr } from '@core/utils'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { isDefined, isNotEmptyStr } from '@core/utils'; import { MatExpansionPanel } from '@angular/material/expansion'; +import { NotificationTargetConfigType, NotificationTargetConfigTypeInfoMap } from '@shared/models/notification.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-2fa-settings', templateUrl: './two-factor-auth-settings.component.html', styleUrls: [ './settings-card.scss', './two-factor-auth-settings.component.scss'] }) -export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { +export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { - private readonly destroy$ = new Subject(); private readonly posIntValidation = [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]; twoFaFormGroup: UntypedFormGroup; twoFactorAuthProviderType = TwoFactorAuthProviderType; twoFactorAuthProvidersData = twoFactorAuthProvidersData; + notificationTargetConfigType = NotificationTargetConfigType; + notificationTargetConfigTypes: NotificationTargetConfigType[] = this.allowNotificationTargetConfigTypes(); + notificationTargetConfigTypeInfoMap = NotificationTargetConfigTypeInfoMap; + + filterByTenants: boolean; + entityType = EntityType; + showMainLoadingBar = false; @ViewChildren(MatExpansionPanel) expansionPanel: QueryList; constructor(protected store: Store, private twoFaService: TwoFactorAuthenticationService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -61,15 +69,11 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI this.build2faSettingsForm(); this.twoFaService.getTwoFaSettings().subscribe((setting) => { this.setAuthConfigFormValue(setting); + this.twoFaFormGroup.markAsUntouched(); + this.twoFaFormGroup.markAsPristine(); }); } - ngOnDestroy() { - super.ngOnDestroy(); - this.destroy$.next(); - this.destroy$.complete(); - } - confirmForm(): UntypedFormGroup { return this.twoFaFormGroup; } @@ -80,7 +84,15 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI this.joinRateLimit(setting, 'verificationCodeCheckRateLimit'); const providers = setting.providers.filter(provider => provider.enable); providers.forEach(provider => delete provider.enable); - const config = Object.assign(setting, {providers}); + const enforcedUsersFilter = this.twoFaFormGroup.get('enforcedUsersFilter').value; + if (enforcedUsersFilter.filterByTenants) { + enforcedUsersFilter.tenantProfilesIds = null; + } else { + enforcedUsersFilter.tenantsIds = null; + } + delete enforcedUsersFilter.filterByTenants; + const config = Object.assign(setting, {providers}, {enforcedUsersFilter}); + this.filterByTenants = this.twoFaFormGroup.get('enforcedUsersFilter.filterByTenants').value; this.twoFaService.saveTwoFaSettings(config).subscribe( (settings) => { this.setAuthConfigFormValue(settings); @@ -117,6 +129,13 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI private build2faSettingsForm(): void { this.twoFaFormGroup = this.fb.group({ + enforceTwoFa: [false], + enforcedUsersFilter: this.fb.group({ + type: [NotificationTargetConfigType.ALL_USERS], + filterByTenants: [true], + tenantsIds: [], + tenantProfilesIds: [] + }), maxVerificationFailuresBeforeUserLockout: [30, [ Validators.pattern(/^\d*$/), Validators.min(0), @@ -130,14 +149,14 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI verificationCodeCheckRateLimitEnable: [false], verificationCodeCheckRateLimitNumber: [{value: 3, disabled: true}, this.posIntValidation], verificationCodeCheckRateLimitTime: [{value: 900, disabled: true}, this.posIntValidation], - minVerificationCodeSendPeriod: ['30', [Validators.required, Validators.min(5), Validators.pattern(/^\d*$/)]], + minVerificationCodeSendPeriod: [30, [Validators.required, Validators.min(5), Validators.pattern(/^\d*$/)]], providers: this.fb.array([]) }); Object.values(TwoFactorAuthProviderType).forEach(provider => { this.buildProvidersSettingsForm(provider); }); this.twoFaFormGroup.get('verificationCodeCheckRateLimitEnable').valueChanges.pipe( - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ).subscribe(value => { if (value) { this.twoFaFormGroup.get('verificationCodeCheckRateLimitNumber').enable({emitEvent: false}); @@ -148,7 +167,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI } }); this.providersForm.valueChanges.pipe( - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ).subscribe((value: TwoFactorAuthProviderConfigForm[]) => { const activeProvider = value.filter(provider => provider.enable); const indexBackupCode = Object.values(TwoFactorAuthProviderType).indexOf(TwoFactorAuthProviderType.BACKUP_CODE); @@ -161,6 +180,22 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI this.providersForm.at(indexBackupCode).get('enable').enable( {emitEvent: false}); } }); + this.twoFaFormGroup.get('enforceTwoFa').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => { + if (value) { + this.twoFaFormGroup.get('enforcedUsersFilter').enable({emitEvent: false}); + } else { + this.twoFaFormGroup.get('enforcedUsersFilter').disable({emitEvent: false}); + } + }); + } + + get atListOneProvider():boolean { + if (this.twoFaFormGroup.get('enforceTwoFa').value) { + return this.providersForm.value.some(value => value.enable); + } + return true; } private setAuthConfigFormValue(settings: TwoFactorAuthSettings) { @@ -172,19 +207,24 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI verificationCodeCheckRateLimitTime: checkRateLimitTime || 900, providers: [] }); + if (settings?.enforceTwoFa) { + this.getByIndexPanel(0).open(); + } if (checkRateLimitNumber > 0) { - this.getByIndexPanel(this.providersForm.length).open(); + this.getByIndexPanel(this.providersForm.length+1).open(); } Object.values(TwoFactorAuthProviderType).forEach((provider, index) => { const findIndex = allowProvidersConfig.indexOf(provider); if (findIndex > -1) { processFormValue.providers.push(Object.assign(settings.providers[findIndex], {enable: true})); - this.getByIndexPanel(index).open(); + this.getByIndexPanel(index+1).open(); } else { processFormValue.providers.push({enable: false}); } }); this.twoFaFormGroup.patchValue(processFormValue); + this.filterByTenants = isDefined(this.filterByTenants) ? this.filterByTenants : !Array.isArray(settings?.enforcedUsersFilter?.tenantProfilesIds); + this.twoFaFormGroup.get('enforcedUsersFilter.filterByTenants').patchValue(this.filterByTenants, {onlySelf: true}); } private buildProvidersSettingsForm(provider: TwoFactorAuthProviderType) { @@ -212,7 +252,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI } const newProviders = this.fb.group(formControlConfig); newProviders.get('enable').valueChanges.pipe( - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ).subscribe(value => { if (value) { newProviders.enable({emitEvent: false}); @@ -245,4 +285,12 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI delete processFormValue[`${property}Number`]; delete processFormValue[`${property}Time`]; } + + private allowNotificationTargetConfigTypes(): NotificationTargetConfigType[] { + return [ + NotificationTargetConfigType.ALL_USERS, + NotificationTargetConfigType.TENANT_ADMINISTRATORS, + NotificationTargetConfigType.SYSTEM_ADMINISTRATORS + ]; + } } diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts index 4e220dd53c..b0606b9c5b 100644 --- a/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts @@ -14,39 +14,130 @@ /// limitations under the License. /// -import { Injectable, NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { DestroyRef, inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; -import { Observable } from 'rxjs'; -import { OAuth2Service } from '@core/http/oauth2.service'; import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component'; import { AlarmsMode } from '@shared/models/alarm.models'; import { MenuId } from '@core/services/menu.models'; +import { RouterTabsComponent } from "@home/components/router-tabs.component"; +import { AlarmRulesTableComponent } from "@home/components/alarm-rules/alarm-rules-table.component"; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DatePipe } from '@angular/common'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; +import { UtilsService } from '@core/services/utils.service'; +import { AlarmRulesTableConfig } from '@home/components/alarm-rules/alarm-rules-table-config'; -@Injectable() -export class OAuth2LoginProcessingUrlResolver { - - constructor(private oauth2Service: OAuth2Service) { - } - - resolve(): Observable { - return this.oauth2Service.getLoginProcessingUrl(); - } -} +export const AlarmRulesTableConfigResolver: ResolveFn = + (_route: ActivatedRouteSnapshot, + _state: RouterStateSnapshot, + calculatedFieldsService = inject(CalculatedFieldsService), + translate = inject(TranslateService), + dialog = inject(MatDialog), + store = inject(Store), + datePipe = inject(DatePipe), + destroyRef = inject(DestroyRef), + importExportService = inject(ImportExportService), + entityDebugSettingsService = inject(EntityDebugSettingsService), + utilsService = inject(UtilsService), + router = inject(Router), + ) => { + return new AlarmRulesTableConfig( + calculatedFieldsService, + translate, + dialog, + datePipe, + null, + store, + destroyRef, + null, + null, + null, + importExportService, + entityDebugSettingsService, + utilsService, + router, + true, + ); + }; const routes: Routes = [ { path: 'alarms', - component: AlarmTableComponent, + component: RouterTabsComponent, data: { auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'alarm.alarms', breadcrumb: { - menuId: MenuId.alarms + menuId: MenuId.alarms_center + } + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + redirectTo: '/alarms/alarms' + } + }, + { + path: 'alarms', + component: AlarmTableComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'alarm.alarms', + breadcrumb: { + menuId: MenuId.alarms + }, + isPage: true, + alarmsMode: AlarmsMode.ALL + } }, - isPage: true, - alarmsMode: AlarmsMode.ALL - } + { + path: 'alarm-rules', + data: { + breadcrumb: { + menuId: MenuId.alarm_rules + }, + }, + children: [ + { + path: '', + component: AlarmRulesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'alarm-rule.alarm-rules', + isPage: true, + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'tune' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN], + title: 'entity.type-calculated-fields', + }, + resolve: { + entitiesTableConfig: AlarmRulesTableConfigResolver + } + } + ] + } + ] } ]; diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html new file mode 100644 index 0000000000..9b38e2bc59 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html @@ -0,0 +1,30 @@ + +@if (entity) { + + + +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts new file mode 100644 index 0000000000..003c1f3b71 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts @@ -0,0 +1,49 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models'; +import type { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { debugCfActionEnabled } from '@shared/models/calculated-field.models'; + +@Component({ + selector: 'tb-alarm-rules-tabs', + templateUrl: './alarm-rules-tabs.component.html', + styleUrls: [] +}) +export class AlarmRulesTabsComponent extends EntityTabsComponent { + + readonly DebugEventType = DebugEventType; + readonly EventType = EventType; + + constructor() { + super(); + } + + get debugActionDisabled(): boolean { + return !debugCfActionEnabled(this.entity); + }; + + onDebugEventSelected(event: CalculatedFieldEventBody) { + (this.entitiesTableConfig as CalculatedFieldsTableConfig).getTestScriptDialog(this.entity, JSON.parse(event.arguments)) + .subscribe((expression) => { + }); + }; +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts index 9438e743d1..4e77b8f5ff 100644 --- a/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts @@ -20,9 +20,12 @@ import { SharedModule } from '@shared/shared.module'; import { HomeDialogsModule } from '../../dialogs/home-dialogs.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { AlarmRoutingModule } from '@home/pages/alarm/alarm-routing.module'; +import { AlarmRulesTabsComponent } from '@home/pages/alarm/alarm-rules-tabs.component'; @NgModule({ - declarations: [], + declarations: [ + AlarmRulesTabsComponent + ], imports: [ CommonModule, SharedModule, diff --git a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-tabs.component.html b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-tabs.component.html index 3218330929..c14adbf8be 100644 --- a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-tabs.component.html @@ -15,17 +15,29 @@ limitations under the License. --> - - - - - - - - - +@if (entity && !isEdit) { + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + } + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + + } +} diff --git a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile.module.ts b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile.module.ts index 2204850bc5..e7c96b68a8 100644 --- a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile.module.ts +++ b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile.module.ts @@ -29,7 +29,7 @@ import { AssetProfileRoutingModule } from './asset-profile-routing.module'; CommonModule, SharedModule, HomeComponentsModule, - AssetProfileRoutingModule + AssetProfileRoutingModule, ] }) export class AssetProfileModule { } diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html b/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html index c3b71437dd..a50bd2a9a7 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html @@ -15,47 +15,52 @@ limitations under the License. --> - - - - - - - - - - - - - - - - - - - - - - - - - - +@if (entity) { + + + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + + + + } + + + + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + + + + + } +} diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.ts index 47509bf309..dace342fab 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.ts @@ -19,6 +19,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; import { AssetInfo } from '@app/shared/models/asset.models'; +import { EntityId } from "@shared/models/id/entity-id"; @Component({ selector: 'tb-asset-tabs', @@ -27,6 +28,8 @@ import { AssetInfo } from '@app/shared/models/asset.models'; }) export class AssetTabsComponent extends EntityTabsComponent { + ownerId: EntityId; + constructor(protected store: Store) { super(store); } @@ -35,4 +38,9 @@ export class AssetTabsComponent extends EntityTabsComponent { super.ngOnInit(); } + protected setEntity(entity: AssetInfo) { + this.ownerId = entity.customerId.id !== this.nullUid ? entity.customerId : entity.tenantId; + super.setEntity(entity); + } + } diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset.module.ts b/ui-ngx/src/app/modules/home/pages/asset/asset.module.ts index d6dcf2b0b4..ec85d828ac 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset.module.ts +++ b/ui-ngx/src/app/modules/home/pages/asset/asset.module.ts @@ -35,7 +35,7 @@ import { AssetTabsComponent } from '@home/pages/asset/asset-tabs.component'; SharedModule, HomeComponentsModule, HomeDialogsModule, - AssetRoutingModule + AssetRoutingModule, ] }) export class AssetModule { } diff --git a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log.module.ts b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log.module.ts index 8670592897..502014d798 100644 --- a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log.module.ts +++ b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log.module.ts @@ -20,8 +20,7 @@ import { SharedModule } from '@shared/shared.module'; import { AuditLogRoutingModule } from '@modules/home/pages/audit-log/audit-log-routing.module'; @NgModule({ - declarations: [ - ], + declarations: [], imports: [ CommonModule, SharedModule, diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-field-page.module.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-field-page.module.ts new file mode 100644 index 0000000000..c9db9860fa --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-field-page.module.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { HomeDialogsModule } from '../../dialogs/home-dialogs.module'; +import { HomeComponentsModule } from '@modules/home/components/home-components.module'; +import { CalculatedFieldsRoutingModule } from '@home/pages/calculated-fields/calculated-fields-routing.module'; +import { CalculatedFieldsModule } from '@home/components/calculated-fields/calculated-field.module'; +import { CalculatedFieldsTabsComponent } from '@home/pages/calculated-fields/calculated-fields-tabs.component'; + +@NgModule({ + declarations: [ + CalculatedFieldsTabsComponent + ], + imports: [ + CommonModule, + SharedModule, + HomeComponentsModule, + HomeDialogsModule, + CalculatedFieldsRoutingModule, + ] +}) +export class CalculatedFieldPageModule { } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts new file mode 100644 index 0000000000..20fe941cb1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts @@ -0,0 +1,113 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { DestroyRef, inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; +import { Authority } from '@shared/models/authority.enum'; +import { MenuId } from '@core/services/menu.models'; +import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DatePipe } from '@angular/common'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; +import { UtilsService } from '@core/services/utils.service'; + +export const CalculatedFieldsTableConfigResolver: ResolveFn = + (_route: ActivatedRouteSnapshot, + _state: RouterStateSnapshot, + calculatedFieldsService = inject(CalculatedFieldsService), + translate = inject(TranslateService), + dialog = inject(MatDialog), + store = inject(Store), + datePipe = inject(DatePipe), + destroyRef = inject(DestroyRef), + importExportService = inject(ImportExportService), + entityDebugSettingsService = inject(EntityDebugSettingsService), + utilsService = inject(UtilsService), + router = inject(Router), + ) => { + return new CalculatedFieldsTableConfig( + calculatedFieldsService, + translate, + dialog, + datePipe, + null, + store, + destroyRef, + null, + null, + null, + importExportService, + entityDebugSettingsService, + utilsService, + router, + true, + ); + }; + +const routes: Routes = [ + { + path: 'calculatedFields', + data: { + breadcrumb: { + menuId: MenuId.calculated_fields + } + }, + children: [ + { + path: '', + component: CalculatedFieldsTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'entity.type-calculated-fields', + isPage: true, + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'mdi:function-variant' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN], + title: 'entity.type-calculated-fields', + }, + resolve: { + entitiesTableConfig: CalculatedFieldsTableConfigResolver + } + } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [] +}) +export class CalculatedFieldsRoutingModule { } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.html b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.html new file mode 100644 index 0000000000..9b38e2bc59 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.html @@ -0,0 +1,30 @@ + +@if (entity) { + + + +} diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts new file mode 100644 index 0000000000..acaacef7a7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts @@ -0,0 +1,57 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models'; +import type { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { debugCfActionEnabled } from '@shared/models/calculated-field.models'; + +@Component({ + selector: 'tb-calculated-fields-tabs', + templateUrl: './calculated-fields-tabs.component.html', + styleUrls: [] +}) +export class CalculatedFieldsTabsComponent extends EntityTabsComponent { + + readonly DebugEventType = DebugEventType; + readonly EventType = EventType; + + constructor() { + super(); + } + + get debugActionDisabled(): boolean { + return !debugCfActionEnabled(this.entity); + }; + + onDebugEventSelected(event: CalculatedFieldEventBody) { + (this.entitiesTableConfig as CalculatedFieldsTableConfig).getTestScriptDialog(this.entity, JSON.parse(event.arguments), false) + .subscribe((expression) => { + (this.entitiesTableConfig as CalculatedFieldsTableConfig).getTable(); + const entityDetailsPanel = this.entitiesTableConfig.getTable().entityDetailsPanel; + entityDetailsPanel.onToggleEditMode(true); + entityDetailsPanel.selectedTab = 0; + setTimeout(() => { + entityDetailsPanel.detailsForm.get('configuration').setValue({...this.entity.configuration, expression}); + entityDetailsPanel.detailsForm.get('configuration').markAsDirty(); + }); + }); + }; +} diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html b/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html index 8f9dc70b06..1c0bb7322b 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html @@ -15,43 +15,57 @@ limitations under the License. --> - - - - - - - - - - - - - - - - - - - - - - - +@if (entity) { + + + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + } + + + + + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + + + + + + } +} 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 4e6c5a6ba2..66d4b4c7b6 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 @@ -28,6 +28,12 @@ [class.!hidden]="isEdit || dashboardScope !== 'tenant'"> {{'dashboard.export' | translate }} + + + + +
+
+ + +
+ +
+ + +
+ diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/import-dashboard-file-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/dashboard/import-dashboard-file-dialog.component.ts new file mode 100644 index 0000000000..d5ea2a9849 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/dashboard/import-dashboard-file-dialog.component.ts @@ -0,0 +1,89 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { FormBuilder, FormGroup } from '@angular/forms'; +import { DashboardService } from '@core/http/dashboard.service'; +import { Dashboard } from '@app/shared/models/dashboard.models'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; + +export interface DashboardInfoDialogData { + dashboard: Dashboard; +} + +@Component({ + selector: 'tb-import-dashboard-file-dialog', + templateUrl: './import-dashboard-file-dialog.component.html', + styleUrls: [] +}) +export class ImportDashboardFileDialogComponent extends DialogComponent implements OnInit { + + private dashboard: Dashboard; + currentFileName: string = ''; + uploadFileFormGroup: FormGroup; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: DashboardInfoDialogData, + private dashboardService: DashboardService, + protected dialogRef: MatDialogRef, + private fb: FormBuilder) { + super(store, router, dialogRef); + this.dashboard = data.dashboard; + } + + ngOnInit(): void { + this.uploadFileFormGroup = this.fb.group({ + file: [null] + }); + } + + cancel(): void { + this.dialogRef.close(); + } + + save() { + const fileControl = this.uploadFileFormGroup.get('file'); + if (!fileControl || !fileControl.value) { + return; + } + + const dashboardContent = { + ...fileControl.value, + description: this.dashboard.configuration.description + }; + this.dashboard.configuration = dashboardContent; + + this.dashboardService.saveDashboard(this.dashboard).subscribe(() => { + this.dialogRef.close(true); + }) + } + + loadDataFromJsonContent(content: string): any { + try { + const importData = JSON.parse(content); + return importData ? importData['configuration'] : importData; + } catch (err) { + this.store.dispatch(new ActionNotificationShow({message: err.message, type: 'error'})); + return null; + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html index ebbab8f20f..69dd15b60f 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html @@ -15,54 +15,92 @@ limitations under the License. --> - -
- - device-profile.transport-type - - - {{deviceTransportTypeTranslations.get(type) | translate}} - - - - {{deviceTransportTypeHints.get(detailsForm.get('transportType').value) | translate}} - - - {{ 'device-profile.transport-type-required' | translate }} - - -
- - -
-
-
- - - - -
-
- +@if (entity) { + +
+ + device-profile.transport-type + + + {{deviceTransportTypeTranslations.get(type) | translate}} + + + + {{deviceTransportTypeHints.get(detailsForm.get('transportType').value) | translate}} + + + {{ 'device-profile.transport-type-required' | translate }} + + +
+ + +
-
- - -
-
- - + + @if (authUser.authority === authorities.TENANT_ADMIN && !isEdit) { + + + + } + + @if (hasOldRules || authUser.authority === authorities.TENANT_ADMIN && !isEdit) { + +
+ @if (hasOldRules && !isEdit) { +
+ + {{ 'alarm-rule.alarm-rules-actual' | translate }} + {{ 'alarm-rule.alarm-rules-old' | translate }} + +
+ } + @if (alarmRulesOldVersion || isEdit) { +
+
+ +
+
+ } @else { +
+ + +
+ } +
+
+ } + + +
+
+ + +
-
- + + @if (!isEdit) { + + + + } + @if (authUser.authority === authorities.TENANT_ADMIN && !isEdit) { + + + + + } +}
@@ -73,13 +111,3 @@
- - - - - - diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts index e7984ee009..1aadb36778 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts @@ -41,6 +41,9 @@ export class DeviceProfileTabsComponent extends EntityTabsComponent, private destroyRef: DestroyRef) { super(store); @@ -57,6 +60,8 @@ export class DeviceProfileTabsComponent extends EntityTabsComponent - - - - - - - - - - - - - - - - - - - - - - - - - - +@if (entity) { + + + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + + + + } + + + + + + + + + + + + + @if (authUser.authority === authorities.TENANT_ADMIN) { + + + + } +} diff --git a/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.ts index 8416be4d96..01e186612b 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.ts @@ -19,6 +19,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { DeviceInfo } from '@shared/models/device.models'; import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { EntityId } from "@shared/models/id/entity-id"; @Component({ selector: 'tb-device-tabs', @@ -27,6 +28,8 @@ import { EntityTabsComponent } from '../../components/entity/entity-tabs.compone }) export class DeviceTabsComponent extends EntityTabsComponent { + ownerId: EntityId; + constructor(protected store: Store) { super(store); } @@ -35,4 +38,9 @@ export class DeviceTabsComponent extends EntityTabsComponent { super.ngOnInit(); } + protected setEntity(entity: DeviceInfo) { + this.ownerId = entity.customerId.id !== this.nullUid ? entity.customerId : entity.tenantId; + super.setEntity(entity); + } + } 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 84412fabf4..a4b7df8739 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 @@ -27,6 +27,7 @@ import { UserModule } from '@modules/home/pages/user/user.module'; import { DeviceModule } from '@modules/home/pages/device/device.module'; import { AssetModule } from '@modules/home/pages/asset/asset.module'; import { EntityViewModule } from '@modules/home/pages/entity-view/entity-view.module'; +import { CalculatedFieldPageModule } from '@home/pages/calculated-fields/calculated-field-page.module'; import { RuleChainModule } from '@modules/home/pages/rulechain/rulechain.module'; import { WidgetLibraryModule } from '@modules/home/pages/widget/widget-library.module'; import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module'; @@ -69,6 +70,7 @@ import { AiModelModule } from '@home/pages/ai-model/ai-model.module'; EdgeModule, EntityViewModule, CustomerModule, + CalculatedFieldPageModule, RuleChainModule, WidgetLibraryModule, DashboardModule, diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts index 51f087f13a..1ea70bc9f6 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts @@ -29,6 +29,7 @@ import { MobileAppService } from '@core/http/mobile-app.service'; export interface MobileAppDialogData { platformType: PlatformType; + name?: string } @Component({ @@ -55,6 +56,9 @@ export class MobileAppDialogComponent extends DialogComponent { this.mobileAppComponent.entityForm.markAsDirty(); + if (this.data.name) { + this.mobileAppComponent.entityForm.get('title').patchValue(this.data.name, {emitEvent: false}); + } this.mobileAppComponent.entityForm.patchValue({platformType: this.data.platformType}); this.mobileAppComponent.entityForm.get('platformType').disable({emitEvent: false}); this.mobileAppComponent.isEdit = true; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html index 9aa4fa60c6..1def8334ad 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html @@ -58,7 +58,7 @@ labelText="mobile.android-application" [entityType]="entityType.MOBILE_APP" [entitySubtype]="platformType.ANDROID" - (createNew)="createApplication('androidAppId', platformType.ANDROID)" + (createNew)="createApplication($event, 'androidAppId', platformType.ANDROID)" formControlName="androidAppId"> diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts index acc1d3785b..1f16673ef5 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts @@ -148,12 +148,13 @@ export class MobileBundleDialogComponent extends DialogComponent(MobileAppDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - platformType + platformType, + name } }).afterClosed() .subscribe((app) => { diff --git a/ui-ngx/src/app/modules/home/pages/mobile/qr-code-widget/mobile-qr-code-widget-settings.component.html b/ui-ngx/src/app/modules/home/pages/mobile/qr-code-widget/mobile-qr-code-widget-settings.component.html index 1a2e2d7b72..76d088a20c 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/qr-code-widget/mobile-qr-code-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/qr-code-widget/mobile-qr-code-widget-settings.component.html @@ -39,6 +39,7 @@
{{ 'mobile.bundle' | translate }}
{{ preview.processedTemplates.MICROSOFT_TEAMS.subject }}
{{ preview.processedTemplates.MICROSOFT_TEAMS.body }} - + @if (preview.processedTemplates.MICROSOFT_TEAMS.button?.enabled) { + + }
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html index 2caff5e475..77ec27dd9b 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html @@ -44,8 +44,8 @@ -
- +
+ notification.action-type - - notification.link - - - {{ 'notification.link-required' | translate }} - - - {{ 'notification.link-max-length' | translate : - {length: actionButtonConfigForm.get('link').getError('maxlength').requiredLength} - }} - - - - + @if(actionButtonConfigForm.get('linkType').value === actionButtonLinkType.LINK) { + + notification.link + + + {{ 'notification.link-required' | translate }} + + + {{ 'notification.link-max-length' | translate : + {length: actionButtonConfigForm.get('link').getError('maxlength').requiredLength} + }} + + + } @else { + - - + }
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 14201b3301..96aa8ba97a 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 @@ -181,6 +181,7 @@ export class TemplateNotificationDialogComponent private allowNotificationType(): NotificationType[] { const sysAdminAllowNotificationTypes = new Set([ NotificationType.ENTITIES_LIMIT, + NotificationType.ENTITIES_LIMIT_INCREASE_REQUEST, NotificationType.API_USAGE_LIMIT, NotificationType.NEW_PLATFORM_VERSION, NotificationType.RATE_LIMITS, 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 50724289e2..774edbd184 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 @@ -58,12 +58,14 @@ [enableFlagsSelect]="true" formControlName="phone"> - + language.language - - - {{ lang ? ('language.locales.' + lang | translate) : ''}} - + + {{ 'language.auto' | translate }} + @for(lang of languageList; track lang) { + {{ 'language.locales.' + lang | 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 fc35321d9a..3ab555a9e2 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 @@ -25,7 +25,6 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; import { ActionAuthUpdateUserDetails } from '@core/auth/auth.actions'; import { environment as env } from '@env/environment'; -import { TranslateService } from '@ngx-translate/core'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { ActivatedRoute } from '@angular/router'; import { isDefinedAndNotNull, isNotEmptyStr, validateEmail } from '@core/utils'; @@ -52,7 +51,6 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir private route: ActivatedRoute, private userService: UserService, private authService: AuthService, - private translate: TranslateService, private unitService: UnitService, private fb: UntypedFormBuilder) { super(store); @@ -82,9 +80,13 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir if (!this.user.additionalInfo) { this.user.additionalInfo = {}; } - this.user.additionalInfo.lang = this.profile.get('language').value; this.user.additionalInfo.homeDashboardId = this.profile.get('homeDashboardId').value; this.user.additionalInfo.homeDashboardHideToolbar = this.profile.get('homeDashboardHideToolbar').value; + if (isNotEmptyStr(this.profile.get('language').value)) { + this.user.additionalInfo.lang = this.profile.get('language').value; + } else { + delete this.user.additionalInfo.lang; + } if (isNotEmptyStr(this.profile.get('unitSystem').value)) { this.user.additionalInfo.unitSystem = this.profile.get('unitSystem').value; } else { @@ -105,7 +107,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir id: user.id, lastName: user.lastName, } })); - this.store.dispatch(new ActionSettingsChangeLanguage({ userLang: user.additionalInfo.lang })); + this.store.dispatch(new ActionSettingsChangeLanguage({ userLang: user.additionalInfo.lang || env.defaultLang })); this.unitService.setUnitSystem(this.user.additionalInfo.unitSystem); this.authService.refreshJwtToken(false); } @@ -115,7 +117,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir private userLoaded(user: User) { this.user = user; this.profile.reset(user); - let lang; + let lang: string = null; let homeDashboardId; let homeDashboardHideToolbar = true; let unitSystem: UnitSystem = null; @@ -131,9 +133,6 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir unitSystem = user.additionalInfo.unitSystem; } } - if (!lang) { - lang = this.translate.currentLang; - } this.profile.get('language').setValue(lang); this.profile.get('unitSystem').setValue(unitSystem); this.profile.get('homeDashboardId').setValue(homeDashboardId); 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 b605a469cd..6afce790b6 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 @@ -36,6 +36,7 @@
(); 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 7f204aac2b..9d855647fa 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 @@ -106,6 +106,7 @@ (EventsDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + title: 'rulenode.events', + debugEventTypes: [DebugEventType.DEBUG_RULE_NODE], + defaultEventType: DebugEventType.DEBUG_RULE_NODE, + tenantId: this.ruleChain.tenantId.id, + entityId: this.editingRuleNode.ruleNodeId, + functionTestButtonLabel: this.ruleNodeTestButtonLabel, + onDebugEventSelected: this.onDebugEventSelected.bind(this) + } + }) + .afterClosed() + .subscribe(); + } + private updateNodeErrorTooltip(node: FcRuleNode) { if (node.error) { const element = $('#' + node.id); diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html index 4da693e331..e68e0fc08a 100644 --- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.html @@ -52,6 +52,19 @@

security.2fa.dialog.scan-qr-code

+

login.enter-key-manually

+
+ {{ totpAuthURLSecret }} + + +

security.2fa.dialog.enter-verification-code

; @@ -55,6 +56,7 @@ export class TotpAuthDialogComponent extends DialogComponent { this.authAccountConfig = accountConfig as TotpTwoFactorAuthAccountConfig; this.totpAuthURL = this.authAccountConfig.authUrl; + this.totpAuthURLSecret = new URL(this.totpAuthURL).searchParams.get('secret'); this.authAccountConfig.useByDefault = true; import('qrcode').then((QRCode) => { unwrapModule(QRCode).toCanvas(this.canvasRef.nativeElement, this.totpAuthURL); diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.html b/ui-ngx/src/app/modules/home/pages/security/security.component.html index f2bdd657a8..4fca803187 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.html @@ -30,6 +30,20 @@
+ +
+ + api-key.api-keys + + +
+
@@ -52,13 +66,13 @@ + && !changePassword.get('newPassword').hasError('passwordSameAsOld')"> {{ 'security.password-requirement.password-not-meet-requirements' | translate }} {{ changePassword.get('newPassword').getError('alreadyUsed') }} - + {{ 'security.password-requirement.password-should-difference' | translate }} @@ -72,7 +86,7 @@ login.new-password-again - + {{ 'security.password-requirement.new-passwords-not-match' | translate }} @@ -134,7 +148,7 @@
diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index d5d671e905..25eb42e673 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -21,9 +21,9 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, + FormGroupDirective, UntypedFormBuilder, - UntypedFormGroup, FormGroupDirective, - NgForm, + UntypedFormGroup, ValidationErrors, ValidatorFn, Validators @@ -48,10 +48,15 @@ import { import { authenticationDialogMap } from '@home/pages/security/authentication-dialog/authentication-dialog.map'; import { takeUntil, tap } from 'rxjs/operators'; import { Observable, of, Subject } from 'rxjs'; -import { isDefinedAndNotNull, isEqual } from '@core/utils'; +import { isDefinedAndNotNull } from '@core/utils'; import { AuthService } from '@core/auth/auth.service'; import { UserPasswordPolicy } from '@shared/models/settings.models'; import { MatCheckboxChange } from '@angular/material/checkbox'; +import { + ApiKeysTableDialogComponent, + ApiKeysTableDialogData +} from '@home/components/api-key/api-keys-table-dialog.component'; +import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/models/password.models'; @Component({ selector: 'tb-security', @@ -164,7 +169,12 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro this.changePassword = this.fb.group({ currentPassword: [''], newPassword: ['', Validators.required], - newPassword2: ['', this.samePasswordValidation(false, 'newPassword')] + newPassword2: [''] + }, { + validators: [ + this.passwordNotSameAsOld(), + passwordsMatchValidator('newPassword', 'newPassword2'), + ] }); } @@ -172,64 +182,36 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro this.authService.getUserPasswordPolicy().subscribe(policy => { this.passwordPolicy = policy; this.changePassword.get('newPassword').setValidators([ - this.passwordStrengthValidator(), - this.samePasswordValidation(true, 'currentPassword'), + passwordStrengthValidator(this.passwordPolicy), Validators.required ]); this.changePassword.get('newPassword').updateValueAndValidity({emitEvent: false}); }); } - private passwordStrengthValidator(): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - const value: string = control.value; - const errors: any = {}; - - if (this.passwordPolicy.minimumUppercaseLetters > 0 && - !new RegExp(`(?:.*?[A-Z]){${this.passwordPolicy.minimumUppercaseLetters}}`).test(value)) { - errors.notUpperCase = true; - } - - if (this.passwordPolicy.minimumLowercaseLetters > 0 && - !new RegExp(`(?:.*?[a-z]){${this.passwordPolicy.minimumLowercaseLetters}}`).test(value)) { - errors.notLowerCase = true; - } - - if (this.passwordPolicy.minimumDigits > 0 - && !new RegExp(`(?:.*?\\d){${this.passwordPolicy.minimumDigits}}`).test(value)) { - errors.notNumeric = true; - } - - if (this.passwordPolicy.minimumSpecialCharacters > 0 && - !new RegExp(`(?:.*?[\\W_]){${this.passwordPolicy.minimumSpecialCharacters}}`).test(value)) { - errors.notSpecial = true; - } + passwordNotSameAsOld(): ValidatorFn { + return (group: AbstractControl): ValidationErrors | null => { + const currentPassControl = group.get('currentPassword'); + const newPassControl = group.get('newPassword'); - if (!this.passwordPolicy.allowWhitespaces && /\s/.test(value)) { - errors.hasWhitespaces = true; - } - if (this.passwordPolicy.minimumLength > 0 && value.length < this.passwordPolicy.minimumLength) { - errors.minLength = true; - } + const current = currentPassControl?.value ?? ''; + const newPass = newPassControl?.value ?? ''; - if (!value.length || this.passwordPolicy.maximumLength > 0 && value.length > this.passwordPolicy.maximumLength) { - errors.maxLength = true; - } - - return isEqual(errors, {}) ? null : errors; - }; - } - - private samePasswordValidation(isSame: boolean, key: string): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - const value: string = control.value; - const keyValue = control.parent?.value[key]; - - if (isSame) { - return value === keyValue ? {samePassword: true} : null; + if (current && newPass && current === newPass) { + newPassControl?.setErrors({ + ...newPassControl.errors, + passwordSameAsOld: true + }); + return { passwordSameAsOld: true }; + } else { + const currentErrors = newPassControl?.errors; + if (currentErrors?.passwordSameAsOld) { + const { passwordSameAsOld, ...rest } = currentErrors; + newPassControl.setErrors(Object.keys(rest).length ? rest : null); + } + return null; } - return value !== keyValue ? {differencePassword: true} : null; }; } @@ -351,22 +333,24 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro onChangePassword(form: FormGroupDirective): void { if (this.changePassword.valid) { this.authService.changePassword(this.changePassword.get('currentPassword').value, - this.changePassword.get('newPassword').value, {ignoreErrors: true}).subscribe(() => { - this.discardChanges(form); - }, - (error) => { - if (error.status === 400 && error.error.message === 'Current password doesn\'t match!') { - this.changePassword.get('currentPassword').setErrors({differencePassword: true}); - } else if (error.status === 400 && error.error.message.startsWith('Password must')) { - this.loadPasswordPolicy(); - } else if (error.status === 400 && error.error.message.startsWith('Password was already used')) { - this.changePassword.get('newPassword').setErrors({alreadyUsed: error.error.message}); - } else { - this.store.dispatch(new ActionNotificationShow({ - message: error.error.message, - type: 'error', - target: 'changePassword' - })); + this.changePassword.get('newPassword').value, {ignoreErrors: true}).subscribe({ + next: () => { + this.discardChanges(form); + }, + error: (error) => { + if (error.status === 400 && error.error.message === 'Current password doesn\'t match!') { + this.changePassword.get('currentPassword').setErrors({ differencePassword: true }); + } else if (error.status === 400 && error.error.message.startsWith('Password must')) { + this.loadPasswordPolicy(); + } else if (error.status === 400 && error.error.message.startsWith('Password was already used')) { + this.changePassword.get('newPassword').setErrors({ alreadyUsed: error.error.message }); + } else { + this.store.dispatch(new ActionNotificationShow({ + message: error.error.message, + type: 'error', + target: 'changePassword' + })); + } } }); } else { @@ -384,4 +368,15 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro newPassword2: '' }); } + + openApiKeysTable() { + this.dialog.open( + ApiKeysTableDialogComponent, { + disableClose: false, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + userId: this.user.id, + } + }).afterClosed().subscribe(); + } } diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html index 3e7a150552..dcad41d86a 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-tabs.component.html @@ -15,24 +15,20 @@ limitations under the License. --> - - - - - - - - - - - +@if (entity) { + + + + + + + + +} diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html index 2f2ee3e924..c2485dc7f7 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html @@ -15,33 +15,27 @@ limitations under the License. --> - - - - - - - - - - - - - - - - - +@if (entity) { + + + + + + + + + + + + + + +} diff --git a/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.html b/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.html index 636b56116d..9dee500758 100644 --- a/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.html @@ -31,7 +31,7 @@
- + user.activation-method diff --git a/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.ts index dddf388eb1..ed39e2b326 100644 --- a/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.ts @@ -84,6 +84,12 @@ export class AddUserDialogComponent extends DialogComponent { diff --git a/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.html b/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.html index 84a8c8dd19..97ece30b0c 100644 --- a/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.html @@ -40,3 +40,7 @@ label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> + + + diff --git a/ui-ngx/src/app/modules/home/pages/user/user.component.html b/ui-ngx/src/app/modules/home/pages/user/user.component.html index 1c3f13fcc2..00a0944043 100644 --- a/ui-ngx/src/app/modules/home/pages/user/user.component.html +++ b/ui-ngx/src/app/modules/home/pages/user/user.component.html @@ -74,7 +74,7 @@
- + user.email @@ -84,30 +84,52 @@ {{ 'user.email-required' | translate }} - + user.first-name - + user.last-name
- + + language.language + + {{ 'language.auto' | translate }} + @for(lang of languageList; track lang) { + {{ 'language.locales.' + lang | translate }} + } + + + + unit.unit-system + + {{ 'unit.unit-system-type.AUTO' | translate }} + @for(unit of UnitSystems; track unit) { + {{ 'unit.unit-system-type.' + unit | translate }} + } + + + user.description
-
+
-
+
{ authority = Authority; + languageList = env.supportedLangs; + UnitSystems = UnitSystems; loginAsUserEnabled$ = this.store.pipe( select(selectAuth), @@ -77,6 +81,8 @@ export class UserComponent extends EntityComponent{ additionalInfo: this.fb.group( { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], + lang: [entity && entity.additionalInfo ? entity.additionalInfo.lang : null], + unitSystem: [entity && entity.additionalInfo ? entity.additionalInfo.unitSystem : null], defaultDashboardId: [entity && entity.additionalInfo ? entity.additionalInfo.defaultDashboardId : null], defaultDashboardFullscreen: [entity && entity.additionalInfo ? entity.additionalInfo.defaultDashboardFullscreen : false], homeDashboardId: [entity && entity.additionalInfo ? entity.additionalInfo.homeDashboardId : null], @@ -94,6 +100,10 @@ export class UserComponent extends EntityComponent{ this.entityForm.patchValue({lastName: entity.lastName}); this.entityForm.patchValue({phone: entity.phone}); this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); + this.entityForm.patchValue({additionalInfo: + {lang: entity.additionalInfo ? entity.additionalInfo.lang : null}}); + this.entityForm.patchValue({additionalInfo: + {unitSystem: entity.additionalInfo ? entity.additionalInfo.unitSystem : null}}); this.entityForm.patchValue({additionalInfo: {defaultDashboardId: entity.additionalInfo ? entity.additionalInfo.defaultDashboardId : null}}); this.entityForm.patchValue({additionalInfo: diff --git a/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts index 05d4e6887e..81a7aa242e 100644 --- a/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts @@ -158,6 +158,12 @@ export class UsersTableConfigResolver { user.tenantId = new TenantId(this.tenantId); user.customerId = new CustomerId(this.customerId); user.authority = this.authority; + if (!user.additionalInfo.lang) { + delete user.additionalInfo.lang; + } + if (!user.additionalInfo.unitSystem) { + delete user.additionalInfo.unitSystem; + } return this.userService.saveUser(user); } diff --git a/ui-ngx/src/app/modules/login/login-routing.module.ts b/ui-ngx/src/app/modules/login/login-routing.module.ts index 18dca78ba2..4f11cc1e92 100644 --- a/ui-ngx/src/app/modules/login/login-routing.module.ts +++ b/ui-ngx/src/app/modules/login/login-routing.module.ts @@ -14,8 +14,8 @@ /// limitations under the License. /// -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { LoginComponent } from './pages/login/login.component'; import { AuthGuard } from '@core/guards/auth.guard'; @@ -25,6 +25,22 @@ import { CreatePasswordComponent } from '@modules/login/pages/login/create-passw import { TwoFactorAuthLoginComponent } from '@modules/login/pages/login/two-factor-auth-login.component'; import { Authority } from '@shared/models/authority.enum'; import { LinkExpiredComponent } from '@modules/login/pages/login/link-expired.component'; +import { ForceTwoFactorAuthLoginComponent } from '@modules/login/pages/login/force-two-factor-auth-login.component'; +import { of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { AuthService } from '@core/auth/auth.service'; +import { UserPasswordPolicy } from '@shared/models/settings.models'; + +const passwordPolicyResolver: ResolveFn = (route: ActivatedRouteSnapshot, + state: RouterStateSnapshot, + router = inject(Router), + authService = inject(AuthService)) => { + return authService.getUserPasswordPolicy({ignoreErrors: true}).pipe( + catchError(() => { + return of({} as UserPasswordPolicy); + }) + ); +}; const routes: Routes = [ { @@ -52,7 +68,10 @@ const routes: Routes = [ title: 'login.reset-password', module: 'public' }, - canActivate: [AuthGuard] + canActivate: [AuthGuard], + resolve: { + passwordPolicy: passwordPolicyResolver + } }, { path: 'login/resetExpiredPassword', @@ -62,7 +81,10 @@ const routes: Routes = [ module: 'public', expiredPassword: true }, - canActivate: [AuthGuard] + canActivate: [AuthGuard], + resolve: { + passwordPolicy: passwordPolicyResolver + } }, { path: 'login/createPassword', @@ -71,7 +93,10 @@ const routes: Routes = [ title: 'login.create-password', module: 'public' }, - canActivate: [AuthGuard] + canActivate: [AuthGuard], + resolve: { + passwordPolicy: passwordPolicyResolver + } }, { path: 'login/mfa', @@ -83,6 +108,16 @@ const routes: Routes = [ }, canActivate: [AuthGuard] }, + { + path: 'login/force-mfa', + component: ForceTwoFactorAuthLoginComponent, + data: { + title: 'login.two-factor-authentication', + auth: [Authority.MFA_CONFIGURATION_TOKEN], + module: 'public' + }, + canActivate: [AuthGuard] + }, { path: 'activationLinkExpired', component: LinkExpiredComponent, diff --git a/ui-ngx/src/app/modules/login/login.module.ts b/ui-ngx/src/app/modules/login/login.module.ts index 45d3c04c41..3cb9dbae0c 100644 --- a/ui-ngx/src/app/modules/login/login.module.ts +++ b/ui-ngx/src/app/modules/login/login.module.ts @@ -25,6 +25,7 @@ import { ResetPasswordComponent } from '@modules/login/pages/login/reset-passwor import { CreatePasswordComponent } from '@modules/login/pages/login/create-password.component'; import { TwoFactorAuthLoginComponent } from '@modules/login/pages/login/two-factor-auth-login.component'; import { LinkExpiredComponent } from '@modules/login/pages/login/link-expired.component'; +import { ForceTwoFactorAuthLoginComponent } from '@modules/login/pages/login/force-two-factor-auth-login.component'; @NgModule({ declarations: [ @@ -33,7 +34,8 @@ import { LinkExpiredComponent } from '@modules/login/pages/login/link-expired.co ResetPasswordComponent, CreatePasswordComponent, TwoFactorAuthLoginComponent, - LinkExpiredComponent + LinkExpiredComponent, + ForceTwoFactorAuthLoginComponent, ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/login/pages/login/create-password.component.html b/ui-ngx/src/app/modules/login/pages/login/create-password.component.html index 06a1e838af..703c710add 100644 --- a/ui-ngx/src/app/modules/login/pages/login/create-password.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/create-password.component.html @@ -15,45 +15,57 @@ limitations under the License. --> -
- - - - login.create-password - +
+ + + login.create-password - +
+ - - + + -
-
- - - common.password - - lock - - - - login.password-again - - lock - - -
- - -
-
-
+ + common.password + + lock + + + {{ 'security.password-requirement.password-not-meet-requirements' | translate }} + + + + login.password-again + + lock + + + {{ 'security.password-requirement.new-passwords-not-match' | translate }} + + +
+ + +
+ + diff --git a/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts b/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts index 27c72dd6f4..03103ae6c3 100644 --- a/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts @@ -14,61 +14,58 @@ /// limitations under the License. /// -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { AuthService } from '@core/auth/auth.service'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { PageComponent } from '@shared/components/page.component'; -import { UntypedFormBuilder } from '@angular/forms'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; -import { TranslateService } from '@ngx-translate/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; -import { Subscription } from 'rxjs'; +import { UserPasswordPolicy } from '@shared/models/settings.models'; +import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/models/password.models'; +import { finalize } from 'rxjs/operators'; +import { PageComponent } from '@shared/components/page.component'; @Component({ selector: 'tb-create-password', templateUrl: './create-password.component.html', - styleUrls: ['./create-password.component.scss'] + styleUrls: ['./password.component.scss'] }) -export class CreatePasswordComponent extends PageComponent implements OnInit, OnDestroy { +export class CreatePasswordComponent extends PageComponent { - activateToken = ''; - sub: Subscription; + passwordPolicy: UserPasswordPolicy; + createPassword: FormGroup; - createPassword = this.fb.group({ - password: [''], - password2: [''] - }); + isLoading = false; - constructor(protected store: Store, - private route: ActivatedRoute, + private activateToken: string; + + constructor(private route: ActivatedRoute, private authService: AuthService, - private translate: TranslateService, - public fb: UntypedFormBuilder) { - super(store); - } + private fb: FormBuilder) { + super(); + this.activateToken = this.route.snapshot.queryParams['activateToken'] || ''; + this.passwordPolicy = this.route.snapshot.data['passwordPolicy']; - ngOnInit() { - this.sub = this.route - .queryParams - .subscribe(params => { - this.activateToken = params.activateToken || ''; - }); + this.buildCreatePasswordForm(); } - ngOnDestroy(): void { - super.ngOnDestroy(); - this.sub.unsubscribe(); + private buildCreatePasswordForm() { + this.createPassword = this.fb.group({ + newPassword: ['', [Validators.required, passwordStrengthValidator(this.passwordPolicy)]], + newPassword2: [''] + }, { + validators: [ + passwordsMatchValidator('newPassword', 'newPassword2'), + ] + }); } onCreatePassword() { - if (this.createPassword.get('password').value !== this.createPassword.get('password2').value) { - this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('login.passwords-mismatch-error'), - type: 'error' })); + if (this.createPassword.invalid) { + this.createPassword.markAllAsTouched(); } else { - this.authService.activate( - this.activateToken, - this.createPassword.get('password').value, true).subscribe(); + this.isLoading = true; + this.authService.activate(this.activateToken, this.createPassword.get('newPassword').value, true).pipe( + finalize(() => {this.isLoading = false;}) + ).subscribe(() => {}); } } } diff --git a/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.html b/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.html new file mode 100644 index 0000000000..495d6fa0cb --- /dev/null +++ b/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.html @@ -0,0 +1,317 @@ + + + + + + + + + diff --git a/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.scss b/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.scss new file mode 100644 index 0000000000..bd37554922 --- /dev/null +++ b/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.scss @@ -0,0 +1,117 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + display: flex; + flex: 1 1 0; + width: 100%; + height: 100%; + + .tb-two-factor-auth-login-content { + background-color: #eee; + --mdc-elevated-card-container-elevation: none; + + .progress-bar { + z-index: 10; + margin-bottom: -4px; + width: 450px; + } + + .tb-two-factor-auth-login-card { + max-height: 100vh; + overflow: auto; + padding: 48px 48px 48px 16px; + + @media #{$mat-xs} { + height: 100%; + } + + @media #{$mat-gt-xs} { + width: 450px !important; + } + + .mat-mdc-card-title { + font: 400 28px / 36px Roboto, "Helvetica Neue", sans-serif; + } + + .mat-mdc-card-header { + padding: 0; + } + + .mat-mdc-card-content { + margin-top: 34px; + margin-left: 40px; + padding: 0; + } + + .mat-body { + letter-spacing: 0.25px; + line-height: 16px; + } + + .backup-code { + p { + text-align: justify; + } + + .container { + border: 1px solid; + border-radius: 4px; + gap: 16px; + display: grid; + grid-template-columns: 1fr 1fr; + justify-items: center; + padding: 16px 0; + margin-bottom: 16px; + + .code { + letter-spacing: 0.25px; + font-family: Roboto Mono, "Helvetica Neue", monospace; + } + } + + .action-buttons { + margin-bottom: 40px; + } + } + } + } + + ::ng-deep { + .tb-two-factor-auth-login-content { + .tb-two-factor-auth-login-card { + button.mat-mdc-icon-button { + .mat-icon { + color: rgba(255, 255, 255, 0.8); + } + } + } + .mat-mdc-form-field .mat-mdc-form-field-hint-wrapper { + color: rgba(255, 255, 255, 0.8); + } + } + + button.provider, button.navigation { + text-align: start; + font-weight: 400; + color: rgba(255, 255, 255, 0.8); + &:not([disabled][disabled]) { + border-color: rgba(255, 255, 255, .8); + } + } + } +} diff --git a/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.ts b/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.ts new file mode 100644 index 0000000000..c5e02c19da --- /dev/null +++ b/ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.ts @@ -0,0 +1,317 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; +import { AuthService } from '@core/auth/auth.service'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { PageComponent } from '@shared/components/page.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; +import { + AccountTwoFaSettings, + BackupCodeTwoFactorAuthAccountConfig, + TotpTwoFactorAuthAccountConfig, + TwoFactorAuthAccountConfig, + twoFactorAuthProvidersEnterCodeCardTranslate, + twoFactorAuthProvidersLoginData, + twoFactorAuthProvidersSuccessCardTranslate, + TwoFactorAuthProviderType +} from '@shared/models/two-factor-auth.models'; +import { phoneNumberPattern } from '@shared/models/settings.models'; +import { deepClone, isDefinedAndNotNull, unwrapModule } from '@core/utils'; +import { MatDialog } from '@angular/material/dialog'; +import { DialogService } from '@core/services/dialog.service'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import printTemplate from '@home/pages/security/authentication-dialog/backup-code-print-template.raw'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { mergeMap, tap } from 'rxjs/operators'; +import { ActionNotificationShow } from "@core/notification/notification.actions"; + +enum ForceTwoFAState { + SETUP = 'setup', + AUTHENTICATOR_APP = 'authenticatorApp', + SMS = 'sms', + EMAIL = 'email', + BACKUP_CODE = 'backupCode', +} + +enum ProvidersState { + INPUT = 'INPUT', + ENTER_CODE = 'ENTER_CODE', + SUCCESS = 'SUCCESS', +} + +enum BackupCodeState { + CODE = 'CODE', + SUCCESS = 'SUCCESS', +} + +@Component({ + selector: 'tb-force-two-factor-auth-login', + templateUrl: './force-two-factor-auth-login.component.html', + styleUrls: ['./force-two-factor-auth-login.component.scss'] +}) +export class ForceTwoFactorAuthLoginComponent extends PageComponent implements OnInit, OnDestroy { + + TwoFactorAuthProviderType = TwoFactorAuthProviderType; + providersData = twoFactorAuthProvidersLoginData; + allowProviders: TwoFactorAuthProviderType[] = []; + config: AccountTwoFaSettings; + + twoFactorAuthProvidersEnterCodeCardTranslate = twoFactorAuthProvidersEnterCodeCardTranslate; + twoFactorAuthProvidersSuccessCardTranslate = twoFactorAuthProvidersSuccessCardTranslate; + + ForceTwoFAState = ForceTwoFAState; + ProvidersState = ProvidersState; + BackupCodeState = BackupCodeState + + state = signal(ForceTwoFAState.SETUP); + appState = signal(ProvidersState.INPUT); + smsState = signal(ProvidersState.INPUT); + emailState = signal(ProvidersState.INPUT); + backupCodeState = signal(BackupCodeState.CODE); + + totpAuthURL: string; + totpAuthURLSecret: string; + backupCode: BackupCodeTwoFactorAuthAccountConfig; + + configForm: UntypedFormGroup; + smsConfigForm: UntypedFormGroup; + emailConfigForm: UntypedFormGroup; + + private providersInfo: TwoFactorAuthProviderType[]; + private authAccountConfig: TwoFactorAuthAccountConfig; + private useByDefault: boolean = true; + + @ViewChild('canvas', {static: false}) canvasRef: ElementRef; + + constructor(protected store: Store, + private authService: AuthService, + private twoFaService: TwoFactorAuthenticationService, + private importExportService: ImportExportService, + public dialog: MatDialog, + public dialogService: DialogService, + private fb: UntypedFormBuilder) { + super(store); + } + + ngOnInit() { + this.providersInfo = this.authService.forceTwoFactorAuthProviders; + this.allowedProviders(); + this.configForm = this.fb.group({ + verificationCode: ['', [ + Validators.required, + Validators.minLength(6), + Validators.maxLength(6), + Validators.pattern(/^\d*$/) + ]] + }); + + this.smsConfigForm = this.fb.group({ + phone: ['', [Validators.required, Validators.pattern(phoneNumberPattern)]] + }); + + this.emailConfigForm = this.fb.group({ + email: [getCurrentAuthUser(this.store).sub, [Validators.required, Validators.email]] + }); + + this.twoFaService.getAccountTwoFaSettings().subscribe(accountConfig => { + if (accountConfig) { + this.config = accountConfig; + this.useByDefault = false; + } + }); + } + + goBackByType(type: TwoFactorAuthProviderType) { + switch (type) { + case TwoFactorAuthProviderType.TOTP: + this.appState.set(ProvidersState.INPUT); + this.updateQRCode(); + break; + case TwoFactorAuthProviderType.SMS: + this.smsState.set(ProvidersState.INPUT); + break; + case TwoFactorAuthProviderType.EMAIL: + this.emailState.set(ProvidersState.INPUT); + break; + } + } + + get isAnyProviderAvailable() { + return this.config?.configs ? Object.keys(this.config?.configs)?.length < this.allowProviders?.length : true; + } + + private allowedProviders() { + if (isDefinedAndNotNull(this.config)) { + this.allowProviders = this.providersInfo; + } else { + this.allowProviders = this.providersInfo.filter(provider => provider !== TwoFactorAuthProviderType.BACKUP_CODE); + } + } + + updateState(type: TwoFactorAuthProviderType) { + switch (type) { + case TwoFactorAuthProviderType.TOTP: + this.state.set(ForceTwoFAState.AUTHENTICATOR_APP); + this.twoFaService.generateTwoFaAccountConfig(TwoFactorAuthProviderType.TOTP).subscribe(accountConfig => { + this.authAccountConfig = accountConfig as TotpTwoFactorAuthAccountConfig; + this.totpAuthURL = this.authAccountConfig.authUrl; + this.totpAuthURLSecret = new URL(this.totpAuthURL).searchParams.get('secret'); + this.authAccountConfig.useByDefault = this.useByDefault; + this.useByDefault = false; + this.updateQRCode(); + }); + break; + case TwoFactorAuthProviderType.SMS: + this.state.set(ForceTwoFAState.SMS); + break; + case TwoFactorAuthProviderType.EMAIL: + this.state.set(ForceTwoFAState.EMAIL); + break; + case TwoFactorAuthProviderType.BACKUP_CODE: + this.state.set(ForceTwoFAState.BACKUP_CODE); + this.twoFaService.generateTwoFaAccountConfig(TwoFactorAuthProviderType.BACKUP_CODE).pipe( + tap((data: BackupCodeTwoFactorAuthAccountConfig) => this.backupCode = data), + mergeMap(data => this.twoFaService.verifyAndSaveTwoFaAccountConfig(data, null, {ignoreLoading: true})) + ).subscribe((config) => { + this.config = config; + }); + break; + } + } + + sendSmsCode() { + if (this.smsConfigForm.valid) { + this.authAccountConfig = { + providerType: TwoFactorAuthProviderType.SMS, + useByDefault: this.useByDefault, + phoneNumber: this.smsConfigForm.get('phone').value as string + }; + this.useByDefault = false; + this.twoFaService.submitTwoFaAccountConfig(this.authAccountConfig).subscribe(() => this.smsState.set(ProvidersState.ENTER_CODE)); + } + } + + sendEmailCode() { + if (this.emailConfigForm.valid) { + this.authAccountConfig = { + providerType: TwoFactorAuthProviderType.EMAIL, + useByDefault: this.useByDefault, + email: this.emailConfigForm.get('email').value as string + }; + this.useByDefault = false; + this.twoFaService.submitTwoFaAccountConfig(this.authAccountConfig).subscribe(() => this.emailState.set(ProvidersState.ENTER_CODE)); + } + } + + tryAnotherWay(type: TwoFactorAuthProviderType) { + this.state.set(ForceTwoFAState.SETUP); + this.configForm.reset(); + switch (type) { + case TwoFactorAuthProviderType.TOTP: + this.appState.set(ProvidersState.INPUT); + break; + case TwoFactorAuthProviderType.SMS: + this.smsState.set(ProvidersState.INPUT); + this.smsConfigForm.reset(); + break; + case TwoFactorAuthProviderType.EMAIL: + this.emailState.set(ProvidersState.INPUT) + this.emailConfigForm.get('email').reset(getCurrentAuthUser(this.store).sub); + break; + } + } + + saveConfig(type: TwoFactorAuthProviderType) { + if (this.configForm.valid) { + this.twoFaService.verifyAndSaveTwoFaAccountConfig(this.authAccountConfig, + this.configForm.get('verificationCode').value).subscribe({ + next: (config) => { + switch (type) { + case TwoFactorAuthProviderType.TOTP: + this.appState.set(ProvidersState.SUCCESS); + break; + case TwoFactorAuthProviderType.SMS: + this.smsState.set(ProvidersState.SUCCESS); + break; + case TwoFactorAuthProviderType.EMAIL: + this.emailState.set(ProvidersState.SUCCESS); + break; + } + this.config = config; + this.authAccountConfig = null; + this.allowedProviders(); + }, + error: error => { + if (error.status === 400) { + this.configForm.get('verificationCode').setErrors({incorrectCode: true}); + } else if (error.status === 429) { + this.configForm.get('verificationCode').setErrors({tooManyRequest: true}); + } else { + this.store.dispatch(new ActionNotificationShow({ + message: error.error.message, + type: 'error', + verticalPosition: 'top', + horizontalPosition: 'left' + })); + } + } + }) + } + } + + private updateQRCode() { + import('qrcode').then((QRCode) => { + unwrapModule(QRCode).toCanvas(this.canvasRef.nativeElement, this.totpAuthURL); + this.canvasRef.nativeElement.style.width = 'auto'; + this.canvasRef.nativeElement.style.height = 'auto'; + }); + } + + ngOnDestroy() { + super.ngOnDestroy(); + } + + cancelLogin() { + this.authService.logout(); + } + + downloadFile() { + this.importExportService.exportText(this.backupCode.codes, 'backup-codes'); + } + + printCode() { + const codeTemplate = deepClone(this.backupCode.codes) + .map(code => `
${code}
`).join(''); + const printPage = printTemplate.replace('${codesBlock}', codeTemplate); + const newWindow = window.open('', 'Print backup code'); + + newWindow.document.open(); + newWindow.document.write(printPage); + + setTimeout(() => { + newWindow.print(); + + newWindow.document.close(); + + setTimeout(() => { + newWindow.close(); + }, 10); + }, 0); + } +} diff --git a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html index 36484d0fdb..15eb084094 100644 --- a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html @@ -23,16 +23,11 @@ {{ title | translate }}
- - -
{{ message | translate }}
- diff --git a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.scss b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.scss index e8db9286b4..3186eefbd5 100644 --- a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.scss +++ b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.scss @@ -20,6 +20,7 @@ flex: 1 1 0; .tb-expired-link-content { background-color: #eee; + --mdc-elevated-card-container-elevation: none; .tb-expired-link-card { letter-spacing: 0.15px; line-height: 24px; diff --git a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.ts b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.ts index 0278dbb232..5bc00c565e 100644 --- a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.ts @@ -15,33 +15,23 @@ /// import { Component } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { PageComponent } from '@shared/components/page.component'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'tb-link-expired', templateUrl: './link-expired.component.html', styleUrls: ['./link-expired.component.scss'] }) -export class LinkExpiredComponent extends PageComponent { +export class LinkExpiredComponent { isPasswordLinkExpired: boolean; title: string; message: string; - constructor(protected store: Store, - private route: ActivatedRoute, - private router: Router) { - super(store); + constructor(private route: ActivatedRoute) { this.isPasswordLinkExpired = this.route.snapshot.data.passwordLinkExpired; this.title = this.isPasswordLinkExpired ? 'login.reset-password-link-expired' : 'login.activation-link-expired'; this.message = this.isPasswordLinkExpired ? 'login.reset-password-link-expired-message' : 'login.activation-link-expired-message'; } - - navigateToLoginPage() { - this.router.navigateByUrl('login'); - } } diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.html b/ui-ngx/src/app/modules/login/pages/login/login.component.html index 8c8c32b2e7..39fd731fea 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.html @@ -17,55 +17,67 @@ -->
diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.scss b/ui-ngx/src/app/modules/login/pages/login/login.component.scss index 5719f58889..bf39ab93aa 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.scss +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.scss @@ -23,45 +23,49 @@ margin-top: 36px; margin-bottom: 76px; background-color: #eee; + --mdc-elevated-card-container-elevation: none; .tb-login-form { @media #{$mat-gt-xs} { - width: 550px !important; + width: 480px !important; } .forgot-password { - padding: 0 0.5em 1em; - .tb-reset-password { - padding: 0 6px; - } + --mat-text-button-horizontal-padding: 0; + --mdc-text-button-container-height: 20px; } .tb-action-button{ - padding: 20px 0 16px; + margin: 20px 0 16px; } } - .oauth-container{ - padding: 0; - - .container-divider { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - width: 100%; + .container-divider { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 100%; + margin-bottom: 16px; - .line { - flex: 1; - } + .mat-divider-horizontal{ + --mat-divider-color: var(--mdc-outlined-text-field-outline-color); + } - .mat-divider-horizontal{ - position: relative; - } + .text { + padding-right: 8px; + padding-left: 8px; + font-size: 16px; + line-height: 24px; + } + } - .text { - padding-right: 10px; - padding-left: 10px; - } + .oauth-container{ + .title { + font-size: 20px; + line-height: 24px; + letter-spacing: .1px; + text-align: center; + margin-bottom: 4px; } .material-icons{ @@ -71,15 +75,33 @@ a.login-with-button { color: rgba(black, 0.87); - background-color: map-get($tb-dark-theme-background, raised-button); + background-color: #ffffff; + } + + .login-button-container { + a.login-with-button { + max-width: 170px; + min-width: 60px; + flex-grow: 1; + flex-basis: 130px; + + .tb-mat-20 { + margin-right: 12px; + } + } - &:hover { - border-bottom: 0; + &:has(> :nth-child(1):last-child) { + a.login-with-button { + max-width: 100%; + } } - .icon { - height: 20px; - width: 20px; + &:has(> :nth-child(2):last-child), + &:has(> :nth-child(4):last-child) { + a.login-with-button { + max-width: 100%; + flex-basis: 180px; + } } } } 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 51d85eb69b..e49309298d 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 @@ -16,15 +16,14 @@ import { Component, OnInit } from '@angular/core'; import { AuthService } from '@core/auth/auth.service'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { PageComponent } from '@shared/components/page.component'; import { UntypedFormBuilder, Validators } from '@angular/forms'; import { HttpErrorResponse } from '@angular/common/http'; import { Constants } from '@shared/models/constants'; import { Router } from '@angular/router'; import { OAuth2ClientLoginInfo } from '@shared/models/oauth2.models'; import { validateEmail } from '@app/core/utils'; +import { PageComponent } from '@shared/components/page.component'; +import { finalize } from 'rxjs/operators'; @Component({ selector: 'tb-login', @@ -34,6 +33,7 @@ import { validateEmail } from '@app/core/utils'; export class LoginComponent extends PageComponent implements OnInit { passwordViolation = false; + isLoading = false; loginFormGroup = this.fb.group({ username: ['', [Validators.required, validateEmail]], @@ -41,11 +41,10 @@ export class LoginComponent extends PageComponent implements OnInit { }); oauth2Clients: Array = null; - constructor(protected store: Store, - private authService: AuthService, + constructor(private authService: AuthService, public fb: UntypedFormBuilder, private router: Router) { - super(store); + super(); } ngOnInit() { @@ -54,9 +53,11 @@ export class LoginComponent extends PageComponent implements OnInit { login(): void { if (this.loginFormGroup.valid) { - this.authService.login(this.loginFormGroup.value).subscribe( - () => {}, - (error: HttpErrorResponse) => { + this.isLoading = true; + this.authService.login(this.loginFormGroup.value).pipe( + finalize(() => {this.isLoading = false;}) + ).subscribe({ + error: (error: HttpErrorResponse) => { if (error && error.error && error.error.errorCode) { if (error.error.errorCode === Constants.serverErrorCode.credentialsExpired) { this.router.navigateByUrl(`login/resetExpiredPassword?resetToken=${error.error.resetToken}`); @@ -65,12 +66,9 @@ export class LoginComponent extends PageComponent implements OnInit { } } } - ); - } else { - Object.keys(this.loginFormGroup.controls).forEach(field => { - const control = this.loginFormGroup.get(field); - control.markAsTouched({onlySelf: true}); }); + } else { + this.loginFormGroup.markAllAsTouched(); } } diff --git a/ui-ngx/src/app/modules/login/pages/login/create-password.component.scss b/ui-ngx/src/app/modules/login/pages/login/password.component.scss similarity index 88% rename from ui-ngx/src/app/modules/login/pages/login/create-password.component.scss rename to ui-ngx/src/app/modules/login/pages/login/password.component.scss index f9a5220daa..34d93f5d38 100644 --- a/ui-ngx/src/app/modules/login/pages/login/create-password.component.scss +++ b/ui-ngx/src/app/modules/login/pages/login/password.component.scss @@ -18,9 +18,10 @@ :host { display: flex; flex: 1 1 0; - .tb-create-password-content { + .tb-password-content { background-color: #eee; - .tb-create-password-card { + --mdc-elevated-card-container-elevation: none; + .tb-password-card { @media #{$mat-gt-xs} { width: 450px !important; } diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.html b/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.html index e71f67fe7b..bbf159107f 100644 --- a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.html @@ -15,41 +15,35 @@ limitations under the License. --> -
- - - - login.request-password-reset - + + + login.request-password-reset +
- +
-
-
- - - login.email - - email - - {{ 'user.invalid-email-format' | translate }} - - -
- - -
-
-
+ + login.email + + email + + {{ 'user.invalid-email-format' | translate }} + + +
+ + +
diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts b/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts index e96e44cedf..b1513105a1 100644 --- a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts @@ -27,9 +27,9 @@ import { validateEmail } from '@app/core/utils'; @Component({ selector: 'tb-reset-password-request', templateUrl: './reset-password-request.component.html', - styleUrls: ['./reset-password-request.component.scss'] + styleUrls: ['./password.component.scss'] }) -export class ResetPasswordRequestComponent extends PageComponent implements OnInit { +export class ResetPasswordRequestComponent extends PageComponent { clicked: boolean = false; @@ -44,9 +44,6 @@ export class ResetPasswordRequestComponent extends PageComponent implements OnIn super(store); } - ngOnInit() { - } - disableInputs() { this.requestPasswordRequest.disable(); this.clicked = true; diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password.component.html b/ui-ngx/src/app/modules/login/pages/login/reset-password.component.html index 39c35ed46c..481fd33df6 100644 --- a/ui-ngx/src/app/modules/login/pages/login/reset-password.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/reset-password.component.html @@ -15,48 +15,60 @@ limitations under the License. --> -
- - - - login.password-reset - - -
login.expired-password-reset-message
+
+ + + login.password-reset + + login.expired-password-reset-message - +
+ - - + +
-
-
- - - login.new-password - - lock - - - - login.new-password-again - - lock - - -
- - -
-
-
+ + login.new-password + + lock + + + {{ 'security.password-requirement.password-not-meet-requirements' | translate }} + + + + login.new-password-again + + lock + + + {{ 'security.password-requirement.new-passwords-not-match' | translate }} + + +
+ + +
+ + diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password.component.ts b/ui-ngx/src/app/modules/login/pages/login/reset-password.component.ts index 225421810a..23e775f085 100644 --- a/ui-ngx/src/app/modules/login/pages/login/reset-password.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/reset-password.component.ts @@ -14,67 +14,60 @@ /// limitations under the License. /// -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { AuthService } from '@core/auth/auth.service'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { PageComponent } from '@shared/components/page.component'; -import { UntypedFormBuilder } from '@angular/forms'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; -import { TranslateService } from '@ngx-translate/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; +import { UserPasswordPolicy } from '@shared/models/settings.models'; +import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/models/password.models'; +import { finalize } from 'rxjs/operators'; @Component({ selector: 'tb-reset-password', templateUrl: './reset-password.component.html', - styleUrls: ['./reset-password.component.scss'] + styleUrls: ['./password.component.scss'] }) -export class ResetPasswordComponent extends PageComponent implements OnInit, OnDestroy { +export class ResetPasswordComponent { isExpiredPassword: boolean; + isLoading = false; - resetToken = ''; - sub: Subscription; + resetPassword: FormGroup; + passwordPolicy: UserPasswordPolicy; - resetPassword = this.fb.group({ - newPassword: [''], - newPassword2: [''] - }); + private resetToken: string; - constructor(protected store: Store, - private route: ActivatedRoute, + constructor(private route: ActivatedRoute, private router: Router, private authService: AuthService, - private translate: TranslateService, - public fb: UntypedFormBuilder) { - super(store); - } + private fb: FormBuilder) { + + this.resetToken = this.route.snapshot.queryParams['resetToken'] || ''; + this.passwordPolicy = this.route.snapshot.data['passwordPolicy']; + this.isExpiredPassword = this.route.snapshot.data['expiredPassword'] ?? false; - ngOnInit() { - this.isExpiredPassword = this.route.snapshot.data.expiredPassword; - this.sub = this.route - .queryParams - .subscribe(params => { - this.resetToken = params.resetToken || ''; - }); + this.buildResetPasswordForm(); } - ngOnDestroy(): void { - super.ngOnDestroy(); - this.sub.unsubscribe(); + private buildResetPasswordForm() { + this.resetPassword = this.fb.group({ + newPassword: ['', [Validators.required, passwordStrengthValidator(this.passwordPolicy)]], + newPassword2: [''] + }, { + validators: [ + passwordsMatchValidator('newPassword', 'newPassword2'), + ] + }); } onResetPassword() { - if (this.resetPassword.get('newPassword').value !== this.resetPassword.get('newPassword2').value) { - this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('login.passwords-mismatch-error'), - type: 'error' })); + if (this.resetPassword.invalid) { + this.resetPassword.markAllAsTouched(); } else { - this.authService.resetPassword( - this.resetToken, - this.resetPassword.get('newPassword').value).subscribe( - () => this.router.navigateByUrl('login') - ); + this.isLoading = true; + this.authService.resetPassword(this.resetToken, this.resetPassword.get('newPassword').value).pipe( + finalize(() => {this.isLoading = false}) + ).subscribe(() => this.router.navigateByUrl('login')); } } } diff --git a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html index 0e73783a5a..f1a58933fc 100644 --- a/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html @@ -41,11 +41,11 @@

{{ providerDescription }}

- + -
diff --git a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts new file mode 100644 index 0000000000..c1defae012 --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts @@ -0,0 +1,47 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { OverlayModule } from '@angular/cdk/overlay'; +import { NgModule } from '@angular/core'; +import { DEFAULT_DIALOG_CONFIG, DialogConfig, DialogModule } from '@angular/cdk/dialog'; +import { MatDialogModule } from '@angular/material/dialog'; +import { DynamicDialog, DynamicMatDialog } from './dynamic-dialog'; +import { DynamicOverlay } from './dynamic-overlay'; +import { DynamicOverlayContainer } from './dynamic-overlay-container'; + +export const DYNAMIC_MAT_DIALOG_PROVIDERS = [ + DynamicOverlayContainer, + DynamicOverlay, + DynamicDialog, + DynamicMatDialog, + { + provide: DEFAULT_DIALOG_CONFIG, + useValue: { + ...new DialogConfig() + } + } +]; + +@NgModule( { + imports: [ + OverlayModule, + DialogModule, + MatDialogModule + ], + providers: DYNAMIC_MAT_DIALOG_PROVIDERS +} ) +export class DynamicMatDialogModule { +} diff --git a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts new file mode 100644 index 0000000000..2fdc63f381 --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts @@ -0,0 +1,85 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { Location } from "@angular/common"; +import { Inject, Injectable, Injector, Optional, SkipSelf, TemplateRef } from '@angular/core'; +import { + MAT_DIALOG_DEFAULT_OPTIONS, + MAT_DIALOG_SCROLL_STRATEGY, + MatDialog, + MatDialogConfig, MatDialogRef +} from '@angular/material/dialog'; +import { DynamicOverlay } from "./dynamic-overlay"; +import { DynamicOverlayContainer } from '@shared/components/dialog/dynamic/dynamic-overlay-container'; +import { ComponentType, ScrollStrategy } from '@angular/cdk/overlay'; +import { DEFAULT_DIALOG_CONFIG, Dialog, DialogConfig } from '@angular/cdk/dialog'; + +export interface DynamicMatDialogConfig extends MatDialogConfig { + containerElement?: HTMLElement; +} + +@Injectable() +export class DynamicMatDialog extends MatDialog { + + private _customOverlay: DynamicOverlay; + + constructor( _overlay: DynamicOverlay, + _injector: Injector, + @Optional() location: Location, + @Inject( MAT_DIALOG_DEFAULT_OPTIONS ) _defaultOptions: MatDialogConfig, + @Inject( MAT_DIALOG_SCROLL_STRATEGY ) _scrollStrategy: ScrollStrategy, + @Optional() @SkipSelf() _parentDialog:DynamicMatDialog, + _overlayContainer: DynamicOverlayContainer) { + + super( _overlay, _injector, location, _defaultOptions, _scrollStrategy, _parentDialog, _overlayContainer ); + this._dialog = _injector.get(DynamicDialog); + this._customOverlay = _overlay; + } + + public open(component: ComponentType | TemplateRef, config?: DynamicMatDialogConfig): MatDialogRef { + if (config?.containerElement) { + config.containerElement.style.transform = 'translateZ(0)'; + this._customOverlay.setContainerElement( config.containerElement ); + } + const ref = super.open(component, config); + if (config?.containerElement) { + ref.afterClosed().subscribe( + { + next: () => { + this._customOverlay.setContainerElement(null); + }, + error: () => { + this._customOverlay.setContainerElement(null); + } + } + ); + } + return ref; + } +} + +@Injectable() +export class DynamicDialog extends Dialog { + constructor( _overlay: DynamicOverlay, + _injector: Injector, + @Inject( DEFAULT_DIALOG_CONFIG ) _defaultOptions: DialogConfig, + @Inject( MAT_DIALOG_SCROLL_STRATEGY ) _scrollStrategy: ScrollStrategy, + @Optional() @SkipSelf() _parentDialog: DynamicDialog, + _overlayContainer: DynamicOverlayContainer) { + + super( _overlay, _injector, _defaultOptions, _parentDialog, _overlayContainer, _scrollStrategy ); + } +} diff --git a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-overlay-container.ts b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-overlay-container.ts new file mode 100644 index 0000000000..83feda56fc --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-overlay-container.ts @@ -0,0 +1,27 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { OverlayContainer } from "@angular/cdk/overlay"; +import { Injectable } from "@angular/core"; + +@Injectable() +export class DynamicOverlayContainer extends OverlayContainer { + + public setContainerElement( containerElement:HTMLElement ):void { + + this._containerElement = containerElement; + } +} diff --git a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-overlay.ts b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-overlay.ts new file mode 100644 index 0000000000..0a809d1c3d --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-overlay.ts @@ -0,0 +1,62 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { + Overlay, + ScrollStrategyOptions, + OverlayKeyboardDispatcher, OverlayOutsideClickDispatcher, OverlayPositionBuilder +} from '@angular/cdk/overlay'; +import { ComponentFactoryResolver, Inject, Injectable, Injector, NgZone } from '@angular/core'; +import { DynamicOverlayContainer } from './dynamic-overlay-container'; +import { DOCUMENT, Location } from '@angular/common'; +import { Directionality } from '@angular/cdk/bidi'; + +@Injectable() +export class DynamicOverlay extends Overlay { + + private _dynamicOverlayContainer: DynamicOverlayContainer; + + constructor( scrollStrategies: ScrollStrategyOptions, + _overlayContainer: DynamicOverlayContainer, + _componentFactoryResolver: ComponentFactoryResolver, + _positionBuilder: OverlayPositionBuilder, + _keyboardDispatcher: OverlayKeyboardDispatcher, + _injector: Injector, + _ngZone: NgZone, + @Inject(DOCUMENT) document: Document, + _directionality: Directionality, + _location: Location, + _outsideClickDispatcher: OverlayOutsideClickDispatcher) { + + super( scrollStrategies, + _overlayContainer, + _componentFactoryResolver, + _positionBuilder, + _keyboardDispatcher, + _injector, + _ngZone, + document, + _directionality, + _location, + _outsideClickDispatcher); + + this._dynamicOverlayContainer = _overlayContainer; + } + + public setContainerElement(containerElement:HTMLElement ): void { + this._dynamicOverlayContainer.setContainerElement( containerElement ); + } +} diff --git a/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.html new file mode 100644 index 0000000000..6c78f822ac --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.html @@ -0,0 +1,40 @@ + +
+ +
+
{{ 'entity.limit-reached' | translate }}
+
+
+
+
+ + +
+
+ {{ 'entity.request-sysadmin-text' | translate }} + {{ 'entity.login-here' | translate }} + {{ 'entity.to-increase-limit' | translate }} +
+
diff --git a/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.scss b/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.scss new file mode 100644 index 0000000000..c888b0ac2c --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.scss @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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'; + +.entity-limit-exceeded-dialog { + max-width: 100%; + height: 100%; + + @media #{$mat-gt-xs} { + max-width: 500px; + max-height: 80vh; + } + + position: relative; + padding: 40px 40px 24px 40px; + display: flex; + flex-direction: column; + gap: 20px; + + .close-btn { + position: absolute; + top: 8px; + right: 8px; + color: rgba(0, 0, 0, 0.54); + } + + .entity-limit-exceeded-bg { + position: relative; + width: 100%; + height: 140px; + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: $tb-primary-color; + mask-image: url(/assets/entity-limit-reached.svg); + -webkit-mask-image: url(/assets/entity-limit-reached.svg); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-size: contain; + mask-position: center; + -webkit-mask-position: center; + } + } + + .entity-limit-exceeded-title { + font-size: 24px; + font-style: normal; + font-weight: 500; + line-height: 32px; + color: rgba(0, 0, 0, 0.87); + text-align: center; + } + + .entity-limit-exceeded-container { + flex: 1; + overflow-y: auto; + display: flex; + flex-direction: column; + justify-content: center; + .entity-limit-exceeded-text { + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + color: rgba(0, 0, 0, 0.76); + text-align: center; + } + } + + .request-entity-limit-increase-sysadmin-prompt { + text-align: center; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + color: rgba(0, 0, 0, 0.54); + } + +} diff --git a/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.ts new file mode 100644 index 0000000000..ccbc3174d7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/entity-limit-exceeded-dialog.component.ts @@ -0,0 +1,94 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { Component, Inject, ViewEncapsulation } 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 { TranslateService } from '@ngx-translate/core'; +import { DialogService } from '@core/services/dialog.service'; +import { AuthService } from '@core/auth/auth.service'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { NotificationService } from '@core/http/notification.service'; + +export interface EntityLimitExceededDialogData { + entityType: EntityType; + limit: number; +} + +// @dynamic +@Component({ + selector: 'tb-entity-limit-exceeded-dialog', + templateUrl: './entity-limit-exceeded-dialog.component.html', + styleUrls: ['./entity-limit-exceeded-dialog.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class EntityLimitExceededDialogComponent extends DialogComponent { + + limitReachedText: string; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: EntityLimitExceededDialogData, + public dialogRef: MatDialogRef, + private authService: AuthService, + private dialogs: DialogService, + private translate: TranslateService, + private notificationService: NotificationService) { + super(store, router, dialogRef); + + let entitiesText: string; + if (data.limit > 1) { + entitiesText = data.limit + ' ' + (this.translate.instant(entityTypeTranslations.get(data.entityType).typePlural) as string).toLowerCase(); + } else { + entitiesText = '1 ' + (this.translate.instant(entityTypeTranslations.get(data.entityType).type) as string).toLowerCase(); + } + this.limitReachedText = this.translate.instant('entity.limit-reached-text', { entities: entitiesText, entity: (this.translate.instant(entityTypeTranslations.get(data.entityType).type) as string).toLowerCase() }); + } + + cancel(): void { + this.dialogRef.close(); + } + + requestLimitIncrease($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.notificationService.sendEntitiesLimitIncreaseRequest(this.data.entityType).subscribe( + () => { + this.dialogRef.close(); + this.dialogs.alert( + this.translate.instant('entity.increase-limit-request-sent-title'), + this.translate.instant('entity.increase-limit-request-sent-text'), + this.translate.instant('action.close') + ); + } + ); + } + + loginAsSysAdmin($event: Event) { + if ($event) { + $event.preventDefault(); + $event.stopPropagation(); + } + this.authService.redirectUrl = `/tenants/${getCurrentAuthUser(this.store).tenantId}`; + this.authService.logout(); + } + +} 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 5ef8d69e7a..8a576133f9 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 @@ -18,7 +18,6 @@ - +
@@ -71,6 +70,11 @@ {{ noEntitiesMatchingText | translate: {entity: searchText} }} + @if (allowCreateNew) { + + entity.create-new-key + + }
diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts index f2ec24fce2..26f4530431 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts @@ -22,7 +22,7 @@ import { catchError, debounceTime, map, share, switchMap, tap } from 'rxjs/opera import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; -import { BaseData } from '@shared/models/base-data'; +import { BaseData, getEntityDisplayName } from '@shared/models/base-data'; import { EntityId } from '@shared/models/id/entity-id'; import { EntityService } from '@core/http/entity.service'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; @@ -138,11 +138,15 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @coerceArray() additionalClasses: Array; + @Input() + @coerceBoolean() + useEntityDisplayName = false; + @Output() entityChanged = new EventEmitter>(); @Output() - createNew = new EventEmitter(); + createNew = new EventEmitter(); @ViewChild('entityInput', {static: true}) entityInput: ElementRef; @@ -297,6 +301,18 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit this.entityRequiredText = 'ai-models.model-required'; this.notFoundEntities = 'ai-models.no-model-text'; break; + case EntityType.DEVICE_PROFILE: + this.entityText = 'device-profile.device-profile'; + this.noEntitiesMatchingText = 'device-profile.no-device-profiles-matching'; + this.entityRequiredText = 'device-profile.device-profile-required'; + this.notFoundEntities = 'device-profile.no-device-profiles-text'; + break; + case EntityType.ASSET_PROFILE: + this.entityText = 'asset-profile.asset-profile'; + this.noEntitiesMatchingText = 'asset-profile.no-asset-profiles-matching'; + this.entityRequiredText = 'asset-profile.asset-profile-required'; + this.notFoundEntities = 'asset-profile.no-asset-profiles-text'; + break; case AliasEntityType.CURRENT_CUSTOMER: this.entityText = 'customer.default-customer'; this.noEntitiesMatchingText = 'customer.no-customers-matching'; @@ -395,7 +411,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit } displayEntityFn(entity?: BaseData): string | undefined { - return entity ? entity.name : undefined; + return entity ? (this.useEntityDisplayName ? getEntityDisplayName(entity) : entity.name) : undefined; } private fetchEntities(searchText?: string): Observable>> { @@ -451,12 +467,16 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit return entityType; } - createNewEntity($event: Event) { + createNewEntity($event: Event, searchText?: string) { $event.stopPropagation(); - this.createNew.emit(); + this.createNew.emit(searchText); } get showEntityLink(): boolean { return this.selectEntityFormGroup.get('entity').value && this.disabled && this.entityURL !== ''; } + + markAsTouched(): void { + this.selectEntityFormGroup.get('entity').markAsTouched(); + } } diff --git a/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.html b/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.html index 28d1aacdc5..defcb90725 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.html @@ -16,23 +16,23 @@ --> - - @if (keyControl.value) { + @if (keyControl.value && keyControl.enabled) { - } @else if (keyControl.hasError('required') && keyControl.touched) { + } @else if (keyControl.hasError('required') && keyControl.touched && keyControl.enabled) { warning @@ -43,7 +43,7 @@ @for (key of filteredKeys$ | async; track key) { } @empty { - @if (!this.keyControl.value) { + @if (!this.keyControl.value && enableAutocomplete) { {{ 'entity.no-keys-found' | translate }} } } diff --git a/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.ts index 0945abdf6e..6aef1d513d 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.ts @@ -14,7 +14,17 @@ /// limitations under the License. /// -import { Component, effect, ElementRef, forwardRef, input, OnChanges, SimpleChanges, ViewChild, } from '@angular/core'; +import { + Component, + effect, + ElementRef, + forwardRef, + Input, + input, + OnChanges, + SimpleChanges, + ViewChild, +} from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -32,6 +42,7 @@ import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry. import { EntitiesKeysByQuery } from '@shared/models/entity.models'; import { EntityFilter } from '@shared/models/query/query.models'; import { isEqual } from '@core/utils'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-entity-key-autocomplete', @@ -53,6 +64,10 @@ export class EntityKeyAutocompleteComponent implements ControlValueAccessor, Val @ViewChild('keyInput', {static: true}) keyInput: ElementRef; + @Input() placeholder = this.translate.instant('action.set'); + @Input() requiredText = this.translate.instant('common.hint.key-required'); + @Input() enableAutocomplete = true; + entityFilter = input.required(); dataKeyType = input.required(); keyScopeType = input(); @@ -67,12 +82,18 @@ export class EntityKeyAutocompleteComponent implements ControlValueAccessor, Val keys$ = this.keyInputSubject.asObservable() .pipe( switchMap(() => { + if (!this.enableAutocomplete) { + return of([] as string[]); + } return this.cachedResult ? of(this.cachedResult) : this.entityService.findEntityKeysByQuery({ pageLink: { page: 0, pageSize: 100 }, entityFilter: this.entityFilter(), - }, this.dataKeyType() === DataKeyType.attribute, this.dataKeyType() === DataKeyType.timeseries, this.keyScopeType()); + }, this.dataKeyType() === DataKeyType.attribute, this.dataKeyType() === DataKeyType.timeseries, this.keyScopeType(), {ignoreLoading: true}); }), map(result => { + if (Array.isArray(result)) { + return result; + } this.cachedResult = result; switch (this.dataKeyType()) { case DataKeyType.attribute: @@ -96,6 +117,7 @@ export class EntityKeyAutocompleteComponent implements ControlValueAccessor, Val constructor( private fb: FormBuilder, private entityService: EntityService, + private translate: TranslateService, ) { this.keyControl.valueChanges .pipe(takeUntilDestroyed()) @@ -118,6 +140,7 @@ export class EntityKeyAutocompleteComponent implements ControlValueAccessor, Val if (filterChanged || keyScopeChanged || keyTypeChanged) { this.keyControl.setValue('', {emitEvent: false}); + this.cachedResult = null; } } @@ -136,10 +159,18 @@ export class EntityKeyAutocompleteComponent implements ControlValueAccessor, Val registerOnTouched(_): void {} validate(): ValidationErrors | null { - return this.keyControl.valid ? null : { keyControl: false }; + return this.keyControl.valid || this.keyControl.disabled ? null : { keyControl: false }; } writeValue(value: string): void { this.keyControl.patchValue(value, {emitEvent: false}); } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.keyControl.disable({emitEvent: false}); + } else { + this.keyControl.enable({emitEvent: false}); + } + } } diff --git a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html index e6a5067783..0469adca74 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html @@ -36,6 +36,7 @@ *ngIf="modelValue.entityType" [required]="required" [entityType]="modelValue.entityType" + [useEntityDisplayName]="useEntityDisplayName" formControlName="entityIds">
diff --git a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts index 3ccc598947..9d209739bf 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts @@ -68,6 +68,9 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit { @Input() additionEntityTypes: {[key in string]: string} = {}; + @Input({transform: booleanAttribute}) + useEntityDisplayName = false; + displayEntityTypeSelect: boolean; private defaultEntityType: EntityType | AliasEntityType = null; diff --git a/ui-ngx/src/app/shared/components/entity/entity-list.component.html b/ui-ngx/src/app/shared/components/entity/entity-list.component.html index 9cc0969a5f..589019b9fa 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-list.component.html @@ -28,7 +28,7 @@ class="tb-chip-row-ellipsis" [removable]="!disabled" (removed)="remove(entity)"> - {{entity.name}} + {{ displayEntityFn(entity) }} close - +
diff --git a/ui-ngx/src/app/shared/components/entity/entity-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-list.component.ts index e480a2fb95..6f31dcb948 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-list.component.ts @@ -39,7 +39,7 @@ import { Observable } from 'rxjs'; import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { EntityType } from '@shared/models/entity-type.models'; -import { BaseData } from '@shared/models/base-data'; +import { BaseData, getEntityDisplayName } from '@shared/models/base-data'; import { EntityId } from '@shared/models/id/entity-id'; import { EntityService } from '@core/http/entity.service'; import { MatAutocomplete } from '@angular/material/autocomplete'; @@ -125,6 +125,10 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan @coerceBoolean() allowCreateNew: boolean; + @Input() + @coerceBoolean() + useEntityDisplayName = false; + @Output() createNew = new EventEmitter(); @@ -279,7 +283,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan } public displayEntityFn(entity?: BaseData): string | undefined { - return entity ? entity.name : undefined; + return entity ? (this.useEntityDisplayName ? getEntityDisplayName(entity) : entity.name) : undefined; } private fetchEntities(searchText?: string): Observable>> { diff --git a/ui-ngx/src/app/shared/components/entity/entity-select.component.html b/ui-ngx/src/app/shared/components/entity/entity-select.component.html index 7d57e575ef..59d5972c65 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-select.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-select.component.html @@ -19,20 +19,25 @@
diff --git a/ui-ngx/src/app/shared/components/entity/entity-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-select.component.ts index f5260d1ec1..62acb20ae5 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-select.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { AfterViewInit, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -26,6 +26,8 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; import { coerceBoolean } from '@shared/decorators/coercion'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatFormFieldAppearance } from '@angular/material/form-field'; +import { BaseData } from '@shared/models/base-data'; +import { EntityAutocompleteComponent } from '@shared/components/entity/entity-autocomplete.component'; @Component({ selector: 'tb-entity-select', @@ -37,7 +39,9 @@ import { MatFormFieldAppearance } from '@angular/material/form-field'; multi: true }] }) -export class EntitySelectComponent implements ControlValueAccessor, OnInit, AfterViewInit { +export class EntitySelectComponent implements ControlValueAccessor, OnInit { + + @ViewChild('entityAutocompleteComponent') entityAutocompleteComponent!: EntityAutocompleteComponent; entitySelectFormGroup: UntypedFormGroup; @@ -62,16 +66,30 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte @Input() appearance: MatFormFieldAppearance = 'fill'; + @Input() + @coerceBoolean() + useEntityDisplayName = false; + + @Input() + filterAllowedEntityTypes = true; + + @Input() + defaultEntityType: AliasEntityType | EntityType; + + @Input() + entityTypeLabel: string; + + @Output() + entityChanged = new EventEmitter>(); + displayEntityTypeSelect: boolean; AliasEntityType = AliasEntityType; - entityTypeNullUUID: Set = new Set([ + entityTypeNullUUID = new Set([ AliasEntityType.CURRENT_TENANT, AliasEntityType.CURRENT_USER, AliasEntityType.CURRENT_USER_OWNER ]); - private readonly defaultEntityType: EntityType | AliasEntityType = null; - private propagateChange = (v: any) => { }; constructor(private store: Store, @@ -82,15 +100,18 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte const entityTypes = this.entityService.prepareAllowedEntityTypesList(this.allowedEntityTypes, this.useAliasEntityTypes); + + let defaultEntityType: EntityType | AliasEntityType = null + if (entityTypes.length === 1) { this.displayEntityTypeSelect = false; - this.defaultEntityType = entityTypes[0]; + defaultEntityType = entityTypes[0]; } else { this.displayEntityTypeSelect = true; } this.entitySelectFormGroup = this.fb.group({ - entityType: [this.defaultEntityType], + entityType: [defaultEntityType], entityId: [null] }); } @@ -122,9 +143,19 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte if (additionNullUIIDEntityTypes.length > 0) { additionNullUIIDEntityTypes.forEach((entityType) => this.entityTypeNullUUID.add(entityType)); } - } - ngAfterViewInit(): void { + if (this.filterAllowedEntityTypes === false) { + if (this.allowedEntityTypes?.length === 1) { + this.displayEntityTypeSelect = false; + this.entitySelectFormGroup.get('entityType').setValue(this.allowedEntityTypes[0]) + } else { + this.displayEntityTypeSelect = true; + } + } + + if (this.defaultEntityType) { + this.entitySelectFormGroup.get('entityType').setValue(this.defaultEntityType); + } } setDisabledState(isDisabled: boolean): void { @@ -172,4 +203,12 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte } } } + + changeEntity(entity: BaseData): void { + this.entityChanged.emit(entity); + } + + entityAutocompleteMarkAsTouched() { + this.entityAutocompleteComponent.markAsTouched(); + } } diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts index 159f410548..ca579f1ac0 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, ReplaySubject, Subscription, throwError } from 'rxjs'; import { debounceTime, map, mergeMap, share } from 'rxjs/operators'; @@ -31,6 +31,8 @@ import { PageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; import { UtilsService } from '@core/services/utils.service'; import { EntityService } from '@core/http/entity.service'; +import { CalculatedFieldType } from "@shared/models/calculated-field.models"; +import { CalculatedFieldsService } from "@core/http/calculated-fields.service"; @Component({ selector: 'tb-entity-subtype-list', @@ -44,7 +46,7 @@ import { EntityService } from '@core/http/entity.service'; } ] }) -export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy { +export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, OnDestroy { entitySubtypeListFormGroup: FormGroup; @@ -77,6 +79,9 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, @Input() entityType: EntityType; + @Input() + calculatedFieldType: CalculatedFieldType; + @Input() emptyInputPlaceholder: string; @@ -114,15 +119,17 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, private dirty = false; - private propagateChange = (v: any) => { }; + private propagateChange = (_v: any) => { }; private hasPageDataEntitySubTypes = new Set([ - EntityType.ALARM + EntityType.ALARM, + EntityType.CALCULATED_FIELD ]); constructor(private broadcast: BroadcastService, public translate: TranslateService, private alarmService: AlarmService, + private calculatedFieldsService: CalculatedFieldsService, private utils: UtilsService, private fb: FormBuilder, private entityService: EntityService) { @@ -194,6 +201,13 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, this.noSubtypesMathingText = 'alarm.no-alarm-types-matching'; this.subtypeListEmptyText = 'alarm.alarm-type-list-empty'; break; + case EntityType.CALCULATED_FIELD: + this.placeholder = this.required ? this.translate.instant('alarm.enter-alarm-rule-type') + : this.translate.instant('alarm-rule.any-type'); + this.secondaryPlaceholder = '+' + this.translate.instant('alarm-rule.alarm-type'); + this.noSubtypesMathingText = 'alarm-rule.no-alarm-rule-types-matching'; + this.subtypeListEmptyText = 'alarm-rule.alarm-rule-type-list-empty'; + break; } if (this.emptyInputPlaceholder) { @@ -211,9 +225,6 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, ); } - ngAfterViewInit(): void { - } - ngOnDestroy(): void { if (this.broadcastSubscription) { this.broadcastSubscription.unsubscribe(); @@ -285,7 +296,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, } selected(event: MatAutocompleteSelectedEvent): void { - this.add(event.option.viewValue); + this.add(event.option.value); this.clear(''); } @@ -315,15 +326,22 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, if (this.hasPageDataEntitySubTypes.has(this.entityType)) { const pageLink = new PageLink(25, 0, searchText); let subTypesPagesObservable: Observable>; + let subTypesCfPagesObservable: Observable>; switch (this.entityType) { case EntityType.ALARM: subTypesPagesObservable = this.alarmService.getAlarmTypes(pageLink, {ignoreLoading: true}); break; + case EntityType.CALCULATED_FIELD: + subTypesCfPagesObservable = this.calculatedFieldsService.getCalculatedFieldNames(pageLink, CalculatedFieldType.ALARM, {ignoreLoading: true}); } if (subTypesPagesObservable) { this.entitySubtypes = subTypesPagesObservable.pipe( map(subTypesPage => subTypesPage.data.map(subType => subType.type)), ); + } else if (subTypesCfPagesObservable) { + this.entitySubtypes = subTypesCfPagesObservable.pipe( + map(subTypesPage => subTypesPage.data) + ); } else { return throwError(null); } diff --git a/ui-ngx/src/app/shared/components/entity/entity-type-select.component.html b/ui-ngx/src/app/shared/components/entity/entity-type-select.component.html index e8df4b017c..65e3615887 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-type-select.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-type-select.component.html @@ -16,7 +16,7 @@ --> {{ label }} diff --git a/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts index c5a5dc5f9e..53e1d59cc1 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts @@ -21,7 +21,7 @@ import { AliasEntityType, EntityType, entityTypeTranslations } from '@app/shared import { EntityService } from '@core/http/entity.service'; import { coerceBoolean } from '@shared/decorators/coercion'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { MatFormFieldAppearance } from '@angular/material/form-field'; +import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; @Component({ selector: 'tb-entity-type-select', @@ -52,8 +52,16 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit, @coerceBoolean() showLabel: boolean; + private labelValue = this.translate.instant('entity.type'); + + get label(): string { + return this.labelValue; + } + @Input() - label = this.translate.instant('entity.type'); + set label(value: string) { + this.labelValue = value ?? this.translate.instant('entity.type'); + } @Input() @coerceBoolean() @@ -68,6 +76,9 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit, @Input() appearance: MatFormFieldAppearance = 'fill'; + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + @Input() @coerceBoolean() inlineField: boolean; @@ -170,4 +181,8 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit, return ''; } } + + markAsTouched(): void { + this.entityTypeFormGroup.get('entityType').markAsTouched(); + } } 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 4bf0833229..9fa293de05 100644 --- a/ui-ngx/src/app/shared/components/file-input.component.ts +++ b/ui-ngx/src/app/shared/components/file-input.component.ts @@ -180,17 +180,18 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, if (readers.length) { Promise.all(readers).then((files) => { - files = files.filter(file => file.fileContent != null || file.files != null); - if (files.length === 1) { - this.fileContent = files[0].fileContent; - this.fileName = files[0].fileName; - this.files = files[0].files; - this.mediaType = files[0].mediaType; + const validResults = files.filter(file => file.fileContent != null || file.files != null); + + if (validResults.length === 1) { + this.fileContent = validResults[0].fileContent; + this.fileName = validResults[0].fileName; + this.files = validResults[0].files; + this.mediaType = validResults[0].mediaType; this.updateModel(); - } else if (files.length > 1) { - this.fileContent = files.map(content => content.fileContent); - this.fileName = files.map(content => content.fileName); - this.files = files.map(content => content.files); + } else if (validResults.length > 1) { + this.fileContent = validResults.map(content => content.fileContent); + this.fileName = validResults.map(content => content.fileName); + this.files = validResults.map(content => content.files); this.updateModel(); } }); @@ -204,29 +205,32 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, private readerAsFile(file: flowjs.FlowFile): Promise { return new Promise((resolve) => { + if (this.workFromFileObj) { + resolve({ + fileContent: null, + fileName: file.name, + files: file.file, + mediaType: file.file.type || null + }); + return; + } + const reader = new FileReader(); reader.onload = () => { let fileName = null; let fileContent = null; - let files = null; let mediaType = null; if (reader.readyState === reader.DONE) { - if (!this.workFromFileObj) { - fileContent = reader.result; - if (fileContent && fileContent.length > 0) { - if (this.contentConvertFunction) { - fileContent = this.contentConvertFunction(fileContent); - } - fileName = fileContent ? file.name : null; - mediaType = file?.file?.type || null; + fileContent = reader.result; + if (fileContent && fileContent.length > 0) { + if (this.contentConvertFunction) { + fileContent = this.contentConvertFunction(fileContent); } - } else if (file.name || file.file){ - files = file.file; - fileName = file.name; - mediaType = file.file.type || null; + fileName = fileContent ? file.name : null; + mediaType = file?.file?.type || null; } } - resolve({fileContent, fileName, files, mediaType}); + resolve({fileContent, fileName, files: null, mediaType}); }; reader.onerror = () => { resolve({fileContent: null, fileName: null, files: null, mediaType: null}); 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 index 906091e102..c11dc6447a 100644 --- a/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.scss +++ b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.scss @@ -17,7 +17,6 @@ display: flex; flex-direction: row; align-items: center; - gap: 4px; } :host { diff --git a/ui-ngx/src/app/shared/components/icon.component.ts b/ui-ngx/src/app/shared/components/icon.component.ts index 18324bb7cb..cd26ecca89 100644 --- a/ui-ngx/src/app/shared/components/icon.component.ts +++ b/ui-ngx/src/app/shared/components/icon.component.ts @@ -33,6 +33,9 @@ import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; import { ContentObserver } from '@angular/cdk/observers'; +import { isTbImage } from '@shared/models/resource.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; const _TbIconBase = mixinColor( class { @@ -70,7 +73,7 @@ const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/; host: { role: 'img', class: 'mat-icon notranslate', - '[attr.data-mat-icon-type]': '!_useSvgIcon ? "font" : "svg"', + '[attr.data-mat-icon-type]': '_useSvgIcon ? "svg" : (_useImageIcon ? null : "font")', '[attr.data-mat-icon-name]': '_svgName', '[attr.data-mat-icon-namespace]': '_svgNamespace', '[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"', @@ -99,6 +102,9 @@ export class TbIconComponent extends _TbIconBase private _textElement = null; + _useImageIcon = false; + private _imageElement = null; + private _previousPath?: string; private _elementsWithExternalReferences?: Map; @@ -109,6 +115,8 @@ export class TbIconComponent extends _TbIconBase private contentObserver: ContentObserver, private renderer: Renderer2, private _iconRegistry: MatIconRegistry, + private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, @Inject(MAT_ICON_LOCATION) private _location: MatIconLocation, private readonly _errorHandler: ErrorHandler) { super(elementRef); @@ -148,16 +156,29 @@ export class TbIconComponent extends _TbIconBase private _updateIcon() { const useSvgIcon = isSvgIcon(this.icon); + const useImageIcon = isTbImage(this.icon); if (this._useSvgIcon !== useSvgIcon) { this._useSvgIcon = useSvgIcon; if (!this._useSvgIcon) { this._updateSvgIcon(undefined); } else { this._updateFontIcon(undefined); + this._updateImageIcon(undefined); + } + } + if (this._useImageIcon !== useImageIcon) { + this._useImageIcon = useImageIcon; + if (!this._useImageIcon) { + this._updateImageIcon(undefined); + } else { + this._updateFontIcon(undefined); + this._updateSvgIcon(undefined); } } if (this._useSvgIcon) { this._updateSvgIcon(this.icon); + } else if (this._useImageIcon) { + this._updateImageIcon(this.icon); } else { this._updateFontIcon(this.icon); } @@ -278,4 +299,49 @@ export class TbIconComponent extends _TbIconBase } } + private _updateImageIcon(rawName: string | undefined) { + if (rawName) { + this._clearImageIcon(); + this.imagePipe.transform(rawName, { asString: true, ignoreLoadingImage: true }).subscribe( + imageUrl => { + const urlStr = imageUrl as string; + const isSvg = urlStr?.startsWith('data:image/svg+xml') || urlStr?.endsWith('.svg'); + if (isSvg) { + const safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(urlStr); + this._iconRegistry + .getSvgIconFromUrl(safeUrl) + .pipe(take(1)) + .subscribe({ + next: (svg) => { + this.renderer.insertBefore(this._elementRef.nativeElement, svg, this._iconNameContent.nativeElement); + this._imageElement = svg; + }, + error: () => this._setImageElement(urlStr) + }); + } else { + this._setImageElement(urlStr); + } + } + ); + } else { + this._clearImageIcon(); + } + } + + private _setImageElement(urlStr: string) { + const imgElement = this.renderer.createElement('img'); + this.renderer.addClass(imgElement, 'mat-icon'); + this.renderer.setAttribute(imgElement, 'alt', 'Image icon'); + this.renderer.setAttribute(imgElement, 'src', urlStr); + this.renderer.insertBefore(this._elementRef.nativeElement, imgElement, this._iconNameContent.nativeElement); + this._imageElement = imgElement; + } + + private _clearImageIcon() { + const elem: HTMLElement = this._elementRef.nativeElement; + if (this._imageElement !== null) { + this.renderer.removeChild(elem, this._imageElement); + this._imageElement = null; + } + } } diff --git a/ui-ngx/src/app/shared/components/json-object-view.component.ts b/ui-ngx/src/app/shared/components/json-object-view.component.ts index 71b7125c08..60a6bd9502 100644 --- a/ui-ngx/src/app/shared/components/json-object-view.component.ts +++ b/ui-ngx/src/app/shared/components/json-object-view.component.ts @@ -15,14 +15,11 @@ /// import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Ace } from 'ace-builds'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { RafService } from '@core/services/raf.service'; import { isDefinedAndNotNull, isUndefined } from '@core/utils'; -import { getAce } from '@shared/models/ace/ace.models'; +import { getAce, updateEditorSize } from '@shared/models/ace/ace.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-json-object-view', @@ -36,7 +33,7 @@ import { getAce } from '@shared/models/ace/ace.models'; } ] }) -export class JsonObjectViewComponent implements OnInit, OnDestroy { +export class JsonObjectViewComponent implements OnInit, OnDestroy, ControlValueAccessor { @ViewChild('jsonViewer', {static: true}) jsonViewerElmRef: ElementRef; @@ -55,96 +52,54 @@ export class JsonObjectViewComponent implements OnInit, OnDestroy { @Input() sort: (key: string, value: any) => any; - private widthValue: boolean; - - get autoWidth(): boolean { - return this.widthValue; - } - @Input() - set autoWidth(value: boolean) { - this.widthValue = coerceBooleanProperty(value); - } - - private heigthValue: boolean; - - get autoHeight(): boolean { - return this.heigthValue; - } + @coerceBoolean() + autoWidth: boolean @Input() - set autoHeight(value: boolean) { - this.heigthValue = coerceBooleanProperty(value); - } + @coerceBoolean() + autoHeight: boolean - constructor(public elementRef: ElementRef, - protected store: Store, - private raf: RafService, - private renderer: Renderer2) { + constructor(private renderer: Renderer2) { } ngOnInit(): void { this.viewerElement = this.jsonViewerElmRef.nativeElement; - let editorOptions: Partial = { + const editorOptions: Partial = { mode: 'ace/mode/java', theme: 'ace/theme/github', showGutter: false, showPrintMargin: false, - readOnly: true - }; - - const advancedOptions = { + readOnly: true, enableSnippets: false, enableBasicAutocompletion: false, enableLiveAutocompletion: false }; - editorOptions = {...editorOptions, ...advancedOptions}; getAce().subscribe( (ace) => { this.jsonViewer = ace.edit(this.viewerElement, editorOptions); this.jsonViewer.session.setUseWrapMode(false); this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); - if (this.contentValue && (this.widthValue || this.heigthValue)) { - this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer); + if (this.contentValue && (this.autoWidth || this.autoHeight)) { + updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer, this.renderer, { + ignoreHeight: !this.autoHeight, + ignoreWidth: !this.autoWidth + }); } } ); } ngOnDestroy(): void { - if (this.jsonViewer) { - this.jsonViewer.destroy(); - } + this.jsonViewer?.destroy(); } - updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { - let newHeight = 200; - let newWidth = 600; - if (content && content.length > 0) { - const lines = content.split('\n'); - newHeight = 17 * lines.length + 17; - let maxLineLength = 0; - lines.forEach((row) => { - const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); - const lineLength = line.length; - maxLineLength = Math.max(maxLineLength, lineLength); - }); - newWidth = 8 * maxLineLength + 16; - } - if (this.heigthValue) { - this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); - } - if (this.widthValue) { - this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px'); - } - editor.resize(); - } registerOnChange(fn: any): void { this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } writeValue(value: any): void { @@ -162,8 +117,11 @@ export class JsonObjectViewComponent implements OnInit, OnDestroy { } if (this.jsonViewer) { this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1); - if (this.contentValue && (this.widthValue || this.heigthValue)) { - this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer); + if (this.contentValue && (this.autoWidth || this.autoHeight)) { + updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer, this.renderer, { + ignoreHeight: !this.autoHeight, + ignoreWidth: !this.autoWidth + }); } } } diff --git a/ui-ngx/src/app/shared/components/logo.component.html b/ui-ngx/src/app/shared/components/logo.component.html index adf6ab0644..dd16e71259 100644 --- a/ui-ngx/src/app/shared/components/logo.component.html +++ b/ui-ngx/src/app/shared/components/logo.component.html @@ -15,5 +15,17 @@ limitations under the License. --> - +@if (isExternal) { + + + +} @else { + + + +} + + + Logo + + diff --git a/ui-ngx/src/app/shared/components/logo.component.scss b/ui-ngx/src/app/shared/components/logo.component.scss index 28516af523..1ce10f8f2d 100644 --- a/ui-ngx/src/app/shared/components/logo.component.scss +++ b/ui-ngx/src/app/shared/components/logo.component.scss @@ -13,12 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +:host { + pointer-events: auto; + a { + border: none; + img { + width: 100%; + height: 100%; + } + } +} + :host-context(.login-logo) { - img.tb-logo-title { - width: 280px; - height: 60px; + width: 280px; + height: 60px; + a { text-decoration: none; - cursor: pointer; border: none; transform: none; diff --git a/ui-ngx/src/app/shared/components/logo.component.ts b/ui-ngx/src/app/shared/components/logo.component.ts index 377c25d0e0..1a91c03751 100644 --- a/ui-ngx/src/app/shared/components/logo.component.ts +++ b/ui-ngx/src/app/shared/components/logo.component.ts @@ -14,19 +14,43 @@ /// limitations under the License. /// -import { Component } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { AuthService } from '@core/auth/auth.service'; +import { AppState } from '@core/core.state'; +import { Store } from '@ngrx/store'; +import { UrlTree } from '@angular/router'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { UrlHolder } from '@shared/pipe/image.pipe'; @Component({ selector: 'tb-logo', templateUrl: './logo.component.html', styleUrls: ['./logo.component.scss'] }) -export class LogoComponent { +export class LogoComponent implements OnInit { - logo = 'assets/logo_title_white.svg'; + @Input() + src: string | UrlHolder = 'assets/logo_title_white.svg'; - gotoThingsboard(): void { - window.open('https://thingsboard.io', '_blank'); + @Input() + link: string | UrlTree; + + @Input() + target: string = null; + + isExternal = false; + + constructor(private authService: AuthService, + private store: Store) { } + ngOnInit() { + if (!this.link) { + const authState = getCurrentAuthState(this.store); + this.link = this.authService.defaultUrl(true, authState); + } + if (typeof this.link === 'string' && this.link.startsWith('http')) { + this.isExternal = true; + } + } } 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 a8db1b7bfd..92cae93988 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 @@ -71,6 +71,10 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit @coerceBoolean() iconClearButton = false; + @Input() + @coerceBoolean() + allowedCustomIcon = false; + private requiredValue: boolean; get required(): boolean { return this.requiredValue; @@ -169,7 +173,8 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit this.viewContainerRef, MaterialIconsComponent, 'left', true, null, { selectedIcon: this.materialIconFormGroup.get('icon').value, - iconClearButton: this.iconClearButton + iconClearButton: this.iconClearButton, + allowedCustomIcon: this.allowedCustomIcon, }, {}, {}, {}, true); diff --git a/ui-ngx/src/app/shared/components/material-icons.component.html b/ui-ngx/src/app/shared/components/material-icons.component.html index d9e87f11fd..97a661bfb1 100644 --- a/ui-ngx/src/app/shared/components/material-icons.component.html +++ b/ui-ngx/src/app/shared/components/material-icons.component.html @@ -16,63 +16,86 @@ -->
-
icon.icons
- - search - - - - -
- - - - +
+ icon.icons + @if (allowedCustomIcon) { + + {{ 'resource.system' | translate }} + {{ 'icon.custom' | translate }} + + + } +
+ @if (!isCustomIcon) { + + search + + + + +
+ + + + +
+
+ +
+
+
{{ 'icon.no-icons-found' | translate:{iconSearch: searchIconControl.value} }}
+
+
+
+ + +
- - -
-
-
{{ 'icon.no-icons-found' | translate:{iconSearch: searchIconControl.value} }}
+ } @else { + + +
+ +
- -
- - - -
+ }
diff --git a/ui-ngx/src/app/shared/components/material-icons.component.ts b/ui-ngx/src/app/shared/components/material-icons.component.ts index f2a91e27ad..8556bdbad4 100644 --- a/ui-ngx/src/app/shared/components/material-icons.component.ts +++ b/ui-ngx/src/app/shared/components/material-icons.component.ts @@ -37,6 +37,7 @@ 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'; +import { isTbImage } from '@shared/models/resource.models'; @Component({ selector: 'tb-material-icons', @@ -61,6 +62,10 @@ export class MaterialIconsComponent extends PageComponent implements OnInit { @coerceBoolean() showTitle = true; + @Input() + @coerceBoolean() + allowedCustomIcon = false; + @Input() popover: TbPopoverComponent; @@ -71,6 +76,8 @@ export class MaterialIconsComponent extends PageComponent implements OnInit { showAllSubject = new BehaviorSubject(false); searchIconControl: UntypedFormControl; + isCustomIcon = false; + iconsRowHeight = 48; iconsPanelHeight: string; @@ -122,14 +129,15 @@ export class MaterialIconsComponent extends PageComponent implements OnInit { map((data) => data.iconRows), share() ); + this.isCustomIcon = isTbImage(this.selectedIcon) } clearSearch() { this.searchIconControl.patchValue('', {emitEvent: true}); } - selectIcon(icon: MaterialIcon) { - this.iconSelected.emit(icon.name); + selectIcon(icon: string) { + this.iconSelected.emit(icon); } clearIcon() { 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 ec984b033d..86a634697a 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.html +++ b/ui-ngx/src/app/shared/components/notification/notification.component.html @@ -42,7 +42,7 @@ check_circle_outline
+ [style.background-color]="alarmColorSeverityBackground()"> {{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 0ff601bc97..7acdfba0c7 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.ts +++ b/ui-ngx/src/app/shared/components/notification/notification.component.ts @@ -17,7 +17,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ActionButtonLinkType, - AlarmSeverityNotificationColors, Notification, NotificationStatus, NotificationType, @@ -25,7 +24,11 @@ import { } from '@shared/models/notification.models'; import { UtilsService } from '@core/services/utils.service'; import { Router } from '@angular/router'; -import { alarmSeverityTranslations } from '@shared/models/alarm.models'; +import { + alarmSeverityBackgroundColors, + alarmSeverityColors, + alarmSeverityTranslations +} from '@shared/models/alarm.models'; import tinycolor from 'tinycolor2'; import { StateObject } from '@core/api/widget-api.models'; import { objToBase64URI } from '@core/utils'; @@ -134,13 +137,13 @@ export class NotificationComponent implements OnInit { } } - alarmColorSeverity(alpha: number) { - return tinycolor(AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity)).setAlpha(alpha).toRgbString(); + alarmColorSeverityBackground() { + return alarmSeverityBackgroundColors.get(this.notification.info.alarmSeverity); } notificationColor(): string { if (this.notification.type === NotificationType.ALARM && !this.notification.info.cleared) { - return AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity); + return alarmSeverityColors.get(this.notification.info.alarmSeverity); } return 'transparent'; } @@ -154,9 +157,11 @@ export class NotificationComponent implements OnInit { notificationIconColor(): object { if (this.notification.type === NotificationType.ALARM) { - return {color: AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity)}; + return {color: alarmSeverityColors.get(this.notification.info.alarmSeverity)}; } else if (this.notification.type === NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT) { return {color: '#D12730'}; + } else if (this.notification.type === NotificationType.ENTITIES_LIMIT_INCREASE_REQUEST) { + return {color: '#305680'}; } return null; } diff --git a/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.html b/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.html new file mode 100644 index 0000000000..534850034a --- /dev/null +++ b/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.html @@ -0,0 +1,35 @@ + + +
+ @for (rule of passwordErrorRules; track $index) { + @if (!rule.policyProp || passwordPolicy[rule.policyProp] > 0) { +

+ + {{ checkForError(rule.key) ? 'mdi:close' : 'mdi:check' }} + + {{ rule.translation | translate : passwordPolicy }} +

+ } + } +
+
+
diff --git a/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.scss b/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.scss new file mode 100644 index 0000000000..a0bd527632 --- /dev/null +++ b/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.scss @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.password-checklist-card { + background-color: #0000009E; + backdrop-filter: blur(8px); + color: white; + padding: 12px 16px; + border-radius: 8px; + position: relative; + min-width: 220px; + display: flex; + gap: 8px; + flex-direction: column; + + & > tb-icon { + color: white; + } + + & > p { + margin: 0; + } + + & > .tooltip-arrow { + position: absolute; + bottom: -6px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid #002b36; + } +} diff --git a/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.ts b/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.ts new file mode 100644 index 0000000000..391ebf1d69 --- /dev/null +++ b/ui-ngx/src/app/shared/components/password-requirements-tooltip.component.ts @@ -0,0 +1,53 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, ViewEncapsulation } from '@angular/core'; +import { CdkOverlayOrigin, ConnectionPositionPair } from '@angular/cdk/overlay'; +import { passwordErrorRules } from '@shared/models/password.models'; +import { AbstractControl } from '@angular/forms'; +import { UserPasswordPolicy } from '@shared/models/settings.models'; +import { POSITION_MAP } from '@shared/models/overlay.models'; + +@Component({ + selector: 'tb-password-requirements-tooltip', + templateUrl: './password-requirements-tooltip.component.html', + styleUrl: './password-requirements-tooltip.component.scss', + encapsulation: ViewEncapsulation.None +}) +export class PasswordRequirementsTooltipComponent { + @Input() passwordControl: AbstractControl; + @Input() passwordPolicy: UserPasswordPolicy; + @Input() trigger: CdkOverlayOrigin; + + passwordErrorRules = passwordErrorRules; + isTooltipOpen = false; + + overlayPositions: ConnectionPositionPair[] = [ + {...POSITION_MAP.top, offsetY: -20} + ]; + + checkForError(errorName: string): boolean { + return this.passwordControl?.hasError(errorName) ?? false; + } + + onFocus(): void { + this.isTooltipOpen = true; + } + + onBlur(): void { + this.isTooltipOpen = false; + } +} diff --git a/ui-ngx/src/app/shared/components/phone-input.component.html b/ui-ngx/src/app/shared/components/phone-input.component.html index 69ba1c6b64..663576bd70 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.html +++ b/ui-ngx/src/app/shared/components/phone-input.component.html @@ -37,12 +37,12 @@ (focus)="focus()" autocomplete="off" [required]="required"> - + - {{ 'phone-input.phone-input-required' | translate }} + {{ requiredErrorText }} - {{ 'phone-input.phone-input-validation' | translate }} + {{ validationErrorText }}
diff --git a/ui-ngx/src/app/shared/components/phone-input.component.ts b/ui-ngx/src/app/shared/components/phone-input.component.ts index 19fc14fdae..435605cb55 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.ts +++ b/ui-ngx/src/app/shared/components/phone-input.component.ts @@ -77,6 +77,15 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida @Input() label = this.translate.instant('phone-input.phone-input-label'); + @Input() + hint = 'phone-input.phone-input-hint'; + + @Input() + requiredErrorText = this.translate.instant('phone-input.phone-input-required'); + + @Input() + validationErrorText = this.translate.instant('phone-input.phone-input-validation'); + get showFlagSelect(): boolean { return this.enableFlagsSelect && !this.isLegacy; } diff --git a/ui-ngx/src/app/shared/components/public-api.ts b/ui-ngx/src/app/shared/components/public-api.ts index afa145d8bd..fb53ea309b 100644 --- a/ui-ngx/src/app/shared/components/public-api.ts +++ b/ui-ngx/src/app/shared/components/public-api.ts @@ -14,6 +14,7 @@ /// limitations under the License. /// +export * from './button/public-api'; export * from './dialog/color-picker-dialog.component'; export * from './dialog/material-icons-dialog.component'; export * from './page.component'; @@ -32,3 +33,4 @@ export * from './hint-tooltip-icon.component'; export * from './grid/scroll-grid-datasource'; export * from './grid/scroll-grid.component'; export * from './table/table-datasource.abstract'; +export * from './phone-input.component'; diff --git a/ui-ngx/src/app/shared/components/string-autocomplete.component.html b/ui-ngx/src/app/shared/components/string-autocomplete.component.html index 8ce80a82e9..8891fad40f 100644 --- a/ui-ngx/src/app/shared/components/string-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/string-autocomplete.component.html @@ -24,28 +24,28 @@ [matAutocomplete]="optionsAutocomplete"> - warning - - {{errorText}} + + {{ getErrorMessage }} - + diff --git a/ui-ngx/src/app/shared/components/string-autocomplete.component.scss b/ui-ngx/src/app/shared/components/string-autocomplete.component.scss index 6d219a04e2..267502ab13 100644 --- a/ui-ngx/src/app/shared/components/string-autocomplete.component.scss +++ b/ui-ngx/src/app/shared/components/string-autocomplete.component.scss @@ -20,23 +20,9 @@ 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; - } - } + &.tb-inline-field { + .mdc-icon-button { + margin-right: 8px; } } } diff --git a/ui-ngx/src/app/shared/components/string-autocomplete.component.ts b/ui-ngx/src/app/shared/components/string-autocomplete.component.ts index 1d3253dbf3..641720d3e2 100644 --- a/ui-ngx/src/app/shared/components/string-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/string-autocomplete.component.ts @@ -14,27 +14,25 @@ /// limitations under the License. /// -import { - Component, - Input, - forwardRef, - OnInit, - ViewChild, - ElementRef -} from '@angular/core'; +import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, - NG_VALUE_ACCESSOR, + FormBuilder, FormControl, - Validators, - FormBuilder + NG_VALUE_ACCESSOR, + ValidatorFn, + Validators } from '@angular/forms'; import { Observable, of } from 'rxjs'; -import { tap, map, switchMap, take } from 'rxjs/operators'; +import { map, switchMap, take, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { coerceBoolean } from '@shared/decorators/coercion'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +export interface ErrorMessageConfig { + [errorKey: string]: string; +} + @Component({ selector: 'tb-string-autocomplete', templateUrl: './string-autocomplete.component.html', @@ -85,6 +83,12 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit @Input() errorText: string; + @Input() + controlValidators: ValidatorFn[] = []; + + @Input() + errorMessages: ErrorMessageConfig; + @Input() @coerceBoolean() showInlineError = false; @@ -106,7 +110,17 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit } ngOnInit() { - this.selectionFormControl = this.fb.control('', this.required ? [Validators.required] : []); + const validators = [Validators.pattern(/.*\S.*/)]; + if (this.controlValidators?.length) { + validators.push(...this.controlValidators); + const parentHasRequired = this.controlValidators.some(v => v === Validators.required); + if (this.required && !parentHasRequired) { + validators.push(Validators.required); + } + } else if (this.required) { + validators.push(Validators.required); + } + this.selectionFormControl = this.fb.control('', validators); this.filteredOptions$ = this.selectionFormControl.valueChanges .pipe( tap(value => this.updateView(value)), @@ -152,7 +166,7 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit updateView(value: string) { this.searchText = value ? value : ''; if (this.modelValue !== value) { - this.modelValue = value; + this.modelValue = value?.trim(); this.propagateChange(this.modelValue); } } @@ -180,4 +194,18 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit this.nameInput.nativeElement.focus(); }, 0); } + + get getErrorMessage(): string { + if (!this.selectionFormControl.errors) { + return ''; + } + if (this.errorMessages) { + for (const errorKey in this.selectionFormControl.errors) { + if (this.errorMessages[errorKey]) { + return this.errorMessages[errorKey]; + } + } + } + return this.errorText; + } } 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 7a0ac10e83..106b79ab9a 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 @@ -52,7 +52,9 @@ @for (value of filteredValues | async; track value.value) { } @empty { - {{ 'common.not-found' | translate }} + @if (!allowUserValue) { + {{ 'common.not-found' | translate }} + } } 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 ed544438b2..c9be6b3c09 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 @@ -210,8 +210,8 @@ export class StringItemsListComponent implements ControlValueAccessor, OnInit { addOnBlur(event: FocusEvent) { const target: HTMLElement = event.relatedTarget as HTMLElement; - if (target && target.tagName !== 'MAT-OPTION') { - this.addItem(this.stringItemInput.nativeElement.value ?? '') + if ((target && target.tagName !== 'MAT-OPTION') || this.allowUserValue) { + this.addItem(this.stringItemInput.nativeElement.value ?? ''); } this.onTouched(); } diff --git a/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.html b/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.html new file mode 100644 index 0000000000..ef2dec83fe --- /dev/null +++ b/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.html @@ -0,0 +1,59 @@ + + + {{label}} +

+  
+  @if (predefinedValuesButton) {
+    
+  }
+  
+    warning
+  
+  
+    {{errorText}}
+  
+
+ +
+ @for(option of filteredOptions; track option; let i = $index) { + + + + } +
+
diff --git a/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.scss b/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.scss new file mode 100644 index 0000000000..aa68113205 --- /dev/null +++ b/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.scss @@ -0,0 +1,98 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "constants"; + +:host { + .highlighted-text { + position: absolute; + inset: 0; + margin: 0; + z-index: 1; + pointer-events: none; + padding-top: var(--mat-form-field-filled-with-label-container-padding-top); + padding-bottom: var(--mat-form-field-filled-with-label-container-padding-bottom); + font: inherit; + letter-spacing: inherit; + text-decoration: inherit; + text-transform: inherit; + overflow: hidden; + color: var(--mdc-filled-text-field-input-text-color, var(--mat-app-on-surface)); + + &.disabled { + color: var(--mdc-filled-text-field-disabled-input-text-color); + } + } + + .input-field-highlight { + position: relative; + background: transparent; + z-index: 2; + color: transparent; + width: 100%; + } +} + +:host ::ng-deep { + + .mdc-text-field--outlined .mat-mdc-form-field-infix { + .highlighted-text { + padding-top: var(--mat-form-field-container-vertical-padding); + padding-bottom: var(--mat-form-field-container-vertical-padding); + } + } + + .highlighted-text { + .highlight { + color: $tb-secondary-color; + text-shadow: 0 0 0.5px var($tb-secondary-color, var(--mat-app-on-surface)); + } + + &.disabled { + .highlight { + color: $tb-primary-color-light; + text-shadow: 0 0 0.5px var($tb-primary-color-light, var(--mdc-filled-text-field-disabled-input-text-color)); + } + } + } +} + +::ng-deep { + div.tb-sting-pattern-autocomplete-panel { + width: 100%; + max-height: 256px; + visibility: visible; + transform-origin: center top; + overflow: auto; + padding: 8px 0; + box-sizing: border-box; + position: static; + border-radius: var(--mat-autocomplete-container-shape, var(--mat-app-corner-extra-small)); + box-shadow: var(--mat-autocomplete-container-elevation-shadow); + background-color: var(--mat-autocomplete-background-color, var(--mat-app-surface-container)) + } + + .cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) & { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + .mat-mdc-autocomplete-panel-above & { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + transform-origin: center bottom; + } + +} diff --git a/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.ts b/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.ts new file mode 100644 index 0000000000..17bce4b9d7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/string-pattern-autocomplete.component.ts @@ -0,0 +1,434 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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, + DestroyRef, + ElementRef, + forwardRef, + Input, + OnChanges, + OnInit, + QueryList, + SimpleChanges, + TemplateRef, + ViewChild, + ViewChildren, + ViewContainerRef +} from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { fromEvent, merge } from 'rxjs'; +import { debounceTime, tap } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + ConnectedPosition, + FlexibleConnectedPositionStrategy, + Overlay, + OverlayRef, + PositionStrategy +} from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; + +@Component({ + selector: 'tb-string-pattern-autocomplete', + templateUrl: './string-pattern-autocomplete.component.html', + styleUrls: ['./string-pattern-autocomplete.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => StringPatternAutocompleteComponent), + multi: true + } + ] +}) +export class StringPatternAutocompleteComponent implements ControlValueAccessor, OnInit, OnChanges { + + @ViewChild('inputRef', {static: true}) inputRef: ElementRef; + @ViewChild('highlightTextRef', {static: true}) highlightTextRef: ElementRef; + @ViewChild('autocompleteTemplate', {static: true}) autocompleteTemplate: TemplateRef; + @ViewChildren('optionItem', { read: ElementRef }) optionItems!: QueryList>; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + required = false; + + @Input({required: true}) + predefinedValues: Array; + + @Input() + placeholderText: string = this.translate.instant('widget-config.set'); + + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + + @Input() + additionalClass: string | string[] | Record; + + @Input() + appearance: MatFormFieldAppearance = 'fill'; + + @Input() + label: string; + + @Input() + tooltipClass = 'tb-error-tooltip'; + + @Input() + errorText: string; + + @Input() + @coerceBoolean() + showInlineError = false; + + @Input() + @coerceBoolean() + predefinedValuesButton = false; + + @Input() + patternSymbol = '$'; + + @Input() + brackets: 'curly' | 'square'; + + selectionFormControl: FormControl; + filteredOptions: Array; + + searchText = ''; + highlightedHtml = ''; + activeOptionIndex = -1; + + private modelValue: string | null; + private overlayRef!: OverlayRef; + + private predefinedValuesButtonMode = false; + + private propagateChange = (_val: any) => { + }; + + constructor(private fb: FormBuilder, + private overlay: Overlay, + private translate: TranslateService, + private viewContainerRef: ViewContainerRef, + private destroyRef: DestroyRef, + private cd: ChangeDetectorRef) { + } + + ngOnInit() { + this.selectionFormControl = this.fb.control('', this.required ? [Validators.required] : []); + merge( + fromEvent(this.inputRef.nativeElement, 'selectionchange'), + this.selectionFormControl.valueChanges.pipe(tap(value => this.updateView(value))) + ).pipe( + debounceTime(50), + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.onSelectionChange(); + this.cd.markForCheck(); + }); + + fromEvent(this.inputRef.nativeElement, 'keydown') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(event => this.handleKeydown(event)); + } + + ngOnChanges(changes: SimpleChanges) { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (propName === 'predefinedValues' || propName === 'patternSymbol') { + this.highlightedHtml = this.getHighlightHtml(this.modelValue); + } + if (propName === 'required') { + if (change.currentValue) { + this.selectionFormControl.addValidators(Validators.required); + } else { + this.selectionFormControl.removeValidators(Validators.required); + } + this.selectionFormControl.updateValueAndValidity({emitEvent: false}) + } + } + } + } + + writeValue(option?: string): void { + this.searchText = ''; + this.modelValue = option ?? null; + this.highlightedHtml = this.getHighlightHtml(this.modelValue); + this.selectionFormControl.patchValue(this.modelValue, {emitEvent: false}); + } + + onFocus() { + this.onSelectionChange(); + } + + 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}); + this.closeAutocomplete(); + } else { + this.selectionFormControl.enable({emitEvent: false}); + } + } + + onInputScroll(event: Event) { + const scrollLeft = (event.target as HTMLInputElement).scrollLeft; + const scrollTop = (event.target as HTMLInputElement).scrollTop; + if (this.highlightTextRef && this.highlightTextRef.nativeElement) { + this.highlightTextRef.nativeElement.scrollLeft = scrollLeft; + this.highlightTextRef.nativeElement.scrollTop = scrollTop; + } + } + + optionSelected(value: string) { + const position = this.inputRef.nativeElement.selectionStart; + const triggerIndex = this.predefinedValuesButtonMode ? position : this.modelValue.lastIndexOf(this.patternSymbol, position - 1); + if (triggerIndex === -1) { + return; + } + let prepareValue: string; + switch (this.brackets) { + case 'curly': + prepareValue = `{${value}}`; + break; + case 'square': + prepareValue = `[${value}]`; + break; + default: + prepareValue = value; + } + const base = this.modelValue ?? ''; + const newText = `${base.substring(0, triggerIndex)}${this.patternSymbol}${prepareValue}${base.substring(position)}`; + this.selectionFormControl.patchValue(newText); + this.searchText = ''; + setTimeout(() => { + this.inputRef.nativeElement.setSelectionRange(triggerIndex + prepareValue.length + 1, triggerIndex + prepareValue.length + 1); + this.inputRef.nativeElement.focus(); + }); + this.closeAutocomplete(); + } + + openValues($event: MouseEvent) { + $event.stopPropagation(); + const selectionStart = this.inputRef.nativeElement.selectionStart; + this.filteredOptions = [...this.predefinedValues]; + this.predefinedValuesButtonMode = true; + this.openAutocomplete(selectionStart); + } + + private updateView(value: string) { + if (this.modelValue !== value) { + this.modelValue = value; + this.highlightedHtml = this.getHighlightHtml(this.modelValue); + this.propagateChange(this.modelValue); + } + } + + private onSelectionChange() { + const selectionStart = this.inputRef.nativeElement.selectionStart; + const selectionEnd = this.inputRef.nativeElement.selectionEnd; + if (selectionStart === selectionEnd && selectionStart > 0 && !this.disabled) { + this.filteredOptions = this.getFilteredOptions(this.modelValue, selectionStart); + this.activeOptionIndex = 0; + } else { + this.filteredOptions = []; + this.activeOptionIndex = -1; + } + if (this.filteredOptions.length) { + this.openAutocomplete(selectionStart); + } else { + this.closeAutocomplete() + } + } + + private getHighlightHtml(text: string): string { + if (!text) { + return ''; + } + let regex: RegExp; + let simpleGroup = false; + text = text.replace(//g, '>'); + switch (this.brackets) { + case 'curly': + regex = new RegExp(`([${this.patternSymbol}](\\{([^}]*)\\}))`, 'g'); + break; + case 'square': + regex = new RegExp(`([${this.patternSymbol}](\\[([^]]*)\\]))`, 'g'); + break; + default: + regex = new RegExp(`([${this.patternSymbol}](\\w+))`, 'g'); + simpleGroup = true + break + } + return text.replace(regex, (_match: string, p1: string, p2: string, p3: string) => { + const value = simpleGroup ? p2 : p3; + if (this.predefinedValues.includes(value)) { + return `${p1}` + } + return p1; + }); + } + + private getFilteredOptions(text: string, index = text.length): Array { + const triggerIndex = text.lastIndexOf(this.patternSymbol, index - 1); + if (triggerIndex === -1) { + return []; + } + + let currentWordEndIndex = text.indexOf(' ', index); + + if (currentWordEndIndex === -1) { + currentWordEndIndex = text.length; + } + + let startOffset = 1; + switch (this.brackets) { + case 'curly': + if (text[triggerIndex + startOffset] === '{') { + startOffset++; + } + break; + case 'square': + if (text[triggerIndex + startOffset] === '[') { + startOffset++; + } + break; + } + + this.searchText = text.substring(triggerIndex + startOffset, currentWordEndIndex).toLowerCase(); + if (this.searchText.includes(' ')) { + return []; + } + + const result = this.predefinedValues.filter(value => value.toLowerCase().startsWith(this.searchText)); + if (result.length === 1 && result[0].toLowerCase() === this.searchText) { + return []; + } + return result; + } + + private openAutocomplete(cursorIndex?: number): void { + const patternIndex = this.predefinedValuesButtonMode ? cursorIndex : this.modelValue.lastIndexOf(this.patternSymbol, cursorIndex - 1); + if (!this.overlayRef) { + this.overlayRef = this.overlay.create({ + positionStrategy: this.getOverlayPosition(patternIndex), + scrollStrategy: this.overlay.scrollStrategies.reposition(), + panelClass: 'tb-select-overlay', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true + }); + this.overlayRef.backdropClick().pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.closeAutocomplete()); + } + if (!this.overlayRef.hasAttached()) { + const templatePortal = new TemplatePortal(this.autocompleteTemplate, this.viewContainerRef); + this.overlayRef.attach(templatePortal); + this.overlayRef.updatePositionStrategy(this.getOverlayPosition(patternIndex)) + } + } + + private closeAutocomplete(): void { + this.predefinedValuesButtonMode = false; + if (this.overlayRef && this.overlayRef.hasAttached()) { + this.overlayRef.detach(); + } + } + + private getOverlayPosition(patternIndex: number): PositionStrategy { + const strategy = this.overlay + .position() + .flexibleConnectedTo(this.inputRef) + .withFlexibleDimensions(false) + .withPush(false); + + this.setStrategyPositions(strategy, patternIndex); + return strategy; + } + + private setStrategyPositions(positionStrategy: FlexibleConnectedPositionStrategy, patternIndex: number) { + let offsetX = patternIndex > 0 ? patternIndex * 8 : 0; + const inputBounds = this.inputRef.nativeElement.getBoundingClientRect(); + if (offsetX + 180 > inputBounds.width) { + offsetX = inputBounds.width - 180; + } + const belowPositions: ConnectedPosition[] = [ + {originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetX}, + {originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetX}, + ]; + + const panelClass = 'mat-mdc-autocomplete-panel-above'; + const abovePositions: ConnectedPosition[] = [ + {originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', panelClass, offsetX}, + {originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', panelClass, offsetX}, + ]; + + const positions = [...belowPositions, ...abovePositions]; + positionStrategy.withPositions(positions); + } + + private setActiveOption(index: number): void { + if (this.filteredOptions.length === 0) { + this.activeOptionIndex = -1; + return; + } + + this.activeOptionIndex = Math.max(-1, Math.min(index, this.filteredOptions.length - 1)); + this.cd.markForCheck(); + + if (this.activeOptionIndex >= 0 && this.optionItems.get(this.activeOptionIndex)) { + this.optionItems.get(this.activeOptionIndex).nativeElement.scrollIntoView({ block: 'nearest' }) + } + } + + private handleKeydown(event: KeyboardEvent): void { + if (!this.overlayRef?.hasAttached()) { + return; + } + + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + this.setActiveOption(this.activeOptionIndex + 1); + break; + case 'ArrowUp': + event.preventDefault(); + this.setActiveOption(this.activeOptionIndex - 1); + break; + case 'Enter': + event.preventDefault(); + if (this.activeOptionIndex >= 0 && this.activeOptionIndex < this.filteredOptions.length) { + this.optionSelected(this.filteredOptions[this.activeOptionIndex]); + } + break; + case 'Escape': + this.closeAutocomplete(); + break; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html b/ui-ngx/src/app/shared/components/time-unit-input.component.html similarity index 62% rename from ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html rename to ui-ngx/src/app/shared/components/time-unit-input.component.html index 8565f1fcd6..1edb3361e1 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html +++ b/ui-ngx/src/app/shared/components/time-unit-input.component.html @@ -15,34 +15,35 @@ limitations under the License. --> -
- + + subscriptSizing="dynamic"> @if (labelText && !inlineField) { {{ labelText }} } - +
@if (inlineField) { - warning - - } @else { - - - {{ hasError }} - + matTooltipPosition="above" + matTooltipClass="tb-error-tooltip" + [matTooltip]="hasError" + *ngIf="hasError" + class="tb-error"> + warning + } + {{ hintText }} + + {{ hasError }} +
- @if (!inlineField) { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts b/ui-ngx/src/app/shared/components/time-unit-input.component.ts similarity index 52% rename from ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts rename to ui-ngx/src/app/shared/components/time-unit-input.component.ts index 484c2da12f..6314c540f9 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/time-unit-input.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -23,9 +23,10 @@ import { NG_VALUE_ACCESSOR, ValidationErrors, Validator, + ValidatorFn, Validators } from '@angular/forms'; -import { TimeUnit, timeUnitTranslations } from '../rule-node-config.models'; +import { TimeUnit, timeUnitTranslations } from '@home/components/rule-node/rule-node-config.models'; import { isDefinedAndNotNull, isNumeric } from '@core/utils'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { coerceBoolean, coerceNumber } from '@shared/decorators/coercion'; @@ -50,11 +51,14 @@ interface TimeUnitInputModel { multi: true }] }) -export class TimeUnitInputComponent implements ControlValueAccessor, Validator, OnInit { +export class TimeUnitInputComponent implements ControlValueAccessor, Validator, OnInit, OnChanges { @Input() labelText: string; + @Input() + hintText: string; + @Input() @coerceBoolean() required: boolean; @@ -76,6 +80,13 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, @Input() maxErrorText: string; + @Input() + @coerceNumber() + stepMultipleOf: number; + + @Input() + stepMultipleOfErrorText: string; + @Input() subscriptSizing: SubscriptSizing = 'fixed'; @@ -86,6 +97,13 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, @coerceBoolean() inlineField: boolean; + @Input() + @coerceBoolean() + sameWidthInputs: boolean = false; + + @Input() + containerClass: string | string[] | Record = "flex gap-4"; + timeUnits = Object.values(TimeUnit).filter(item => item !== TimeUnit.MILLISECONDS) as TimeUnit[]; timeUnitTranslations = timeUnitTranslations; @@ -102,6 +120,8 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, [TimeUnit.SECONDS, SECOND/SECOND], ]); + minValueValidator = 0; + private modelValue: number; private propagateChange: (value: any) => void = () => {}; @@ -111,39 +131,16 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, } ngOnInit() { - if (this.maxTime) { - const maxTimeMs = this.maxTime * SECOND; - if (maxTimeMs < MINUTE) { - this.timeUnits = this.timeUnits.filter(item => item !== TimeUnit.MINUTES && item !== TimeUnit.HOURS && item !== TimeUnit.DAYS); - } else if (maxTimeMs < HOUR) { - this.timeUnits = this.timeUnits.filter(item => item !== TimeUnit.HOURS && item !== TimeUnit.DAYS); - } else if (maxTimeMs < DAY) { - this.timeUnits = this.timeUnits.filter(item => item !== TimeUnit.DAYS); - } + if (isDefinedAndNotNull(this.maxTime)) { + this.updatedAllowTimeUnitInterval(this.maxTime); } - if(this.required || this.maxTime) { - const timeControl = this.timeInputForm.get('time'); - const validators = [Validators.pattern(/^\d*$/)]; - if (this.required) { - validators.push(Validators.required); - } - if (this.maxTime) { - validators.push((control: AbstractControl) => - Validators.max(Math.floor(this.maxTime / this.timeIntervalsInSec.get(this.timeInputForm.get('timeUnit').value)))(control) - ); - } - if (isDefinedAndNotNull(this.minTime)) { - validators.push(Validators.min(this.minTime)); - } - timeControl.setValidators(validators); - timeControl.updateValueAndValidity({ emitEvent: false }); - } + this.refreshTimeValidators(); this.timeInputForm.get('timeUnit').valueChanges.pipe( takeUntilDestroyed(this.destroyRef) ).subscribe(() => { - this.timeInputForm.get('time').updateValueAndValidity({onlySelf: true}); + this.refreshTimeValidators(); this.timeInputForm.get('time').markAsTouched({onlySelf: true}); }); @@ -154,6 +151,10 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, }); } + get minValue(): number { + return Math.ceil(this.minTime / this.timeIntervalsInSec.get(this.timeInputForm.get('timeUnit').value)); + } + get hasError(): string { if (this.timeInputForm.get('time').hasError('required') && this.requiredText) { return this.requiredText; @@ -161,6 +162,25 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, return this.minErrorText; } else if (this.timeInputForm.get('time').hasError('max') && this.maxErrorText) { return this.maxErrorText; + } else if (this.timeInputForm.get('time').hasError('stepMultipleOf') && this.stepMultipleOfErrorText) { + return this.stepMultipleOfErrorText; + } + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (propName === 'maxTime') { + if (isDefinedAndNotNull(this.maxTime)) { + this.timeUnits = Object.values(TimeUnit).filter(item => item !== TimeUnit.MILLISECONDS) as TimeUnit[]; + this.updatedAllowTimeUnitInterval(this.maxTime); + } + } + if (['minTime', 'maxTime', 'required', 'stepMultipleOf'].includes(propName)) { + this.refreshTimeValidators(); + } + } } } @@ -176,7 +196,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, this.timeInputForm.disable({emitEvent: false}); } else { this.timeInputForm.enable({emitEvent: false}); - if(this.timeInputForm.invalid) { + if(!this.timeInputForm.valid) { setTimeout(() => this.updatedModel(this.timeInputForm.value, true)) } } @@ -185,7 +205,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, writeValue(sec: number) { if (sec !== this.modelValue) { if (isDefinedAndNotNull(sec) && isNumeric(sec) && Number(sec) !== 0) { - this.timeInputForm.patchValue(this.parseTime(sec), {emitEvent: false}); + this.timeInputForm.patchValue(this.parseTime(sec), {emitEvent: true}); this.modelValue = sec; } else { this.timeInputForm.patchValue({ @@ -198,7 +218,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, } validate(): ValidationErrors | null { - return this.timeInputForm.valid ? null : { + return this.timeInputForm.disabled || this.timeInputForm.valid ? null : { timeInput: false }; } @@ -223,4 +243,80 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, } } + private createStepMultipleOfValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const time = control.value; + if (!isDefinedAndNotNull(time) || !isNumeric(time)) { + return null; + } + const numericTime = Number(time); + if (numericTime === 0) { + return null; + } + + const timeUnit = control.parent?.get('timeUnit')?.value as TimeUnit; + if (!timeUnit) { + return null; + } + + const unitInSec = this.timeIntervalsInSec.get(timeUnit); + const totalTimeInSec = numericTime * unitInSec; + const multipleOfVal = this.stepMultipleOf; + + let isValid: boolean; + if (totalTimeInSec < multipleOfVal) { + isValid = (multipleOfVal % totalTimeInSec === 0); + } else { + isValid = (totalTimeInSec % multipleOfVal === 0); + } + return isValid ? null : { stepMultipleOf: true }; + }; + } + + private updatedAllowTimeUnitInterval(maxTime: number) { + const maxTimeMs = maxTime * SECOND; + this.timeUnits = Object.values(TimeUnit).filter(item => item !== TimeUnit.MILLISECONDS) as TimeUnit[]; + if (maxTimeMs < MINUTE) { + this.timeUnits = this.timeUnits.filter(item => item !== TimeUnit.MINUTES && item !== TimeUnit.HOURS && item !== TimeUnit.DAYS); + } else if (maxTimeMs < HOUR) { + this.timeUnits = this.timeUnits.filter(item => item !== TimeUnit.HOURS && item !== TimeUnit.DAYS); + } else if (maxTimeMs < DAY) { + this.timeUnits = this.timeUnits.filter(item => item !== TimeUnit.DAYS); + } + } + + private refreshTimeValidators() { + const timeControl = this.timeInputForm.get('time'); + if (!timeControl) return; + + const currentUnit = this.timeInputForm.get('timeUnit')?.value as TimeUnit; + if (!currentUnit) return; + + const secondsInUnit = this.timeIntervalsInSec.get(currentUnit) ?? 1; + const validators: ValidatorFn[] = [Validators.pattern(/^\d*$/)]; + + if (this.required) { + validators.push(Validators.required); + } + + if (isDefinedAndNotNull(this.minTime)) { + this.minValueValidator = Math.ceil(this.minTime / secondsInUnit); + validators.push(Validators.min(this.minValueValidator)); + } else { + this.minValueValidator = 0; + validators.push(Validators.min(this.minValueValidator)); + } + + if (isDefinedAndNotNull(this.maxTime)) { + const maxInCurrentUnit = Math.floor(this.maxTime / secondsInUnit); + validators.push(Validators.max(maxInCurrentUnit)); + } + + if (isDefinedAndNotNull(this.stepMultipleOf) && this.stepMultipleOf > 0) { + validators.push(this.createStepMultipleOfValidator()); + } + + timeControl.setValidators(validators); + timeControl.updateValueAndValidity({ onlySelf: true, emitEvent: false }); + } } diff --git a/ui-ngx/src/app/shared/components/time/datapoints-limit.component.scss b/ui-ngx/src/app/shared/components/time/datapoints-limit.component.scss index be3170428c..37b0e5b46e 100644 --- a/ui-ngx/src/app/shared/components/time/datapoints-limit.component.scss +++ b/ui-ngx/src/app/shared/components/time/datapoints-limit.component.scss @@ -29,6 +29,9 @@ .mdc-slider { height: 40px; + .mdc-slider__input { + top: 0; + } ::ng-deep { .mdc-slider__thumb { left: -20px; diff --git a/ui-ngx/src/app/shared/components/time/datapoints-limit.component.ts b/ui-ngx/src/app/shared/components/time/datapoints-limit.component.ts index ad41e51d2a..aa10635542 100644 --- a/ui-ngx/src/app/shared/components/time/datapoints-limit.component.ts +++ b/ui-ngx/src/app/shared/components/time/datapoints-limit.component.ts @@ -29,6 +29,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { TimeService } from '@core/services/time.service'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-datapoints-limit', @@ -69,7 +70,11 @@ export class DatapointsLimitComponent implements ControlValueAccessor, Validator @Input() disabled: boolean; - private propagateChange = (v: any) => { }; + private propagateChangeValue: any; + + private propagateChange = (v: any) => { + this.propagateChangeValue = v; + }; private destroy$ = new Subject(); @@ -79,6 +84,9 @@ export class DatapointsLimitComponent implements ControlValueAccessor, Validator registerOnChange(fn: any): void { this.propagateChange = fn; + if (isDefined(this.propagateChangeValue)) { + this.propagateChange(this.propagateChangeValue); + } } registerOnTouched(fn: any): void { @@ -115,19 +123,20 @@ export class DatapointsLimitComponent implements ControlValueAccessor, Validator } } - private checkLimit(limit?: number): number { - if (!limit || limit < this.minDatapointsLimit()) { - return this.minDatapointsLimit(); + writeValue(value: number | null): void { + this.modelValue = value; + let limit = this.modelValue; + if (!limit) { + limit = Math.ceil(this.maxDatapointsLimit() / 2); + } else if (limit < this.minDatapointsLimit()) { + limit = this.minDatapointsLimit(); } else if (limit > this.maxDatapointsLimit()) { - return this.maxDatapointsLimit(); + limit = this.maxDatapointsLimit(); } - return limit; - } - writeValue(value: number | null): void { - this.modelValue = this.checkLimit(value); + this.updateView(limit); this.datapointsLimitFormGroup.patchValue( - { limit: this.modelValue }, {emitEvent: false} + { limit: limit }, {emitEvent: false} ); } diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html index 446d55d7b2..c3aee54ac9 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html @@ -301,6 +301,16 @@
+ @if (showSaveAsDefault) { +
+
timewindow.save-current-settings-as-default
+
+ + {{ 'timewindow.hide-option-from-end-users' | translate }} + +
+
+ }
diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index 430ddd5f22..ed56dfa46f 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -36,7 +36,15 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TimeService } from '@core/services/time.service'; -import { deepClone, isDefined, isDefinedAndNotNull, isObject, mergeDeep } from '@core/utils'; +import { + deepClean, + deepClone, + deleteFalseProperties, + isDefined, + isDefinedAndNotNull, + isEmpty, + mergeDeepIgnoreArray +} from '@core/utils'; import { ToggleHeaderOption } from '@shared/components/toggle-header.component'; import { TranslateService } from '@ngx-translate/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -54,6 +62,7 @@ import { export interface TimewindowConfigDialogData { quickIntervalOnly: boolean; aggregation: boolean; + showSaveAsDefault: boolean; timewindow: Timewindow; } @@ -68,6 +77,8 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On aggregation = false; + showSaveAsDefault = false; + timewindowForm: FormGroup; historyTypes = HistoryWindowType; @@ -132,6 +143,7 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On super(store); this.quickIntervalOnly = data.quickIntervalOnly; this.aggregation = data.aggregation; + this.showSaveAsDefault = data.showSaveAsDefault; this.timewindow = data.timewindow; if (!this.quickIntervalOnly) { @@ -152,73 +164,75 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm = this.fb.group({ selectedTab: [isDefined(this.timewindow.selectedTab) ? this.timewindow.selectedTab : TimewindowType.REALTIME], realtime: this.fb.group({ - realtimeType: [ isDefined(realtime?.realtimeType) ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL ], - timewindowMs: [ isDefined(realtime?.timewindowMs) ? this.timewindow.realtime.timewindowMs : null ], - interval: [ isDefined(realtime?.interval) ? this.timewindow.realtime.interval : null ], - quickInterval: [ isDefined(realtime?.quickInterval) ? this.timewindow.realtime.quickInterval : null ], - disableCustomInterval: [ isDefinedAndNotNull(this.timewindow.realtime?.disableCustomInterval) - ? this.timewindow.realtime?.disableCustomInterval : false ], - disableCustomGroupInterval: [ isDefinedAndNotNull(this.timewindow.realtime?.disableCustomGroupInterval) - ? this.timewindow.realtime?.disableCustomGroupInterval : false ], - hideInterval: [ isDefinedAndNotNull(this.timewindow.realtime.hideInterval) - ? this.timewindow.realtime.hideInterval : false ], + realtimeType: [ this.quickIntervalOnly + ? RealtimeWindowType.INTERVAL + : (isDefined(realtime?.realtimeType) ? realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL) ], + timewindowMs: [ isDefined(realtime?.timewindowMs) ? realtime.timewindowMs : null ], + interval: [ isDefined(realtime?.interval) ? realtime.interval : null ], + quickInterval: [ isDefined(realtime?.quickInterval) ? realtime.quickInterval : null ], + disableCustomInterval: [ isDefinedAndNotNull(realtime?.disableCustomInterval) + ? realtime.disableCustomInterval : false ], + disableCustomGroupInterval: [ isDefinedAndNotNull(realtime?.disableCustomGroupInterval) + ? realtime.disableCustomGroupInterval : false ], + hideInterval: [ isDefinedAndNotNull(realtime?.hideInterval) + ? realtime.hideInterval : false ], hideLastInterval: [{ - value: isDefinedAndNotNull(this.timewindow.realtime.hideLastInterval) - ? this.timewindow.realtime.hideLastInterval : false, - disabled: this.timewindow.realtime.hideInterval + value: isDefinedAndNotNull(realtime?.hideLastInterval) + ? realtime.hideLastInterval : false, + disabled: realtime?.hideInterval }], hideQuickInterval: [{ - value: isDefinedAndNotNull(this.timewindow.realtime.hideQuickInterval) - ? this.timewindow.realtime.hideQuickInterval : false, - disabled: this.timewindow.realtime.hideInterval + value: isDefinedAndNotNull(realtime?.hideQuickInterval) + ? realtime.hideQuickInterval : false, + disabled: realtime?.hideInterval }], advancedParams: this.fb.group({ - allowedLastIntervals: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.allowedLastIntervals) - ? this.timewindow.realtime.advancedParams.allowedLastIntervals : null ], - allowedQuickIntervals: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.allowedQuickIntervals) - ? this.timewindow.realtime.advancedParams.allowedQuickIntervals : null ], - lastAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.lastAggIntervalsConfig) - ? this.timewindow.realtime.advancedParams.lastAggIntervalsConfig : null ], - quickAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.quickAggIntervalsConfig) - ? this.timewindow.realtime.advancedParams.quickAggIntervalsConfig : null ] + allowedLastIntervals: [ isDefinedAndNotNull(realtime?.advancedParams?.allowedLastIntervals) + ? realtime.advancedParams.allowedLastIntervals : null ], + allowedQuickIntervals: [ isDefinedAndNotNull(realtime?.advancedParams?.allowedQuickIntervals) + ? realtime.advancedParams.allowedQuickIntervals : null ], + lastAggIntervalsConfig: [ isDefinedAndNotNull(realtime?.advancedParams?.lastAggIntervalsConfig) + ? realtime.advancedParams.lastAggIntervalsConfig : null ], + quickAggIntervalsConfig: [ isDefinedAndNotNull(realtime?.advancedParams?.quickAggIntervalsConfig) + ? realtime.advancedParams.quickAggIntervalsConfig : null ] }) }), history: this.fb.group({ - historyType: [ isDefined(history?.historyType) ? this.timewindow.history.historyType : HistoryWindowType.LAST_INTERVAL ], - timewindowMs: [ isDefined(history?.timewindowMs) ? this.timewindow.history.timewindowMs : null ], - interval: [ isDefined(history?.interval) ? this.timewindow.history.interval : null ], - fixedTimewindow: [ isDefined(history?.fixedTimewindow) ? this.timewindow.history.fixedTimewindow : null ], - quickInterval: [ isDefined(history?.quickInterval) ? this.timewindow.history.quickInterval : null ], - disableCustomInterval: [ isDefinedAndNotNull(this.timewindow.history?.disableCustomInterval) - ? this.timewindow.history?.disableCustomInterval : false ], - disableCustomGroupInterval: [ isDefinedAndNotNull(this.timewindow.history?.disableCustomGroupInterval) - ? this.timewindow.history?.disableCustomGroupInterval : false ], - hideInterval: [ isDefinedAndNotNull(this.timewindow.history.hideInterval) - ? this.timewindow.history.hideInterval : false ], + historyType: [ isDefined(history?.historyType) ? history.historyType : HistoryWindowType.LAST_INTERVAL ], + timewindowMs: [ isDefined(history?.timewindowMs) ? history.timewindowMs : null ], + interval: [ isDefined(history?.interval) ? history.interval : null ], + fixedTimewindow: [ isDefined(history?.fixedTimewindow) ? history.fixedTimewindow : null ], + quickInterval: [ isDefined(history?.quickInterval) ? history.quickInterval : null ], + disableCustomInterval: [ isDefinedAndNotNull(history?.disableCustomInterval) + ? history.disableCustomInterval : false ], + disableCustomGroupInterval: [ isDefinedAndNotNull(history?.disableCustomGroupInterval) + ? history.disableCustomGroupInterval : false ], + hideInterval: [ isDefinedAndNotNull(history?.hideInterval) + ? history.hideInterval : false ], hideLastInterval: [{ - value: isDefinedAndNotNull(this.timewindow.history.hideLastInterval) - ? this.timewindow.history.hideLastInterval : false, - disabled: this.timewindow.history.hideInterval + value: isDefinedAndNotNull(history?.hideLastInterval) + ? history.hideLastInterval : false, + disabled: history?.hideInterval }], hideQuickInterval: [{ - value: isDefinedAndNotNull(this.timewindow.history.hideQuickInterval) - ? this.timewindow.history.hideQuickInterval : false, - disabled: this.timewindow.history.hideInterval + value: isDefinedAndNotNull(history?.hideQuickInterval) + ? history.hideQuickInterval : false, + disabled: history?.hideInterval }], hideFixedInterval: [{ - value: isDefinedAndNotNull(this.timewindow.history.hideFixedInterval) - ? this.timewindow.history.hideFixedInterval : false, - disabled: this.timewindow.history.hideInterval + value: isDefinedAndNotNull(history?.hideFixedInterval) + ? history.hideFixedInterval : false, + disabled: history?.hideInterval }], advancedParams: this.fb.group({ - allowedLastIntervals: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.allowedLastIntervals) - ? this.timewindow.history.advancedParams.allowedLastIntervals : null ], - allowedQuickIntervals: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.allowedQuickIntervals) - ? this.timewindow.history.advancedParams.allowedQuickIntervals : null ], - lastAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.lastAggIntervalsConfig) - ? this.timewindow.history.advancedParams.lastAggIntervalsConfig : null ], - quickAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.quickAggIntervalsConfig) - ? this.timewindow.history.advancedParams.quickAggIntervalsConfig : null ] + allowedLastIntervals: [ isDefinedAndNotNull(history?.advancedParams?.allowedLastIntervals) + ? history.advancedParams.allowedLastIntervals : null ], + allowedQuickIntervals: [ isDefinedAndNotNull(history?.advancedParams?.allowedQuickIntervals) + ? history.advancedParams.allowedQuickIntervals : null ], + lastAggIntervalsConfig: [ isDefinedAndNotNull(history?.advancedParams?.lastAggIntervalsConfig) + ? history.advancedParams.lastAggIntervalsConfig : null ], + quickAggIntervalsConfig: [ isDefinedAndNotNull(history?.advancedParams?.quickAggIntervalsConfig) + ? history.advancedParams.quickAggIntervalsConfig : null ] }) }), aggregation: this.fb.group({ @@ -233,7 +247,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On hideAggInterval: [ isDefinedAndNotNull(this.timewindow.hideAggInterval) ? this.timewindow.hideAggInterval : false ], hideTimezone: [ isDefinedAndNotNull(this.timewindow.hideTimezone) - ? this.timewindow.hideTimezone : false ] + ? this.timewindow.hideTimezone : false ], + hideSaveAsDefault: [ isDefinedAndNotNull(this.timewindow.hideSaveAsDefault) + ? this.timewindow.hideSaveAsDefault : false ], }); this.updateValidators(this.timewindowForm.get('aggregation.type').value); @@ -415,16 +431,11 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On realtimeDisableCustomInterval, historyDisableCustomInterval, timewindowFormValue.realtime.advancedParams, timewindowFormValue.history.advancedParams, this.realtimeTimewindowOptions, this.historyTimewindowOptions); - this.timewindowForm.patchValue({ - hideAggregation: timewindowFormValue.hideAggregation, - hideAggInterval: timewindowFormValue.hideAggInterval, - hideTimezone: timewindowFormValue.hideTimezone - }); } update() { const timewindowFormValue = this.timewindowForm.getRawValue(); - this.timewindow = mergeDeep(this.timewindow, timewindowFormValue); + this.timewindow = mergeDeepIgnoreArray(this.timewindow, timewindowFormValue); const realtimeConfigurableLastIntervalsAvailable = !(timewindowFormValue.hideAggInterval && (timewindowFormValue.realtime.hideInterval || timewindowFormValue.realtime.hideLastInterval)); @@ -435,82 +446,50 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On const historyConfigurableQuickIntervalsAvailable = !(timewindowFormValue.hideAggInterval && (timewindowFormValue.history.hideInterval || timewindowFormValue.history.hideQuickInterval)); - if (realtimeConfigurableLastIntervalsAvailable && timewindowFormValue.realtime.advancedParams.allowedLastIntervals?.length) { - this.timewindow.realtime.advancedParams.allowedLastIntervals = timewindowFormValue.realtime.advancedParams.allowedLastIntervals; - } else { + if (!realtimeConfigurableLastIntervalsAvailable) { delete this.timewindow.realtime.advancedParams.allowedLastIntervals; } - if (realtimeConfigurableQuickIntervalsAvailable && timewindowFormValue.realtime.advancedParams.allowedQuickIntervals?.length) { - this.timewindow.realtime.advancedParams.allowedQuickIntervals = timewindowFormValue.realtime.advancedParams.allowedQuickIntervals; - } else { + if (!realtimeConfigurableQuickIntervalsAvailable) { delete this.timewindow.realtime.advancedParams.allowedQuickIntervals; } - if (realtimeConfigurableLastIntervalsAvailable && isObject(timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig) && - Object.keys(timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig).length) { + if (realtimeConfigurableLastIntervalsAvailable && !isEmpty(timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig)) { this.timewindow.realtime.advancedParams.lastAggIntervalsConfig = timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig; } else { delete this.timewindow.realtime.advancedParams.lastAggIntervalsConfig; } - if (realtimeConfigurableQuickIntervalsAvailable && isObject(timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig) && - Object.keys(timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig).length) { + if (realtimeConfigurableQuickIntervalsAvailable && !isEmpty(timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig)) { this.timewindow.realtime.advancedParams.quickAggIntervalsConfig = timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig; } else { delete this.timewindow.realtime.advancedParams.quickAggIntervalsConfig; } - if (historyConfigurableLastIntervalsAvailable && timewindowFormValue.history.advancedParams.allowedLastIntervals?.length) { - this.timewindow.history.advancedParams.allowedLastIntervals = timewindowFormValue.history.advancedParams.allowedLastIntervals; - } else { + if (!historyConfigurableLastIntervalsAvailable) { delete this.timewindow.history.advancedParams.allowedLastIntervals; } - if (historyConfigurableQuickIntervalsAvailable && timewindowFormValue.history.advancedParams.allowedQuickIntervals?.length) { - this.timewindow.history.advancedParams.allowedQuickIntervals = timewindowFormValue.history.advancedParams.allowedQuickIntervals; - } else { + if (!historyConfigurableQuickIntervalsAvailable) { delete this.timewindow.history.advancedParams.allowedQuickIntervals; } - if (historyConfigurableLastIntervalsAvailable && isObject(timewindowFormValue.history.advancedParams.lastAggIntervalsConfig) && - Object.keys(timewindowFormValue.history.advancedParams.lastAggIntervalsConfig).length) { + if (historyConfigurableLastIntervalsAvailable && !isEmpty(timewindowFormValue.history.advancedParams.lastAggIntervalsConfig)) { this.timewindow.history.advancedParams.lastAggIntervalsConfig = timewindowFormValue.history.advancedParams.lastAggIntervalsConfig; } else { delete this.timewindow.history.advancedParams.lastAggIntervalsConfig; } - if (historyConfigurableQuickIntervalsAvailable && isObject(timewindowFormValue.history.advancedParams.quickAggIntervalsConfig) && - Object.keys(timewindowFormValue.history.advancedParams.quickAggIntervalsConfig).length) { + if (historyConfigurableQuickIntervalsAvailable && !isEmpty(timewindowFormValue.history.advancedParams.quickAggIntervalsConfig)) { this.timewindow.history.advancedParams.quickAggIntervalsConfig = timewindowFormValue.history.advancedParams.quickAggIntervalsConfig; } else { delete this.timewindow.history.advancedParams.quickAggIntervalsConfig; } - if (!Object.keys(this.timewindow.realtime.advancedParams).length) { - delete this.timewindow.realtime.advancedParams; - } - if (!Object.keys(this.timewindow.history.advancedParams).length) { - delete this.timewindow.history.advancedParams; - } - - if (timewindowFormValue.allowedAggTypes?.length && !timewindowFormValue.hideAggregation) { - this.timewindow.allowedAggTypes = timewindowFormValue.allowedAggTypes; - } else { + if (timewindowFormValue.hideAggregation) { delete this.timewindow.allowedAggTypes; } - if (!this.timewindow.realtime.disableCustomInterval) { - delete this.timewindow.realtime.disableCustomInterval; - } - if (!this.timewindow.realtime.disableCustomGroupInterval) { - delete this.timewindow.realtime.disableCustomGroupInterval; - } - if (!this.timewindow.history.disableCustomInterval) { - delete this.timewindow.history.disableCustomInterval; - } - if (!this.timewindow.history.disableCustomGroupInterval) { - delete this.timewindow.history.disableCustomGroupInterval; - } - if (!this.aggregation) { delete this.timewindow.aggregation; } - this.dialogRef.close(this.timewindow); + + deleteFalseProperties(this.timewindow); + this.dialogRef.close(deepClean(this.timewindow)); } cancel() { 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 031846299f..6a6ca10734 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 @@ -173,6 +173,11 @@ + @if (saveAsDefaultAvailable) { + + {{ 'timewindow.save-current-settings-as-default' | translate }} + + }
diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.scss b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.scss index 75bc80bd8f..8eabb2df49 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.scss +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.scss @@ -39,5 +39,9 @@ &-settings-btn { color: rgba(0, 0, 0, 0.54); } + + &-checkbox { + --mdc-checkbox-state-layer-size: 24px; + } } } diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 253aec6d7d..ea14ea334f 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -27,12 +27,14 @@ import { } from '@angular/core'; import { AggregationType, + clearTimewindowConfig, currentHistoryTimewindow, currentRealtimeTimewindow, historyAllowedAggIntervals, HistoryWindowType, historyWindowTypeTranslations, Interval, + MINUTE, QuickTimeInterval, realtimeAllowedAggIntervals, RealtimeWindowType, @@ -45,7 +47,7 @@ import { import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { TimeService } from '@core/services/time.service'; import { deepClone, isDefined } from '@core/utils'; import { OverlayRef } from '@angular/cdk/overlay'; @@ -68,6 +70,7 @@ export interface TimewindowPanelData { timezone: boolean; isEdit: boolean; panelMode: boolean; + showSaveAsDefault?: boolean; } export const TIMEWINDOW_PANEL_DATA = new InjectionToken('TimewindowPanelData'); @@ -109,6 +112,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O aggregationTypes = AggregationType; result: Timewindow; + saveTimewindow: boolean; + saveTimewindowControl: FormControl; timewindowTypeOptions: ToggleHeaderOption[] = [{ name: this.translate.instant('timewindow.history'), @@ -124,6 +129,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O historyTypeSelectionAvailable: boolean; historyIntervalSelectionAvailable: boolean; aggregationOptionsAvailable: boolean; + saveAsDefaultAvailable: boolean; realtimeDisableCustomInterval: boolean; realtimeDisableCustomGroupInterval: boolean; @@ -167,14 +173,14 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O }); } - if ((this.isEdit || !this.timewindow.realtime.hideLastInterval) && !this.quickIntervalOnly) { + if ((this.isEdit || !this.timewindow.realtime?.hideLastInterval) && !this.quickIntervalOnly) { this.realtimeTimewindowOptions.push({ name: this.translate.instant(realtimeWindowTypeTranslations.get(RealtimeWindowType.LAST_INTERVAL)), value: this.realtimeTypes.LAST_INTERVAL }); } - if (this.isEdit || !this.timewindow.realtime.hideQuickInterval || this.quickIntervalOnly) { + if (this.isEdit || !this.timewindow.realtime?.hideQuickInterval || this.quickIntervalOnly) { this.realtimeTimewindowOptions.push({ name: this.translate.instant(realtimeWindowTypeTranslations.get(RealtimeWindowType.INTERVAL)), value: this.realtimeTypes.INTERVAL @@ -188,21 +194,21 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O }); } - if (this.isEdit || !this.timewindow.history.hideLastInterval) { + if (this.isEdit || !this.timewindow.history?.hideLastInterval) { this.historyTimewindowOptions.push({ name: this.translate.instant(historyWindowTypeTranslations.get(HistoryWindowType.LAST_INTERVAL)), value: this.historyTypes.LAST_INTERVAL }); } - if (this.isEdit || !this.timewindow.history.hideFixedInterval) { + if (this.isEdit || !this.timewindow.history?.hideFixedInterval) { this.historyTimewindowOptions.push({ name: this.translate.instant(historyWindowTypeTranslations.get(HistoryWindowType.FIXED)), value: this.historyTypes.FIXED }); } - if (this.isEdit || !this.timewindow.history.hideQuickInterval) { + if (this.isEdit || !this.timewindow.history?.hideQuickInterval) { this.historyTimewindowOptions.push({ name: this.translate.instant(historyWindowTypeTranslations.get(HistoryWindowType.INTERVAL)), value: this.historyTypes.INTERVAL @@ -211,13 +217,15 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O this.realtimeTypeSelectionAvailable = this.realtimeTimewindowOptions.length > 1; this.historyTypeSelectionAvailable = this.historyTimewindowOptions.length > 1; - this.realtimeIntervalSelectionAvailable = this.isEdit || !(this.timewindow.realtime.hideInterval || - (this.timewindow.realtime.hideLastInterval && this.timewindow.realtime.hideQuickInterval)); - this.historyIntervalSelectionAvailable = this.isEdit || !(this.timewindow.history.hideInterval || - (this.timewindow.history.hideLastInterval && this.timewindow.history.hideQuickInterval && this.timewindow.history.hideFixedInterval)); + this.realtimeIntervalSelectionAvailable = this.isEdit || !(this.timewindow.realtime?.hideInterval || + (this.timewindow.realtime?.hideLastInterval && this.timewindow.realtime?.hideQuickInterval)); + this.historyIntervalSelectionAvailable = this.isEdit || !(this.timewindow.history?.hideInterval || + (this.timewindow.history?.hideLastInterval && this.timewindow.history?.hideQuickInterval && this.timewindow.history?.hideFixedInterval)); this.aggregationOptionsAvailable = this.aggregation && (this.isEdit || !(this.timewindow.hideAggregation && this.timewindow.hideAggInterval)); + + this.saveAsDefaultAvailable = this.data.showSaveAsDefault && (!this.timewindow.hideSaveAsDefault || this.isEdit); } ngOnInit(): void { @@ -230,28 +238,28 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O const aggregation = this.timewindow.aggregation; if (!this.isEdit) { - if (realtime.hideLastInterval && !realtime.hideQuickInterval) { + if (realtime?.hideLastInterval && !realtime?.hideQuickInterval) { realtime.realtimeType = RealtimeWindowType.INTERVAL; } - if (realtime.hideQuickInterval && !realtime.hideLastInterval) { + if (realtime?.hideQuickInterval && !realtime?.hideLastInterval) { realtime.realtimeType = RealtimeWindowType.LAST_INTERVAL; } - if (history.hideLastInterval) { + if (history?.hideLastInterval) { if (!history.hideFixedInterval) { history.historyType = HistoryWindowType.FIXED; } else if (!history.hideQuickInterval) { history.historyType = HistoryWindowType.INTERVAL; } } - if (history.hideFixedInterval) { + if (history?.hideFixedInterval) { if (!history.hideLastInterval) { history.historyType = HistoryWindowType.LAST_INTERVAL; } else if (!history.hideQuickInterval) { history.historyType = HistoryWindowType.INTERVAL; } } - if (history.hideQuickInterval) { + if (history?.hideQuickInterval) { if (!history.hideLastInterval) { history.historyType = HistoryWindowType.LAST_INTERVAL; } else if (!history.hideFixedInterval) { @@ -260,34 +268,40 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } } + if (this.saveAsDefaultAvailable) { + this.saveTimewindowControl = this.fb.control({value: this.isEdit, disabled: this.isEdit}); + } + this.timewindowForm = this.fb.group({ selectedTab: [isDefined(this.timewindow.selectedTab) ? this.timewindow.selectedTab : TimewindowType.REALTIME], realtime: this.fb.group({ realtimeType: [{ - value: isDefined(realtime?.realtimeType) ? realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL, - disabled: realtime.hideInterval + value: this.quickIntervalOnly + ? RealtimeWindowType.INTERVAL + : (isDefined(realtime?.realtimeType) ? realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL), + disabled: realtime?.hideInterval }], timewindowMs: [{ - value: isDefined(realtime?.timewindowMs) ? realtime.timewindowMs : null, - disabled: realtime.hideInterval || realtime.hideLastInterval + value: isDefined(realtime?.timewindowMs) ? realtime.timewindowMs : MINUTE, + disabled: realtime?.hideInterval || realtime?.hideLastInterval }], interval: [{ value:isDefined(realtime?.interval) ? realtime.interval : null, disabled: hideAggInterval }], quickInterval: [{ - value: isDefined(realtime?.quickInterval) ? realtime.quickInterval : null, - disabled: realtime.hideInterval || realtime.hideQuickInterval + value: isDefined(realtime?.quickInterval) ? realtime.quickInterval : QuickTimeInterval.CURRENT_DAY, + disabled: realtime?.hideInterval || realtime?.hideQuickInterval }] }), history: this.fb.group({ historyType: [{ value: isDefined(history?.historyType) ? history.historyType : HistoryWindowType.LAST_INTERVAL, - disabled: history.hideInterval + disabled: history?.hideInterval }], timewindowMs: [{ - value: isDefined(history?.timewindowMs) ? history.timewindowMs : null, - disabled: history.hideInterval || history.hideLastInterval + value: isDefined(history?.timewindowMs) ? history.timewindowMs : MINUTE, + disabled: history?.hideInterval || history?.hideLastInterval }], interval: [{ value:isDefined(history?.interval) ? history.interval : null, @@ -296,11 +310,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O fixedTimewindow: [{ value: isDefined(history?.fixedTimewindow) && this.timewindow.selectedTab === TimewindowType.HISTORY && history.historyType === HistoryWindowType.FIXED ? history.fixedTimewindow : null, - disabled: history.hideInterval || history.hideFixedInterval + disabled: history?.hideInterval || history?.hideFixedInterval }], quickInterval: [{ - value: isDefined(history?.quickInterval) ? history.quickInterval : null, - disabled: history.hideInterval || history.hideQuickInterval + value: isDefined(history?.quickInterval) ? history.quickInterval : QuickTimeInterval.CURRENT_DAY, + disabled: history?.hideInterval || history?.hideQuickInterval }] }), aggregation: this.fb.group({ @@ -379,8 +393,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O this.timewindowForm.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => { - this.prepareTimewindowConfig(); - this.changeTimewindow.emit(this.timewindow); + this.changeTimewindow.emit(this.prepareTimewindowConfig()); }); } } @@ -407,27 +420,30 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } update() { - this.prepareTimewindowConfig(); - this.result = this.timewindow; + this.result = this.prepareTimewindowConfig(); + this.saveTimewindow = this.saveAsDefaultAvailable && this.saveTimewindowControl.enabled && this.saveTimewindowControl.value; this.overlayRef?.dispose(); } - private prepareTimewindowConfig() { + private prepareTimewindowConfig(clearConfig = true): Timewindow { const timewindowFormValue = this.timewindowForm.getRawValue(); this.timewindow.selectedTab = timewindowFormValue.selectedTab; - this.timewindow.realtime = {...this.timewindow.realtime, ...{ - realtimeType: timewindowFormValue.realtime.realtimeType, - timewindowMs: timewindowFormValue.realtime.timewindowMs, - quickInterval: timewindowFormValue.realtime.quickInterval, - interval: timewindowFormValue.realtime.interval - }}; - this.timewindow.history = {...this.timewindow.history, ...{ - historyType: timewindowFormValue.history.historyType, - timewindowMs: timewindowFormValue.history.timewindowMs, - interval: timewindowFormValue.history.interval, - fixedTimewindow: timewindowFormValue.history.fixedTimewindow, - quickInterval: timewindowFormValue.history.quickInterval, - }}; + if (this.timewindow.selectedTab === TimewindowType.REALTIME) { + this.timewindow.realtime = {...this.timewindow.realtime, ...{ + realtimeType: timewindowFormValue.realtime.realtimeType, + timewindowMs: timewindowFormValue.realtime.timewindowMs, + quickInterval: timewindowFormValue.realtime.quickInterval, + interval: timewindowFormValue.realtime.interval, + }}; + } else { + this.timewindow.history = {...this.timewindow.history, ...{ + historyType: timewindowFormValue.history.historyType, + timewindowMs: timewindowFormValue.history.timewindowMs, + fixedTimewindow: timewindowFormValue.history.fixedTimewindow, + quickInterval: timewindowFormValue.history.quickInterval, + interval: timewindowFormValue.history.interval, + }}; + } if (this.aggregation) { this.timewindow.aggregation = { @@ -438,6 +454,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O if (this.timezone) { this.timewindow.timezone = timewindowFormValue.timezone; } + + if (clearConfig) { + return clearTimewindowConfig(this.timewindow, this.quickIntervalOnly, this.historyOnly, this.aggregation, this.timezone); + } else { + return deepClone(this.timewindow); + } } private updateTimewindowForm() { @@ -547,7 +569,6 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } openTimewindowConfig() { - this.prepareTimewindowConfig(); this.dialog.open( TimewindowConfigDialogComponent, { autoFocus: false, @@ -556,7 +577,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O data: { quickIntervalOnly: this.quickIntervalOnly, aggregation: this.aggregation, - timewindow: deepClone(this.timewindow) + timewindow: this.prepareTimewindowConfig(false), + showSaveAsDefault: this.data.showSaveAsDefault } }).afterClosed() .subscribe((res) => { @@ -569,12 +591,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } private updateTimewindowAdvancedParams() { - this.realtimeDisableCustomInterval = this.timewindow.realtime.disableCustomInterval; - this.realtimeDisableCustomGroupInterval = this.timewindow.realtime.disableCustomGroupInterval; - this.historyDisableCustomInterval = this.timewindow.history.disableCustomInterval; - this.historyDisableCustomGroupInterval = this.timewindow.history.disableCustomGroupInterval; + this.realtimeDisableCustomInterval = this.timewindow.realtime?.disableCustomInterval; + this.realtimeDisableCustomGroupInterval = this.timewindow.realtime?.disableCustomGroupInterval; + this.historyDisableCustomInterval = this.timewindow.history?.disableCustomInterval; + this.historyDisableCustomGroupInterval = this.timewindow.history?.disableCustomGroupInterval; - if (this.timewindow.realtime.advancedParams) { + if (this.timewindow.realtime?.advancedParams) { this.realtimeAdvancedParams = this.timewindow.realtime.advancedParams; this.realtimeAllowedLastIntervals = this.timewindow.realtime.advancedParams.allowedLastIntervals; this.realtimeAllowedQuickIntervals = this.timewindow.realtime.advancedParams.allowedQuickIntervals; @@ -583,7 +605,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O this.realtimeAllowedLastIntervals = null; this.realtimeAllowedQuickIntervals = null; } - if (this.timewindow.history.advancedParams) { + if (this.timewindow.history?.advancedParams) { this.historyAdvancedParams = this.timewindow.history.advancedParams; this.historyAllowedLastIntervals = this.timewindow.history.advancedParams.allowedLastIntervals; this.historyAllowedQuickIntervals = this.timewindow.history.advancedParams.allowedQuickIntervals; 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 1d46e6dee2..cf5c1e5852 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -19,12 +19,14 @@ import { Component, DestroyRef, ElementRef, + EventEmitter, forwardRef, HostBinding, Injector, Input, OnChanges, OnInit, + Output, SimpleChanges, StaticProvider, ViewChild, @@ -189,6 +191,13 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan @coerceBoolean() panelMode = true; + @Input() + @coerceBoolean() + showSaveAsDefault = false; + + @Output() + saveAsDefault = new EventEmitter(); + innerValue: Timewindow; timewindowDisabled: boolean; @@ -261,6 +270,7 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan timezone: this.timezone, isEdit: this.isEdit, panelMode: this.panelMode, + showSaveAsDefault: this.showSaveAsDefault, } as TimewindowPanelData }, { @@ -280,7 +290,7 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan this.innerValue = componentRef.instance.result; this.timewindowDisabled = this.isTimewindowDisabled(); this.updateDisplayValue(); - this.notifyChanged(); + this.notifyChanged(this.showSaveAsDefault && componentRef.instance.saveTimewindow); } }); this.cd.detectChanges(); @@ -299,7 +309,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan private onHistoryOnlyChanged(): boolean { if (this.historyOnlyValue && this.innerValue && this.innerValue.selectedTab !== TimewindowType.HISTORY) { - this.innerValue.selectedTab = TimewindowType.HISTORY; + this.innerValue = initModelFromDefaultTimewindow(this.innerValue, this.quickIntervalOnly, this.historyOnly, + this.timeService, this.aggregation); this.updateDisplayValue(); return true; } @@ -319,7 +330,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan } writeValue(obj: Timewindow): void { - this.innerValue = initModelFromDefaultTimewindow(obj, this.quickIntervalOnly, this.historyOnly, this.timeService); + this.innerValue = initModelFromDefaultTimewindow(obj, this.quickIntervalOnly, this.historyOnly, this.timeService, + this.aggregation); this.timewindowDisabled = this.isTimewindowDisabled(); if (this.onHistoryOnlyChanged()) { setTimeout(() => { @@ -333,8 +345,11 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan } } - notifyChanged() { + notifyChanged(notifySaveAsDefault = false) { this.propagateChange(cloneSelectedTimewindow(this.innerValue)); + if (notifySaveAsDefault) { + this.saveAsDefault.emit(this.innerValue); + } } displayValue(): string { @@ -401,6 +416,7 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan timezone: this.timezone, isEdit: this.isEdit, panelMode: this.panelMode, + showSaveAsDefault: this.showSaveAsDefault, } const injector = Injector.create({ providers: [{ provide: TIMEWINDOW_PANEL_DATA, useValue: panelData }], @@ -412,7 +428,7 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan ).subscribe(value => { this.innerValue = value; this.timewindowDisabled = this.isTimewindowDisabled(); - this.notifyChanged(); + this.notifyChanged(this.showSaveAsDefault && componentRef.instance.saveTimewindow); }) } } diff --git a/ui-ngx/src/app/shared/components/vc/branch-autocomplete.component.html b/ui-ngx/src/app/shared/components/vc/branch-autocomplete.component.html index 18b28a90ee..26df6befba 100644 --- a/ui-ngx/src/app/shared/components/vc/branch-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/vc/branch-autocomplete.component.html @@ -15,7 +15,10 @@ limitations under the License. --> - + {{ 'version-control.branch' | translate }} ; 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 index 464389b664..198d6fecf2 100644 --- 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 @@ -39,7 +39,7 @@ import { 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'; +import { getAce, updateEditorSize } from '@shared/models/ace/ace.models'; export interface ImportDialogCsvData { entityType: EntityType; @@ -283,21 +283,9 @@ export class ImportDialogCsvComponent extends DialogComponent 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/shared/import-export/import-export.service.ts b/ui-ngx/src/app/shared/import-export/import-export.service.ts index f284f05478..5bb73b846a 100644 --- a/ui-ngx/src/app/shared/import-export/import-export.service.ts +++ b/ui-ngx/src/app/shared/import-export/import-export.service.ts @@ -183,8 +183,8 @@ export class ImportExportService { }); } - public openCalculatedFieldImportDialog(): Observable { - return this.openImportDialog('calculated-fields.import', 'calculated-fields.file').pipe( + public openCalculatedFieldImportDialog(importTitle = 'calculated-fields.import', importFileLabel = 'calculated-fields.file'): Observable { + return this.openImportDialog(importTitle, importFileLabel).pipe( catchError(() => of(null)), ); } diff --git a/ui-ngx/src/app/shared/models/ace/ace.models.ts b/ui-ngx/src/app/shared/models/ace/ace.models.ts index 2ba42e362c..c14a803293 100644 --- a/ui-ngx/src/app/shared/models/ace/ace.models.ts +++ b/ui-ngx/src/app/shared/models/ace/ace.models.ts @@ -19,6 +19,7 @@ import { Observable } from 'rxjs/internal/Observable'; import { forkJoin, from, of } from 'rxjs'; import { map, mergeMap, tap } from 'rxjs/operators'; import { unwrapModule } from '@core/utils'; +import { Renderer2 } from '@angular/core'; let aceDependenciesLoaded = false; let aceModule: any; @@ -98,6 +99,46 @@ export function getAceDiff(): Observable { } } +export function updateEditorSize(editorElement: any, content: string, editor: Ace.Editor, renderer: Renderer2, options?: { + showGutter?: boolean, + ignoreHeight?: boolean, + ignoreWidth?: boolean, + setMinHeight?: boolean +}): void { + let newHeight = 200; + let newWidth = 600; + if (content && content.length > 0) { + if (editor.renderer.lineHeight <= 0) { + editor.renderer.updateFull(true); + } + const lines = content.split('\n'); + newHeight = editor.renderer.lineHeight * lines.length + 16; + let maxLineLength = 0; + lines.forEach((row) => { + const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); + const lineLength = line.length; + maxLineLength = Math.max(maxLineLength, lineLength); + }); + if (options?.showGutter) { + maxLineLength += lines.length.toString().length; + } + newWidth = 10 * maxLineLength + 16; + if (options?.showGutter) { + newWidth += 32; + } + } + if (!options.ignoreHeight) { + renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); + } + if (options.setMinHeight) { + renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px'); + } + if (!options.ignoreWidth) { + renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px'); + } + editor.resize(); +} + export class Range implements Ace.Range { public start: Ace.Point; diff --git a/ui-ngx/src/app/shared/models/alarm-rule.models.ts b/ui-ngx/src/app/shared/models/alarm-rule.models.ts new file mode 100644 index 0000000000..e2d7b8b154 --- /dev/null +++ b/ui-ngx/src/app/shared/models/alarm-rule.models.ts @@ -0,0 +1,310 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { CustomTimeSchedulerItem } from "@shared/models/device.models"; +import { DashboardId } from "@shared/models/id/dashboard-id"; +import { TimeUnit } from "@shared/models/time/time.models"; +import { ComplexOperation, EntityKeyValueType, FilterPredicateType } from "@shared/models/query/query.models"; +import { EntityType } from "@shared/models/entity-type.models"; +import { Observable } from "rxjs"; +import { + CalculatedField, + CalculatedFieldArgument, + CalculatedFieldEventArguments +} from "@shared/models/calculated-field.models"; + +export const alarmRuleEntityTypeList = [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE]; + +export enum AlarmRuleScheduleType { + ANY_TIME = 'ANY_TIME', + SPECIFIC_TIME = 'SPECIFIC_TIME', + CUSTOM = 'CUSTOM' +} + +export const AlarmRuleScheduleTypeTranslationMap = new Map( + [ + [AlarmRuleScheduleType.ANY_TIME, 'alarm-rule.schedule.any-time'], + [AlarmRuleScheduleType.SPECIFIC_TIME, 'alarm-rule.schedule.specific-time'], + [AlarmRuleScheduleType.CUSTOM, 'alarm-rule.schedule.custom'] + ] +); + +export enum AlarmRuleConditionType { + SIMPLE = 'SIMPLE', + DURATION = 'DURATION', + REPEATING = 'REPEATING' +} + +export const AlarmRuleConditionTypeTranslationMap = new Map( + [ + [AlarmRuleConditionType.SIMPLE, 'alarm-rule.conditions.simple'], + [AlarmRuleConditionType.DURATION, 'alarm-rule.conditions.duration'], + [AlarmRuleConditionType.REPEATING, 'alarm-rule.conditions.repeating'] + ] +); + +export enum AlarmRuleExpressionType { + SIMPLE = 'SIMPLE', + TBEL = 'TBEL', +} + +export const FilterPredicateTypeTranslationMap = new Map( + [ + [FilterPredicateType.STRING, 'alarm-rule.filter-predicate-type.string'], + [FilterPredicateType.NUMERIC, 'alarm-rule.filter-predicate-type.numeric'], + [FilterPredicateType.BOOLEAN, 'alarm-rule.filter-predicate-type.boolean'], + [FilterPredicateType.COMPLEX, 'alarm-rule.filter-predicate-type.complex'] + ] +); + +export interface AlarmRule { + condition: AlarmRuleCondition; + alarmDetails?: string; + dashboardId?: DashboardId; +} + +export interface AlarmRuleCondition { + type: AlarmRuleConditionType; + expression: AlarmRuleExpression; + schedule?: AlarmRuleSchedule; + unit?: TimeUnit; + value?: AlarmRuleValue; + count?: AlarmRuleValue; +} + +export interface AlarmRuleExpression { + type: AlarmRuleExpressionType; + expression?: string; + filters?: Array; + operation?: ComplexOperation; +} + +export interface AlarmRuleSchedule { + staticValue?: { + type?: AlarmRuleScheduleType; + timezone?: string; + daysOfWeek?: number[]; + startsOn?: number; + endsOn?: number; + items?: CustomTimeSchedulerItem[]; + }; + dynamicValueArgument?: string; +} + +export interface AlarmRuleFilter { + argument: string; + valueType: EntityKeyValueType; + operation: ComplexOperation; + predicates: AlarmRuleFilterPredicate[]; +} + +export interface AlarmRulePredicateInfo { + keyFilterPredicate: AlarmRuleFilterPredicate; +} + +export type AlarmRuleFilterPredicate = StringAlarmRuleFilterPredicate | + NumericAlarmRuleFilterPredicate | + BooleanAlarmRuleFilterPredicate | + ComplexAlarmRuleFilterPredicate | + NoDataAlarmRuleFilterPredicate; + +export interface AlarmRuleValue { + dynamicValueArgument?: string; + staticValue?: T +} + +export interface StringAlarmRuleFilterPredicate { + type: AlarmRuleFilterPredicateType.STRING; + operation: AlarmRuleStringOperation; + value: AlarmRuleValue; + ignoreCase: boolean; +} + +export interface NumericAlarmRuleFilterPredicate { + type: AlarmRuleFilterPredicateType.NUMERIC; + operation: AlarmRuleNumericOperation; + value: AlarmRuleValue; +} + +export interface BooleanAlarmRuleFilterPredicate { + type: AlarmRuleFilterPredicateType.BOOLEAN; + operation: AlarmRuleBooleanOperation; + value: AlarmRuleValue; +} + +export interface NoDataAlarmRuleFilterPredicate { + type: AlarmRuleFilterPredicateType.NO_DATA; + unit: TimeUnit, + operation: AlarmRuleStringOperation.NO_DATA | AlarmRuleNumericOperation.NO_DATA | AlarmRuleBooleanOperation.NO_DATA; + duration: AlarmRuleValue; +} + +export interface BaseComplexFilterPredicate { + type: AlarmRuleFilterPredicateType.COMPLEX; + operation: ComplexOperation; + predicates: Array; +} + +export type ComplexAlarmRuleFilterPredicate = BaseComplexFilterPredicate; + +export interface AlarmRuleFilterConfig { + name?: Array; + entityType?: EntityType; + entities?: Array; +} + +export const filterOperationTranslationMap = new Map( + [ + [ComplexOperation.AND, 'alarm-rule.filter-operation.and'], + [ComplexOperation.OR, 'alarm-rule.filter-operation.or'], + ] +); + +export enum AlarmRuleFilterPredicateType { + STRING = 'STRING', + NUMERIC = 'NUMERIC', + BOOLEAN = 'BOOLEAN', + COMPLEX = 'COMPLEX', + NO_DATA = 'NO_DATA' +} + +export enum AlarmRuleStringOperation { + EQUAL = 'EQUAL', + NOT_EQUAL = 'NOT_EQUAL', + NO_DATA = 'NO_DATA', + STARTS_WITH = 'STARTS_WITH', + ENDS_WITH = 'ENDS_WITH', + CONTAINS = 'CONTAINS', + NOT_CONTAINS = 'NOT_CONTAINS', + IN = 'IN', + NOT_IN = 'NOT_IN', +} + +export const alarmRuleStringOperationTranslationMap = new Map( + [ + [AlarmRuleStringOperation.EQUAL, 'filter.operation.equal'], + [AlarmRuleStringOperation.NOT_EQUAL, 'filter.operation.not-equal'], + [AlarmRuleStringOperation.STARTS_WITH, 'filter.operation.starts-with'], + [AlarmRuleStringOperation.ENDS_WITH, 'filter.operation.ends-with'], + [AlarmRuleStringOperation.CONTAINS, 'filter.operation.contains'], + [AlarmRuleStringOperation.NOT_CONTAINS, 'filter.operation.not-contains'], + [AlarmRuleStringOperation.IN, 'filter.operation.in'], + [AlarmRuleStringOperation.NOT_IN, 'filter.operation.not-in'], + [AlarmRuleStringOperation.NO_DATA, 'alarm-rule.missing-for'] + ] +); + +export enum AlarmRuleNumericOperation { + EQUAL = 'EQUAL', + NOT_EQUAL = 'NOT_EQUAL', + NO_DATA = 'NO_DATA', + GREATER = 'GREATER', + LESS = 'LESS', + GREATER_OR_EQUAL = 'GREATER_OR_EQUAL', + LESS_OR_EQUAL = 'LESS_OR_EQUAL' +} + +export const alarmRuleNumericOperationTranslationMap = new Map( + [ + [AlarmRuleNumericOperation.EQUAL, 'filter.operation.equal'], + [AlarmRuleNumericOperation.NOT_EQUAL, 'filter.operation.not-equal'], + [AlarmRuleNumericOperation.GREATER, 'filter.operation.greater'], + [AlarmRuleNumericOperation.LESS, 'filter.operation.less'], + [AlarmRuleNumericOperation.GREATER_OR_EQUAL, 'filter.operation.greater-or-equal'], + [AlarmRuleNumericOperation.LESS_OR_EQUAL, 'filter.operation.less-or-equal'], + [AlarmRuleNumericOperation.NO_DATA, 'alarm-rule.missing-for'] + ] +); + +export enum AlarmRuleBooleanOperation { + EQUAL = 'EQUAL', + NOT_EQUAL = 'NOT_EQUAL', + NO_DATA = 'NO_DATA', +} + +export const alarmRuleBooleanOperationTranslationMap = new Map( + [ + [AlarmRuleBooleanOperation.EQUAL, 'filter.operation.equal'], + [AlarmRuleBooleanOperation.NOT_EQUAL, 'filter.operation.not-equal'], + [AlarmRuleBooleanOperation.NO_DATA, 'alarm-rule.missing-for'] + ] +); + +export const alarmRuleDefaultScript = + '// Triggers when temperature is above 20 degrees\n' + + 'return temperature > 20;' + +export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit?: boolean, expression?: string) => Observable; + +export function checkPredicates(predicates: any[], validSet: Set): boolean { + for (const predicate of predicates) { + if (!predicate) continue; + if (predicate?.value?.dynamicValueArgument) { + if (!validSet.has(predicate.value.dynamicValueArgument)) { + return false; + } + } + if (predicate.type === 'COMPLEX' && Array.isArray(predicate.predicates)) { + if (!checkPredicates(predicate.predicates, validSet)) { + return false; + } + } + } + return true; +} + +export function areFilterAndPredicateArgumentsValid(obj: any, args: Record): boolean { + const validSet = new Set(Object.keys(args)); + const filter = obj || []; + if (filter.argument && !validSet.has(filter.argument)) { + return false; + } + if (Array.isArray(filter.predicates)) { + if (!checkPredicates(filter.predicates, validSet)) { + return false; + } + } + return true; +} + +export function areFiltersAndPredicateArgumentsValid(obj: any, args: Record): boolean { + const validSet = new Set(Object.keys(args)); + const filters = obj || []; + for (const filter of filters) { + if (filter.argument && !validSet.has(filter.argument)) { + return false; + } + } + for (const filter of filters) { + if (Array.isArray(filter.predicates)) { + if (!checkPredicates(filter.predicates, validSet)) { + return false; + } + } + } + return true; +} + +export function isPredicateArgumentsValid(predicates: any, args: Record): boolean { + const validSet = new Set(Object.keys(args)); + if (Array.isArray(predicates)) { + if (!checkPredicates(predicates, validSet)) { + return false; + } + } + return true; +} diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index 1574120c94..2ee9a18415 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -88,11 +88,21 @@ export const alarmSearchStatusTranslations = new Map( export const alarmSeverityColors = new Map( [ - [AlarmSeverity.CRITICAL, 'red'], - [AlarmSeverity.MAJOR, 'orange'], - [AlarmSeverity.MINOR, '#ffca3d'], - [AlarmSeverity.WARNING, '#abab00'], - [AlarmSeverity.INDETERMINATE, 'green'] + [AlarmSeverity.CRITICAL, 'var(--tb-alarm-severity-critical, rgb(209, 39, 48))'], + [AlarmSeverity.MAJOR, 'var(--tb-alarm-severity-major, rgb(246, 103, 22))'], + [AlarmSeverity.MINOR, 'var(--tb-alarm-severity-minor, rgb(250, 164, 5))'], + [AlarmSeverity.WARNING, 'var(--tb-alarm-severity-warning, rgb(242, 218, 5))'], + [AlarmSeverity.INDETERMINATE, 'var(--tb-alarm-severity-indeterminate, rgba(0, 0, 0, 0.38))'] + ] +); + +export const alarmSeverityBackgroundColors = new Map( + [ + [AlarmSeverity.CRITICAL, `var(--tb-alarm-severity-critical-bg, rgba(209, 39, 48, 0.06))`], + [AlarmSeverity.MAJOR, 'var(--tb-alarm-severity-major-bg, rgba(246, 103, 22, 0.06))'], + [AlarmSeverity.MINOR, 'var(--tb-alarm-severity-minor-bg, rgba(250, 164, 5, 0.06))'], + [AlarmSeverity.WARNING, 'var(--tb-alarm-severity-warning-bg, rgba(242, 218, 5, 0.06))'], + [AlarmSeverity.INDETERMINATE, 'var(--tb-alarm-severity-indeterminate-bg, rgba(0, 0, 0, 0.06))'] ] ); @@ -120,12 +130,27 @@ export enum AlarmCommentType { OTHER = 'OTHER' } +export enum AlarmMessage { + ACKED_BY_USER = "alarm.system-comments.acked-by-user", + CLEARED_BY_USER = "alarm.system-comments.cleared-by-user", + ASSIGNED_TO_USER = "alarm.system-comments.assigned-to-user", + UNASSIGNED_BY_USER = "alarm.system-comments.unassigned-to-user", + UNASSIGNED_FROM_DELETED_USER = "alarm.system-comments.unassigned-from-deleted-user", + COMMENT_DELETED = "alarm.system-comments.comment-deleted", + SEVERITY_CHANGED = "alarm.system-comments.severity-changed", +} + export interface AlarmComment extends BaseData { alarmId: AlarmId; userId?: UserId; type: AlarmCommentType; comment: { text: string; + subtype?: keyof typeof AlarmMessage; + userName?: string; + assigneeName?: string; + oldSeverity?: AlarmSeverity; + newSeverity?: AlarmSeverity; edited?: boolean; editedOn?: number; }; @@ -140,6 +165,7 @@ export interface AlarmCommentInfo extends AlarmComment { export interface AlarmInfo extends Alarm { originatorName: string; originatorLabel: string; + originatorDisplayName?: string; assignee: AlarmAssignee; } @@ -172,6 +198,7 @@ export const simulatedAlarm: AlarmInfo = { clearTs: 0, assignTs: 0, originatorName: 'Simulated', + originatorDisplayName: 'Simulated', originatorLabel: 'Simulated', assignee: { firstName: '', @@ -242,6 +269,11 @@ export const alarmFields: {[fieldName: string]: AlarmField} = { value: 'originatorName', name: 'alarm.originator' }, + originatorDisplayName: { + keyName: 'originatorDisplayName', + value: 'originatorDisplayName', + name: 'alarm.originator' + }, originatorLabel: { keyName: 'originatorLabel', value: 'originatorLabel', @@ -271,6 +303,11 @@ export const alarmFields: {[fieldName: string]: AlarmField} = { keyName: 'assignee', value: 'assignee', name: 'alarm.assignee' + }, + details: { + keyName: 'details', + value: 'details', + name: 'alarm.details' } }; diff --git a/ui-ngx/src/app/shared/models/api-key.models.ts b/ui-ngx/src/app/shared/models/api-key.models.ts new file mode 100644 index 0000000000..6d18a905c5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/api-key.models.ts @@ -0,0 +1,34 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { HasTenantId } from '@shared/models/entity.models'; +import { ApiKeyId } from '@shared/models/id/api-key-id'; +import { UserId } from '@shared/models/id/user-id'; + +export const userInfoCommand = (baseUrl: string, apiKey: string): string => `curl -X GET "${baseUrl}/api/auth/user" -H "Content-Type: application/json" -H "X-Authorization: ApiKey ${apiKey}"` + +export interface ApiKeyInfo extends BaseData, HasTenantId { + enabled: boolean; + expirationTime: number; + description: string; + expired: boolean; + userId: UserId; +} + +export interface ApiKey extends ApiKeyInfo { + value: string; +} diff --git a/ui-ngx/src/app/shared/models/audit-log.models.ts b/ui-ngx/src/app/shared/models/audit-log.models.ts index e8cc99507d..59bd97bde6 100644 --- a/ui-ngx/src/app/shared/models/audit-log.models.ts +++ b/ui-ngx/src/app/shared/models/audit-log.models.ts @@ -20,6 +20,7 @@ import { CustomerId } from './id/customer-id'; import { EntityId } from './id/entity-id'; import { UserId } from './id/user-id'; import { TenantId } from './id/tenant-id'; +import { isArraysEqualIgnoreUndefined } from "@core/utils"; export enum AuditLogMode { TENANT, @@ -132,3 +133,11 @@ export interface AuditLog extends BaseData { actionStatus: ActionStatus; actionFailureDetails: string; } + +export interface AuditLogFilter { + actionTypes: string[]; +} + +export const auditLogFilterEquals = (filter1?: AuditLogFilter, filter2?: AuditLogFilter): boolean => { + return isArraysEqualIgnoreUndefined(filter1.actionTypes, filter2.actionTypes); +}; diff --git a/ui-ngx/src/app/shared/models/authority.enum.ts b/ui-ngx/src/app/shared/models/authority.enum.ts index b790e2cd56..8efaad725c 100644 --- a/ui-ngx/src/app/shared/models/authority.enum.ts +++ b/ui-ngx/src/app/shared/models/authority.enum.ts @@ -20,5 +20,6 @@ export enum Authority { CUSTOMER_USER = 'CUSTOMER_USER', REFRESH_TOKEN = 'REFRESH_TOKEN', ANONYMOUS = 'ANONYMOUS', - PRE_VERIFICATION_TOKEN = 'PRE_VERIFICATION_TOKEN' + PRE_VERIFICATION_TOKEN = 'PRE_VERIFICATION_TOKEN', + MFA_CONFIGURATION_TOKEN = 'MFA_CONFIGURATION_TOKEN' } diff --git a/ui-ngx/src/app/shared/models/base-data.ts b/ui-ngx/src/app/shared/models/base-data.ts index 125fcfb814..d4716881d9 100644 --- a/ui-ngx/src/app/shared/models/base-data.ts +++ b/ui-ngx/src/app/shared/models/base-data.ts @@ -16,7 +16,9 @@ import { EntityId } from '@shared/models/id/entity-id'; import { HasUUID } from '@shared/models/id/has-uuid'; -import { isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; +import { EntityType } from '@shared/models/entity-type.models'; +import { User } from '@shared/models/user.model'; export declare type HasId = EntityId | HasUUID; @@ -49,3 +51,12 @@ export function hasIdEquals(id1: HasId, id2: HasId): boolean { return id1 === id2; } } + +export function getEntityDisplayName(entity: BaseData): string { + if (entity?.id?.entityType === EntityType.USER) { + const user = entity as User; + const userName = (user?.firstName ?? '') + " " + (user?.lastName ?? ''); + return isNotEmptyStr(userName) ? userName.trim() : entity?.name; + } + return isNotEmptyStr(entity?.label) ? entity.label : entity?.name; +} diff --git a/ui-ngx/src/app/shared/models/calculated-field.models.ts b/ui-ngx/src/app/shared/models/calculated-field.models.ts index 7e3a79efd7..201d23f7d0 100644 --- a/ui-ngx/src/app/shared/models/calculated-field.models.ts +++ b/ui-ngx/src/app/shared/models/calculated-field.models.ts @@ -14,11 +14,7 @@ /// limitations under the License. /// -import { - HasEntityDebugSettings, - HasTenantId, - HasVersion -} from '@shared/models/entity.models'; +import { HasEntityDebugSettings, HasTenantId, HasVersion } from '@shared/models/entity.models'; import { BaseData, ExportableEntity } from '@shared/models/base-data'; import { CalculatedFieldId } from '@shared/models/id/calculated-field-id'; import { EntityId } from '@shared/models/id/entity-id'; @@ -33,45 +29,266 @@ import { dotOperatorHighlightRule, endGroupHighlightRule } from '@shared/models/ace/ace.models'; +import { EntitySearchDirection } from '@shared/models/relation.models'; +import { AbstractControl, FormControl, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { AlarmRule } from "@shared/models/alarm-rule.models"; +import { AlarmSeverity } from "@shared/models/alarm.models"; + +export const FORBIDDEN_NAMES = ['ctx', 'e', 'pi']; -export interface CalculatedField extends Omit, 'label'>, HasVersion, HasEntityDebugSettings, HasTenantId, ExportableEntity { - configuration: CalculatedFieldConfiguration; - type: CalculatedFieldType; +interface BaseCalculatedField extends Omit, 'label'>, HasVersion, HasEntityDebugSettings, HasTenantId, ExportableEntity { entityId: EntityId; } +export interface CalculatedFieldSimple extends BaseCalculatedField { + type: CalculatedFieldType.SIMPLE; + configuration: CalculatedFieldSimpleConfiguration; +} + +export interface CalculatedFieldScript extends BaseCalculatedField { + type: CalculatedFieldType.SCRIPT; + configuration: CalculatedFieldScriptConfiguration; +} + +export interface CalculatedFieldGeofencing extends BaseCalculatedField { + type: CalculatedFieldType.GEOFENCING; + configuration: CalculatedFieldGeofencingConfiguration; +} + +export interface CalculatedFieldPropagation extends BaseCalculatedField { + type: CalculatedFieldType.PROPAGATION; + configuration: CalculatedFieldPropagationConfiguration; +} + +export interface CalculatedFieldAlarmRule extends BaseCalculatedField { + type: CalculatedFieldType.ALARM; + configuration: CalculatedFieldAlarmRuleConfiguration; +} + +export interface CalculatedFieldRelatedEntityAggregation extends BaseCalculatedField { + type: CalculatedFieldType.RELATED_ENTITIES_AGGREGATION; + configuration: CalculatedFieldRelatedAggregationConfiguration; +} + + +export type CalculatedField = + | CalculatedFieldSimple + | CalculatedFieldScript + | CalculatedFieldGeofencing + | CalculatedFieldPropagation + | CalculatedFieldRelatedEntityAggregation + | CalculatedFieldAlarmRule; + +export type CalculatedFieldInfo = CalculatedField & {entityName: string}; + export enum CalculatedFieldType { SIMPLE = 'SIMPLE', SCRIPT = 'SCRIPT', + GEOFENCING = 'GEOFENCING', + PROPAGATION = 'PROPAGATION', + RELATED_ENTITIES_AGGREGATION = 'RELATED_ENTITIES_AGGREGATION', + ENTITY_AGGREGATION = 'ENTITY_AGGREGATION', + ALARM = 'ALARM', +} + +export interface CalculatedFieldTypeTranslate { + name: string; + hint?: string; } -export const CalculatedFieldTypeTranslations = new Map( +export const CalculatedFieldTypeTranslations = new Map( [ - [CalculatedFieldType.SIMPLE, 'calculated-fields.type.simple'], - [CalculatedFieldType.SCRIPT, 'calculated-fields.type.script'], + [CalculatedFieldType.SIMPLE, { + name: 'calculated-fields.type.simple', + hint: 'calculated-fields.type.simple-hint', + }], + [CalculatedFieldType.SCRIPT, { + name: 'calculated-fields.type.script', + hint: 'calculated-fields.type.script-hint', + }], + [CalculatedFieldType.GEOFENCING, { + name: 'calculated-fields.type.geofencing', + hint: 'calculated-fields.type.geofencing-hint', + }], + [CalculatedFieldType.PROPAGATION, { + name: 'calculated-fields.type.propagation', + hint: 'calculated-fields.type.propagation-hint', + }], + [CalculatedFieldType.RELATED_ENTITIES_AGGREGATION, { + name: 'calculated-fields.type.related-entities-aggregation', + hint: 'calculated-fields.type.related-entities-aggregation-hint' + }], + [CalculatedFieldType.ENTITY_AGGREGATION, { + name: 'calculated-fields.type.time-series-data-aggregation', + hint: 'calculated-fields.type.time-series-data-aggregation-hint', + }], ] ) -export interface CalculatedFieldConfiguration { - type: CalculatedFieldType; +export const calculatedFieldTypes = Object.values(CalculatedFieldType).filter(type => type !== CalculatedFieldType.ALARM) + +export type CalculatedFieldConfiguration = + | CalculatedFieldSimpleConfiguration + | CalculatedFieldScriptConfiguration + | CalculatedFieldGeofencingConfiguration + | CalculatedFieldPropagationConfiguration + | CalculatedFieldRelatedAggregationConfiguration + | CalculatedFieldEntityAggregationConfiguration + | CalculatedFieldAlarmRuleConfiguration; + +export interface CalculatedFieldSimpleConfiguration { + type: CalculatedFieldType.SIMPLE; + expression: string; + arguments: Record; + useLatestTs: boolean; + output: CalculatedFieldSimpleOutput; +} + +export interface CalculatedFieldScriptConfiguration { + type: CalculatedFieldType.SCRIPT; expression: string; arguments: Record; output: CalculatedFieldOutput; } -export interface CalculatedFieldOutput { - type: OutputType; +export interface CalculatedFieldGeofencingConfiguration { + type: CalculatedFieldType.GEOFENCING; + zoneGroups: Record; + scheduledUpdateEnabled: boolean; + scheduledUpdateInterval?: number; + output: CalculatedFieldOutput; +} + +export interface CalculatedFieldRelatedAggregationConfiguration { + type: CalculatedFieldType.RELATED_ENTITIES_AGGREGATION; + relation: RelationPathLevel; + arguments: Record; + metrics: Record; + deduplicationIntervalInSec: number; + scheduledUpdateInterval?: number; + useLatestTs: boolean; + output: CalculatedFieldOutput & { decimalsByDefault?: number; }; +} + +export interface CalculatedFieldEntityAggregationConfiguration { + type: CalculatedFieldType.ENTITY_AGGREGATION; + arguments: Record; + metrics: Record; + interval: AggInterval; + watermark?: WatermarkConfig; + produceIntermediateResult?: boolean; + output: CalculatedFieldOutput & { decimalsByDefault?: number; }; +} + +export interface WatermarkConfig { + duration: number; +} + +interface BasePropagationConfiguration { + type: CalculatedFieldType.PROPAGATION; + relation: RelationPathLevel; + arguments: Record; + output: CalculatedFieldOutput; +} + +interface CalculatedFieldAlarmRuleConfiguration { + type: CalculatedFieldType.ALARM; + arguments: Record; + createRules: Record; + clearRule?: AlarmRule; + propagate: boolean; + propagateToOwner: boolean; + propagateToTenant: boolean; + propagateRelationTypes?: Array; +} + +export interface PropagationWithNoExpression extends BasePropagationConfiguration { + applyExpressionToResolvedArguments: false; +} + +export interface PropagationWithExpression extends BasePropagationConfiguration { + applyExpressionToResolvedArguments: true; + expression: string; +} + +export type CalculatedFieldPropagationConfiguration = + | PropagationWithNoExpression + | PropagationWithExpression; + +export type CalculatedFieldOutput = + | CalculatedFieldOutputAttribute + | CalculatedFieldOutputTimeSeries; + +export interface CalculatedFieldOutputAttribute { + type: OutputType.Attribute, + scope: AttributeScope; + strategy: AttributeOutputStrategy; +} + +export interface CalculatedFieldOutputTimeSeries { + type: OutputType.Timeseries; + strategy: TimeSeriesOutputStrategy; +} + +export type CalculatedFieldSimpleOutput = CalculatedFieldOutput & { name: string; - scope?: AttributeScope; decimalsByDefault?: number; } +export type AttributeOutputStrategy = + | AttributeImmediateOutputStrategy + | AttributeRuleChainOutputStrategy; + +export interface AttributeImmediateOutputStrategy { + type: OutputStrategyType.IMMEDIATE; + updateAttributesOnlyOnValueChange: boolean; + sendAttributesUpdatedNotification: boolean; + saveAttribute: boolean; + sendWsUpdate: boolean; + processCfs: boolean; +} + +export interface AttributeRuleChainOutputStrategy { + type: OutputStrategyType.RULE_CHAIN; +} + +export type TimeSeriesOutputStrategy = + | TimeSeriesRuleChainOutputStrategy + | TimeSeriesImmediateOutputStrategy; + +export interface TimeSeriesRuleChainOutputStrategy { + type: OutputStrategyType.IMMEDIATE; + ttl: number; + saveTimeSeries: boolean; + saveLatest: boolean; + sendWsUpdate: boolean; + processCfs: boolean; +} + +export interface TimeSeriesImmediateOutputStrategy { + type: OutputStrategyType.RULE_CHAIN; +} + +export enum OutputStrategyType { + IMMEDIATE = 'IMMEDIATE', + RULE_CHAIN = 'RULE_CHAIN' +} + +export const OutputStrategyTypeTranslations = new Map( + [ + [OutputStrategyType.IMMEDIATE, 'calculated-fields.output-strategy.process-right-away'], + [OutputStrategyType.RULE_CHAIN, 'calculated-fields.output-strategy.process-rule-chains'], + ] +) + export enum ArgumentEntityType { Current = 'CURRENT', Device = 'DEVICE', Asset = 'ASSET', Customer = 'CUSTOMER', Tenant = 'TENANT', + Owner = 'CURRENT_OWNER', + RelationQuery = 'RELATION_PATH_QUERY', } export const ArgumentEntityTypeTranslations = new Map( @@ -81,6 +298,43 @@ export const ArgumentEntityTypeTranslations = new Map( + [ + [GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, 'calculated-fields.report-transition-event-and-presence'], + [GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_ONLY, 'calculated-fields.report-transition-event-only'], + [GeofencingReportStrategy.REPORT_PRESENCE_STATUS_ONLY, 'calculated-fields.report-presence-status-only'] + ] +) + +export const GeofencingDirectionTranslations = new Map( + [ + [EntitySearchDirection.FROM, 'calculated-fields.direction-from'], + [EntitySearchDirection.TO, 'calculated-fields.direction-to'], + ] +) + +export const GeofencingDirectionLevelTranslations = new Map( + [ + [EntitySearchDirection.FROM, 'calculated-fields.direction-down'], + [EntitySearchDirection.TO, 'calculated-fields.direction-up'], + ] +) + +export const PropagationDirectionTranslations = new Map( + [ + [EntitySearchDirection.FROM, 'calculated-fields.direction-down-child'], + [EntitySearchDirection.TO, 'calculated-fields.direction-up-parent'], ] ) @@ -127,10 +381,115 @@ export interface CalculatedFieldArgument { refEntityKey: RefEntityKey; defaultValue?: string; refEntityId?: RefEntityId; + refDynamicSourceConfiguration?: RefDynamicSourceConfiguration; limit?: number; timeWindow?: number; } +export interface RefDynamicSourceConfiguration { + type: ArgumentEntityType.Owner; +} + +export enum AggFunction { + AVG='AVG', + MIN='MIN', + MAX='MAX', + SUM='SUM', + COUNT='COUNT', + COUNT_UNIQUE='COUNT_UNIQUE' +} + +export const AggFunctionTranslations = new Map([ + [AggFunction.AVG, 'calculated-fields.metrics.aggregation-type.avg'], + [AggFunction.MIN, 'calculated-fields.metrics.aggregation-type.min'], + [AggFunction.MAX, 'calculated-fields.metrics.aggregation-type.max'], + [AggFunction.SUM, 'calculated-fields.metrics.aggregation-type.sum'], + [AggFunction.COUNT, 'calculated-fields.metrics.aggregation-type.count'], + [AggFunction.COUNT_UNIQUE, 'calculated-fields.metrics.aggregation-type.count-unique'], +]) + +export enum AggIntervalType { + HOUR = 'HOUR', + DAY = 'DAY', + WEEK = 'WEEK', + WEEK_SUN_SAT = 'WEEK_SUN_SAT', + MONTH = 'MONTH', + QUARTER = 'QUARTER', + YEAR = 'YEAR', + CUSTOM = 'CUSTOM' +} + +export const AggIntervalTypeTranslations = new Map( + [ + [AggIntervalType.HOUR, 'calculated-fields.aggregate-period.hour'], + [AggIntervalType.DAY, 'calculated-fields.aggregate-period.day'], + [AggIntervalType.WEEK, 'calculated-fields.aggregate-period.week'], + [AggIntervalType.WEEK_SUN_SAT, 'calculated-fields.aggregate-period.week-sun-sat'], + [AggIntervalType.MONTH, 'calculated-fields.aggregate-period.month'], + [AggIntervalType.QUARTER, 'calculated-fields.aggregate-period.quarter'], + [AggIntervalType.YEAR, 'calculated-fields.aggregate-period.year'], + [AggIntervalType.CUSTOM, 'calculated-fields.aggregate-period.custom'] + ] +); + +export interface AggInterval { + type: AggIntervalType; + tz: string; + offsetSec?: number + durationSec?: number +} + +export interface CalculatedFieldAggMetric { + function: AggFunction; + filter?: string; + input: AggKeyInput | AggFunctionInput; + defaultValue?: number; +} + +export interface CalculatedFieldAggMetricValue extends CalculatedFieldAggMetric { + name: string; +} + +export enum AggInputType { + key = 'key', + function = 'function' +} + +export const AggInputTypeTranslations = new Map([ + [AggInputType.key, 'calculated-fields.metrics.value-source-type.key'], + [AggInputType.function, 'calculated-fields.metrics.value-source-type.function'], +]) + +export interface AggKeyInput { + type: AggInputType.key; + key: string; +} + +export interface AggFunctionInput { + type: AggInputType.function; + function: string; +} + +export interface CalculatedFieldGeofencing { + perimeterKeyName: string; + reportStrategy: GeofencingReportStrategy; + refEntityId?: RefEntityId; + refDynamicSourceConfiguration: RefDynamicSourceGeofencingConfiguration; + createRelationsWithMatchedZones: boolean; + relationType: string; + direction: EntitySearchDirection; +} + +export interface RefDynamicSourceGeofencingConfiguration { + type: ArgumentEntityType.RelationQuery | ArgumentEntityType.Owner; + levels?: Array; +} + +export interface CalculatedFieldGeofencingValue extends CalculatedFieldGeofencing { + name: string; + entityName?: string; +} + export interface RefEntityKey { key: string; type: ArgumentType; @@ -147,7 +506,7 @@ export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument { entityName?: string; } -export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record, closeAllOnSave?: boolean) => Observable; +export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record, closeAllOnSave?: boolean, expression?: string) => Observable; export interface CalculatedFieldTestScriptInputParams { arguments: CalculatedFieldEventArguments; @@ -166,7 +525,7 @@ export const ArgumentEntityTypeParamsMap =new Map { - switch (entityId.entityType) { + switch (entityId?.entityType) { case EntityType.ASSET_PROFILE: return { assetTypes: [entityName], @@ -185,11 +544,22 @@ export const getCalculatedFieldCurrentEntityFilter = (entityName: string, entity } } +export const debugCfActionEnabled = (cf: CalculatedField) => { + return (cf.type === CalculatedFieldType.SCRIPT || + (cf.type === CalculatedFieldType.PROPAGATION && cf.configuration.applyExpressionToResolvedArguments) + ); +} + export interface CalculatedFieldArgumentValueBase { argumentName: string; type: ArgumentType; } +export interface RelationPathLevel { + direction: EntitySearchDirection; + relationType: string; +} + export interface CalculatedFieldAttributeArgumentValue extends CalculatedFieldArgumentValueBase { ts: number; value: ValueType; @@ -211,6 +581,25 @@ export type CalculatedFieldArgumentEventValue = CalculatedF export type CalculatedFieldEventArguments = Record>; +export const calculatedFieldsEntityTypeList = [EntityType.DEVICE, EntityType.ASSET, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE]; + +export const defaultCalculatedFieldOutput: CalculatedFieldOutputTimeSeries = { + type: OutputType.Timeseries, + strategy: { + type: OutputStrategyType.IMMEDIATE, + ttl: 0, + saveTimeSeries: true, + saveLatest: true, + sendWsUpdate: true, + processCfs: true + } +} + +export const defaultSimpleCalculatedFieldOutput: CalculatedFieldSimpleOutput = { + name: '', + ...defaultCalculatedFieldOutput +} + export const CalculatedFieldCtxLatestTelemetryArgumentAutocomplete = { meta: 'object', type: '{ ts: number; value: any; }', @@ -654,3 +1043,54 @@ export const calculatedFieldDefaultScript = 'return {\n' + ' "temperatureC": (temperatureF - 32) / 1.8\n' + '};' + +export function notEmptyObjectValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; + if (typeof value === 'object' && value !== null && Object.keys(value).length === 0) { + return {emptyObject: true}; + } + return null; + }; +} + +export function forbiddenNamesValidator(forbiddenNames: string[]): ValidatorFn { + const forbiddenNameSet = new Set(forbiddenNames); + + return (control: FormControl) => { + const trimmedValue = (control.value || '').trim(); + return forbiddenNameSet.has(trimmedValue) ? { forbiddenName: true } : null; + }; +} + +export function uniqueNameValidator(existingNames: string[]): ValidatorFn { + const namesSet = new Set((existingNames || []).map(name => name.toLowerCase())); + + return (control: FormControl) => { + const newName = (control.value || '').trim().toLowerCase(); + + if (!newName) { + return null; + } + + return namesSet.has(newName) ? { duplicateName: true } : null; + }; +} + +export interface CalculatedFieldsQuery { + types: Array; + entityType?: EntityType; + entities?: Array; + name?: Array; +} + +export const calculatedFieldMetricFilterDefaultScript = + '// Sample filter script to include only active and unoccupied parking spaces\n' + + '// Goal: Count only parking spaces that are active and currently free\n\n' + + 'return active == true && occupied == false;'; + +export const calculatedFieldMetricMapDefaultScript = + '// Sample map script to convert temperature from Fahrenheit to Celsius\n' + + '// Goal: Apply conversion per entity before aggregation (e.g., for average temperature)\n\n' + + 'var temperatureC = (temperature - 32) / 1.8;\n' + + 'return toFixed(temperatureC, 2);'; diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 9f3889d277..2890b4827c 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -31,6 +31,7 @@ export const Constants = { itemNotFound: 32, tooManyRequests: 33, tooManyUpdates: 34, + entitiesLimitExceeded: 41, passwordViolation: 45 }, entryPoints: { @@ -52,6 +53,7 @@ export const serverErrorCodesTranslations = new Map([ [Constants.serverErrorCode.itemNotFound, 'server-error.item-not-found'], [Constants.serverErrorCode.tooManyRequests, 'server-error.too-many-requests'], [Constants.serverErrorCode.tooManyUpdates, 'server-error.too-many-updates'], + [Constants.serverErrorCode.entitiesLimitExceeded, 'server-error.entities-limit-exceeded'], ]); export const MediaBreakpoints = { @@ -184,6 +186,7 @@ export const HelpLinks = { entitiesImport: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/bulk-provisioning`, rulechains: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/rule-chains`, lwm2mResourceLibrary: `${helpBaseUrl}/docs${docPlatformPrefix}/reference/lwm2m-api`, + jsExtension: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/contribution/ui/advanced-development`, dashboards: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/dashboards`, otaUpdates: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ota-updates`, widgetTypes: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/widget-library/#widget-types`, @@ -214,8 +217,10 @@ export const HelpLinks = { mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, calculatedField: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/calculated-fields/`, aiModels: `${helpBaseUrl}/docs${docPlatformPrefix}/samples/analytics/ai-models/`, + apiKeys: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/security/api-keys/`, timewindowSettings: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/dashboards/#time-window`, - trendzSettings: `${helpBaseUrl}/docs/trendz/` + trendzSettings: `${helpBaseUrl}/docs/trendz/`, + alarmRules: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/alarm-rules/`, } }; /* eslint-enable max-len */ diff --git a/ui-ngx/src/app/shared/models/dashboard.models.ts b/ui-ngx/src/app/shared/models/dashboard.models.ts index 1f797da162..5bd00f2549 100644 --- a/ui-ngx/src/app/shared/models/dashboard.models.ts +++ b/ui-ngx/src/app/shared/models/dashboard.models.ts @@ -186,8 +186,8 @@ export interface DashboardConfiguration { settings?: DashboardSettings; widgets?: {[id: string]: Widget } | Widget[]; states?: {[id: string]: DashboardState }; - entityAliases?: EntityAliases; - filters?: Filters; + entityAliases: EntityAliases; + filters: Filters; [key: string]: any; } diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 8e29b5b2b4..3cf1199980 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, HasTenantId, HasVersion } from '@shared/models/entity.models'; +import { EntityInfoData, HasTenantId, HasVersion, SaveEntityParams } from '@shared/models/entity.models'; import { FilterPredicateValue, KeyFilter } from '@shared/models/query/query.models'; import { TimeUnit } from '@shared/models/time/time.models'; import _moment from 'moment'; @@ -739,6 +739,10 @@ export interface DeviceInfoFilter { active?: boolean; } +export interface SaveDeviceParams extends SaveEntityParams { + accessToken?: string; +} + export class DeviceInfoQuery { pageLink: PageLink; 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 8f65a189d7..71fd1be479 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -52,6 +52,7 @@ export enum EntityType { MOBILE_APP = 'MOBILE_APP', CALCULATED_FIELD = 'CALCULATED_FIELD', AI_MODEL = 'AI_MODEL', + API_KEY = 'API_KEY', } export enum AliasEntityType { @@ -489,7 +490,7 @@ export const entityTypeTranslations = new Map([ [NotificationType.ALARM_ASSIGNMENT, 'assignment_turned_in'], [NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'], [NotificationType.ENTITIES_LIMIT, 'data_thresholding'], + [NotificationType.ENTITIES_LIMIT_INCREASE_REQUEST, 'mdi:file-cog'], [NotificationType.API_USAGE_LIMIT, 'insert_chart'], [NotificationType.TASK_PROCESSING_FAILURE, 'warning'], [NotificationType.RESOURCES_SHORTAGE, 'warning'] ]); -export const AlarmSeverityNotificationColors = new Map( - [ - [AlarmSeverity.CRITICAL, '#D12730'], - [AlarmSeverity.MAJOR, '#FEAC0C'], - [AlarmSeverity.MINOR, '#F2DA05'], - [AlarmSeverity.WARNING, '#F66716'], - [AlarmSeverity.INDETERMINATE, '#00000061'] - ] -); - export enum ActionButtonLinkType { LINK = 'LINK', DASHBOARD = 'DASHBOARD' @@ -623,6 +615,12 @@ export const NotificationTemplateTypeTranslateMap = new Map{ + return (group: AbstractControl): ValidationErrors | null => { + const newPassControl = group.get(firstControlName); + const confirmControl = group.get(secondControlName); + + if (!newPassControl || !confirmControl) { + return null; + } + + const newPass = newPassControl.value ?? ''; + const confirm = confirmControl.value ?? ''; + + if ((newPass || confirm) && confirm !== newPass) { + confirmControl.setErrors({ passwordsNotMatch: true }); + return { passwordsNotMatch: true }; + } else { + const currentErrors = confirmControl?.errors; + if (currentErrors?.['passwordsNotMatch']) { + const { passwordsNotMatch, ...rest } = currentErrors; + confirmControl?.setErrors(Object.keys(rest).length ? rest : null); + } + return null; + } + }; +} + +export const passwordStrengthValidator = (passwordPolicy: UserPasswordPolicy): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const value: string = control.value; + const errors: any = {}; + + if (passwordPolicy.minimumUppercaseLetters > 0 && + !new RegExp(`(?:.*?[A-Z]){${passwordPolicy.minimumUppercaseLetters}}`).test(value)) { + errors.notUpperCase = true; + } + + if (passwordPolicy.minimumLowercaseLetters > 0 && + !new RegExp(`(?:.*?[a-z]){${passwordPolicy.minimumLowercaseLetters}}`).test(value)) { + errors.notLowerCase = true; + } + + if (passwordPolicy.minimumDigits > 0 + && !new RegExp(`(?:.*?\\d){${passwordPolicy.minimumDigits}}`).test(value)) { + errors.notNumeric = true; + } + + if (passwordPolicy.minimumSpecialCharacters > 0 && + !new RegExp(`(?:.*?[\\W_]){${passwordPolicy.minimumSpecialCharacters}}`).test(value)) { + errors.notSpecial = true; + } + + if (!passwordPolicy.allowWhitespaces && /\s/.test(value)) { + errors.hasWhitespaces = true; + } + + if (passwordPolicy.minimumLength > 0 && value.length < passwordPolicy.minimumLength) { + errors.minLength = true; + } + + if (!value.length || passwordPolicy.maximumLength > 0 && value.length > passwordPolicy.maximumLength) { + errors.maxLength = true; + } + + return isEqual(errors, {}) ? null : errors; + }; +} diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index b3f21b0abe..7b35639aaf 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -71,3 +71,4 @@ export * from './query/query.models'; export * from './regex.constants'; export * from './trendz-settings.models'; export * from './ai-model.models'; +export * from './password.models'; diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index eacb68de35..71934a777f 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -89,7 +89,7 @@ export interface TbResourceInfo extends Omit, 'name' | export type ResourceInfo = TbResourceInfo; export interface Resource extends ResourceInfo { - data?: string; + data?: any; name?: string; } @@ -190,6 +190,7 @@ export const isImageResourceUrl = (url: string): boolean => url && IMAGES_URL_RE export const isJSResourceUrl = (url: string): boolean => url && RESOURCES_URL_REGEXP.test(url); export const isJSResource = (url: string): boolean => url?.startsWith(TB_RESOURCE_PREFIX); +export const isTbImage = (url: string): boolean => url?.startsWith(TB_IMAGE_PREFIX); export const extractParamsFromImageResourceUrl = (url: string): {type: ImageResourceType; key: string} => { const res = url.match(IMAGES_URL_REGEXP); diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 323bd7687e..262ecfc62a 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -19,6 +19,11 @@ import { TenantId } from './id/tenant-id'; import { TenantProfileId } from '@shared/models/id/tenant-profile-id'; import { BaseData, ExportableEntity } from '@shared/models/base-data'; import { QueueInfo } from '@shared/models/queue.models'; +import { FormControl } from '@angular/forms'; + +export type FormControlsFrom = { + [K in keyof T]-?: FormControl; +}; export enum TenantProfileType { DEFAULT = 'DEFAULT' @@ -101,6 +106,15 @@ export interface DefaultTenantProfileConfiguration { maxCalculatedFieldsPerEntity: number; maxArgumentsPerCF: number; + maxRelationLevelPerCfArgument: number; + minAllowedDeduplicationIntervalInSecForCF: number; + minAllowedAggregationIntervalInSecForCF: number; + maxRelatedEntitiesToReturnPerCfArgument: number; + minAllowedScheduledUpdateIntervalInSecForCF: number; + intermediateAggregationIntervalInSecForCF: number; + cfReevaluationCheckInterval: number; + alarmsReevaluationInterval: number; + maxDataPointsPerRollingArg: number; maxStateSizeInKBytes: number; maxSingleValueArgumentSizeInKBytes: number; @@ -165,6 +179,14 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxCalculatedFieldsPerEntity: 5, maxArgumentsPerCF: 10, maxDataPointsPerRollingArg: 1000, + maxRelationLevelPerCfArgument: 2, + minAllowedDeduplicationIntervalInSecForCF: 10, + minAllowedAggregationIntervalInSecForCF: 60, + maxRelatedEntitiesToReturnPerCfArgument: 100, + minAllowedScheduledUpdateIntervalInSecForCF: 10, + intermediateAggregationIntervalInSecForCF: 300, + cfReevaluationCheckInterval: 60, + alarmsReevaluationInterval: 60, maxStateSizeInKBytes: 32, maxSingleValueArgumentSizeInKBytes: 2, calculatedFieldDebugEventsRateLimit: '' 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 5fa8207eee..13baa42cb2 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -15,7 +15,7 @@ /// import { TimeService } from '@core/services/time.service'; -import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isUndefined } from '@app/core/utils'; +import { deepClean, deepClone, isDefined, isDefinedAndNotNull, isNumeric, isUndefined } from '@app/core/utils'; import moment_ from 'moment'; import * as momentTz from 'moment-timezone'; import { IntervalType } from '@shared/models/telemetry/telemetry.models'; @@ -169,6 +169,7 @@ export interface Timewindow { history?: HistoryWindow; aggregation?: Aggregation; timezone?: string; + hideSaveAsDefault?: boolean; } export interface SubscriptionAggregation extends Aggregation { @@ -279,40 +280,29 @@ export const historyInterval = (timewindowMs: number): Timewindow => ({ } }); -export const defaultTimewindow = (timeService: TimeService): Timewindow => { +export const defaultTimewindow = (timeService: TimeService, isDashboard = false): Timewindow => { const currentTime = moment().valueOf(); return { - displayValue: '', - hideAggregation: false, - hideAggInterval: false, - hideTimezone: false, selectedTab: TimewindowType.REALTIME, realtime: { realtimeType: RealtimeWindowType.LAST_INTERVAL, - interval: SECOND, - timewindowMs: MINUTE, + interval: MINUTE, + timewindowMs: HOUR, quickInterval: QuickTimeInterval.CURRENT_DAY, - hideInterval: false, - hideLastInterval: false, - hideQuickInterval: false }, history: { historyType: HistoryWindowType.LAST_INTERVAL, - interval: SECOND, - timewindowMs: MINUTE, + interval: MINUTE, + timewindowMs: HOUR, fixedTimewindow: { startTimeMs: currentTime - DAY, endTimeMs: currentTime }, quickInterval: QuickTimeInterval.CURRENT_DAY, - hideInterval: false, - hideLastInterval: false, - hideFixedInterval: false, - hideQuickInterval: false }, aggregation: { - type: AggregationType.AVG, - limit: Math.floor(timeService.getMaxDatapointsLimit() / 2) + type: isDashboard ? AggregationType.NONE : AggregationType.AVG , + limit: isDashboard ? timeService.getMaxDatapointsLimit() : Math.floor(timeService.getMaxDatapointsLimit() / 2) } }; }; @@ -326,40 +316,51 @@ const getTimewindowType = (timewindow: Timewindow): TimewindowType => { }; export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalOnly: boolean, - historyOnly: boolean, timeService: TimeService): Timewindow => { - const model = defaultTimewindow(timeService); + historyOnly: boolean, timeService: TimeService, hasAggregation: boolean, + isDashboard = false): Timewindow => { + const model = defaultTimewindow(timeService, isDashboard); if (value) { if (value.allowedAggTypes?.length) { model.allowedAggTypes = value.allowedAggTypes; } - model.hideAggregation = value.hideAggregation; - model.hideAggInterval = value.hideAggInterval; - model.hideTimezone = value.hideTimezone; + if (value.hideAggregation) { + model.hideAggregation = value.hideAggregation; + } + if (value.hideAggInterval) { + model.hideAggInterval = value.hideAggInterval; + } + if (value.hideTimezone) { + model.hideTimezone = value.hideTimezone; + } + if (value.hideSaveAsDefault) { + model.hideSaveAsDefault = value.hideSaveAsDefault; + } + model.selectedTab = getTimewindowType(value); // for backward compatibility - if (isDefinedAndNotNull((value as any).hideInterval)) { + if ((value as any).hideInterval) { model.realtime.hideInterval = (value as any).hideInterval; model.history.hideInterval = (value as any).hideInterval; delete (value as any).hideInterval; } - if (isDefinedAndNotNull((value as any).hideLastInterval)) { + if ((value as any).hideLastInterval) { model.realtime.hideLastInterval = (value as any).hideLastInterval; delete (value as any).hideLastInterval; } - if (isDefinedAndNotNull((value as any).hideQuickInterval)) { + if ((value as any).hideQuickInterval) { model.realtime.hideQuickInterval = (value as any).hideQuickInterval; delete (value as any).hideQuickInterval; } if (isDefined(value.realtime)) { - if (isDefinedAndNotNull(value.realtime.hideInterval)) { + if (value.realtime.hideInterval) { model.realtime.hideInterval = value.realtime.hideInterval; } - if (isDefinedAndNotNull(value.realtime.hideLastInterval)) { + if (value.realtime.hideLastInterval) { model.realtime.hideLastInterval = value.realtime.hideLastInterval; } - if (isDefinedAndNotNull(value.realtime.hideQuickInterval)) { + if (value.realtime.hideQuickInterval) { model.realtime.hideQuickInterval = value.realtime.hideQuickInterval; } if (value.realtime.disableCustomInterval) { @@ -393,16 +394,16 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO } } if (isDefined(value.history)) { - if (isDefinedAndNotNull(value.history.hideInterval)) { + if (value.history.hideInterval) { model.history.hideInterval = value.history.hideInterval; } - if (isDefinedAndNotNull(value.history.hideLastInterval)) { + if (value.history.hideLastInterval) { model.history.hideLastInterval = value.history.hideLastInterval; } - if (isDefinedAndNotNull(value.history.hideFixedInterval)) { + if (value.history.hideFixedInterval) { model.history.hideFixedInterval = value.history.hideFixedInterval; } - if (isDefinedAndNotNull(value.history.hideQuickInterval)) { + if (value.history.hideQuickInterval) { model.history.hideQuickInterval = value.history.hideQuickInterval; } if (value.history.disableCustomInterval) { @@ -451,7 +452,9 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO } model.aggregation.limit = value.aggregation.limit || Math.floor(timeService.getMaxDatapointsLimit() / 2); } - model.timezone = value.timezone; + if (value.timezone) { + model.timezone = value.timezone; + } } if (quickIntervalOnly) { model.realtime.realtimeType = RealtimeWindowType.INTERVAL; @@ -459,7 +462,7 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO if (historyOnly) { model.selectedTab = TimewindowType.HISTORY; } - return model; + return clearTimewindowConfig(model, quickIntervalOnly, historyOnly, hasAggregation); }; export const toHistoryTimewindow = (timewindow: Timewindow, startTimeMs: number, endTimeMs: number, @@ -1108,19 +1111,97 @@ export const cloneSelectedTimewindow = (timewindow: Timewindow): Timewindow => { if (timewindow.allowedAggTypes?.length) { cloned.allowedAggTypes = timewindow.allowedAggTypes; } - cloned.hideAggregation = timewindow.hideAggregation || false; - cloned.hideAggInterval = timewindow.hideAggInterval || false; - cloned.hideTimezone = timewindow.hideTimezone || false; + if (timewindow.hideAggregation) { + cloned.hideAggregation = timewindow.hideAggregation; + } + if (timewindow.hideAggInterval) { + cloned.hideAggInterval = timewindow.hideAggInterval; + } + if (timewindow.hideTimezone) { + cloned.hideTimezone = timewindow.hideTimezone; + } + if (timewindow.hideSaveAsDefault) { + cloned.hideSaveAsDefault = timewindow.hideSaveAsDefault; + } if (isDefined(timewindow.selectedTab)) { cloned.selectedTab = timewindow.selectedTab; } - cloned.realtime = deepClone(timewindow.realtime); - cloned.history = deepClone(timewindow.history); - cloned.aggregation = deepClone(timewindow.aggregation); - cloned.timezone = timewindow.timezone; + if (isDefined(timewindow.realtime)) { + cloned.realtime = deepClone(timewindow.realtime); + } + if (isDefined(timewindow.history)) { + cloned.history = deepClone(timewindow.history); + } + if (isDefined(timewindow.aggregation)) { + cloned.aggregation = deepClone(timewindow.aggregation); + } + if (timewindow.timezone) { + cloned.timezone = timewindow.timezone; + } return cloned; }; +export const clearTimewindowConfig = (timewindow: Timewindow, quickIntervalOnly: boolean, + historyOnly: boolean, hasAggregation: boolean, hasTimezone = true): Timewindow => { + const noneAggregation = hasAggregation && timewindow.aggregation?.type === AggregationType.NONE; + if (timewindow.selectedTab === TimewindowType.REALTIME) { + if (quickIntervalOnly || timewindow.realtime.realtimeType === RealtimeWindowType.INTERVAL) { + delete timewindow.realtime.timewindowMs; + } else { + delete timewindow.realtime.quickInterval; + } + + delete timewindow.history?.historyType; + delete timewindow.history?.timewindowMs; + delete timewindow.history?.fixedTimewindow; + delete timewindow.history?.quickInterval; + + delete timewindow.history?.interval; + if (!hasAggregation || noneAggregation) { + delete timewindow.realtime.interval; + } + } else { + if (timewindow.history.historyType === HistoryWindowType.LAST_INTERVAL) { + delete timewindow.history.fixedTimewindow; + delete timewindow.history.quickInterval; + } else if (timewindow.history.historyType === HistoryWindowType.FIXED) { + delete timewindow.history.timewindowMs; + delete timewindow.history.quickInterval; + } else if (timewindow.history.historyType === HistoryWindowType.INTERVAL) { + delete timewindow.history.timewindowMs; + delete timewindow.history.fixedTimewindow; + } else { + delete timewindow.history.timewindowMs; + delete timewindow.history.fixedTimewindow; + delete timewindow.history.quickInterval; + } + + delete timewindow.realtime?.realtimeType; + delete timewindow.realtime?.timewindowMs; + delete timewindow.realtime?.quickInterval; + + delete timewindow.realtime?.interval; + if (!hasAggregation || noneAggregation) { + delete timewindow.history.interval; + } + } + + if (!hasAggregation) { + delete timewindow.aggregation; + } else if (!noneAggregation) { + delete timewindow.aggregation.limit; + } + + if (historyOnly) { + delete timewindow.realtime; + } + + if (!hasTimezone) { + delete timewindow.timezone; + } + return deepClean(timewindow); +}; + export interface TimeInterval { name: string; translateParams: {[key: string]: any}; @@ -1198,6 +1279,16 @@ export const defaultTimeIntervals = new Array( translateParams: {hours: 5}, value: 5 * HOUR }, + { + name: 'timeinterval.hours-interval', + translateParams: {hours: 6}, + value: 6 * HOUR + }, + { + name: 'timeinterval.hours-interval', + translateParams: {hours: 8}, + value: 8 * HOUR + }, { name: 'timeinterval.hours-interval', translateParams: {hours: 10}, diff --git a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts index 98e801e632..7b74d282ba 100644 --- a/ui-ngx/src/app/shared/models/two-factor-auth.models.ts +++ b/ui-ngx/src/app/shared/models/two-factor-auth.models.ts @@ -14,7 +14,11 @@ /// limitations under the License. /// +import { UsersFilter } from '@shared/models/notification.models'; + export interface TwoFactorAuthSettings { + enforceTwoFa: boolean; + enforcedUsersFilter: UsersFilter; maxVerificationFailuresBeforeUserLockout: number; providers: Array; totalAllowedTimeForVerification: number; @@ -24,12 +28,18 @@ export interface TwoFactorAuthSettings { } export interface TwoFactorAuthSettingsForm extends TwoFactorAuthSettings{ + enforceTwoFa: boolean; + enforcedUsersFilter: UsersFilterWithFilterByTenant; providers: Array; verificationCodeCheckRateLimitEnable: boolean; verificationCodeCheckRateLimitNumber: number; verificationCodeCheckRateLimitTime: number; } +export interface UsersFilterWithFilterByTenant extends UsersFilter{ + filterByTenants?: boolean; +} + export type TwoFactorAuthProviderConfig = Partial; @@ -183,3 +193,61 @@ export const twoFactorAuthProvidersLoginData = new Map>( + [ + [ + TwoFactorAuthProviderType.TOTP, { + name: 'login.enable-authenticator-app', + description: 'login.enable-authenticator-app-description' + } + ], + [ + TwoFactorAuthProviderType.SMS, { + name: 'login.enable-authenticator-sms', + description: 'login.enable-authenticator-sms-description' + } + ], + [ + TwoFactorAuthProviderType.EMAIL, { + name: 'login.enable-authenticator-email', + description: 'login.enable-authenticator-email-description' + } + ], + [ + TwoFactorAuthProviderType.BACKUP_CODE, { + name: 'security.2fa.provider.backup_code', + description: 'login.backup-code-auth-description' + } + ] + ] +); + +export const twoFactorAuthProvidersSuccessCardTranslate = new Map>( + [ + [ + TwoFactorAuthProviderType.TOTP, { + name: 'login.authenticator-app-success', + description: 'login.authenticator-app-success-description' + } + ], + [ + TwoFactorAuthProviderType.SMS, { + name: 'login.authenticator-sms-success', + description: 'login.authenticator-sms-success-description' + } + ], + [ + TwoFactorAuthProviderType.EMAIL, { + name: 'login.authenticator-email-success', + description: 'login.authenticator-email-success-description' + } + ], + [ + TwoFactorAuthProviderType.BACKUP_CODE, { + name: 'login.authenticator-backup-code-success', + description: 'login.authenticator-backup-code-success-description' + } + ] + ] +); diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index d3a27d1442..be4300fea2 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -265,4 +265,4 @@ export interface EntityDataInfo { hasCalculatedFields: boolean; } -export const typesWithCalculatedFields = new Set([EntityType.DEVICE, EntityType.ASSET, EntityType.ASSET_PROFILE, EntityType.DEVICE_PROFILE]); +export const typesWithCalculatedFields = new Set([EntityType.DEVICE, EntityType.ASSET, EntityType.ASSET_PROFILE, EntityType.DEVICE_PROFILE, EntityType.CUSTOMER]); diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 43ef0d7df6..1a2f7e1c8b 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -33,7 +33,7 @@ import { PageComponent } from '@shared/components/page.component'; import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit, Type } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { AbstractControl, UntypedFormGroup } from '@angular/forms'; +import { AbstractControl, UntypedFormGroup, ValidatorFn } from '@angular/forms'; import { Observable } from 'rxjs'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; @@ -51,6 +51,7 @@ import { TbFunction } from '@shared/models/js-function.models'; import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { TbUnit } from '@shared/models/unit.models'; +import { ImageResourceInfo } from '@shared/models/resource.models'; export enum widgetType { timeseries = 'timeseries', @@ -71,6 +72,8 @@ export interface WidgetTypeData { template: WidgetTypeTemplate; } +export const widgetTitleAutocompleteValues = ['entityName', 'entityLabel']; + export const widgetTypesData = new Map( [ [ @@ -398,7 +401,6 @@ export interface DataKey extends KeyInfo { inLegend?: boolean; isAdditional?: boolean; origDataKeyIndex?: number; - _hash?: number; } export type CellClickColumnInfo = Pick; @@ -489,6 +491,14 @@ export const targetDeviceValid = (targetDevice?: TargetDevice): boolean => ((targetDevice.type === TargetDeviceType.device && !!targetDevice.deviceId) || (targetDevice.type === TargetDeviceType.entity && !!targetDevice.entityAliasId)); +export const widgetTypeHasTimewindow = (type: widgetType): boolean => { + return type === widgetType.timeseries || type === widgetType.alarm; +} + +export const widgetTypeCanHaveTimewindow = (type: widgetType): boolean => { + return widgetTypeHasTimewindow(type) || type === widgetType.latest; +} + export const datasourcesHasAggregation = (datasources?: Array): boolean => { if (datasources) { const foundDatasource = datasources.find(datasource => { @@ -622,11 +632,36 @@ export enum WidgetMobileActionType { deviceProvision = 'deviceProvision', } +export interface ActionConfig { + title: string, + formControlName: string, + functionName: string, + functionArgs: string[], + helpId?: string +} + +export enum ProvisionType { + auto = 'auto', + wiFi = 'wiFi', + ble = 'ble', + softAp = 'softAp' +} + +export const provisionTypeTranslationMap = new Map( + [ + [ ProvisionType.auto, 'widget-action.mobile.auto' ], + [ ProvisionType.wiFi, 'widget-action.mobile.wi-fi' ], + [ ProvisionType.ble, 'widget-action.mobile.ble' ], + [ ProvisionType.softAp, 'widget-action.mobile.soft-ap' ], + ] +); + export enum MapItemType { marker = 'marker', polygon = 'polygon', rectangle = 'rectangle', - circle = 'circle' + circle = 'circle', + polyline = 'polyline' } export const widgetActionTypes = Object.keys(WidgetActionType) @@ -666,6 +701,7 @@ export const mapItemTypeTranslationMap = new Map( [ MapItemType.polygon, 'widget-action.map-item.polygon' ], [ MapItemType.rectangle, 'widget-action.map-item.rectangle' ], [ MapItemType.circle, 'widget-action.map-item.circle' ], + [ MapItemType.polyline, 'widget-action.map-item.polyline' ] ] ) @@ -675,6 +711,7 @@ export interface MobileLaunchResult { export interface MobileImageResult { imageUrl: string; + imageInfo?: ImageResourceInfo; } export interface MobileQrCodeResult { @@ -706,10 +743,12 @@ export interface WidgetMobileActionResult { export interface ProvisionSuccessDescriptor { handleProvisionSuccessFunction: TbFunction; + provisionType?: string; } export interface ProcessImageDescriptor { processImageFunction: TbFunction; + saveToGallery?: boolean; } export interface ProcessLaunchResultDescriptor { @@ -743,6 +782,7 @@ export interface WidgetMobileActionDescriptor extends WidgetMobileActionDescript type: WidgetMobileActionType; handleErrorFunction?: TbFunction; handleEmptyResultFunction?: TbFunction; + handleNonMobileFallbackFunction?: TbFunction; } export interface CustomActionDescriptor { @@ -789,6 +829,8 @@ export interface MapItemTooltips { finishRect?: string; startCircle?: string; finishCircle?: string; + startPolyline?: string; + finishPolyline?: string; } export const mapItemTooltipsTranslation: Required = Object.freeze({ @@ -799,7 +841,9 @@ export const mapItemTooltipsTranslation: Required = Object.free startRect: 'widgets.maps.data-layer.polygon.rectangle-place-first-point-hint', finishRect: 'widgets.maps.data-layer.polygon.finish-rectangle-hint', startCircle: 'widgets.maps.data-layer.circle.place-circle-center-hint', - finishCircle: 'widgets.maps.data-layer.circle.finish-circle-hint' + finishCircle: 'widgets.maps.data-layer.circle.finish-circle-hint', + startPolyline: 'widgets.maps.data-layer.polyline.polyline-place-first-point-hint', + finishPolyline: 'widgets.maps.data-layer.polyline.finish-polyline-hint' }) export interface WidgetActionDescriptor extends WidgetAction { @@ -1108,5 +1152,4 @@ export abstract class WidgetSettingsComponent extends PageComponent implements protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { } - } diff --git a/ui-ngx/src/app/shared/models/widget/home-widgets/api-usage-model.definition.ts b/ui-ngx/src/app/shared/models/widget/home-widgets/api-usage-model.definition.ts new file mode 100644 index 0000000000..fbbcbd7105 --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget/home-widgets/api-usage-model.definition.ts @@ -0,0 +1,94 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models'; +import { FilterInfo, Filters } from '@shared/models/query/query.models'; +import { Dashboard } from '@shared/models/dashboard.models'; +import { Datasource, DatasourceType, Widget } from '@shared/models/widget.models'; +import { WidgetModelDefinition } from '@shared/models/widget/widget-model.definition'; +import { + ApiUsageWidgetSettings, + getUniqueDataKeys +} from '@home/components/widget/lib/settings/cards/api-usage-settings.component.models'; + +interface AliasFilterPair { + alias?: EntityAliasInfo; + filter?: FilterInfo; +} + +interface ApiUsageDatasourcesInfo { + ds?: AliasFilterPair; +} + +export const ApiUsageModelDefinition: WidgetModelDefinition = { + testWidget(widget: Widget): boolean { + if (widget?.config?.settings) { + const settings = widget.config.settings; + if (settings.apiUsageDataKeys && Array.isArray(settings.apiUsageDataKeys)) { + return true; + } + } + return false; + }, + prepareExportInfo(dashboard: Dashboard, widget: Widget): ApiUsageDatasourcesInfo { + const settings: ApiUsageWidgetSettings = widget.config.settings as ApiUsageWidgetSettings; + const info: ApiUsageDatasourcesInfo = {}; + if (settings.dsEntityAliasId) { + info.ds = prepareExportDataSourcesInfo(dashboard, settings.dsEntityAliasId); + } + return info; + }, + updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: ApiUsageDatasourcesInfo): void { + const settings: ApiUsageWidgetSettings = widget.config.settings as ApiUsageWidgetSettings; + if (info?.ds?.alias) { + settings.dsEntityAliasId = getEntityAliasId(entityAliases, info.ds.alias); + } + }, + datasources(widget: Widget): Datasource[] { + const settings: ApiUsageWidgetSettings = widget.config.settings as ApiUsageWidgetSettings; + const datasources: Datasource[] = []; + if (settings.apiUsageDataKeys?.length && settings.dsEntityAliasId) { + datasources.push({ + type: DatasourceType.entity, + name: '', + entityAliasId: settings.dsEntityAliasId, + dataKeys: getUniqueDataKeys(settings.apiUsageDataKeys) + }); + } + return datasources; + }, + hasTimewindow(): boolean { + return false; + }, + datasourcesHasAggregation(): boolean { + return false; + }, + datasourcesHasOnlyComparisonAggregation(): boolean { + return false; + } +}; + +const prepareExportDataSourcesInfo = (dashboard: Dashboard, settings: string): AliasFilterPair => { + const aliasAndFilter: AliasFilterPair = {}; + const entityAlias = dashboard.configuration.entityAliases[settings]; + if (entityAlias) { + aliasAndFilter.alias = { + alias: entityAlias.alias, + filter: entityAlias.filter + }; + } + return aliasAndFilter; +} diff --git a/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts b/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts index 4505b14771..38cc0b52f4 100644 --- a/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts +++ b/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts @@ -17,10 +17,18 @@ import { EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models'; import { FilterInfo, Filters, getFilterId } from '@shared/models/query/query.models'; import { Dashboard } from '@shared/models/dashboard.models'; -import { Datasource, DatasourceType, Widget } from '@shared/models/widget.models'; import { + Datasource, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + DatasourceType, + Widget +} from '@shared/models/widget.models'; +import { + additionalMapDataSourcesToDatasources, BaseMapSettings, MapDataLayerSettings, + MapDataLayerType, MapDataSourceSettings, mapDataSourceSettingsToDatasource, MapType @@ -121,9 +129,26 @@ export const MapModelDefinition: WidgetModelDefinition = { datasources.push(...getMapDataLayersDatasources(settings.circles)); } if (settings.additionalDataSources?.length) { - datasources.push(...getMapDataLayersDatasources(settings.additionalDataSources)); + datasources.push(...additionalMapDataSourcesToDatasources(settings.additionalDataSources)); } return datasources; + }, + hasTimewindow(widget: Widget): boolean { + const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; + if (settings.trips?.length) { + return true; + } else { + const datasources: Datasource[] = getMapLatestDataLayersDatasources(settings); + return datasourcesHasAggregation(datasources); + } + }, + datasourcesHasAggregation(widget: Widget): boolean { + const datasources: Datasource[] = getMapLatestDataLayersDatasources(widget.config.settings as BaseMapSettings); + return datasourcesHasAggregation(datasources); + }, + datasourcesHasOnlyComparisonAggregation(widget: Widget): boolean { + const datasources: Datasource[] = getMapLatestDataLayersDatasources(widget.config.settings as BaseMapSettings); + return datasourcesHasOnlyComparisonAggregation(datasources); } }; @@ -211,15 +236,38 @@ const prepareAliasAndFilterPair = (dashboard: Dashboard, settings: MapDataSource } } -const getMapDataLayersDatasources = (settings: MapDataLayerSettings[] | MapDataSourceSettings[]): Datasource[] => { +const getMapDataLayersDatasources = (settings: MapDataLayerSettings[], + includeDataKeys = false, dataLayerType?: MapDataLayerType): Datasource[] => { const datasources: Datasource[] = []; settings.forEach((dsSettings) => { - datasources.push(mapDataSourceSettingsToDatasource(dsSettings)); - if ((dsSettings as MapDataLayerSettings).additionalDataSources?.length) { - (dsSettings as MapDataLayerSettings).additionalDataSources.forEach((ds) => { - datasources.push(mapDataSourceSettingsToDatasource(ds)); + const datasource: Datasource = mapDataSourceSettingsToDatasource(dsSettings, null, includeDataKeys, dataLayerType); + datasources.push(datasource); + if (dsSettings.additionalDataSources?.length) { + dsSettings.additionalDataSources.forEach((ds) => { + const additionalDatasource: Datasource = mapDataSourceSettingsToDatasource(ds); + if (includeDataKeys) { + additionalDatasource.dataKeys.push(...datasource.dataKeys); + } + datasources.push(additionalDatasource); }); } }); return datasources; }; + +const getMapLatestDataLayersDatasources = (settings: BaseMapSettings): Datasource[] => { + const datasources: Datasource[] = []; + if (settings.markers?.length) { + datasources.push(...getMapDataLayersDatasources(settings.markers, true, 'markers')); + } + if (settings.polygons?.length) { + datasources.push(...getMapDataLayersDatasources(settings.polygons, true, 'polygons')); + } + if (settings.circles?.length) { + datasources.push(...getMapDataLayersDatasources(settings.circles, true, 'circles')); + } + if (settings.additionalDataSources?.length) { + datasources.push(...additionalMapDataSourcesToDatasources(settings.additionalDataSources)); + } + return datasources; +}; diff --git a/ui-ngx/src/app/shared/models/widget/maps/map.models.ts b/ui-ngx/src/app/shared/models/widget/maps/map.models.ts index 6db3b1481b..0f1c979425 100644 --- a/ui-ngx/src/app/shared/models/widget/maps/map.models.ts +++ b/ui-ngx/src/app/shared/models/widget/maps/map.models.ts @@ -24,6 +24,7 @@ import { } from '@shared/models/widget.models'; import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { + deepClone, guid, hashCode, isDefinedAndNotNull, @@ -61,19 +62,47 @@ export interface TbMapDatasource extends Datasource { mapDataIds: string[]; } -export const mapDataSourceSettingsToDatasource = (settings: MapDataSourceSettings, id = guid()): TbMapDatasource => { +export const mapDataSourceSettingsToDatasource = (settings: MapDataSourceSettings | MapDataLayerSettings, + id = guid(), + includeDataKeys = false, dataLayerType?: MapDataLayerType): TbMapDatasource => { + const dataKeys = includeDataKeys ? mapDataLayerDatasourceDataKeys((settings as MapDataLayerSettings), dataLayerType) : []; return { type: settings.dsType, name: settings.dsLabel, deviceId: settings.dsDeviceId, entityAliasId: settings.dsEntityAliasId, filterId: settings.dsFilterId, - dataKeys: [], + dataKeys: dataKeys, latestDataKeys: [], mapDataIds: [id] }; }; +const mapDataLayerDatasourceDataKeys = (settings: MapDataLayerSettings, + dataLayerType: MapDataLayerType): DataKey[] => { + const dataKeys = settings.additionalDataKeys?.length ? deepClone(settings.additionalDataKeys) : []; + switch (dataLayerType) { + case 'trips': + const tripsSettings = settings as TripsDataLayerSettings; + dataKeys.push(tripsSettings.xKey, tripsSettings.yKey); + break; + case 'markers': + const markersSettings = settings as MarkersDataLayerSettings; + dataKeys.push(markersSettings.xKey, markersSettings.yKey); + break; + case 'polygons': + dataKeys.push((settings as PolygonsDataLayerSettings).polygonKey); + break; + case 'circles': + dataKeys.push((settings as CirclesDataLayerSettings).circleKey); + break; + case 'polylines': + dataKeys.push((settings as PolylinesDataLayerSettings).polylineKey); + break; + } + return dataKeys; +}; + export enum DataLayerPatternType { pattern = 'pattern', @@ -133,6 +162,7 @@ export interface DataLayerEditSettings { snappable: boolean; } + export interface MapDataLayerSettings extends MapDataSourceSettings { additionalDataSources?: MapDataSourceSettings[]; additionalDataKeys?: DataKey[]; @@ -140,7 +170,7 @@ export interface MapDataLayerSettings extends MapDataSourceSettings { tooltip: DataLayerTooltipSettings; click: WidgetAction; groups?: string[]; - edit: DataLayerEditSettings; + edit: DataLayerEditSettings; } export const defaultBaseDataLayerSettings = (mapType: MapType): Partial => ({ @@ -156,7 +186,7 @@ export const defaultBaseDataLayerSettings = (mapType: MapType): Partial${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details' - : '${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details', + : '${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details', offsetX: 0, offsetY: -1 }, @@ -170,9 +200,9 @@ export const defaultBaseDataLayerSettings = (mapType: MapType): Partial { if (!dataLayer.dsType || ![DatasourceType.function, DatasourceType.device, DatasourceType.entity].includes(dataLayer.dsType)) { @@ -196,7 +226,7 @@ export const mapDataLayerValid = (dataLayer: MapDataLayerSettings, type: MapData case 'markers': const markersDataLayer = dataLayer as MarkersDataLayerSettings; if (!markersDataLayer.xKey?.type || !markersDataLayer.xKey?.name || - !markersDataLayer.yKey?.type || !markersDataLayer.xKey?.name) { + !markersDataLayer.yKey?.type || !markersDataLayer.xKey?.name) { return false; } break; @@ -206,6 +236,12 @@ export const mapDataLayerValid = (dataLayer: MapDataLayerSettings, type: MapData return false; } break; + case 'polylines': + const polylinesDataLayer = dataLayer as PolylinesDataLayerSettings; + if (!polylinesDataLayer.polylineKey?.type || !polylinesDataLayer.polylineKey?.name) { + return false; + } + break; case 'circles': const circlesDataLayer = dataLayer as CirclesDataLayerSettings; if (!circlesDataLayer.circleKey?.type || !circlesDataLayer.circleKey?.name) { @@ -274,6 +310,7 @@ export interface MarkerIconSettings extends BaseMarkerShapeSettings { iconContainer?: MarkerIconContainer; icon: string; } + export interface MarkerClusteringSettings { enable: boolean; zoomOnClick: boolean; @@ -574,8 +611,11 @@ export const defaultBasePolygonsDataLayerSettings = (mapType: MapType): Partial< color: '#3388ff', }, strokeWeight: 3 -} as Partial, defaultBaseDataLayerSettings(mapType), - {label: {show: false}, tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'}} as Partial) + } as Partial, defaultBaseDataLayerSettings(mapType), + { + label: {show: false}, + tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'} + } as Partial) export interface CirclesDataLayerSettings extends ShapeDataLayerSettings { circleKey: DataKey; @@ -625,8 +665,45 @@ export const defaultBaseCirclesDataLayerSettings = (mapType: MapType): Partial, defaultBaseDataLayerSettings(mapType), - {label: {show: false}, tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'}} as Partial) + } as Partial, defaultBaseDataLayerSettings(mapType), + { + label: {show: false}, + tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'} + } as Partial) + +export interface PolylinesDataLayerSettings extends ShapeDataLayerSettings { + polylineKey: DataKey; +} + +export const defaultPolylinesDataLayerSettings = (mapType: MapType, functionsOnly = false): PolylinesDataLayerSettings => mergeDeep({ + dsType: functionsOnly ? DatasourceType.function : DatasourceType.entity, + dsLabel: functionsOnly ? 'First polyline' : '', + polylineKey: { + name: functionsOnly ? 'f(x)' : 'perimeter', + label: 'perimeter', + type: functionsOnly ? DataKeyType.function : DataKeyType.attribute, + settings: {}, + color: materialColors[0].value + } +} as PolylinesDataLayerSettings, defaultBasePolylinesDataLayerSettings(mapType) as PolylinesDataLayerSettings); + +export const defaultBasePolylinesDataLayerSettings = (mapType: MapType): Partial => mergeDeep({ + fillType: ShapeFillType.color, + fillColor: { + type: DataLayerColorType.constant, + color: 'rgba(51,136,255,0.2)', + }, + strokeColor: { + type: DataLayerColorType.constant, + color: '#3388ff', + }, + strokeWeight: 3 + } as Partial, defaultBaseDataLayerSettings(mapType), + { + label: {show: false}, + tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'} + } as Partial) + export const defaultMapDataLayerSettings = (mapType: MapType, dataLayerType: MapDataLayerType, functionsOnly = false): MapDataLayerSettings => { switch (dataLayerType) { @@ -638,6 +715,8 @@ export const defaultMapDataLayerSettings = (mapType: MapType, dataLayerType: Map return defaultPolygonsDataLayerSettings(mapType, functionsOnly); case 'circles': return defaultCirclesDataLayerSettings(mapType, functionsOnly); + case 'polylines': + return defaultPolylinesDataLayerSettings(mapType, functionsOnly); } }; @@ -651,6 +730,8 @@ export const defaultBaseMapDataLayerSettings = ( return defaultBasePolygonsDataLayerSettings(mapType) as T; case 'circles': return defaultBaseCirclesDataLayerSettings(mapType) as T; + case 'polylines': + return defaultBasePolylinesDataLayerSettings(mapType) as T; } } @@ -658,10 +739,11 @@ export interface AdditionalMapDataSourceSettings extends MapDataSourceSettings { dataKeys: DataKey[]; } -export const additionalMapDataSourcesToDatasources = (additionalMapDataSources: AdditionalMapDataSourceSettings[]): TbMapDatasource[] => { +export const additionalMapDataSourcesToDatasources = (additionalMapDataSources: AdditionalMapDataSourceSettings[], + includeDataKeys = true): TbMapDatasource[] => { return additionalMapDataSources.map(addDs => { const res = mapDataSourceSettingsToDatasource(addDs); - res.dataKeys = addDs.dataKeys; + res.dataKeys = includeDataKeys ? addDs.dataKeys : []; return res; }); }; @@ -700,13 +782,13 @@ export const additionalMapDataSourceValid = (dataSource: AdditionalMapDataSource }; export const additionalMapDataSourceValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - const dataSource: AdditionalMapDataSourceSettings = control.value; - if (!additionalMapDataSourceValid(dataSource)) { - return { - dataSource: true - }; - } - return null; + const dataSource: AdditionalMapDataSourceSettings = control.value; + if (!additionalMapDataSourceValid(dataSource)) { + return { + dataSource: true + }; + } + return null; }; export const defaultAdditionalMapDataSourceSettings = (functionsOnly = false): AdditionalMapDataSourceSettings => { @@ -788,6 +870,7 @@ export interface BaseMapSettings { markers: MarkersDataLayerSettings[]; polygons: PolygonsDataLayerSettings[]; circles: CirclesDataLayerSettings[]; + polylines: PolylinesDataLayerSettings[]; additionalDataSources: AdditionalMapDataSourceSettings[]; controlsPosition: MapControlsPosition; zoomActions: MapZoomAction[]; @@ -812,6 +895,7 @@ export const defaultBaseMapSettings: BaseMapSettings = { markers: [], polygons: [], circles: [], + polylines: [], additionalDataSources: [], controlsPosition: MapControlsPosition.topleft, zoomActions: [MapZoomAction.scroll, MapZoomAction.doubleClick, MapZoomAction.controlButtons], @@ -1218,6 +1302,12 @@ export type TbPolyData = L.LatLngTuple[] | L.LatLngTuple[][] | L.LatLngTuple[][] export type TbPolygonCoordinate = L.LatLng | L.LatLng[] | L.LatLng[][]; export type TbPolygonCoordinates = TbPolygonCoordinate[]; +export type TbPolylineRawCoordinate = L.LatLngTuple | L.LatLngTuple[] | L.LatLngTuple[][]; +export type TbPolylineRawCoordinates = TbPolylineRawCoordinate[]; +export type TbPolylineData = L.LatLngTuple[] | L.LatLngTuple[][] | L.LatLngTuple[][][]; +export type TbPolylineCoordinate = L.LatLng | L.LatLng[] | L.LatLng[][]; +export type TbPolylineCoordinates = TbPolylineCoordinate[]; + export interface TbCircleData { latitude: number; longitude: number; diff --git a/ui-ngx/src/app/shared/models/widget/widget-model.definition.ts b/ui-ngx/src/app/shared/models/widget/widget-model.definition.ts index d042987223..04c44874a1 100644 --- a/ui-ngx/src/app/shared/models/widget/widget-model.definition.ts +++ b/ui-ngx/src/app/shared/models/widget/widget-model.definition.ts @@ -14,23 +14,60 @@ /// limitations under the License. /// -import { Datasource, Widget } from '@shared/models/widget.models'; +import { + Datasource, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + Widget, + widgetType, + widgetTypeHasTimewindow +} from '@shared/models/widget.models'; import { Dashboard } from '@shared/models/dashboard.models'; import { EntityAliases } from '@shared/models/alias.models'; import { Filters } from '@shared/models/query/query.models'; import { MapModelDefinition } from '@shared/models/widget/maps/map-model.definition'; +import { ApiUsageModelDefinition } from '@shared/models/widget/home-widgets/api-usage-model.definition'; export interface WidgetModelDefinition { testWidget(widget: Widget): boolean; prepareExportInfo(dashboard: Dashboard, widget: Widget): T; updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: T): void; datasources(widget: Widget): Datasource[]; + hasTimewindow(widget: Widget): boolean; + datasourcesHasAggregation(widget: Widget): boolean; + datasourcesHasOnlyComparisonAggregation(widget: Widget): boolean; } const widgetModelRegistry: WidgetModelDefinition[] = [ - MapModelDefinition + MapModelDefinition, + ApiUsageModelDefinition ]; export const findWidgetModelDefinition = (widget: Widget): WidgetModelDefinition => { return widgetModelRegistry.find(def => def.testWidget(widget)); } + +export const widgetHasTimewindow = (widget: Widget): boolean => { + const widgetDefinition = findWidgetModelDefinition(widget); + if (widgetDefinition) { + return widgetDefinition.hasTimewindow(widget); + } + return widgetTypeHasTimewindow(widget.type) + || (widget.type === widgetType.latest && datasourcesHasAggregation(widget.config.datasources)); +}; + +export const widgetDatasourcesHasAggregation = (widget: Widget): boolean => { + const widgetDefinition = findWidgetModelDefinition(widget); + if (widgetDefinition) { + return widgetDefinition.datasourcesHasAggregation(widget); + } + return widget.type === widgetType.latest && datasourcesHasAggregation(widget.config.datasources); +}; + +export const widgetDatasourcesHasOnlyComparisonAggregation = (widget: Widget): boolean => { + const widgetDefinition = findWidgetModelDefinition(widget); + if (widgetDefinition) { + return widgetDefinition.datasourcesHasOnlyComparisonAggregation(widget); + } + return widget.type === widgetType.latest && datasourcesHasOnlyComparisonAggregation(widget.config.datasources); +}; diff --git a/ui-ngx/src/app/shared/pipe/date-expiration.pipe.ts b/ui-ngx/src/app/shared/pipe/date-expiration.pipe.ts new file mode 100644 index 0000000000..b040a4d4b4 --- /dev/null +++ b/ui-ngx/src/app/shared/pipe/date-expiration.pipe.ts @@ -0,0 +1,42 @@ +/// +/// Copyright © 2016-2026 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 { Pipe, PipeTransform } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe'; +import { isDefined } from '@core/utils'; + +@Pipe({ + name: 'dateExpiration' +}) +export class DateExpirationPipe implements PipeTransform { + + constructor(private millisecondsToTimeString: MillisecondsToTimeStringPipe, private datePipe: DatePipe) { + } + + transform(expirationMs: number, arg?: any): string { + const displayDate = isDefined(arg?.displayDate) ? arg.displayDate : true; + const dateFormat = isDefined(arg?.dateFormat) ? arg.dateFormat : ' (dd/MM/yyyy)'; + const shortFormat = isDefined(arg?.shortFormat) ? arg.shortFormat : true; + const onlyFirstDigit = isDefined(arg?.onlyFirstDigit) ? arg.onlyFirstDigit : true; + let time = this.millisecondsToTimeString.transform(expirationMs, shortFormat, onlyFirstDigit); + if (displayDate) { + const exactDate = this.datePipe.transform(expirationMs + Date.now(), dateFormat); + time += exactDate; + } + return time; + } +} diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index bb71f79b2a..6f06744d60 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -220,14 +220,24 @@ import { CountryData } from '@shared/models/country.models'; import { SvgXmlComponent } from '@shared/components/svg-xml.component'; import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component'; import { AggregationTypeSelectComponent } from '@shared/components/time/aggregation/aggregation-type-select.component'; -import { AggregationOptionsConfigPanelComponent } from '@shared/components/time/aggregation/aggregation-options-config-panel.component'; +import { + AggregationOptionsConfigPanelComponent +} from '@shared/components/time/aggregation/aggregation-options-config-panel.component'; import { IntervalOptionsConfigPanelComponent } from '@shared/components/time/interval-options-config-panel.component'; -import { GroupingIntervalOptionsComponent } from '@shared/components/time/aggregation/grouping-interval-options.component'; +import { + GroupingIntervalOptionsComponent +} from '@shared/components/time/aggregation/grouping-interval-options.component'; import { JsFuncModulesComponent } from '@shared/components/js-func-modules.component'; import { JsFuncModuleRowComponent } from '@shared/components/js-func-module-row.component'; import { EntityKeyAutocompleteComponent } from '@shared/components/entity/entity-key-autocomplete.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { MqttVersionSelectComponent } from '@shared/components/mqtt-version-select.component'; +import { PasswordRequirementsTooltipComponent } from '@shared/components/password-requirements-tooltip.component'; +import { StringPatternAutocompleteComponent } from '@shared/components/string-pattern-autocomplete.component'; +import { TimeUnitInputComponent } from '@shared/components/time-unit-input.component'; +import { DateExpirationPipe } from '@shared/pipe/date-expiration.pipe'; +import { EntityLimitExceededDialogComponent } from '@shared/components/dialog/entity-limit-exceeded-dialog.component'; +import { DynamicMatDialogModule } from '@shared/components/dialog/dynamic/dynamic-dialog.module'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; @@ -236,6 +246,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) @NgModule({ providers: [ DatePipe, + SelectableColumnsPipe, MillisecondsToTimeStringPipe, EnumToArrayPipe, HighlightPipe, @@ -365,6 +376,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) TodoDialogComponent, ColorPickerDialogComponent, MaterialIconsDialogComponent, + EntityLimitExceededDialogComponent, ColorInputComponent, MaterialIconSelectComponent, NodeScriptTestDialogComponent, @@ -392,6 +404,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ShortNumberPipe, SelectableColumnsPipe, KeyboardShortcutPipe, + DateExpirationPipe, TbJsonToStringDirective, JsonObjectEditDialogComponent, HistorySelectorComponent, @@ -443,6 +456,9 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ScadaSymbolInputComponent, EntityKeyAutocompleteComponent, MqttVersionSelectComponent, + PasswordRequirementsTooltipComponent, + TimeUnitInputComponent, + StringPatternAutocompleteComponent ], imports: [ CommonModule, @@ -505,7 +521,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) useFactory: MarkedOptionsFactory, deps: [MarkedOptionsService] } - }) + }), + DynamicMatDialogModule ], exports: [ FooterComponent, @@ -630,6 +647,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) TodoDialogComponent, ColorPickerDialogComponent, MaterialIconsDialogComponent, + EntityLimitExceededDialogComponent, ColorInputComponent, MaterialIconSelectComponent, NodeScriptTestDialogComponent, @@ -657,6 +675,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) SafePipe, ShortNumberPipe, SelectableColumnsPipe, + DateExpirationPipe, RouterModule, TranslateModule, JsonObjectEditDialogComponent, @@ -707,6 +726,10 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ScadaSymbolInputComponent, EntityKeyAutocompleteComponent, MqttVersionSelectComponent, + PasswordRequirementsTooltipComponent, + TimeUnitInputComponent, + StringPatternAutocompleteComponent, + DynamicMatDialogModule ] }) export class SharedModule { } diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json index 9738e9ac0f..b5e26f6ec8 100644 --- a/ui-ngx/src/assets/dashboard/api_usage.json +++ b/ui-ngx/src/assets/dashboard/api_usage.json @@ -25,8 +25,7 @@ "useCellStyleFunction": false, "useCellContentFunction": true, "cellContentFunction": "return JSON.parse(value).ruleChainName;" - }, - "_hash": 0.9954481282345906 + } }, { "name": "ruleEngineException", @@ -37,8 +36,7 @@ "useCellStyleFunction": false, "useCellContentFunction": true, "cellContentFunction": "return JSON.parse(value).ruleNodeName;" - }, - "_hash": 0.18580357036589978 + } }, { "name": "ruleEngineException", @@ -49,8 +47,7 @@ "useCellStyleFunction": false, "useCellContentFunction": true, "cellContentFunction": "return JSON.parse(value).message;" - }, - "_hash": 0.7255162989552142 + } } ], "alarmFilterConfig": { @@ -64,22 +61,19 @@ "type": "entityField", "label": "Queue name", "color": "#ffc107", - "settings": {}, - "_hash": 0.6889245277142959 + "settings": {} }, { "name": "serviceId", "type": "entityField", "label": "Service Id", "color": "#607d8b", - "settings": {}, - "_hash": 0.7972226575450785 + "settings": {} } ] } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, "selectedTab": 0, @@ -99,10 +93,14 @@ "settings": { "showTimestamp": true, "displayPagination": true, - "defaultPageSize": 10 + "defaultPageSize": 10, + "sortOrder": { + "property": "name", + "direction": "ASC" + } }, "title": "{i18n:api-usage.exceptions}", - "dropShadow": true, + "dropShadow": false, "enableFullscreen": true, "titleStyle": { "fontSize": "16px", @@ -121,395 +119,1772 @@ "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "", - "configMode": "basic" + "configMode": "basic", + "borderRadius": "12px", + "titleColor": "rgba(0, 0, 0, 0.54)", + "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 + }, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + } }, "id": "a669cf86-e715-efa4-dd9a-b839abf499e9", "typeFullFqn": "system.cards.timeseries_table" }, - "aab68ab5-8e40-8694-c55c-8eb1c89b88fb": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, + "fa938580-33db-f1b3-fafc-bc3e3784ad57": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, "config": { "datasources": [ { "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", "dataKeys": [ { - "name": "transportMsgLimit", + "name": "successfulMsgs", "type": "timeseries", - "label": "limit", + "label": "{i18n:api-usage.successful}", "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "circle", + "pointSize": 12, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "aggregationType": null, "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" + "usePostProcessing": null, + "postFuncBody": null }, { - "name": "transportMsgCount", + "name": "failedMsgs", "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, + "label": "{i18n:api-usage.permanent-failures}", + "color": "#ef5350", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "circle", + "pointSize": 12, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "aggregationType": null, "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;\n", - "aggregationType": "NONE" + "usePostProcessing": null, + "postFuncBody": null }, { - "name": "transportDataPointsLimit", + "name": "tmpFailed", "type": "timeseries", - "label": "pointsLimit", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.22082255831864894, + "label": "{i18n:api-usage.processing-failures}", + "color": "#ffc107", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "circle", + "pointSize": 12, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "aggregationType": null, "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#ffc107", + "settings": {} }, { - "name": "transportDataPointsCount", - "type": "timeseries", - "label": "pointsCount", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.6340356364819146, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "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}\";" - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "apiStatus", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.430957831457494, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.662147926074595, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return '{i18n:api-usage.messages}';" - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "pointsUnit", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.44620898738917947, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return '{i18n:api-usage.data-points}';" + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#607d8b", + "settings": {} } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } + ] } ], "timewindow": { - "displayValue": "", + "hideAggregation": false, + "hideAggInterval": false, "selectedTab": 0, "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1708518962586, - "endTimeMs": 1708605362586 - }, - "quickInterval": "CURRENT_DAY" + "timewindowMs": 3600000, + "interval": 1000 }, "aggregation": { - "type": "AVG", - "limit": 25000 + "type": "NONE", + "limit": 10000 } }, - "showTitle": false, - "backgroundColor": "#fff", + "showTitle": true, + "backgroundColor": "#FFFFFF", "color": "rgba(0, 0, 0, 0.87)", "padding": "0px", "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "", - "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const 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 let key = '';\n for (const power of powers) {\n const reduced = number / power.value;\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\nconst [apiUsageBar2, apiUsagePercent2, apiUsageValue2] = calculateBarValues(data[0].pointsCount, data[0].pointsLimit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `${data[0].title}` +\n '
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].pointsUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent2}
` +\n '
' +\n `
${apiUsageValue2}
` +\n '
' +\n '
' + \n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": "\n" - }, - "title": "Transport", - "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": "", - "actions": { - "elementClick": [ - { - "name": "transport_details", - "icon": "insert_chart", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "openDashboardState", - "targetDashboardStateId": "transport", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "a60e09be-1bea-dfc3-6abb-f87e73256899" - } - ] - } - }, - "row": 0, - "col": 0, - "id": "aab68ab5-8e40-8694-c55c-8eb1c89b88fb" - }, - "a84fa70a-ddfa-3b24-9aa4-cf9ce91f919a": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "apiStatus", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'enabled';", - "aggregationType": "NONE" - }, - { - "name": "ruleEngineExecutionLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "ruleEngineExecutionCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" }, - { - "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}\";" + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" }, - { - "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}\";" - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1708518962586, - "endTimeMs": 1708605362586 + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" }, - "quickInterval": "CURRENT_DAY" + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "", - "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const 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 let key = '';\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
${title}
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '' +\n '
' +\n '
'\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": "\n" - }, - "title": "Rule Engine execution", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipHideZeroValues": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 300, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "padding": "12px", + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)" + }, + "title": "{i18n:api-usage.queue-stats}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": {}, + "showTitleIcon": false, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "units": "", + "decimals": null, + "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": "12px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "fa938580-33db-f1b3-fafc-bc3e3784ad57" + }, + "2ee89893-4e38-5331-95b7-3fd4f310c5a7": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "dataKeys": [ + { + "name": "timeoutMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.permanent-timeouts}", + "color": "#4caf50", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "circle", + "pointSize": 12, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tmpTimeout", + "type": "timeseries", + "label": "{i18n:api-usage.processing-timeouts}", + "color": "#9c27b0", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "circle", + "pointSize": 12, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#f44336", + "settings": {} + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#ffc107", + "settings": {} + } + ] + } + ], + "timewindow": { + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 3600000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 10000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipHideZeroValues": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 300, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "padding": "12px", + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)" + }, + "title": "{i18n:api-usage.processing-failures-and-timeouts}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": {}, + "showTitleIcon": false, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "units": "", + "decimals": null, + "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": "12px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "2ee89893-4e38-5331-95b7-3fd4f310c5a7" + }, + "85240e8c-7af7-90a9-ad0a-726013c479a6": { + "typeFullFqn": "system.time_series_chart", + "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 + }, + "type": "bar" + }, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": false, + "postFuncBody": null, + "aggregationType": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": null, + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 3600000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px", + "tooltipStackedShowTotal": false + }, + "title": "{i18n:api-usage.transport-messages-hourly-activity}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [] + }, + "showTitleIcon": false, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", + "titleTooltip": "", + "widgetStyle": null, + "widgetCss": "", + "pageSize": 1024, + "units": "", + "decimals": null, + "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": "12px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "85240e8c-7af7-90a9-ad0a-726013c479a6" + }, + "d0a10a8f-8f48-f9d6-8306-d12af9b49690": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "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 + }, + "type": "bar" + }, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": null, + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 3600000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "showLegend": false, - "useDashboardTimewindow": true, + "title": "{i18n:api-usage.transport-data-point-hourly-activity}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [] + }, + "showTitleIcon": false, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", + "titleTooltip": "", + "widgetStyle": {}, "widgetCss": "", "pageSize": 1024, + "units": "", + "decimals": null, "noDataDisplayMessage": "", - "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" - } - ] - } + "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": "12px", + "iconSize": "0px" }, "row": 0, "col": 0, - "id": "a84fa70a-ddfa-3b24-9aa4-cf9ce91f919a" + "id": "d0a10a8f-8f48-f9d6-8306-d12af9b49690" }, - "d70d26d4-e22d-4ca9-9ea7-f9c87c093321": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, + "4544080d-9b6f-b592-9cd4-0e0335d33857": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, "config": { "datasources": [ { @@ -519,130 +1894,75 @@ "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": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "jsExecutionCount", - "type": "timeseries", - "label": "jsCount", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "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": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;" - }, - { - "name": "tbelExecutionCount", - "type": "timeseries", - "label": "tbelCount", - "color": "#e91e63", - "settings": {}, - "_hash": 0.9506731992087107, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;" - }, - { - "name": "tbelExecutionApiState", + "name": "ruleEngineExecutionCountHourly", "type": "timeseries", - "label": "tbelUnit", - "color": "#ffeb3b", - "settings": {}, - "_hash": 0.3673530683177082, - "aggregationType": "NONE", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "emptyCircle", + "pointSize": 4, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.tbel}\";" + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null } ], "alarmFilterConfig": { @@ -650,250 +1970,326 @@ "ACTIVE" ] } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" + } + ], + "timewindow": { + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 3600000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1708518962586, - "endTimeMs": 1708605362586 + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" }, - "quickInterval": "CURRENT_DAY" + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "", - "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const 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 let key = '';\n for (const power of powers) {\n const reduced = number / power.value;\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [jsUsageBar, jsUsagePercent, jsUsageValue] = calculateBarValues(data[0].jsCount, data[0].jsLimit);\nconst [tbelUsageBar, tbelUsagePercent, tbelUsageValue] = calculateBarValues(data[0].tbelCount, data[0].tbelLimit);\n\nconst jsApiState = data[0].jsApiState;\nconst tbelApiState = data[0].tbelApiState;\nlet currentState;\nif (jsApiState === 'DISABLED' || tbelApiState === 'DISABLED') {\n currentState = 'DISABLED';\n} else if (jsApiState === 'WARNING' || tbelApiState === 'WARNING') {\n currentState = 'WARNING';\n} else {\n currentState = 'ENABLED';\n}\nconst cardClass = currentState.toLowerCase()\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `${data[0].title}` +\n '
' +\n `
${currentState}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].jsUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${jsUsagePercent}
` +\n '
' +\n `
${jsUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].tbelUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${tbelUsagePercent}
` +\n '
' +\n `
${tbelUsageValue}
` +\n '
' +\n '
' + \n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": "\n" - }, - "title": "JavaScript functions", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", + "title": "{i18n:api-usage.rule-engine-hourly-activity}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", "actions": { - "elementClick": [ + "headerButton": [ { - "name": "script_functions_details", - "icon": "insert_chart", + "name": "{i18n:api-usage.view-statistics}", + "buttonType": "icon", + "icon": "show_chart", + "buttonColor": "rgba(0, 0, 0, 0.54)", + "customButtonStyle": {}, "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "openDashboardState", - "targetDashboardStateId": "script_functions", + "targetDashboardStateId": "rule_engine_statistics", "setEntityId": false, "stateEntityParamName": null, "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "d4961bea-84de-e1af-e50f-666b98d34cd5" + "id": "f9f08190-9ed9-d802-5b7a-c57ff84b5648" } ] - } - }, - "row": 0, - "col": 0, - "id": "d70d26d4-e22d-4ca9-9ea7-f9c87c093321" - }, - "4d3ea95c-3188-9872-1817-2f989c7729e0": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "storageDataPointsCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "apiStatus", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'enabled';", - "aggregationType": "NONE" - }, - { - "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}\";" - } - ], - "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": 1708518962586, - "endTimeMs": 1708605362586 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "", - "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const 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 let key = '';\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
${title}
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": "\n" - }, - "title": "Telemetry persistence", "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, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", + "titleTooltip": "", + "widgetStyle": {}, "widgetCss": "", "pageSize": 1024, + "units": "", + "decimals": null, "noDataDisplayMessage": "", - "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" - } - ] - } + "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": "12px", + "iconSize": "0px" }, "row": 0, "col": 0, - "id": "4d3ea95c-3188-9872-1817-2f989c7729e0" + "id": "4544080d-9b6f-b592-9cd4-0e0335d33857" }, - "2d0d6ff6-cd59-51d4-b916-38e22cdd0702": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, + "5d0f2f57-499d-1324-8e1b-cfbc0b3149d2": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, "config": { "datasources": [ { @@ -903,72 +2299,75 @@ "filterId": null, "dataKeys": [ { - "name": "createdAlarmsLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "createdAlarmsCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "apiStatus", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'enabled';", - "aggregationType": "NONE" - }, - { - "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", + "name": "storageDataPointsCountHourly", "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.9964061963495883, + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2, + "showPoints": false, + "showPointLabel": false, + "pointLabelPosition": "top", + "pointLabelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "pointShape": "emptyCircle", + "pointSize": 4, + "fillAreaSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + }, + "barSettings": { + "showBorder": false, + "borderWidth": 2, + "borderRadius": 0, + "showLabel": false, + "labelPosition": "top", + "labelFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.76)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.alarms-created}\";" + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null } ], "alarmFilterConfig": { @@ -976,90 +2375,310 @@ "ACTIVE" ] } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" + } + ], + "timewindow": { + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 3600000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1708518962586, - "endTimeMs": 1708605362586 + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" }, - "quickInterval": "CURRENT_DAY" + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "", - "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const 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 let key = '';\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
${title}
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": "\n" + "title": "{i18n:api-usage.data-points-storage-days-hourly-activity}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [] }, - "title": "Alarm created", "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": false, "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "widgetCss": "", "pageSize": 1024, + "units": "", + "decimals": null, "noDataDisplayMessage": "", - "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" - } - ] - } + "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": "12px", + "iconSize": "0px" }, "row": 0, "col": 0, - "id": "2d0d6ff6-cd59-51d4-b916-38e22cdd0702" + "id": "5d0f2f57-499d-1324-8e1b-cfbc0b3149d2" }, - "120573cc-e246-eb49-7d80-68e5d3b3c0cc": { - "typeFullFqn": "system.cards.markdown_card", - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, + "fb155957-1af4-233e-e2fb-09e648e75d6e": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, "config": { "datasources": [ { @@ -1069,128 +2688,39 @@ "filterId": null, "dataKeys": [ { - "name": "emailApiState", + "name": "transportMsgCountHourly", "type": "timeseries", - "label": "apiState", + "label": "{i18n:api-usage.transport-messages}", "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": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "emailCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "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": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "smsCount", - "type": "timeseries", - "label": "pointsCount", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.6340356364819146, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", - "aggregationType": "NONE" - }, - { - "name": "notificationApiState", - "type": "timeseries", - "label": "title", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.6894070537030252, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.notifications}\";", - "aggregationType": "NONE" - }, - { - "name": "notificationApiState", - "type": "timeseries", - "label": "unit", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.0005447336528170421, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return '{i18n:api-usage.email}';" - }, - { - "name": "notificationApiState", - "type": "timeseries", - "label": "pointsUnit", - "color": "#e91e63", - "settings": {}, - "_hash": 0.12117146988088967, - "aggregationType": "NONE", + "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 + }, + "type": "bar" + }, "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return '{i18n:api-usage.sms}';" + "usePostProcessing": null, + "postFuncBody": null } ], "alarmFilterConfig": { @@ -1201,83 +2731,310 @@ } ], "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, "history": { "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, + "timewindowMs": 2592000000, + "interval": 86400000, "fixedTimewindow": { - "startTimeMs": 1708518962586, - "endTimeMs": 1708605362586 + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 }, "quickInterval": "CURRENT_DAY" }, "aggregation": { - "type": "AVG", + "type": "SUM", "limit": 25000 - } + }, + "timezone": null }, - "showTitle": false, - "backgroundColor": "#fff", + "showTitle": true, + "backgroundColor": "#FFFFFF", "color": "rgba(0, 0, 0, 0.87)", "padding": "0px", "settings": { - "useMarkdownTextFunction": true, - "markdownTextPattern": "", - "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const 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 let key = '';\n for (const power of powers) {\n const reduced = number / power.value;\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\nconst [apiUsageBar2, apiUsagePercent2, apiUsageValue2] = calculateBarValues(data[0].pointsCount, data[0].pointsLimit);\n\nconst apiState = data[0].apiState;\nconst apiStatePoint = data[0].apiStatePoint;\nlet currentState;\nif (apiState === 'DISABLED' || apiStatePoint === 'DISABLED') {\n currentState = 'DISABLED';\n} else if (apiState === 'WARNING' || apiStatePoint === 'WARNING') {\n currentState = 'WARNING';\n} else {\n currentState = 'ENABLED';\n}\n\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `${data[0].title}` +\n '
' +\n `
${currentState}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].pointsUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent2}
` +\n '
' +\n `
${apiUsageValue2}
` +\n '
' +\n '
' + \n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": "\n" + "yAxes": { + "default": { + "units": null, + "decimals": 0, + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "left", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "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;", + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "stack": false, + "xAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "bottom", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "showLegend": true, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.54)", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" + }, + "title": "{i18n:api-usage.transport-msg-daily-activity}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": {}, + "showTitleIcon": false, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal", + "lineHeight": "20px" }, - "title": "Notifications (Email/SMS)", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": false, "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "displayTimewindow": true, "widgetCss": "", "pageSize": 1024, + "units": "", + "decimals": null, "noDataDisplayMessage": "", - "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" - } - ] - } + "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": "12px", + "iconSize": "0px" }, "row": 0, "col": 0, - "id": "120573cc-e246-eb49-7d80-68e5d3b3c0cc" + "id": "fb155957-1af4-233e-e2fb-09e648e75d6e" }, - "63f99d90-23ab-f8c2-3290-1e693ded5a2e": { + "4817e33b-87be-5be3-eaca-ca68a2eb4e0c": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -1319,75 +3076,43 @@ }, "type": "bar" }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": false, - "postFuncBody": null, - "aggregationType": 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 - }, - "type": "bar" - }, - "_hash": 0.46849996721308895, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, "postFuncBody": null } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } + ] } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, - "selectedTab": 0, + "selectedTab": 1, "realtime": { "realtimeType": 0, - "timewindowMs": 86400000, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, + "history": { + "historyType": 0, + "interval": 2592000000, + "timewindowMs": 31536000000, + "fixedTimewindow": null, "quickInterval": "CURRENT_DAY", - "interval": 300000 + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 50000 + "limit": 25000 }, "timezone": null }, @@ -1460,7 +3185,8 @@ "weight": "400", "lineHeight": "1" }, - "tickLabelColor": null, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -1471,9 +3197,9 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, - "absoluteWidth": 3600000 + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -1490,7 +3216,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -1499,7 +3225,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -1516,7 +3243,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -1548,41 +3277,97 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.transport-hourly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.transport-msg-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", - "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" - } - ] - }, + "actions": {}, "showTitleIcon": false, "titleIcon": "thermostat", "iconColor": "#1F6BDD", "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -1607,14 +3392,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "63f99d90-23ab-f8c2-3290-1e693ded5a2e" + "id": "4817e33b-87be-5be3-eaca-ca68a2eb4e0c" }, - "a2b7e906-2d8a-41a8-99a6-409531bfa743": { + "79056202-c92b-1dae-ce49-318ec52e2d3b": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -1628,70 +3413,35 @@ "filterId": null, "dataKeys": [ { - "name": "ruleEngineExecutionCountHourly", + "name": "transportDataPointsCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4CAF50", "settings": { - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "bar", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "emptyCircle", - "pointSize": 4, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } + "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 }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, @@ -1708,22 +3458,23 @@ } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_YEAR", - "interval": 7200000 + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { - "type": "NONE", - "limit": 50000 + "type": "SUM", + "limit": 25000 }, "timezone": null }, @@ -1797,6 +3548,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -1807,9 +3559,9 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, - "absoluteWidth": 3600000 + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -1826,7 +3578,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -1835,7 +3587,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -1852,7 +3605,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -1884,51 +3639,97 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.rule-engine-hourly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.transport-data-points-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", - "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" - } - ] - }, + "actions": {}, "showTitleIcon": false, "titleIcon": "thermostat", "iconColor": "#1F6BDD", "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -1953,14 +3754,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "a2b7e906-2d8a-41a8-99a6-409531bfa743" + "id": "79056202-c92b-1dae-ce49-318ec52e2d3b" }, - "ca996b66-ab7e-f977-152c-98e4ebf2a901": { + "966ffee7-ba0d-8e54-f903-e8d015ca8cd2": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -1974,11 +3775,12 @@ "filterId": null, "dataKeys": [ { - "name": "jsExecutionCountHourly", + "name": "transportDataPointsCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4CAF50", "settings": { + "yAxisId": "default", "showInLegend": true, "dataHiddenByDefault": false, "type": "bar", @@ -2001,6 +3803,8 @@ "lineHeight": "1" }, "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "enablePointLabelBackground": false, + "pointLabelBackground": "rgba(255,255,255,0.56)", "pointShape": "emptyCircle", "pointSize": 4, "fillAreaSettings": { @@ -2027,6 +3831,8 @@ "lineHeight": "1" }, "labelColor": "rgba(0, 0, 0, 0.76)", + "enableLabelBackground": false, + "labelBackground": "rgba(255,255,255,0.56)", "backgroundSettings": { "type": "none", "opacity": 0.4, @@ -2035,81 +3841,13 @@ "end": 0 } } - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": null - }, - { - "name": "tbelExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.tbel-executions}", - "color": "#4caf50", - "settings": { - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "bar", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "emptyCircle", - "pointSize": 4, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } + "comparisonSettings": { + "showValuesForComparison": false, + "comparisonValuesLabel": "", + "color": "" } }, - "_hash": 0.6818645685001823, "aggregationType": null, "units": null, "decimals": null, @@ -2126,22 +3864,33 @@ } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, - "selectedTab": 0, + "selectedTab": 1, "realtime": { "realtimeType": 0, - "timewindowMs": 86400000, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, + "history": { + "historyType": 0, + "interval": 2592000000, + "timewindowMs": 31536000000, + "fixedTimewindow": null, "quickInterval": "CURRENT_DAY", - "interval": 3600000 + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 50000 + "limit": 25000 }, "timezone": null }, @@ -2215,6 +3964,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -2225,9 +3975,9 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, - "absoluteWidth": 3600000 + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -2244,7 +3994,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -2253,11 +4003,109 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" }, - "showTooltip": true, - "tooltipTrigger": "axis", - "tooltipValueFont": { + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { "family": "Roboto", "size": 12, "sizeUnit": "px", @@ -2265,82 +4113,39 @@ "weight": "500", "lineHeight": "16px" }, - "tooltipValueColor": "rgba(0, 0, 0, 0.76)", - "tooltipShowDate": true, - "tooltipDateFormat": { - "format": "yyyy-MM-dd HH:mm:ss", - "lastUpdateAgo": false, - "custom": false - }, - "tooltipDateFont": { + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { "family": "Roboto", - "size": 11, + "size": 12, "sizeUnit": "px", "style": "normal", "weight": "400", "lineHeight": "16px" }, - "tooltipDateColor": "rgba(0, 0, 0, 0.76)", - "tooltipDateInterval": true, - "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", - "tooltipBackgroundBlur": 4, - "animation": { - "animation": true, - "animationThreshold": 2000, - "animationDuration": 1000, - "animationEasing": "cubicOut", - "animationDelay": 0, - "animationDurationUpdate": 300, - "animationEasingUpdate": "cubicOut", - "animationDelayUpdate": 0 - }, - "background": { - "type": "color", - "color": "#fff", - "overlay": { - "enabled": false, - "color": "rgba(255,255,255,0.72)", - "blur": 3 - } - } + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.scripts-hourly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.transport-data-points-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", - "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" - } - ] - }, + "actions": {}, "showTitleIcon": false, "titleIcon": "thermostat", "iconColor": "#1F6BDD", "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -2365,14 +4170,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "ca996b66-ab7e-f977-152c-98e4ebf2a901" + "id": "966ffee7-ba0d-8e54-f903-e8d015ca8cd2" }, - "a3c2f1bb-7d3a-f11c-7b3d-28cd84fdfe34": { + "84fbe63a-bcb6-7bc1-8af0-46b3b1ee5adc": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -2386,11 +4191,12 @@ "filterId": null, "dataKeys": [ { - "name": "storageDataPointsCountHourly", + "name": "ruleEngineExecutionCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#AB00FF", "settings": { + "yAxisId": "default", "showInLegend": true, "dataHiddenByDefault": false, "type": "bar", @@ -2413,6 +4219,8 @@ "lineHeight": "1" }, "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "enablePointLabelBackground": false, + "pointLabelBackground": "rgba(255,255,255,0.56)", "pointShape": "emptyCircle", "pointSize": 4, "fillAreaSettings": { @@ -2439,6 +4247,8 @@ "lineHeight": "1" }, "labelColor": "rgba(0, 0, 0, 0.76)", + "enableLabelBackground": false, + "labelBackground": "rgba(255,255,255,0.56)", "backgroundSettings": { "type": "none", "opacity": 0.4, @@ -2447,15 +4257,19 @@ "end": 0 } } + }, + "comparisonSettings": { + "showValuesForComparison": false, + "comparisonValuesLabel": "", + "color": "" } }, - "_hash": 0.0661644137210089, + "aggregationType": null, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": null + "postFuncBody": null } ], "alarmFilterConfig": { @@ -2466,22 +4280,23 @@ } ], "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 + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { - "type": "NONE", - "limit": 50000 + "type": "SUM", + "limit": 25000 }, "timezone": null }, @@ -2555,6 +4370,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -2565,9 +4381,9 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, - "absoluteWidth": 3600000 + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -2584,7 +4400,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -2593,7 +4409,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -2610,7 +4427,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -2642,24 +4461,100 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.telemetry-persistence-hourly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.rule-engine-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", "actions": { "headerButton": [ { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", + "name": "{i18n:api-usage.view-statistics}", + "buttonType": "icon", + "icon": "show_chart", + "buttonColor": "rgba(0, 0, 0, 0.54)", + "customButtonStyle": {}, + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", "type": "openDashboardState", - "targetDashboardStateId": "telemetry_persistence", - "setEntityId": false, + "targetDashboardStateId": "rule_engine_statistics", + "setEntityId": true, "stateEntityParamName": null, "openRightLayout": false, - "id": "16707efb-e572-bd02-c219-55fc1b0f672a" + "openInSeparateDialog": false, + "openInPopover": false, + "id": "2592147a-3f62-987a-78c0-cdb775fb4233" } ] }, @@ -2669,14 +4564,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -2701,14 +4596,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "a3c2f1bb-7d3a-f11c-7b3d-28cd84fdfe34" + "id": "84fbe63a-bcb6-7bc1-8af0-46b3b1ee5adc" }, - "5cebd4f1-ff6e-62f9-025c-8e7583c3d66a": { + "43a2b982-6c02-d9bd-71ee-34e8e6cf8893": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -2722,11 +4617,12 @@ "filterId": null, "dataKeys": [ { - "name": "createdAlarmsCountHourly", + "name": "ruleEngineExecutionCount", "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#AB00FF", "settings": { + "yAxisId": "default", "showInLegend": true, "dataHiddenByDefault": false, "type": "bar", @@ -2749,6 +4645,8 @@ "lineHeight": "1" }, "pointLabelColor": "rgba(0, 0, 0, 0.76)", + "enablePointLabelBackground": false, + "pointLabelBackground": "rgba(255,255,255,0.56)", "pointShape": "emptyCircle", "pointSize": 4, "fillAreaSettings": { @@ -2775,6 +4673,8 @@ "lineHeight": "1" }, "labelColor": "rgba(0, 0, 0, 0.76)", + "enableLabelBackground": false, + "labelBackground": "rgba(255,255,255,0.56)", "backgroundSettings": { "type": "none", "opacity": 0.4, @@ -2783,15 +4683,19 @@ "end": 0 } } + }, + "comparisonSettings": { + "showValuesForComparison": false, + "comparisonValuesLabel": "", + "color": "" } }, - "_hash": 0.0661644137210089, + "aggregationType": null, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": null + "postFuncBody": null } ], "alarmFilterConfig": { @@ -2802,22 +4706,33 @@ } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, - "selectedTab": 0, + "selectedTab": 1, "realtime": { "realtimeType": 0, - "timewindowMs": 86400000, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, + "history": { + "historyType": 0, + "interval": 2592000000, + "timewindowMs": 31536000000, + "fixedTimewindow": null, "quickInterval": "CURRENT_DAY", - "interval": 300000 + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 50000 + "limit": 25000 }, "timezone": null }, @@ -2891,6 +4806,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -2901,9 +4817,9 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, - "absoluteWidth": 3600000 + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -2920,7 +4836,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -2929,7 +4845,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -2946,7 +4863,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -2956,51 +4875,122 @@ "weight": "400", "lineHeight": "16px" }, - "tooltipDateColor": "rgba(0, 0, 0, 0.76)", - "tooltipDateInterval": true, - "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", - "tooltipBackgroundBlur": 4, - "animation": { - "animation": true, - "animationThreshold": 2000, - "animationDuration": 1000, - "animationEasing": "cubicOut", - "animationDelay": 0, - "animationDurationUpdate": 300, - "animationEasingUpdate": "cubicOut", - "animationDelayUpdate": 0 - }, - "background": { - "type": "color", - "color": "#fff", - "overlay": { - "enabled": false, - "color": "rgba(255,255,255,0.72)", - "blur": 3 - } - } + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.alarms-created-hourly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.rule-engine-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", "actions": { "headerButton": [ { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", + "name": "{i18n:api-usage.view-statistics}", + "buttonType": "icon", + "icon": "show_chart", + "buttonColor": "rgba(0, 0, 0, 0.54)", + "customButtonStyle": {}, + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", "type": "openDashboardState", - "targetDashboardStateId": "alarms_created", - "setEntityId": false, + "targetDashboardStateId": "rule_engine_statistics", + "setEntityId": true, "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, "openRightLayout": false, - "id": "371882f9-ea23-3abc-fca8-9449c5dfdd6b" + "openInSeparateDialog": false, + "openInPopover": false, + "id": "b6ba96cf-48b8-f40f-f010-10b95e7dc819" } ] }, @@ -3010,14 +5000,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -3042,14 +5032,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "5cebd4f1-ff6e-62f9-025c-8e7583c3d66a" + "id": "43a2b982-6c02-d9bd-71ee-34e8e6cf8893" }, - "bc0c8840-a9b5-5583-de7b-9e9450f5d8fe": { + "76fe83c9-c30f-00a5-6299-40c759ca6705": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -3063,142 +5053,35 @@ "filterId": null, "dataKeys": [ { - "name": "emailCountHourly", + "name": "jsExecutionCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#4caf50", + "label": "{i18n:api-usage.javascript-function-executions}", + "color": "#FF9900", "settings": { - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "bar", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "emptyCircle", - "pointSize": 4, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } + "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 }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } - }, - "_hash": 0.1348755140779876, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": null - }, - { - "name": "smsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "showInLegend": true, - "dataHiddenByDefault": false, "type": "bar", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "emptyCircle", - "pointSize": 4, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, @@ -3215,24 +5098,16 @@ } ], "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 + "interval": 3600000, + "timewindowMs": 86400000 }, "aggregation": { "type": "NONE", "limit": 50000 - }, - "timezone": null + } }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -3304,6 +5179,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -3314,9 +5190,9 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, - "absoluteWidth": 3600000 + "relative": true, + "relativeWidth": 6, + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -3333,7 +5209,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -3342,7 +5218,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -3359,7 +5236,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -3391,46 +5270,97 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } - }, - "title": "{i18n:api-usage.notifications-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": null, - "configMode": "basic", - "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" - } - ] + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, + "title": "{i18n:api-usage.javascript-function-executions-hourly-activity}", + "dropShadow": false, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": {}, "showTitleIcon": false, "titleIcon": "thermostat", "iconColor": "#1F6BDD", "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -3455,14 +5385,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "bc0c8840-a9b5-5583-de7b-9e9450f5d8fe" + "id": "76fe83c9-c30f-00a5-6299-40c759ca6705" }, - "0b091dc3-eec3-847e-d0ad-fdf12d474e7a": { + "a43598d1-7bfd-f329-ee61-c343f34f069f": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -3476,46 +5406,10 @@ "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 - }, - "type": "bar" - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCountHourly", + "name": "jsExecutionCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", + "label": "{i18n:api-usage.javascript-function-executions}", + "color": "#FF9900", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -3538,22 +5432,25 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.46849996721308895, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, @@ -3644,6 +5541,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -3654,8 +5552,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -3673,7 +5571,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -3682,7 +5580,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -3699,7 +5598,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -3731,10 +5632,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.transport-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.javascript-function-executions-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -3745,14 +5715,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -3777,14 +5747,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "0b091dc3-eec3-847e-d0ad-fdf12d474e7a" + "id": "a43598d1-7bfd-f329-ee61-c343f34f069f" }, - "536d7104-49f8-fde6-5827-61b8419f15ec": { + "3ebd62a8-dcb7-c96b-8571-e61084248f5b": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -3798,47 +5768,10 @@ "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 - }, - "type": "bar" - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": null - }, - { - "name": "transportDataPointsCount", + "name": "jsExecutionCount", "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", + "label": "{i18n:api-usage.javascript-function-executions}", + "color": "#FF9900", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -3861,9 +5794,9 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.46849996721308895, "units": null, "decimals": null, "funcBody": null, @@ -3871,35 +5804,37 @@ "postFuncBody": null, "aggregationType": null } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } + ] } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, "hideTimezone": false, "selectedTab": 1, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, "history": { "historyType": 0, + "interval": 2592000000, "timewindowMs": 31536000000, - "interval": 86400000, - "fixedTimewindow": { - "startTimeMs": 1709729389667, - "endTimeMs": 1709815789667 - }, - "quickInterval": "CURRENT_DAY" + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 1000 + "limit": 25000 }, "timezone": null }, @@ -3973,6 +5908,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -3985,7 +5921,7 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -4002,7 +5938,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -4011,59 +5947,131 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" }, - "showTooltip": true, - "tooltipTrigger": "axis", - "tooltipValueFont": { + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { "family": "Roboto", "size": 12, "sizeUnit": "px", "style": "normal", - "weight": "500", + "weight": "400", "lineHeight": "16px" }, - "tooltipValueColor": "rgba(0, 0, 0, 0.76)", - "tooltipShowDate": true, - "tooltipDateFormat": { - "format": "yyyy-MM-dd HH:mm:ss", - "lastUpdateAgo": false, - "custom": false + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" }, - "tooltipDateFont": { + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { "family": "Roboto", - "size": 11, + "size": 12, "sizeUnit": "px", "style": "normal", "weight": "400", "lineHeight": "16px" }, - "tooltipDateColor": "rgba(0, 0, 0, 0.76)", - "tooltipDateInterval": true, - "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", - "tooltipBackgroundBlur": 4, - "animation": { - "animation": true, - "animationThreshold": 2000, - "animationDuration": 1000, - "animationEasing": "cubicOut", - "animationDelay": 0, - "animationDurationUpdate": 300, - "animationEasingUpdate": "cubicOut", - "animationDelayUpdate": 0 - }, - "background": { - "type": "color", - "color": "#fff", - "overlay": { - "enabled": false, - "color": "rgba(255,255,255,0.72)", - "blur": 3 - } - } + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.transport-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.javascript-function-executions-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -4074,14 +6082,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -4106,14 +6114,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "536d7104-49f8-fde6-5827-61b8419f15ec" + "id": "3ebd62a8-dcb7-c96b-8571-e61084248f5b" }, - "c77e417c-ad9d-8e23-3ea1-c75edd653bc0": { + "88e25971-e5cb-eebb-3c7c-1ce33a8a38f4": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -4127,10 +6135,10 @@ "filterId": null, "dataKeys": [ { - "name": "ruleEngineExecutionCountHourly", + "name": "tbelExecutionCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", + "label": "{i18n:api-usage.tbel-function-executions}", + "color": "#4CAF50", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -4153,41 +6161,35 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000, - "fixedTimewindow": { - "startTimeMs": 1709729900300, - "endTimeMs": 1709816300300 - }, - "quickInterval": "CURRENT_DAY" + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 }, "aggregation": { - "type": "SUM", - "limit": 25000 - }, - "timezone": null + "type": "NONE", + "limit": 50000 + } }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -4259,6 +6261,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -4269,8 +6272,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -4288,7 +6291,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -4297,7 +6300,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -4314,7 +6318,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -4346,10 +6352,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.rule-engine-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.tbel-function-executions-hourly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -4360,14 +6435,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -4392,14 +6467,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "c77e417c-ad9d-8e23-3ea1-c75edd653bc0" + "id": "88e25971-e5cb-eebb-3c7c-1ce33a8a38f4" }, - "870904d2-d2e1-a1b9-ce56-b03fd47259b5": { + "a1b5731c-e3b3-8cfb-7c50-3abcdce891d2": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -4413,10 +6488,10 @@ "filterId": null, "dataKeys": [ { - "name": "ruleEngineExecutionCount", + "name": "tbelExecutionCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", + "label": "{i18n:api-usage.tbel-function-executions}", + "color": "#4CAF50", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -4439,32 +6514,44 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, "history": { "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { - "type": "NONE", - "limit": 1000 - } + "type": "SUM", + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -4536,6 +6623,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -4548,7 +6636,7 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -4565,7 +6653,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -4574,7 +6662,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -4591,7 +6680,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -4623,10 +6714,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.rule-engine-monthly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.tbel-function-executions-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -4637,14 +6797,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -4669,14 +6829,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "870904d2-d2e1-a1b9-ce56-b03fd47259b5" + "id": "a1b5731c-e3b3-8cfb-7c50-3abcdce891d2" }, - "c66e5060-57fd-11e7-6616-65b82c294ac2": { + "efc8d4e9-dee2-b677-c378-c1a666543bf4": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -4690,10 +6850,10 @@ "filterId": null, "dataKeys": [ { - "name": "jsExecutionCountHourly", + "name": "tbelExecutionCount", "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", + "label": "{i18n:api-usage.tbel-function-executions}", + "color": "#4CAF50", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -4716,48 +6876,49 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" - }, - "_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": { - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.5212969314724616, - "aggregationType": null, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } ] } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, "history": { "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 + "interval": 2592000000, + "timewindowMs": 31536000000, + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { - "type": "SUM", - "limit": 1000 - } + "type": "NONE", + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -4829,6 +6990,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -4839,8 +7001,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -4858,7 +7020,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -4867,7 +7029,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -4884,7 +7047,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -4916,10 +7081,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.scripts-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.tbel-function-executions-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -4930,14 +7164,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -4962,14 +7196,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "c66e5060-57fd-11e7-6616-65b82c294ac2" + "id": "efc8d4e9-dee2-b677-c378-c1a666543bf4" }, - "d0e8603e-5d2e-9287-e2c6-8ccbe9c66806": { + "1249d3e2-6b3a-4e4a-65e9-6ed22959871e": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -4983,10 +7217,10 @@ "filterId": null, "dataKeys": [ { - "name": "jsExecutionCount", + "name": "storageDataPointsCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039EE", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -5009,48 +7243,44 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" - }, - "_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": { - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.49748239768082403, - "aggregationType": null, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, "history": { "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { - "type": "NONE", - "limit": 1000 - } + "type": "SUM", + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -5122,6 +7352,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -5134,12 +7365,12 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { - "relative": false, + "relative": true, "relativeWidth": 2, - "absoluteWidth": 900000000 + "absoluteWidth": 1000 } }, "showLegend": true, @@ -5151,7 +7382,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -5160,7 +7391,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -5177,7 +7409,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -5209,10 +7443,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.scripts-monthly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.data-points-storage-days-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -5223,14 +7526,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -5255,14 +7558,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "d0e8603e-5d2e-9287-e2c6-8ccbe9c66806" + "id": "1249d3e2-6b3a-4e4a-65e9-6ed22959871e" }, - "7f4100d2-41be-4954-d353-1d45000dbbbb": { + "c2f2da29-741d-54f6-5f1d-6f6ae616ea02": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -5276,10 +7579,10 @@ "filterId": null, "dataKeys": [ { - "name": "storageDataPointsCountHourly", + "name": "storageDataPointsCount", "type": "timeseries", "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", + "color": "#1039EE", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -5302,32 +7605,54 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, "history": { "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 + "interval": 2592000000, + "timewindowMs": 31536000000, + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { - "type": "SUM", - "limit": 1000 - } + "type": "NONE", + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -5399,6 +7724,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -5409,8 +7735,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -5428,7 +7754,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -5437,7 +7763,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -5454,7 +7781,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -5486,10 +7815,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.telemetry-persistence-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.data-points-storage-days-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -5500,14 +7898,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -5532,14 +7930,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "7f4100d2-41be-4954-d353-1d45000dbbbb" + "id": "c2f2da29-741d-54f6-5f1d-6f6ae616ea02" }, - "226ef8c9-8488-3664-21ac-0b6217336202": { + "8e07dbe5-aa7a-19c1-c470-5f055df948a7": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -5553,10 +7951,10 @@ "filterId": null, "dataKeys": [ { - "name": "storageDataPointsCount", + "name": "createdAlarmsCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", + "label": "{i18n:api-usage.alarms-created}", + "color": "#D35A00", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -5579,31 +7977,34 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 }, "aggregation": { "type": "NONE", - "limit": 1000 + "limit": 50000 } }, "showTitle": true, @@ -5676,6 +8077,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -5688,7 +8090,7 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -5705,7 +8107,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -5714,7 +8116,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -5731,7 +8134,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -5763,10 +8168,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.telemetry-persistence-monthly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.alarms-created-hourly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -5777,14 +8251,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -5809,14 +8283,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "226ef8c9-8488-3664-21ac-0b6217336202" + "id": "8e07dbe5-aa7a-19c1-c470-5f055df948a7" }, - "bef6c27b-9fe7-ee92-40d9-9696c501a1f9": { + "e0fe9887-d61c-7813-05a7-f60811e5c5bf": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -5833,7 +8307,7 @@ "name": "createdAlarmsCountHourly", "type": "timeseries", "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", + "color": "#D35A00", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -5843,7 +8317,7 @@ "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);", + "pointShapeFormatter": "", "showPointsLineWidth": 5, "showPointsRadius": 3, "showSeparateAxis": false, @@ -5856,32 +8330,44 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, "history": { "historyType": 0, "timewindowMs": 2592000000, - "interval": 86400000 + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "SUM", - "limit": 1000 - } + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -5953,6 +8439,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -5963,8 +8450,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -5982,7 +8469,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -5991,7 +8478,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -6008,7 +8496,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -6040,10 +8530,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, "title": "{i18n:api-usage.alarms-created-daily-activity}", - "dropShadow": true, + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -6054,14 +8613,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -6086,14 +8645,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "bef6c27b-9fe7-ee92-40d9-9696c501a1f9" + "id": "e0fe9887-d61c-7813-05a7-f60811e5c5bf" }, - "52305cf8-2258-5745-a0e7-41a171594bb3": { + "99a40c35-c232-16c5-c42f-3cc80ddb9243": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -6110,7 +8669,7 @@ "name": "createdAlarmsCount", "type": "timeseries", "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", + "color": "#D35A00", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -6120,7 +8679,7 @@ "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);", + "pointShapeFormatter": "", "showPointsLineWidth": 5, "showPointsRadius": 3, "showSeparateAxis": false, @@ -6133,32 +8692,54 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, "history": { "historyType": 0, + "interval": 2592000000, "timewindowMs": 31536000000, - "interval": 1000 + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 1000 - } + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -6230,6 +8811,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -6242,7 +8824,7 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -6259,7 +8841,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -6268,7 +8850,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -6285,7 +8868,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -6317,10 +8902,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, "title": "{i18n:api-usage.alarms-created-monthly-activity}", - "dropShadow": true, + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -6331,14 +8985,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -6363,14 +9017,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "52305cf8-2258-5745-a0e7-41a171594bb3" + "id": "99a40c35-c232-16c5-c42f-3cc80ddb9243" }, - "36fdf999-ca22-9a4c-269d-3f004d792792": { + "407f7630-406e-9c24-cb3d-b1cbdd190f15": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -6387,7 +9041,7 @@ "name": "emailCountHourly", "type": "timeseries", "label": "{i18n:api-usage.email-messages}", - "color": "#d35a00", + "color": "#F36021", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -6410,31 +9064,34 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 }, "aggregation": { - "type": "SUM", - "limit": 1000 + "type": "NONE", + "limit": 50000 } }, "showTitle": true, @@ -6507,6 +9164,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -6517,8 +9175,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -6536,7 +9194,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -6545,7 +9203,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -6562,7 +9221,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -6594,10 +9255,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.email-messages-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.emails-hourly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -6608,14 +9338,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -6640,14 +9370,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "36fdf999-ca22-9a4c-269d-3f004d792792" + "id": "407f7630-406e-9c24-cb3d-b1cbdd190f15" }, - "9a191755-499d-535e-86c5-061102729c02": { + "b12fb875-89fe-af4c-b344-bf4178de419f": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -6661,10 +9391,10 @@ "filterId": null, "dataKeys": [ { - "name": "smsCountHourly", + "name": "emailCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", + "label": "{i18n:api-usage.email-messages}", + "color": "#F36021", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -6687,32 +9417,44 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, "history": { "historyType": 0, "timewindowMs": 2592000000, - "interval": 86400000 + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "SUM", - "limit": 1000 - } + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -6784,6 +9526,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -6794,8 +9537,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -6813,7 +9556,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -6822,7 +9565,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -6839,7 +9583,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -6871,10 +9617,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.sms-messages-daily-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.emails-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -6885,14 +9700,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -6917,14 +9732,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "9a191755-499d-535e-86c5-061102729c02" + "id": "b12fb875-89fe-af4c-b344-bf4178de419f" }, - "4b266318-8357-33ef-ca5a-74cbf90e014f": { + "0b00099d-d131-3e8b-97ce-c4b8d7bcab1f": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -6941,7 +9756,7 @@ "name": "emailCount", "type": "timeseries", "label": "{i18n:api-usage.email-messages}", - "color": "#d35a00", + "color": "#F36021", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -6964,32 +9779,49 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } ] } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 1, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, "history": { "historyType": 0, + "interval": 2592000000, "timewindowMs": 31536000000, - "interval": 1000 + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 1000 - } + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -7061,6 +9893,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -7073,7 +9906,7 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -7090,7 +9923,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -7099,7 +9932,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -7116,7 +9950,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -7148,10 +9984,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.email-messages-monthly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.emails-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -7162,14 +10067,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -7194,14 +10099,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "4b266318-8357-33ef-ca5a-74cbf90e014f" + "id": "0b00099d-d131-3e8b-97ce-c4b8d7bcab1f" }, - "5aa33b0b-3bd5-7fe7-ee72-f564c2ca79d8": { + "5648a56e-5a33-3018-92bd-d8e3dbe8aeee": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -7215,10 +10120,10 @@ "filterId": null, "dataKeys": [ { - "name": "smsCount", + "name": "smsCountHourly", "type": "timeseries", "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", + "color": "#F36021", "settings": { "excludeFromStacking": false, "hideDataByDefault": false, @@ -7241,31 +10146,34 @@ "comparisonSettings": { "showValuesForComparison": true }, - "type": "bar" + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.0661644137210089, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 + "timewindow": { + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 3600000, + "timewindowMs": 86400000 }, "aggregation": { "type": "NONE", - "limit": 1000 + "limit": 50000 } }, "showTitle": true, @@ -7338,6 +10246,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -7350,7 +10259,7 @@ "groupWidth": { "relative": true, "relativeWidth": 6, - "absoluteWidth": 900000000 + "absoluteWidth": 1800000 }, "barWidth": { "relative": true, @@ -7367,7 +10276,7 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", @@ -7376,7 +10285,8 @@ "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -7393,7 +10303,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -7425,10 +10337,79 @@ "color": "rgba(255,255,255,0.72)", "blur": 3 } - } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, + "padding": "12px" }, - "title": "{i18n:api-usage.sms-messages-monthly-activity}", - "dropShadow": true, + "title": "{i18n:api-usage.sms-messages-hourly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -7439,14 +10420,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -7471,14 +10452,14 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "5aa33b0b-3bd5-7fe7-ee72-f564c2ca79d8" + "id": "5648a56e-5a33-3018-92bd-d8e3dbe8aeee" }, - "fa938580-33db-f1b3-fafc-bc3e3784ad57": { + "ab5518c1-34d6-7e17-04b4-6520496d5fe1": { "typeFullFqn": "system.time_series_chart", "type": "timeseries", "sizeX": 8, @@ -7487,266 +10468,75 @@ "datasources": [ { "type": "entity", - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, "dataKeys": [ { - "name": "successfulMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.successful}", - "color": "#4caf50", - "settings": { - "yAxisId": "default", - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "line", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2.5, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "circle", - "pointSize": 12, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } - }, - "_hash": 0.15490750967648736, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "failedMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.permanent-failures}", - "color": "#ef5350", - "settings": { - "yAxisId": "default", - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "line", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2.5, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "circle", - "pointSize": 12, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } - }, - "_hash": 0.4186621166514697, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "tmpFailed", + "name": "smsCountHourly", "type": "timeseries", - "label": "{i18n:api-usage.processing-failures}", - "color": "#ffc107", + "label": "{i18n:api-usage.sms-messages}", + "color": "#F36021", "settings": { - "yAxisId": "default", - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "line", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2.5, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "circle", - "pointSize": 12, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } + "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 }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.49891007198715376, - "aggregationType": null, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null + "postFuncBody": null, + "aggregationType": null } ], "alarmFilterConfig": { "statusList": [ "ACTIVE" ] - }, - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#ffc107", - "settings": {}, - "_hash": 0.7021721434431745 - }, - { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#607d8b", - "settings": {}, - "_hash": 0.5924381120750077 - } - ] + } } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 3600000, - "interval": 1000 + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { - "type": "NONE", - "limit": 10000 - } + "type": "SUM", + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -7818,6 +10608,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -7828,8 +10619,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -7847,16 +10638,17 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", "sortDataKeys": false, - "showMin": true, - "showMax": true, + "showMin": false, + "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null }, "showTooltip": true, "tooltipTrigger": "axis", @@ -7873,7 +10665,9 @@ "tooltipDateFormat": { "format": "yyyy-MM-dd HH:mm:ss", "lastUpdateAgo": false, - "custom": false + "custom": false, + "auto": true, + "autoDateFormatSettings": {} }, "tooltipDateFont": { "family": "Roboto", @@ -7885,13 +10679,12 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, - "tooltipHideZeroValues": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { "animation": true, "animationThreshold": 2000, - "animationDuration": 300, + "animationDuration": 1000, "animationEasing": "cubicOut", "animationDelay": 0, "animationDurationUpdate": 300, @@ -7907,10 +10700,78 @@ "blur": 3 } }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, "padding": "12px" }, - "title": "{i18n:api-usage.queue-stats}", - "dropShadow": true, + "title": "{i18n:api-usage.sms-messages-daily-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -7921,241 +10782,128 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" - }, - "titleColor": "rgba(0, 0, 0, 0.87)", - "titleTooltip": "", - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "units": "", - "decimals": null, - "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" - }, - "row": 0, - "col": 0, - "id": "fa938580-33db-f1b3-fafc-bc3e3784ad57" - }, - "2ee89893-4e38-5331-95b7-3fd4f310c5a7": { - "typeFullFqn": "system.time_series_chart", - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", - "dataKeys": [ - { - "name": "timeoutMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.permanent-timeouts}", - "color": "#4caf50", - "settings": { - "yAxisId": "default", - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "line", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2.5, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "circle", - "pointSize": 12, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } - }, - "_hash": 0.565222981550328, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, + "lineHeight": "20px" + }, + "titleColor": "rgba(0, 0, 0, 0.54)", + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "units": "", + "decimals": null, + "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": "12px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "ab5518c1-34d6-7e17-04b4-6520496d5fe1" + }, + "2e7326ac-98d3-e68c-b7cf-948118a3f140": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ { - "name": "tmpTimeout", + "name": "smsCount", "type": "timeseries", - "label": "{i18n:api-usage.processing-timeouts}", - "color": "#9c27b0", + "label": "{i18n:api-usage.sms-messages}", + "color": "#F36021", "settings": { - "yAxisId": "default", - "showInLegend": true, - "dataHiddenByDefault": false, - "type": "line", - "lineSettings": { - "showLine": true, - "step": false, - "stepType": "start", - "smooth": false, - "lineType": "solid", - "lineWidth": 2.5, - "showPoints": false, - "showPointLabel": false, - "pointLabelPosition": "top", - "pointLabelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "pointLabelColor": "rgba(0, 0, 0, 0.76)", - "pointShape": "circle", - "pointSize": 12, - "fillAreaSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } + "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 }, - "barSettings": { - "showBorder": false, - "borderWidth": 2, - "borderRadius": 0, - "showLabel": false, - "labelPosition": "top", - "labelFont": { - "family": "Roboto", - "size": 11, - "sizeUnit": "px", - "style": "normal", - "weight": "400", - "lineHeight": "1" - }, - "labelColor": "rgba(0, 0, 0, 0.76)", - "backgroundSettings": { - "type": "none", - "opacity": 0.4, - "gradient": { - "start": 100, - "end": 0 - } - } - } + "type": "bar", + "yAxisId": "default" }, - "_hash": 0.2679547062508352, - "aggregationType": null, "units": null, "decimals": null, "funcBody": null, "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - }, - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#f44336", - "settings": {}, - "_hash": 0.7066844328378095 - }, - { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#ffc107", - "settings": {}, - "_hash": 0.1371570237026627 + "postFuncBody": null, + "aggregationType": null } ] } ], "timewindow": { - "hideInterval": false, "hideAggregation": false, "hideAggInterval": false, - "selectedTab": 0, + "hideTimezone": false, + "selectedTab": 1, "realtime": { - "timewindowMs": 3600000, - "interval": 1000 + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false + }, + "history": { + "historyType": 0, + "interval": 2592000000, + "timewindowMs": 31536000000, + "fixedTimewindow": null, + "quickInterval": "CURRENT_DAY", + "hideInterval": false, + "hideLastInterval": false, + "hideFixedInterval": false, + "hideQuickInterval": false }, "aggregation": { "type": "NONE", - "limit": 10000 - } + "limit": 25000 + }, + "timezone": null }, "showTitle": true, "backgroundColor": "#FFFFFF", @@ -8227,6 +10975,7 @@ "lineHeight": "1" }, "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, "showTicks": true, "ticksColor": "rgba(0, 0, 0, 0.54)", "showLine": true, @@ -8237,8 +10986,8 @@ "noAggregationBarWidthSettings": { "strategy": "group", "groupWidth": { - "relative": false, - "relativeWidth": 2, + "relative": true, + "relativeWidth": 6, "absoluteWidth": 1800000 }, "barWidth": { @@ -8256,20 +11005,118 @@ "weight": "400", "lineHeight": "16px" }, - "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendLabelColor": "rgba(0, 0, 0, 0.54)", "legendConfig": { "direction": "column", "position": "bottom", "sortDataKeys": false, - "showMin": true, - "showMax": true, + "showMin": false, + "showMax": false, "showAvg": false, "showTotal": true, - "showLatest": false + "showLatest": false, + "valueFormat": null + }, + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false, + "auto": true, + "autoDateFormatSettings": {} + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": true, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "comparisonXAxis": { + "show": true, + "label": "", + "labelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "600", + "lineHeight": "1" + }, + "labelColor": "rgba(0, 0, 0, 0.54)", + "position": "top", + "showTickLabels": true, + "tickLabelFont": { + "family": "Roboto", + "size": 10, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "1" + }, + "tickLabelColor": "rgba(0, 0, 0, 0.54)", + "ticksFormat": {}, + "showTicks": true, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": true, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": true, + "splitLinesColor": "rgba(0, 0, 0, 0.12)" + }, + "grid": { + "show": false, + "backgroundColor": null, + "borderWidth": 1, + "borderColor": "#ccc" + }, + "legendColumnTitleFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" }, - "showTooltip": true, - "tooltipTrigger": "axis", - "tooltipValueFont": { + "legendColumnTitleColor": "rgba(0, 0, 0, 0.38)", + "legendValueFont": { "family": "Roboto", "size": 12, "sizeUnit": "px", @@ -8277,49 +11124,21 @@ "weight": "500", "lineHeight": "16px" }, - "tooltipValueColor": "rgba(0, 0, 0, 0.76)", - "tooltipShowDate": true, - "tooltipDateFormat": { - "format": "yyyy-MM-dd HH:mm:ss", - "lastUpdateAgo": false, - "custom": false - }, - "tooltipDateFont": { + "legendValueColor": "rgba(0, 0, 0, 0.87)", + "tooltipLabelFont": { "family": "Roboto", - "size": 11, + "size": 12, "sizeUnit": "px", "style": "normal", "weight": "400", "lineHeight": "16px" }, - "tooltipDateColor": "rgba(0, 0, 0, 0.76)", - "tooltipDateInterval": true, - "tooltipHideZeroValues": true, - "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", - "tooltipBackgroundBlur": 4, - "animation": { - "animation": true, - "animationThreshold": 2000, - "animationDuration": 300, - "animationEasing": "cubicOut", - "animationDelay": 0, - "animationDurationUpdate": 300, - "animationEasingUpdate": "cubicOut", - "animationDelayUpdate": 0 - }, - "background": { - "type": "color", - "color": "#fff", - "overlay": { - "enabled": false, - "color": "rgba(255,255,255,0.72)", - "blur": 3 - } - }, + "tooltipLabelColor": "rgba(0, 0, 0, 0.76)", + "tooltipHideZeroValues": null, "padding": "12px" }, - "title": "{i18n:api-usage.processing-failures-and-timeouts}", - "dropShadow": true, + "title": "{i18n:api-usage.sms-messages-monthly-activity}", + "dropShadow": false, "enableFullscreen": true, "titleStyle": null, "configMode": "basic", @@ -8330,14 +11149,14 @@ "useDashboardTimewindow": false, "displayTimewindow": true, "titleFont": { - "size": 16, + "size": 14, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal", - "lineHeight": "24px" + "lineHeight": "20px" }, - "titleColor": "rgba(0, 0, 0, 0.87)", + "titleColor": "rgba(0, 0, 0, 0.54)", "titleTooltip": "", "widgetStyle": {}, "widgetCss": "", @@ -8362,12 +11181,319 @@ "displayTypePrefix": true }, "margin": "0px", - "borderRadius": "0px", + "borderRadius": "12px", "iconSize": "0px" }, "row": 0, "col": 0, - "id": "2ee89893-4e38-5331-95b7-3fd4f310c5a7" + "id": "2e7326ac-98d3-e68c-b7cf-948118a3f140" + }, + "07e3a570-c961-b72d-3371-5b29f3617b73": { + "typeFullFqn": "system.api_usage", + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "dataKeys": [] + } + ], + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0", + "settings": { + "dsEntityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "apiUsageDataKeys": [ + { + "label": "{i18n:api-usage.transport-messages}", + "state": "transport_messages", + "status": { + "name": "transportApiState", + "label": "transportApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "transportMsgLimit", + "label": "transportMsgLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "transportMsgCount", + "label": "transportMsgCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.transport-data-points}", + "state": "transport_data_points", + "status": { + "name": "transportApiState", + "label": "transportApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "transportDataPointsLimit", + "label": "transportDataPointsLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "transportDataPointsCount", + "label": "transportDataPointsCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.rule-engine-executions}", + "state": "rule_engine_executions", + "status": { + "name": "ruleEngineApiState", + "label": "ruleEngineApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "ruleEngineExecutionLimit", + "label": "ruleEngineExecutionLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "ruleEngineExecutionCount", + "label": "ruleEngineExecutionCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.javascript-function-executions}", + "state": "javascript_function_executions", + "status": { + "name": "jsExecutionApiState", + "label": "jsExecutionApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "jsExecutionLimit", + "label": "jsExecutionLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "jsExecutionCount", + "label": "jsExecutionCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.tbel-function-executions}", + "state": "tbel_function_executions", + "status": { + "name": "tbelExecutionApiState", + "label": "tbelExecutionApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "tbelExecutionLimit", + "label": "tbelExecutionLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "tbelExecutionCount", + "label": "tbelExecutionCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.data-points-storage-days}", + "state": "data_points_storage_days", + "status": { + "name": "dbApiState", + "label": "dbApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "storageDataPointsLimit", + "label": "storageDataPointsLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "storageDataPointsCount", + "label": "storageDataPointsCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.alarms-created}", + "state": "alarms_created", + "status": { + "name": "alarmApiState", + "label": "alarmApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "createdAlarmsLimit", + "label": "createdAlarmsLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "createdAlarmsCount", + "label": "createdAlarmsCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.emails}", + "state": "emails", + "status": { + "name": "emailApiState", + "label": "emailApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "emailLimit", + "label": "emailLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "emailCount", + "label": "emailCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + }, + { + "label": "{i18n:api-usage.sms}", + "state": "sms", + "status": { + "name": "smsApiState", + "label": "smsApiState", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "maxLimit": { + "name": "smsLimit", + "label": "smsLimit", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + }, + "current": { + "name": "smsCount", + "label": "smsCount", + "type": "timeseries", + "settings": {}, + "color": "#2196f3" + } + } + ], + "targetDashboardState": "default", + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "padding": "0" + }, + "title": "{i18n:api-usage.api-usage}", + "decimals": null, + "showTitleIcon": false, + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": ".tb-widget-header {\n height: 48px;\n align-items: center !important;\n padding: 5px 10px 0 10px;\n}\n\n@media screen and (min-width: 960px) {\n .tb-widget {\n margin-right: 0px !important;\n }\n}", + "titleStyle": {}, + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "headerButton": [ + { + "name": "{i18n:widgets.api-usage.go-to-main-state}", + "buttonType": "stroked", + "showIcon": false, + "icon": "undo", + "buttonColor": "#305680", + "buttonBorderColor": "#0000001F", + "customButtonStyle": { + "padding": "0 16px" + }, + "useShowWidgetActionFunction": true, + "showWidgetActionFunction": "return widgetContext.stateController.getStateId() !== widgetContext.settings.targetDashboardState && widgetContext.settings.targetDashboardState;", + "type": "custom", + "customFunction": "const state = widgetContext.settings.targetDashboardState?.length ? widgetContext.settings.targetDashboardState : 'default';\nwidgetContext.stateController.updateState(state, widgetContext.stateController.getStateParams(), false);", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "1ea1cca6-47d1-3539-d051-9535129fb12b" + } + ] + }, + "titleFont": { + "size": 14, + "sizeUnit": "px", + "family": null, + "weight": "500", + "style": null, + "lineHeight": "20px" + }, + "borderRadius": "12px", + "titleColor": "rgba(0, 0, 0, 0.54)", + "margin": "12px" + }, + "row": 0, + "col": 0, + "id": "07e3a570-c961-b72d-3371-5b29f3617b73" } }, "states": { @@ -8377,346 +11503,843 @@ "layouts": { "main": { "widgets": { - "aab68ab5-8e40-8694-c55c-8eb1c89b88fb": { - "sizeX": 4, - "sizeY": 2, + "07e3a570-c961-b72d-3371-5b29f3617b73": { + "sizeX": 24, + "sizeY": 39, "row": 0, "col": 0 - }, - "a84fa70a-ddfa-3b24-9aa4-cf9ce91f919a": { - "sizeX": 4, - "sizeY": 2, + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 12, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 100, + "outerMargin": false, + "layoutType": "divider", + "minColumns": 12, + "viewFormat": "grid", + "rowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "85240e8c-7af7-90a9-ad0a-726013c479a6": { + "sizeX": 7, + "sizeY": 5, "row": 0, - "col": 4 + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "d70d26d4-e22d-4ca9-9ea7-f9c87c093321": { - "sizeX": 4, - "sizeY": 2, + "d0a10a8f-8f48-f9d6-8306-d12af9b49690": { + "sizeX": 7, + "sizeY": 5, "row": 0, - "col": 8 + "col": 7, + "resizable": true, + "mobileHeight": 6 + }, + "4544080d-9b6f-b592-9cd4-0e0335d33857": { + "sizeX": 7, + "sizeY": 5, + "row": 5, + "col": 0, + "resizable": true, + "mobileHeight": 6 + }, + "5d0f2f57-499d-1324-8e1b-cfbc0b3149d2": { + "sizeX": 7, + "sizeY": 5, + "row": 5, + "col": 7, + "resizable": true, + "mobileHeight": 6 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": true, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "mobileDisplayLayoutFirst": false + } + } + } + }, + "rule_engine_statistics": { + "name": "{i18n:api-usage.rule-engine-statistics}", + "root": false, + "layouts": { + "main": { + "widgets": { + "a669cf86-e715-efa4-dd9a-b839abf499e9": { + "sizeX": 24, + "sizeY": 5, + "row": 7, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "4d3ea95c-3188-9872-1817-2f989c7729e0": { - "sizeX": 4, - "sizeY": 2, + "fa938580-33db-f1b3-fafc-bc3e3784ad57": { + "sizeX": 12, + "sizeY": 7, "row": 0, - "col": 12 + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "2d0d6ff6-cd59-51d4-b916-38e22cdd0702": { - "sizeX": 4, - "sizeY": 2, + "2ee89893-4e38-5331-95b7-3fd4f310c5a7": { + "sizeX": 12, + "sizeY": 7, "row": 0, - "col": 16 - }, - "120573cc-e246-eb49-7d80-68e5d3b3c0cc": { - "sizeX": 4, - "sizeY": 2, + "col": 12, + "resizable": true, + "mobileHeight": 6 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true, + "layoutType": "default", + "minColumns": 24, + "viewFormat": "grid", + "rowHeight": 70 + } + } + } + }, + "transport_messages": { + "name": "{i18n:api-usage.transport-messages}", + "root": false, + "layouts": { + "main": { + "widgets": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { + "sizeX": 24, + "sizeY": 39, "row": 0, - "col": 20 - }, - "63f99d90-23ab-f8c2-3290-1e693ded5a2e": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 0 - }, - "a2b7e906-2d8a-41a8-99a6-409531bfa743": { - "sizeX": 8, + "col": 0, + "resizable": true, + "mobileHeight": 4 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "fb155957-1af4-233e-e2fb-09e648e75d6e": { + "sizeX": 6, "sizeY": 4, - "row": 2, - "col": 8 + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "ca996b66-ab7e-f977-152c-98e4ebf2a901": { - "sizeX": 8, + "4817e33b-87be-5be3-eaca-ca68a2eb4e0c": { + "sizeX": 6, "sizeY": 4, - "row": 2, - "col": 16 + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 }, - "a3c2f1bb-7d3a-f11c-7b3d-28cd84fdfe34": { - "sizeX": 8, + "85240e8c-7af7-90a9-ad0a-726013c479a6": { + "sizeX": 12, "sizeY": 4, - "row": 6, + "resizable": true, + "row": 0, "col": 0 - }, - "5cebd4f1-ff6e-62f9-025c-8e7583c3d66a": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 8 - }, - "bc0c8840-a9b5-5583-de7b-9e9450f5d8fe": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 16 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 100, - "outerMargin": true + "mobileRowHeight": 70, + "mobileDisplayLayoutFirst": false } } } }, - "transport": { - "name": "{i18n:api-usage.transport}", + "transport_data_points": { + "name": "{i18n:api-usage.transport-data-points}", "root": false, "layouts": { "main": { "widgets": { - "0b091dc3-eec3-847e-d0ad-fdf12d474e7a": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { "sizeX": 24, - "sizeY": 6, + "sizeY": 39, "row": 0, "col": 0 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "79056202-c92b-1dae-ce49-318ec52e2d3b": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "536d7104-49f8-fde6-5827-61b8419f15ec": { - "sizeX": 24, - "sizeY": 6, - "row": 6, + "966ffee7-ba0d-8e54-f903-e8d015ca8cd2": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 + }, + "d0a10a8f-8f48-f9d6-8306-d12af9b49690": { + "sizeX": 12, + "sizeY": 4, + "resizable": true, + "row": 0, "col": 0 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } }, - "rule_engine_execution": { + "rule_engine_executions": { "name": "{i18n:api-usage.rule-engine-executions}", "root": false, "layouts": { "main": { "widgets": { - "c77e417c-ad9d-8e23-3ea1-c75edd653bc0": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { "sizeX": 24, - "sizeY": 6, + "sizeY": 39, "row": 0, "col": 0 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "84fbe63a-bcb6-7bc1-8af0-46b3b1ee5adc": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "870904d2-d2e1-a1b9-ce56-b03fd47259b5": { - "sizeX": 24, - "sizeY": 6, - "row": 6, + "43a2b982-6c02-d9bd-71ee-34e8e6cf8893": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 + }, + "4544080d-9b6f-b592-9cd4-0e0335d33857": { + "sizeX": 12, + "sizeY": 4, + "resizable": true, + "row": 0, "col": 0 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } }, - "telemetry_persistence": { - "name": "{i18n:api-usage.telemetry-persistence}", + "javascript_function_executions": { + "name": "{i18n:api-usage.javascript-function-executions}", "root": false, "layouts": { "main": { "widgets": { - "7f4100d2-41be-4954-d353-1d45000dbbbb": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { "sizeX": 24, - "sizeY": 6, + "sizeY": 39, "row": 0, "col": 0 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "76fe83c9-c30f-00a5-6299-40c759ca6705": { + "sizeX": 12, + "sizeY": 4, + "row": 0, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "226ef8c9-8488-3664-21ac-0b6217336202": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 + "a43598d1-7bfd-f329-ee61-c343f34f069f": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 + }, + "3ebd62a8-dcb7-c96b-8571-e61084248f5b": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } }, - "rule_engine_statistics": { - "name": "{i18n:api-usage.rule-engine-statistics}", + "tbel_function_executions": { + "name": "{i18n:api-usage.tbel-function-executions}", "root": false, "layouts": { "main": { "widgets": { - "a669cf86-e715-efa4-dd9a-b839abf499e9": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { "sizeX": 24, - "sizeY": 5, - "row": 7, + "sizeY": 39, + "row": 0, "col": 0 - }, - "fa938580-33db-f1b3-fafc-bc3e3784ad57": { + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "88e25971-e5cb-eebb-3c7c-1ce33a8a38f4": { "sizeX": 12, - "sizeY": 7, + "sizeY": 4, + "row": 0, + "col": 0, + "resizable": true, + "mobileHeight": 6 + }, + "a1b5731c-e3b3-8cfb-7c50-3abcdce891d2": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 + }, + "efc8d4e9-dee2-b677-c378-c1a666543bf4": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": true, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "mobileDisplayLayoutFirst": false + } + } + } + }, + "data_points_storage_days": { + "name": "{i18n:api-usage.data-points-storage-days}", + "root": false, + "layouts": { + "main": { + "widgets": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { + "sizeX": 24, + "sizeY": 39, "row": 0, "col": 0 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "1249d3e2-6b3a-4e4a-65e9-6ed22959871e": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "2ee89893-4e38-5331-95b7-3fd4f310c5a7": { + "c2f2da29-741d-54f6-5f1d-6f6ae616ea02": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 + }, + "5d0f2f57-499d-1324-8e1b-cfbc0b3149d2": { "sizeX": 12, - "sizeY": 7, + "sizeY": 4, + "resizable": true, "row": 0, - "col": 12 + "col": 0 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } }, - "notifications": { - "name": "{i18n:api-usage.notifications-email-sms}", + "emails": { + "name": "{i18n:api-usage.emails}", "root": false, "layouts": { "main": { "widgets": { - "36fdf999-ca22-9a4c-269d-3f004d792792": { - "sizeX": 12, - "sizeY": 6, + "07e3a570-c961-b72d-3371-5b29f3617b73": { + "sizeX": 24, + "sizeY": 39, "row": 0, "col": 0 - }, - "9a191755-499d-535e-86c5-061102729c02": { + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "407f7630-406e-9c24-cb3d-b1cbdd190f15": { "sizeX": 12, - "sizeY": 6, + "sizeY": 4, "row": 0, - "col": 12 + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "4b266318-8357-33ef-ca5a-74cbf90e014f": { - "sizeX": 12, - "sizeY": 6, - "row": 6, - "col": 0 + "b12fb875-89fe-af4c-b344-bf4178de419f": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "5aa33b0b-3bd5-7fe7-ee72-f564c2ca79d8": { - "sizeX": 12, - "sizeY": 6, - "row": 6, - "col": 12 + "0b00099d-d131-3e8b-97ce-c4b8d7bcab1f": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } }, - "alarms_created": { - "name": "{i18n:api-usage.alarms-created}", + "sms": { + "name": "{i18n:api-usage.sms}", "root": false, "layouts": { "main": { "widgets": { - "bef6c27b-9fe7-ee92-40d9-9696c501a1f9": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { "sizeX": 24, - "sizeY": 6, + "sizeY": 39, "row": 0, "col": 0 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "5648a56e-5a33-3018-92bd-d8e3dbe8aeee": { + "sizeX": 12, + "sizeY": 4, + "row": 0, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "52305cf8-2258-5745-a0e7-41a171594bb3": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 + "ab5518c1-34d6-7e17-04b4-6520496d5fe1": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 + }, + "2e7326ac-98d3-e68c-b7cf-948118a3f140": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } }, - "script_functions": { - "name": "{i18n:api-usage.scripts}", + "alarms_created": { + "name": "{i18n:api-usage.alarms-created}", "root": false, "layouts": { "main": { "widgets": { - "c66e5060-57fd-11e7-6616-65b82c294ac2": { + "07e3a570-c961-b72d-3371-5b29f3617b73": { "sizeX": 24, - "sizeY": 6, + "sizeY": 39, "row": 0, "col": 0 + } + }, + "gridSettings": { + "layoutType": "divider", + "backgroundColor": "#eeeeee", + "columns": 12, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", + "autoFillHeight": true, + "rowHeight": 70, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "layoutDimension": { + "type": "percentage", + "leftWidthPercentage": 30 + } + } + }, + "right": { + "widgets": { + "8e07dbe5-aa7a-19c1-c470-5f055df948a7": { + "sizeX": 12, + "sizeY": 4, + "row": 0, + "col": 0, + "resizable": true, + "mobileHeight": 6 }, - "d0e8603e-5d2e-9287-e2c6-8ccbe9c66806": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 + "e0fe9887-d61c-7813-05a7-f60811e5c5bf": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 0, + "resizable": true, + "mobileHeight": 6 + }, + "99a40c35-c232-16c5-c42f-3cc80ddb9243": { + "sizeX": 6, + "sizeY": 4, + "row": 4, + "col": 6, + "resizable": true, + "mobileHeight": 6 } }, "gridSettings": { + "layoutType": "divider", "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, + "columns": 12, + "margin": 12, + "outerMargin": true, "backgroundSizeMode": "100%", + "minColumns": 12, + "viewFormat": "grid", "autoFillHeight": true, + "rowHeight": 70, "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "outerMargin": true + "mobileDisplayLayoutFirst": false } } } @@ -8743,24 +12366,15 @@ }, "filters": {}, "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, "selectedTab": 0, "realtime": { "realtimeType": 0, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY", - "interval": 3600000 + "timewindowMs": 86400000 }, "aggregation": { "type": "NONE", "limit": 50000 - }, - "timezone": null + } }, "settings": { "stateControllerId": "entity", @@ -8776,7 +12390,7 @@ "dashboardLogoUrl": null, "hideToolbar": false, "showUpdateDashboardImage": false, - "dashboardCss": ".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\n.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: 12px 12px 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.action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n.card .unit {\n color: #666666;\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} " + "dashboardCss": ".tb-widget-actions {\n --mat-icon-color: rgba(0, 0, 0, 0.54);\n}" } }, "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 index 17d7262f23..f3c24e1298 100644 --- a/ui-ngx/src/assets/dashboard/customer_user_home_page.json +++ b/ui-ngx/src/assets/dashboard/customer_user_home_page.json @@ -12,11 +12,6 @@ "sizeY": 3, "config": { "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, "showTitle": false, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -108,7 +103,6 @@ "label": "totalDevices", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -130,7 +124,6 @@ "label": "activeDevices", "color": "#4caf50", "settings": {}, - "_hash": 0.1262449138010293, "aggregationType": null, "units": null, "decimals": null, @@ -152,7 +145,6 @@ "label": "inactiveDevices", "color": "#f44336", "settings": {}, - "_hash": 0.39119172615806797, "aggregationType": null, "units": null, "decimals": null, @@ -163,30 +155,6 @@ ] } ], - "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)", @@ -210,7 +178,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -238,7 +205,6 @@ "label": "totalAlarms", "color": "#ffc107", "settings": {}, - "_hash": 0.8743321483507485, "aggregationType": null, "units": null, "decimals": null, @@ -265,7 +231,6 @@ "label": "assignedToMeAlarms", "color": "#ffc107", "settings": {}, - "_hash": 0.6392390736755882, "aggregationType": null, "units": null, "decimals": null, @@ -293,7 +258,6 @@ "label": "criticalAlarms", "color": "#ffc107", "settings": {}, - "_hash": 0.8329478098552843, "aggregationType": null, "units": null, "decimals": null, @@ -312,37 +276,13 @@ } } ], - "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": "", + "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" }, @@ -359,7 +299,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -429,7 +368,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 20, - "outerMargin": true + "outerMargin": true, + "layoutType": "default" } } } @@ -521,33 +461,14 @@ } }, "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" + "timewindowMs": 60000 }, "aggregation": { - "type": "AVG", - "limit": 25000 + "type": "AVG" } }, "settings": { @@ -567,6 +488,5 @@ "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/assets/dashboard/sys_admin_home_page.json b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json index 739402e72f..408c9d2eb6 100644 --- a/ui-ngx/src/assets/dashboard/sys_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json @@ -12,30 +12,6 @@ "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)", @@ -59,7 +35,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -87,7 +62,6 @@ "label": "clusterMode", "color": "#2196f3", "settings": {}, - "_hash": 0.7272123990942316, "aggregationType": "NONE", "units": null, "decimals": null, @@ -98,30 +72,6 @@ ] } ], - "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)", @@ -148,7 +98,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -164,30 +113,6 @@ "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)", @@ -211,7 +136,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -227,30 +151,6 @@ "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)", @@ -273,7 +173,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -289,30 +188,6 @@ "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)", @@ -335,7 +210,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -351,11 +225,6 @@ "sizeY": 3, "config": { "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, "showTitle": false, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -394,16 +263,14 @@ "type": "timeseries", "label": "cpuUsage", "color": "#2196f3", - "settings": {}, - "_hash": 0.659021918423117 + "settings": {} }, { "name": "cpuCount", "type": "timeseries", "label": "cpuCount", "color": "#4caf50", - "settings": {}, - "_hash": 0.5332753234169949 + "settings": {} }, { "name": "cpuUsage", @@ -411,7 +278,6 @@ "label": "status", "color": "#f44336", "settings": {}, - "_hash": 0.025084892962804473, "aggregationType": "NONE", "units": null, "decimals": null, @@ -425,7 +291,6 @@ "label": "statusTooltip", "color": "#ffc107", "settings": {}, - "_hash": 0.8542003006820891, "aggregationType": "NONE", "units": null, "decimals": null, @@ -436,30 +301,6 @@ ] } ], - "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)", @@ -483,7 +324,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -511,7 +351,6 @@ "label": "memoryUsage", "color": "#2196f3", "settings": {}, - "_hash": 0.659021918423117, "aggregationType": "NONE", "units": null, "decimals": null, @@ -525,7 +364,6 @@ "label": "totalMemory", "color": "#4caf50", "settings": {}, - "_hash": 0.5332753234169949, "aggregationType": "NONE", "units": null, "decimals": null, @@ -539,7 +377,6 @@ "label": "status", "color": "#f44336", "settings": {}, - "_hash": 0.025084892962804473, "aggregationType": "NONE", "units": null, "decimals": null, @@ -553,7 +390,6 @@ "label": "statusTooltip", "color": "#ffc107", "settings": {}, - "_hash": 0.8542003006820891, "aggregationType": "NONE", "units": null, "decimals": null, @@ -564,30 +400,6 @@ ] } ], - "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)", @@ -611,7 +423,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -639,7 +450,6 @@ "label": "discUsage", "color": "#2196f3", "settings": {}, - "_hash": 0.659021918423117, "aggregationType": "NONE", "units": null, "decimals": null, @@ -653,7 +463,6 @@ "label": "totalDiscSpace", "color": "#4caf50", "settings": {}, - "_hash": 0.5332753234169949, "aggregationType": "NONE", "units": null, "decimals": null, @@ -667,7 +476,6 @@ "label": "status", "color": "#f44336", "settings": {}, - "_hash": 0.025084892962804473, "aggregationType": "NONE", "units": null, "decimals": null, @@ -681,7 +489,6 @@ "label": "statusTooltip", "color": "#ffc107", "settings": {}, - "_hash": 0.8542003006820891, "aggregationType": "NONE", "units": null, "decimals": null, @@ -692,30 +499,6 @@ ] } ], - "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)", @@ -739,7 +522,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -766,16 +548,14 @@ "type": "timeseries", "label": "cpuUsage", "color": "#2196f3", - "settings": {}, - "_hash": 0.659021918423117 + "settings": {} }, { "name": "cpuCount", "type": "timeseries", "label": "cpuCount", "color": "#4caf50", - "settings": {}, - "_hash": 0.5332753234169949 + "settings": {} }, { "name": "cpuUsage", @@ -783,7 +563,6 @@ "label": "cpuStatus", "color": "#f44336", "settings": {}, - "_hash": 0.025084892962804473, "aggregationType": "NONE", "units": null, "decimals": null, @@ -797,7 +576,6 @@ "label": "cpuStatusTooltip", "color": "#ffc107", "settings": {}, - "_hash": 0.8542003006820891, "aggregationType": "NONE", "units": null, "decimals": null, @@ -810,8 +588,7 @@ "type": "timeseries", "label": "memoryUsage", "color": "#607d8b", - "settings": {}, - "_hash": 0.9761283269385537 + "settings": {} }, { "name": "totalMemory", @@ -819,7 +596,6 @@ "label": "totalMemory", "color": "#9c27b0", "settings": {}, - "_hash": 0.6363130990513808, "aggregationType": "NONE", "units": null, "decimals": null, @@ -833,7 +609,6 @@ "label": "memoryStatus", "color": "#8bc34a", "settings": {}, - "_hash": 0.15388812616419556, "aggregationType": "NONE", "units": null, "decimals": null, @@ -847,7 +622,6 @@ "label": "memoryStatusTooltip", "color": "#3f51b5", "settings": {}, - "_hash": 0.8086245517967001, "aggregationType": "NONE", "units": null, "decimals": null, @@ -860,8 +634,7 @@ "type": "timeseries", "label": "discUsage", "color": "#e91e63", - "settings": {}, - "_hash": 0.4278686534236851 + "settings": {} }, { "name": "totalDiscSpace", @@ -869,7 +642,6 @@ "label": "totalDiscSpace", "color": "#ffeb3b", "settings": {}, - "_hash": 0.543030429255416, "aggregationType": "NONE", "units": null, "decimals": null, @@ -883,7 +655,6 @@ "label": "discStatus", "color": "#03a9f4", "settings": {}, - "_hash": 0.29138586081227835, "aggregationType": "NONE", "units": null, "decimals": null, @@ -897,7 +668,6 @@ "label": "discStatusTooltip", "color": "#ff9800", "settings": {}, - "_hash": 0.08995267596665912, "aggregationType": "NONE", "units": null, "decimals": null, @@ -908,30 +678,6 @@ ] } ], - "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)", @@ -955,7 +701,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -971,30 +716,6 @@ "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)", @@ -1021,7 +742,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1049,7 +769,6 @@ "label": "tenantsCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1060,30 +779,6 @@ ] } ], - "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)", @@ -1107,7 +802,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1135,7 +829,6 @@ "label": "tenantProfilesCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1146,30 +839,6 @@ ] } ], - "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)", @@ -1193,7 +862,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1221,7 +889,6 @@ "label": "devicesCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1232,30 +899,6 @@ ] } ], - "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)", @@ -1279,7 +922,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1307,7 +949,6 @@ "label": "assetsCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1318,30 +959,6 @@ ] } ], - "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)", @@ -1365,7 +982,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1393,7 +1009,6 @@ "label": "usersCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1404,30 +1019,6 @@ ] } ], - "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)", @@ -1451,7 +1042,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1479,7 +1069,6 @@ "label": "customersCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1490,30 +1079,6 @@ ] } ], - "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)", @@ -1537,7 +1102,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1565,7 +1129,6 @@ "label": "tenantsCount", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -1587,7 +1150,6 @@ "label": "tenantProfilesCount", "color": "#4caf50", "settings": {}, - "_hash": 0.04291827117688696, "aggregationType": null, "units": null, "decimals": null, @@ -1609,7 +1171,6 @@ "label": "devicesCount", "color": "#f44336", "settings": {}, - "_hash": 0.45105711028955886, "aggregationType": null, "units": null, "decimals": null, @@ -1631,7 +1192,6 @@ "label": "assetsCount", "color": "#ffc107", "settings": {}, - "_hash": 0.17028494648519876, "aggregationType": null, "units": null, "decimals": null, @@ -1653,7 +1213,6 @@ "label": "usersCount", "color": "#607d8b", "settings": {}, - "_hash": 0.0362458342595664, "aggregationType": null, "units": null, "decimals": null, @@ -1675,7 +1234,6 @@ "label": "customersCount", "color": "#9c27b0", "settings": {}, - "_hash": 0.7596585662918276, "aggregationType": null, "units": null, "decimals": null, @@ -1686,30 +1244,6 @@ ] } ], - "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)", @@ -1733,7 +1267,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1749,30 +1282,6 @@ "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)", @@ -1799,7 +1308,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -1885,7 +1393,6 @@ } } }, - "_hash": 0.39181957822569946, "decimals": 0 } ], @@ -2212,7 +1719,6 @@ } } }, - "_hash": 0.39181957822569946, "decimals": null, "aggregationType": null, "units": null, @@ -2285,7 +1791,6 @@ } } }, - "_hash": 0.9392996197028385, "aggregationType": null, "units": null, "decimals": null, @@ -2358,7 +1863,6 @@ } } }, - "_hash": 0.027335636460212864, "aggregationType": null, "units": null, "decimals": null, @@ -2626,7 +2130,8 @@ "enableFullscreen": false, "widgetStyle": {}, "widgetCss": "", - "noDataDisplayMessage": "" + "noDataDisplayMessage": "", + "datasources": [] }, "row": 0, "col": 0, @@ -2669,7 +2174,8 @@ "showTitleIcon": false, "titleTooltip": "", "titleStyle": null, - "borderRadius": "12px" + "borderRadius": "12px", + "datasources": [] }, "row": 0, "col": 0, @@ -2709,7 +2215,8 @@ "fontWeight": 400 }, "widgetCss": "", - "noDataDisplayMessage": "" + "noDataDisplayMessage": "", + "datasources": [] }, "row": 0, "col": 0, @@ -2795,7 +2302,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 20, - "outerMargin": true + "outerMargin": true, + "layoutType": "default" } } } @@ -2822,7 +2330,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": true, "mobileRowHeight": 70, - "outerMargin": true + "outerMargin": true, + "layoutType": "default" } } } @@ -2857,7 +2366,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": true, "mobileRowHeight": 70, - "outerMargin": false + "outerMargin": false, + "layoutType": "default" } } } @@ -2896,7 +2406,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": true, "mobileRowHeight": 70, - "outerMargin": false + "outerMargin": false, + "layoutType": "default" } } } @@ -2923,7 +2434,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": true, "mobileRowHeight": 70, - "outerMargin": false + "outerMargin": false, + "layoutType": "default" } } } @@ -2980,7 +2492,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "layoutType": "default" } } } @@ -3007,7 +2520,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": true, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "layoutType": "default" } } } @@ -3041,7 +2555,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 20 + "mobileRowHeight": 20, + "layoutType": "default" } } } @@ -3113,33 +2628,14 @@ }, "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" + "timewindowMs": 60000 }, "aggregation": { - "type": "AVG", - "limit": 25000 + "type": "AVG" } }, "settings": { @@ -3160,4 +2656,4 @@ } }, "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 index 6e3b698200..f09ab73de7 100644 --- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -12,30 +12,6 @@ "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)", @@ -59,7 +35,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -75,11 +50,6 @@ "sizeY": 3, "config": { "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, "showTitle": false, "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", @@ -196,7 +166,6 @@ "label": "totalDevices", "color": "#2196f3", "settings": {}, - "_hash": 0.8491768696709192, "aggregationType": null, "units": null, "decimals": null, @@ -218,7 +187,6 @@ "label": "activeDevices", "color": "#4caf50", "settings": {}, - "_hash": 0.1262449138010293, "aggregationType": null, "units": null, "decimals": null, @@ -240,7 +208,6 @@ "label": "inactiveDevices", "color": "#f44336", "settings": {}, - "_hash": 0.39119172615806797, "aggregationType": null, "units": null, "decimals": null, @@ -251,30 +218,6 @@ ] } ], - "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)", @@ -298,7 +241,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -326,7 +268,6 @@ "label": "totalAlarms", "color": "#ffc107", "settings": {}, - "_hash": 0.8743321483507485, "aggregationType": null, "units": null, "decimals": null, @@ -353,7 +294,6 @@ "label": "assignedToMeAlarms", "color": "#ffc107", "settings": {}, - "_hash": 0.6392390736755882, "aggregationType": null, "units": null, "decimals": null, @@ -381,7 +321,6 @@ "label": "criticalAlarms", "color": "#ffc107", "settings": {}, - "_hash": 0.8329478098552843, "aggregationType": null, "units": null, "decimals": null, @@ -400,37 +339,13 @@ } } ], - "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": "", + "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" }, @@ -447,7 +362,6 @@ "fontWeight": 400 }, "showLegend": false, - "useDashboardTimewindow": true, "widgetCss": "", "pageSize": 1024, "noDataDisplayMessage": "" @@ -533,7 +447,6 @@ } } }, - "_hash": 0.39181957822569946, "decimals": 0 } ], @@ -859,7 +772,6 @@ } } }, - "_hash": 0.39181957822569946, "decimals": 0, "aggregationType": null, "funcBody": null, @@ -1146,7 +1058,8 @@ "fontWeight": 400 }, "widgetCss": "", - "noDataDisplayMessage": "" + "noDataDisplayMessage": "", + "datasources": [] }, "row": 0, "col": 0, @@ -1169,7 +1082,8 @@ "enableFullscreen": false, "widgetStyle": {}, "widgetCss": "", - "noDataDisplayMessage": "" + "noDataDisplayMessage": "", + "datasources": [] }, "row": 0, "col": 0, @@ -1212,7 +1126,8 @@ "showTitleIcon": false, "titleTooltip": "", "titleStyle": null, - "borderRadius": "12px" + "borderRadius": "12px", + "datasources": [] }, "row": 0, "col": 0, @@ -1298,7 +1213,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": false, "mobileRowHeight": 20, - "outerMargin": true + "outerMargin": true, + "layoutType": "default" } } } @@ -1325,7 +1241,8 @@ "backgroundImageUrl": null, "mobileAutoFillHeight": true, "mobileRowHeight": 70, - "outerMargin": true + "outerMargin": true, + "layoutType": "default" } } } @@ -1352,7 +1269,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": true, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "layoutType": "default" } } } @@ -1386,7 +1304,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 20 + "mobileRowHeight": 20, + "layoutType": "default" } } } @@ -1478,33 +1397,14 @@ } }, "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" + "timewindowMs": 60000 }, "aggregation": { - "type": "AVG", - "limit": 25000 + "type": "AVG" } }, "settings": { @@ -1525,4 +1425,4 @@ } }, "name": "Tenant Administrator Home Page" -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/entity-limit-reached.svg b/ui-ngx/src/assets/entity-limit-reached.svg new file mode 100644 index 0000000000..55b53f8808 --- /dev/null +++ b/ui-ngx/src/assets/entity-limit-reached.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/help/en_US/alarm-rule/alarm_rule_schedule_format.md b/ui-ngx/src/assets/help/en_US/alarm-rule/alarm_rule_schedule_format.md new file mode 100644 index 0000000000..bc332f1ea5 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/alarm-rule/alarm_rule_schedule_format.md @@ -0,0 +1,127 @@ +#### Active-all-the-time schedule format + +For a schedule that is always active, the argument can be an empty JSON object or a JSON object in the following format: + +```javascript +{ + "type": "ANY_TIME" +} +``` + +#### Specific time schedule format + +The argument value for a specific time schedule must be a JSON object in the following format (the `type` field is optional): + +```javascript +{ + "type": "SPECIFIC_TIME", + "daysOfWeek": [ + 2, + 4 + ], + "endsOn": 0, + "startsOn": 0, + "timezone": "Europe/Kiev" +} +``` + +
    +
  • +timezone: the name of the timezone. +
  • +
  • +daysOfWeek: days of the week (Monday = 1, Tuesday = 2, ..., Sunday = 7) when the schedule should be active. +
  • +
  • +startsOn: time of day in milliseconds from the start of the day (00:00) when the schedule becomes active on the selected days. +
  • +
  • +endsOn: time of day in milliseconds from the start of the day (00:00) when the schedule stops being active on the selected days. +If this value is not provided or equals 0, it defaults to the full day (24 hours in milliseconds). +
  • +
+ +If both startsOn and endsOn are 0, the schedule is active for the entire day. + +#### Custom time schedule format + +The argument value for a specific time schedule must be a JSON object in the following format (the `type` field is optional): + +```javascript +{ + "type": "CUSTOM", + "timezone": "Europe/Kiev", + "items": [ + { + "dayOfWeek": 1, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + }, + { + "dayOfWeek": 2, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + }, + { + "dayOfWeek": 3, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + }, + { + "dayOfWeek": 4, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + }, + { + "dayOfWeek": 5, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + }, + { + "dayOfWeek": 6, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + }, + { + "dayOfWeek": 7, + "enabled": true, + "endsOn": 0, + "startsOn": 0 + } + ] +} +``` + +
    +
  • +timezone: the name of the timezone. +
  • +
  • +items: a list of day-specific schedule entries. +
  • +
+ +Each item represents one day of the week and contains: +
    +
  • +dayOfWeek: the day number (Monday = 1, Tuesday = 2, ..., Sunday = 7) +
  • +
  • +enabled: a boolean value that defines whether this day is active in the schedule. +
  • +
  • +startsOn: time of day in milliseconds from the start of the day (00:00) when the schedule becomes active for that day. +
  • +
  • +endsOn: time of day in milliseconds from the start of the day (00:00) when the schedule stops being active for that day. +If this value is not provided or equals 0, it defaults to the full day (24 hours in milliseconds). +
  • +
+ +If both startsOn and endsOn are 0, the schedule is active for the entire day. diff --git a/ui-ngx/src/assets/help/en_US/alarm-rule/expression_fn.md b/ui-ngx/src/assets/help/en_US/alarm-rule/expression_fn.md new file mode 100644 index 0000000000..0670384bd8 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/alarm-rule/expression_fn.md @@ -0,0 +1,47 @@ +## Alarm rule condition TBEL script function + +The **expression()** function is a user-defined script that enables custom condition expressions using [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. +It receives arguments configured in the alarm rule setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments. + +### Function signature + +```javascript +function expression(ctx, arg1, arg2, ...): boolean +``` + +### Supported arguments + +There are two types of arguments supported in the alarm rule configuration: attributes and latest telemetry. + +These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. + +### Usage + +**Example: Convert temperature from Fahrenheit to Celsius and raise the alarm if the Celsius value is greater than 36** + +```javascript +var temperatureC = (temperatureF - 32) / 1.8; +return temperatureC > 36; +``` + +Alternatively, use `ctx` to access the argument as an object: + +```json +{ + "temperatureF": { + "ts": 1740644636669, + "value": 98.7 + } +} +``` + +You may notice that the object includes both the `value` of an argument and its timestamp as `ts`. +The `ctx` object also includes the property `latestTs`, which represents the latest timestamp of the arguments telemetry in milliseconds. + +Let's modify the expression that converts Fahrenheit to Celsius to also check if the temperature's timestamp is exactly at the start of an hour: + +```javascript +var temperatureC = (ctx.args.temperatureF.value - 32) / 1.8; +var temperatureTs = ctx.args.temperatureF.ts; +return temperatureC > 36 && ((temperatureTs / 1000) % 3600) == 0; +``` diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index 1d45dea3c8..6129dac351 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -11,12 +11,16 @@ function calculate(ctx, arg1, arg2, ...): object | object[] ### Supported Arguments +Arguments are passed to the function by **name** defined in the calculated field configuration. + There are three types of arguments supported in the calculated field configuration: #### Attribute and Latest Telemetry Arguments These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. +#### Direct argument access via **``** + **Example: Convert Temperature from Fahrenheit to Celsius** ```javascript @@ -26,7 +30,9 @@ return { } ``` -Alternatively, using `ctx` to access the argument as an object: +#### Accessing argument via **`ctx.args.`** + +In addition to direct access, arguments can be accessed via the `ctx.args.` object, which includes both the `value` of an argument and its timestamp as `ts`: ```json { @@ -37,7 +43,6 @@ Alternatively, using `ctx` to access the argument as an object: } ``` -You may notice that the object includes both the `value` of an argument and its timestamp as `ts`. Let's modify the function that converts Fahrenheit to Celsius to also return the timestamp information: ```javascript diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md new file mode 100644 index 0000000000..22316fe418 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md @@ -0,0 +1,70 @@ +## Calculated Field TBEL Filter Function + +The **filter()** function is a [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) script used in aggregation metrics of a calculated field. + +It receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments. + +It allows you to include or exclude related entities from aggregation based on their telemetry or attribute values. + +The filter is evaluated per related entity before the aggregation function is applied. + +### Function Signature + +```javascript +function filter(ctx, arg1, arg2, ...): boolean +``` + +### Supported Arguments + +Arguments are passed to the function by **name** defined in the calculated field configuration. + +There are two types of arguments supported in the calculated field configuration: **Attribute and Latest Telemetry Arguments** + +These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. + +#### Direct argument access via **``** + +**Example: Count free parking spaces** + +**Goal**: Include only parking spaces that are active and not occupied. + +```javascript +return active == true && occupied == false; +``` + +Only entities that satisfy this condition will be included in the aggregation. + +#### Accessing argument via **`ctx.args.`** + +In addition to direct access, arguments can be accessed via the `ctx.args.` object, which includes both the `value` of an argument and its timestamp as `ts`: + +```json +{ + "consumption": { + "ts": 1740644656669, + "value": 542.6 + } +} +``` + +The `ctx.latestTs` property represents the latest timestamp across all related entities and their arguments participating in the aggregation. + +**Example: Calculate the total consumption across multiple related pumps** + +**Scenario**: Each pump reports consumption telemetry approximately every 10 minutes, but reporting times may vary due to network delays (up to ~30 seconds). +To avoid counting outdated values, only recently updated telemetry should be included. + +**Goal**: Include only pumps whose consumption value was reported within 1 minute of the latest timestamp. + +```javascript +var ONE_MINUTE = 60 * 1000; +return (ctx.latestTs - ctx.args.consumption.ts) <= ONE_MINUTE; +``` + +### Function return format + +The function **must** return a boolean: +- `true` → include entity in aggregation +- `false` → exclude entity from aggregation + +Any other return type is considered invalid. diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md new file mode 100644 index 0000000000..41ccf3540b --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md @@ -0,0 +1,79 @@ +## Calculated Field TBEL Map Function + +The **map()** function is a [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) script used in aggregation metrics of a calculated field. + +It determines the value applied by each related entity to the aggregation. + +The function receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments. + +The function is evaluated **per related entity**, after filtering is applied. + +### Function Signature + +```javascript +function map(ctx, arg1, arg2, ...): number | boolean | string +``` + +### Supported Arguments + +Arguments are passed to the function by **name** defined in the calculated field configuration. + +There are two types of arguments supported in the calculated field configuration: **Attribute and Latest Telemetry Arguments** + +These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. + +#### Direct argument access via **``** + +**Example: Calculate average temperature across sensors** + +**Scenario**: Multiple related sensors report temperature in Fahrenheit. You want to aggregate temperature values, but the aggregation must be performed in Celsius. + +**Goal**: Convert temperature to Celsius before aggregation + +```javascript +var temperatureC = (temperature - 32) / 1.8; +return toFixed(temperatureC, 2); +``` + +Instead of creating a separate calculated field for conversion, the transformation is performed per entity, before aggregation. + +#### Accessing argument via **`ctx.args.`** + +In addition to direct access, arguments can be accessed via the `ctx.args.` object, which includes both the `value` of an argument and its timestamp as `ts`: + +```json +{ + "temperature": { + "ts": 1740644656761, + "value": 33.6 + } +} +``` + +The `ctx.latestTs` property represents the latest timestamp across all related entities and their arguments participating in the aggregation. + +**Example: Calculate average temperature** + +**Scenario**: Each sensor reports temperature approximately every 10 minutes, but reporting times may vary due to network delays (up to ~30 seconds). +If a temperature value is outdated, you want the entity to remain part of the aggregation, but contribute a default value instead of a stale one. + +**Goal**: Return a default value when the temperature was not reported within 1 minute of the latest aggregation timestamp. + +```javascript +var ONE_MINUTE = 60 * 1000; +if ((ctx.latestTs - ctx.args.temperature.ts) > ONE_MINUTE) { + return 0; +}; +return temperature; +``` + +### Function return format + +The returned value is passed directly to the aggregation engine: + +| Aggregation function | Expected return value | +|--------------------------------|-----------------------| +| Count, Count unique | any value | +| Sum, Average, Minimum, Maximum | number | + +Returning a value of an incompatible type may result in the entity being ignored or the aggregation producing incorrect results. diff --git a/ui-ngx/src/assets/help/en_US/notification/alarm.md b/ui-ngx/src/assets/help/en_US/notification/alarm.md index 00ba021e1b..e0e0dc3e8c 100644 --- a/ui-ngx/src/assets/help/en_US/notification/alarm.md +++ b/ui-ngx/src/assets/help/en_US/notification/alarm.md @@ -16,11 +16,13 @@ Available template parameters: * `alarmStatus` - the alarm status; * `alarmOriginatorEntityType` - the entity type of the alarm originator, e.g. 'Device'; * `alarmOriginatorName` - the name of the alarm originator, e.g. 'Sensor T1'; +* `alarmOriginatorLabel` - the label of the alarm originator, e.g. 'Sensor T1'; * `alarmOriginatorId` - the alarm originator entity id as uuid string; * `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; +* `details.` - any key field from the alarm's details. Fox example, if details are `{"data": "Temperature is 25"}`, use `${details.data}` to access "Temperature is 25"; Parameter names must be wrapped using `${...}`. For example: `${action}`. You may also modify the value of the parameter with one of the suffixes: diff --git a/ui-ngx/src/assets/help/en_US/notification/alarm_assignment.md b/ui-ngx/src/assets/help/en_US/notification/alarm_assignment.md index aa80b13b35..53b2ade36d 100644 --- a/ui-ngx/src/assets/help/en_US/notification/alarm_assignment.md +++ b/ui-ngx/src/assets/help/en_US/notification/alarm_assignment.md @@ -15,6 +15,7 @@ Available template parameters: * `alarmStatus` - the alarm status; * `alarmOriginatorEntityType` - the entity type of the alarm originator, e.g. 'Device'; * `alarmOriginatorName` - the name of the alarm originator, e.g. 'Sensor T1'; +* `alarmOriginatorLabel` - the label of the alarm originator, e.g. 'Sensor T1'; * `alarmOriginatorId` - the alarm originator entity id as uuid string; * `assigneeTitle` - title of the assignee; * `assigneeEmail` - email of the assignee; diff --git a/ui-ngx/src/assets/help/en_US/notification/alarm_comment.md b/ui-ngx/src/assets/help/en_US/notification/alarm_comment.md index 150e76babc..41ada1fba2 100644 --- a/ui-ngx/src/assets/help/en_US/notification/alarm_comment.md +++ b/ui-ngx/src/assets/help/en_US/notification/alarm_comment.md @@ -15,6 +15,7 @@ Available template parameters: * `alarmStatus` - the alarm status; * `alarmOriginatorEntityType` - the entity type of the alarm originator, e.g. 'Device'; * `alarmOriginatorName` - the name of the alarm originator, e.g. 'Sensor T1'; +* `alarmOriginatorLabel` - the label of the alarm originator, e.g. 'Sensor T1'; * `alarmOriginatorId` - the alarm originator entity id as uuid string; * `comment` - text of the comment; * `action` - one of: 'added', 'updated'; diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md b/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md index 712f2b45e7..0df4732ab4 100644 --- a/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md +++ b/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md @@ -12,6 +12,9 @@ Available template parameters: * `edgeId` - the edge id as uuid string; * `edgeName` - the name of the edge; * `failureMsg` - the string representation of the failure, occurred on the Edge; +* `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: `${edgeName}`. You may also modify the value of the parameter with one of the suffixes: diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_connection.md b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md index 37f0ec7573..4c97f6ccde 100644 --- a/ui-ngx/src/assets/help/en_US/notification/edge_connection.md +++ b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md @@ -12,6 +12,9 @@ Available template parameters: * `edgeId` - the edge id as uuid string; * `edgeName` - the name of the edge; * `eventType` - the string representation of the connectivity status: connected or disconnected; +* `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: `${edgeName}`. You may also modify the value of the parameter with one of the suffixes: diff --git a/ui-ngx/src/assets/help/en_US/notification/entities_limit_increase_request.md b/ui-ngx/src/assets/help/en_US/notification/entities_limit_increase_request.md new file mode 100644 index 0000000000..388f0f532e --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/entities_limit_increase_request.md @@ -0,0 +1,46 @@ +#### Entity count limit increase request 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: + +* `entityType` - one of: 'Device', 'Asset', 'Customer', 'User', 'Dashboard', 'Rule chain', 'Edge'; +* `userEmail` - email of the user who sends the request; +* `increaseLimitActionLabel` - label of the button used to open Limits Management page, for ex: 'Set new limit'; +* `increaseLimitLink` - link to the Limits Management page; +* `baseUrl` - used to construct the full URL for the Limits Management page in email notifications; + +Parameter names must be wrapped using `${...}`. For example: `${userEmail}`. +You may also modify the value of the parameter with one of the suffixes: + +* `upperCase`, for example - `${userEmail:upperCase}` +* `lowerCase`, for example - `${userEmail:lowerCase}` +* `capitalize`, for example - `${userEmail:capitalize}` + +
+ +##### Examples + +Let's assume the notification about the increasing limit of the maximum allowed devices for the tenant. +The following template: + +```text +${userEmail} has reached the maximum number of ${entityType:lowerCase}s allowed and is requesting an increase to the ${entityType:lowerCase} limit. +{:copy-code} +``` + +will be transformed to: + +```text +johndoe@company.com has reached the maximum number of devices allowed and is requesting an increase to the device limit. +``` + +
+ +
+
diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md index 6ea03a514c..2c0634a5f0 100644 --- a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -13,6 +13,9 @@ Available template parameters: * `usage` - the current usage value of the resource; * `serviceId` - the service id (convenient in cluster setup); * `serviceType` - the service type (convenient in cluster setup); +* `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: `${resource}`. You may also modify the value of the parameter with one of the suffixes: diff --git a/ui-ngx/src/assets/help/en_US/notification/task_processing_failure.md b/ui-ngx/src/assets/help/en_US/notification/task_processing_failure.md index 0bcf107298..da46dc11bd 100644 --- a/ui-ngx/src/assets/help/en_US/notification/task_processing_failure.md +++ b/ui-ngx/src/assets/help/en_US/notification/task_processing_failure.md @@ -16,6 +16,9 @@ Available template parameters: * `entityType` - the type of the entity to which the task is related; * `entityId` - the id of the entity to which the task is related; * `attempt` - the number of attempts processing the task +* `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: `${entityType}`. You may also modify the value of the parameter with one of the suffixes: diff --git a/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/create_dialog_js.md b/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/create_dialog_js.md index bc8823c777..3bb410f10b 100644 --- a/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/create_dialog_js.md +++ b/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/create_dialog_js.md @@ -79,7 +79,7 @@ function AddEntityDialogController(instance) { const mapType = widgetContext.mapInstance.type(); attributes.push({key: mapType === 'image' ? 'xPos' : 'latitude', value: additionalParams.coordinates.x}); attributes.push({key: mapType === 'image' ? 'yPos' : 'longitude', value: additionalParams.coordinates.y}); - } else if (mapItemType === 'Rectangle' || mapItemType === 'Polygon') { + } else if (mapItemType === 'Rectangle' || mapItemType === 'Polygon' || mapItemType === 'Line') { attributes.push({key: 'perimeter', value: additionalParams.coordinates}); } else if (mapItemType === 'Circle') { attributes.push({key: 'circle', value: additionalParams.coordinates}); diff --git a/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/place_map_item_action.md b/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/place_map_item_action.md index 131a674099..b192042d2f 100644 --- a/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/place_map_item_action.md +++ b/ui-ngx/src/assets/help/en_US/widget/action/place_map_item/place_map_item_action.md @@ -26,8 +26,9 @@ A JavaScript function triggered after a map item is placed. Optionally uses an H
  • coordinates: Coordinates - Represents geographical coordinates of the placed map item. The actual format of this parameter depends on the type of the selected map item:
    • Marker: {x: number; y: number}, where x represents latitude, and y represents longitude.
    • -
    • Polygon, Rectangle: TbPolygonRawCoordinates contains an array of points defining the shape boundaries.
    • -
    • Circle: TbCircleData contains center coordinates and radius information.
    • +
    • Polygon, Rectangle: TbPolygonRawCoordinates contains an array of points defining the shape boundaries.
    • +
    • Circle: TbCircleData contains center coordinates and radius information.
    • +
    • Polyline: TbPolylineRawCoordinates contains an array of points defining the polyline.
    Note: The coordinates will be automatically converted according to the selected map type.
  • 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 be95f136ad..9af015d033 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 @@ -5,7 +5,7 @@ *function (data, dsData): string* -A JavaScript function used to compute color of the trip path point. +A JavaScript function used to compute color of the path point. **Parameters:** @@ -15,7 +15,7 @@ A JavaScript function used to compute color of the trip path point. **Returns:** -Should return string value presenting color of the trip path point. +Should return string value presenting color of the path point. In case no data is returned, color value from **Color** settings field will be used. diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md index 4ce996626a..e9e202ef85 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md @@ -5,7 +5,7 @@ *function (data, dsData): string* -A JavaScript function used to compute text or HTML code to be displayed in the trip path point tooltip. +A JavaScript function used to compute text or HTML code to be displayed in the path point tooltip. **Parameters:** diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_label_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_label_fn.md new file mode 100644 index 0000000000..b2eafef8cb --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_label_fn.md @@ -0,0 +1,22 @@ +#### Polyline label function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute text or HTML code of the polyline label. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML of the polyline label. + +
    + +{% include widget/lib/map/label_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_stroke_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_stroke_color_fn.md new file mode 100644 index 0000000000..6b49763393 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_stroke_color_fn.md @@ -0,0 +1,24 @@ +#### Polyline stroke color function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute stroke color of the polyline. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting stroke color of the polyline. + +In case no data is returned, color value from **Color** settings field will be used. + +
    + +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_tooltip_fn.md new file mode 100644 index 0000000000..0de9b8ee7a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polyline_tooltip_fn.md @@ -0,0 +1,22 @@ +#### Polyline tooltip function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute text or HTML code to be displayed in the polyline tooltip. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML for the polyline tooltip. + +
    + +{% include widget/lib/map/tooltip_fn_examples %} diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index f5bebc2798..e171d4ed85 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -3882,7 +3882,6 @@ "two-factor-authentication": "المصادقة الثنائية العاملة", "passwords-mismatch-error": "يجب أن تكون كلمات المرور المدخلة متطابقة!", "password-again": "إعادة كلمة المرور", - "sign-in": "الرجاء تسجيل الدخول", "username": "اسم المستخدم (البريد الإلكتروني)", "remember-me": "تذكرني", "forgot-password": "نسيت كلمة المرور؟", 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 8633400394..5278a2c6a9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -3233,7 +3233,6 @@ "create-password": "Crear contrasenya", "passwords-mismatch-error": "¡Les contraseñas introduïdes han de ser iguals!", "password-again": "Repetiu la contrasenya de nou", - "sign-in": "Per favor, inicieu sessió", "username": "Nom d'usuari (correu electrònic)", "remember-me": "Recordar-me", "forgot-password": "Ha oblidat la contrasenya?", 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 041e3efe56..d065dc2ffd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -2151,7 +2151,6 @@ "create-password": "Vytvořit heslo", "passwords-mismatch-error": "Zadaná hesla se musí shodovat!", "password-again": "Heslo znovu", - "sign-in": "Prosím zaregistrujte se", "username": "Uživatelské jméno (email)", "remember-me": "Zapamatovat si mě", "forgot-password": "Zapomněli jste heslo?", 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 c0554d1cca..69ab79f43b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -78,6 +78,7 @@ "show-more": "Vis mere", "dont-show-again": "Vis ikke igen", "see-documentation": "Se dokumentation", + "see-debug-events": "Se debug-hændelser", "clear": "Ryd", "upload": "Upload", "delete-anyway": "Slet alligevel", @@ -485,6 +486,7 @@ "2fa": { "2fa": "To-faktor autentificering", "available-providers": "Tilgængelige udbydere", + "available-providers-required": "Mindst én 2FA-udbyder skal være konfigureret.", "issuer-name": "Udsteders navn", "issuer-name-required": "Udsteders navn er påkrævet.", "max-verification-failures-before-user-lockout": "Maks. antal verificeringsfejl før kontolåsning", @@ -513,7 +515,9 @@ "verification-message-template-required": "Skabelon for verificeringsbesked er påkrævet.", "within-time": "Inden for tid (sek)", "within-time-pattern": "Tiden skal være et positivt heltal.", - "within-time-required": "Tid er påkrævet." + "within-time-required": "Tid er påkrævet.", + "force-2fa": "Gennemtving tofaktorgodkendelse", + "enforce-for": "Gennemtving for" }, "jwt": { "security-settings": "JWT sikkerhedsindstillinger", @@ -545,16 +549,11 @@ "slack-settings": "Slack-indstillinger", "mobile-settings": "Mobilindstillinger", "firebase-service-account-file": "Firebase servicekonto-legitimationsoplysninger JSON-fil", - "select-firebase-service-account-file": "Træk og slip din Firebase servicekonto-legitimationsfil eller ", - "trendz": "Trendz", - "trendz-settings": "Trendz-indstillinger", - "trendz-url": "Trendz URL", - "trendz-url-required": "Trendz URL er påkrævet", - "trendz-api-key": "Trendz API-nøgle", - "trendz-enable": "Aktivér Trendz" + "select-firebase-service-account-file": "Træk og slip din Firebase servicekonto-legitimationsfil eller " }, "alarm": { "alarm": "Alarm", + "alarm-list": "Alarmliste", "alarms": "Alarmer", "all-alarms": "Alle alarmer", "select-alarm": "Vælg alarm", @@ -655,7 +654,16 @@ "alarm-type": "Alarmtype", "enter-alarm-type": "Indtast alarmtype", "no-alarm-types-matching": "Ingen alarmtyper matcher '{{entitySubtype}}'.", - "alarm-type-list-empty": "Ingen alarmtyper valgt." + "alarm-type-list-empty": "Ingen alarmtyper valgt.", + "system-comments": { + "acked-by-user": "Alarmen blev kvitteret af bruger {{userName}}", + "cleared-by-user": "Alarmen blev ryddet af bruger {{userName}}", + "assigned-to-user": "Alarmen blev tildelt af bruger {{userName}} til bruger {{assigneeName}}", + "unassigned-to-user": "Alarmens tildeling blev fjernet af bruger {{userName}}", + "unassigned-from-deleted-user": "Alarmens tildeling blev fjernet, fordi bruger {{userName}} - blev slettet", + "comment-deleted": "Bruger {{userName}} slettede sin kommentar", + "severity-changed": "Alarmens alvorlighed blev opdateret fra {{oldSeverity}} til {{newSeverity}}" + } }, "alarm-activity": { "add": "Tilføj en kommentar...", @@ -760,6 +768,7 @@ "name-max-length": "Navn skal være mindre end 256 tegn", "label-max-length": "Etiket skal være mindre end 256 tegn", "description": "Beskrivelse", + "description-required": "Beskrivelse er påkrævet.", "type": "Type", "type-required": "Type er påkrævet.", "details": "Detaljer", @@ -873,6 +882,9 @@ "alarms-created-monthly-activity": "Månedlig aktivitet for oprettede alarmer", "data-points": "Datapunkter", "data-points-storage-days": "Opbevaringsdage for datapunkter", + "data-points-storage-days-hourly-activity": "Datalagringsdage for datapunkter – timeaktivitet", + "data-points-storage-days-daily-activity": "Datalagringsdage for datapunkter – dagsaktivitet", + "data-points-storage-days-monthly-activity": "Datalagringsdage for datapunkter – månedsaktivitet", "device-api": "Enheds-API", "email": "E-mail", "email-messages": "E-mail-beskeder", @@ -906,6 +918,7 @@ "rule-node": "Rule Node", "sms": "SMS", "sms-messages": "SMS-beskeder", + "sms-messages-hourly-activity": "SMS-beskeder – timeaktivitet", "sms-messages-daily-activity": "Daglig aktivitet for SMS-beskeder", "sms-messages-monthly-activity": "Månedlig aktivitet for SMS-beskeder", "successful": "${entityName} succesfuld", @@ -915,13 +928,40 @@ "telemetry-persistence-hourly-activity": "Timebaseret aktivitet for telemetri-persistens", "telemetry-persistence-monthly-activity": "Månedlig aktivitet for telemetri-persistens", "transport": "Transport", - "transport-daily-activity": "Daglig transportaktivitet", - "transport-data-points": "Transport-datapunkter", - "transport-hourly-activity": "Timebaseret transportaktivitet", - "transport-messages": "Transportbeskeder", - "transport-monthly-activity": "Månedlig transportaktivitet", + "transport-msg-hourly-activity": "Transportbeskeder – timeaktivitet", + "transport-msg-daily-activity": "Transportbeskeder – dagsaktivitet", + "transport-msg-monthly-activity": "Transportbeskeder – månedsaktivitet", + "transport-daily-activity": "Transport – dagsaktivitet", + "transport-data-points": "Transportdatapunkter", + "transport-data-points-hourly-activity": "Transportdatapunkter – timeaktivitet", + "transport-data-points-daily-activity": "Transportdatapunkter – dagsaktivitet", + "transport-data-points-monthly-activity": "Transportdatapunkter – månedsaktivitet", "view-details": "Vis detaljer", - "view-statistics": "Vis statistik" + "view-statistics": "Vis statistik", + "transport-messages": "Transportbeskeder", + "transport-messages-hourly-activity": "Transportbeskeder – timeaktivitet", + "transport-data-point-hourly-activity": "Transportdatapunkt – timeaktivitet", + "javascript-function-executions": "JavaScript-funktionsudførsler", + "javascript-function-executions-hourly-activity": "JavaScript-funktionsudførsler – timeaktivitet", + "javascript-function-executions-daily-activity": "JavaScript-funktionsudførsler – dagsaktivitet", + "javascript-function-executions-monthly-activity": "JavaScript-funktionsudførsler – månedsaktivitet", + "tbel-function-executions": "TBEL-funktionsudførsler", + "tbel-function-executions-hourly-activity": "TBEL-funktionsudførsler – timeaktivitet", + "tbel-function-executions-daily-activity": "TBEL-funktionsudførsler – dagsaktivitet", + "tbel-function-executions-monthly-activity": "TBEL-funktionsudførsler – månedsaktivitet", + "created-reports": "Oprettede rapporter", + "created-reports-hourly-activity": "Oprettede rapporter – timeaktivitet", + "created-reports-daily-activity": "Oprettede rapporter – dagsaktivitet", + "created-reports-monthly-activity": "Oprettede rapporter – månedsaktivitet", + "emails": "Emails", + "emails-hourly-activity": "Emails – timeaktivitet", + "emails-daily-activity": "Emails – dagsaktivitet", + "emails-monthly-activity": "Emails – månedsaktivitet", + "status": { + "enabled": "Aktiveret", + "disabled": "Deaktiveret", + "warning": "Advarsel" + } }, "api-limit": { "cassandra-write-queries-core": "REST API Cassandra-skriveforespørgsler", @@ -946,6 +986,40 @@ "edge-uplink-messages": "Edge-oplinkbeskeder", "edge-uplink-messages-per-edge": "Edge-oplinkbeskeder pr. edge" }, + "api-key": { + "api-key": "API-nøgle", + "api-keys": "API-nøgler", + "delete-api-key-title": "Er du sikker på, at du vil slette API-nøglen '{{name}}'?", + "delete-api-key-text": "Vær forsigtig, efter bekræftelsen kan nøglen ikke gendannes.", + "delete-api-keys-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 API-nøgle} other {# API-nøgler} }?", + "delete-api-keys-text": "Vær forsigtig, efter bekræftelsen kan alle valgte nøgler ikke gendannes.", + "expiration-date": "Udløbsdato", + "date": "Dato", + "description": "Beskrivelse", + "disable": "Deaktiver", + "edit-description": "Rediger beskrivelse", + "enable": "Aktivér API-nøgle ", + "expiration-time": "Udløbstid", + "expiration-time-never": "Aldrig", + "expiration-time-custom": "Tilpasset", + "generate": "Generér", + "generate-title": "Generér API-nøgle", + "generate-text": "Bemærk: API-nøglen arver tilladelserne fra den bruger, den oprettes til.", + "generated-api-key-title": "API-nøgle genereret. Lad os tjekke forbindelsen!", + "generated-api-key-copy": "Sørg for at kopiere og gemme din API-nøgle nu, da du ikke kan se den igen.", + "generated-api-key-command": "Brug følgende instruktioner til at tjekke forbindelsen. Som resultat bør du modtage oplysninger om den aktuelle bruger:", + "generated-api-key-insecure-url": "Hvis du udfører kommandoer over en usikker HTTP-forbindelse, sendes din API-nøgle ukrypteret, hvilket gør den sårbar over for opsnapning.", + "list": "{ count, plural, =1 {Én API-nøgle} other {Liste over # API-nøgler} }", + "manage": "Administrér", + "manage-api-keys": "Administrér API-nøgler", + "no-found": "Ingen API-nøgler fundet", + "selected-api-keys": "{ count, plural, =1 {1 API-nøgle} other {# API-nøgler} } valgt", + "search": "Søg efter API-nøgler", + "status": "Status", + "status-active": "Aktiv", + "status-inactive": "Inaktiv", + "status-expired": "Udløbet" + }, "audit-log": { "audit": "Revision", "audit-logs": "Revisionslogge", @@ -999,7 +1073,11 @@ "type-provision-failure": "Klargøring af device mislykkedes", "type-timeseries-updated": "Telemetri opdateret", "type-timeseries-deleted": "Telemetri slettet", - "type-sms-sent": "SMS sendt" + "type-sms-sent": "SMS sendt", + "any-type": "Enhver type", + "audit-log-filter-title": "Auditlogfilter", + "filter-title": "Filter", + "filter-types": "Auditlogtyper" }, "debug-settings": { "label": "Fejlfindingskonfiguration", @@ -1020,12 +1098,25 @@ "selected-fields": "{ count, plural, =1 {1 beregnet felt} other {# beregnede felter} } valgt", "type": { "simple": "Simpel", - "script": "Script" + "simple-hint": "Simpel aritmetisk beregning baseret på inputargumenter.", + "script": "Script", + "script-hint": "Beregning over definerede argumenter ved brug af et TBEL-script.", + "geofencing": "Geofencing", + "geofencing-hint": "Evaluering af entitetens GPS-position og overgange i forhold til konfigurerede geofencing-zonegrupper.", + "propagation": "Propagering", + "propagation-hint": "Propagering af data til overordnede eller underordnede entiteter baseret på relationsretning og -type.", + "related-entities-aggregation": "Aggregering af relaterede entiteter", + "related-entities-aggregation-hint": "Aggregering af seneste data fra relaterede entiteter.", + "time-series-data-aggregation": "Aggregering af tidsseriedata", + "time-series-data-aggregation-hint": "Aggregering af historiske data fra en aktuel entitet." }, + "preview": "Forhåndsvisning", "arguments": "Argumenter", "decimals-by-default": "Decimaler som standard", "debugging": "Fejlfinding af beregnet felt", + "calculated-field-details": "Detaljer for beregnet felt", "argument-name": "Argumentnavn", + "name": "Navn", "datasource": "Datakilde", "add-argument": "Tilføj argument", "test-script-function": "Test scriptfunktion", @@ -1037,8 +1128,9 @@ "argument-asset": "Aktiv", "argument-customer": "Kunde", "argument-tenant": "Aktuel lejer", + "argument-owner": "Nuværende ejer", + "argument-relation-query": "Relaterede entiteter", "argument-type": "Argumenttype", - "see-debug-events": "Se fejlsøgningshændelser", "attribute": "Attribut", "copy-argument-name": "Kopiér argumentnavn", "timeseries-key": "Tidsserienøgle", @@ -1051,12 +1143,14 @@ "shared-attributes": "Delte attributter", "attribute-key": "Attributnøgle", "default-value": "Standardværdi", + "default-value-required": "Standardværdi er påkrævet.", "limit": "Maks. værdier", "time-window": "Tidsvindue", "customer-name": "Kundenavn", "asset-name": "Aktivnavn", "timeseries": "Tidsserie", "output": "Output", + "output-hint": "Definerer, hvordan output behandles.", "create": "Opret nyt beregnet felt", "file": "Beregnet felt-fil", "invalid-file-error": "Ugyldigt filformat. Sørg for, at filen er en gyldig JSON-fil.", @@ -1070,8 +1164,174 @@ "delete-multiple-text": "Vær forsigtig, efter bekræftelse vil alle valgte beregnede felter blive fjernet og alle relaterede data ikke kunne gendannes.", "test-with-this-message": "Test med denne meddelelse", "use-latest-timestamp": "Brug seneste tidsstempel", + "entity-coordinates": "Entitetkoordinater", + "latitude-time-series-key": "Breddegradstidsserienøgle", + "latitude-time-series-key-required": "Breddegradstidsserienøgle er påkrævet.", + "longitude-time-series-key": "Længdegradstidsserienøgle", + "longitude-time-series-key-required": "Længdegradstidsserienøgle er påkrævet.", + "geofencing-zone-groups": "Geofencingzonegrupper", + "geofencing-zone-groups-settings": "Geofencingzonegruppeindstillinger", + "target-zone": "Målzone", + "perimeter-key": "Perimeternøgle", + "report-strategy": "Rapporteringsstrategi", + "no-zone-configured": "Mindst én zone er påkrævet.", + "no-zone-configured-required": "Mindst én zonegruppe skal være konfigureret.", + "add-zone-group": "Tilføj zonegruppe", + "report-transition-event-only": "Kun overgangshændelser", + "report-presence-status-only": "Kun tilstedeværelsesstatus", + "report-transition-event-and-presence": "Tilstedeværelsesstatus og overgangshændelser", + "perimeter-attribute-key": "Perimeterattributnøgle", + "perimeter-attribute-key-required": "Perimeterattributnøgle er påkrævet.", + "perimeter-attribute-key-pattern": "Perimeterattributnøgle er ugyldig.", + "entity-zone-relationship": "Sti fra Entitet til zoner", + "direction": "Relationsretning", + "direction-from": "Fra entitet til zone", + "direction-to": "Fra zone til entitet", + "relation-type": "Relationstype", + "create-relation-with-matched-zones": "Opret relationer for kildeentitet med matchende zoner", + "relation-level": "Relationsniveau", + "fetch-last-available-level": "Hent kun senest tilgængelige niveau", + "zone-group-refresh-interval": "Opdateringsinterval for zonegrupper", + "copy-zone-group-name": "Kopiér zonegruppenavn", + "open-details-page": "Åbn entitetens detaljeside", + "level": "Niveau", + "direction-level": "Retning", + "direction-up": "Op", + "direction-up-parent": "Op til overordnet", + "direction-down": "Ned", + "direction-down-child": "Ned til underordnet", + "add-level": "Tilføj niveau", + "delete-level": "Slet niveau", + "no-level": "Intet niveau konfigureret", + "levels-required": "Mindst ét niveau skal være konfigureret.", + "max-allowed-levels-error": "Relationsniveau overskrider det maksimalt tilladte.", + "propagation-path-related-entities": "Propageringssti til relaterede entiteter", + "propagate-type": { + "arguments-only": "Kun argumenter", + "expression-result": "Beregningsresultat" + }, + "script": "Script", + "data-propagate": "Data, der skal propageres", + "output-key": "Outputnøgle", + "copy-output-key": "Kopiér outputnøgle", + "aggregation-path-related-entities": "Aggregeringssti til relaterede entiteter", + "deduplication-interval": "Deduplikeringsinterval", + "deduplication-interval-min": "Deduplikeringsinterval skal være mindst {{ sec }} sekunder.", + "deduplication-interval-hint": "Minimumstid mellem telemetriaggregeringer.", + "deduplication-interval-required": "Deduplikeringsinterval er påkrævet.", + "calculated-field-filter-title": "Filter for beregnet felt", + "filter-title": "Filter", + "calculated-field-types": "Typer af beregnede felter", + "events": "Hændelser", + "any-type": "Enhver type", + "metrics": { + "metrics": "Metrikker", + "metrics-empty": "Mindst én metrik skal være konfigureret.", + "metric-name": "Metriknavn", + "metric-name-required": "Metriknavn er påkrævet.", + "metric-name-pattern": "Metriknavn er ugyldigt.", + "metric-name-duplicate": "Der findes allerede en metrik med dette navn.", + "metric-name-max-length": "Metriknavn skal være under 256 tegn.", + "metric-name-forbidden": "Metriknavn er reserveret og kan ikke bruges.", + "copy-metric-name": "Kopiér metriknavn", + "argument-name": "Argumentnavn", + "aggregation": "Aggregering", + "aggregation-type": { + "avg": "Gennemsnit", + "min": "Minimum", + "max": "Maksimum", + "sum": "Sum", + "count": "Antal", + "count-unique": "Antal unikke" + }, + "filtered": "Filtreret", + "value-source": "Værdikilde", + "value-source-hint": "Definerer, hvordan værdien til aggregering hentes.", + "value-source-type": { + "key": "Nøgle", + "function": "Funktion" + }, + "no-metrics-configured": "Mindst én metrik er påkrævet.", + "add-metric": "Tilføj metrik", + "max-metrics": "Maksimalt antal metrikker er nået.", + "metric-settings": "Metrikindstillinger", + "filter": "Filter", + "filter-hint": "Aktiverer filtrering af entiteter under aggregering. Filterfunktionen skal returnere en boolesk værdi og kan bruge alle konfigurerede argumenter." + }, + "output-strategy": { + "strategy": "Strategi", + "process-right-away": "Behandl med det samme", + "process-rule-chains": "Behandl via Regelkæder", + "save-time-series": "Gem i tidsserie", + "save-database": "Gem i database", + "save-latest-values": "Gem i seneste værdier", + "send-web-sockets": "Send til WebSockets", + "save-calculated-fields": "Send til beregnede felter", + "update-attribute-only-on-value-change": "Opdater attribut kun ved værdiskift", + "send-attributes-updated-notification": "Send notifikation om opdaterede attributter", + "ttl": "Tilpasset TTL", + "ttl-required": "TTL er påkrævet", + "ttl-min": "Kun 0 som minimum-TTL er tilladt", + "processing-parameters": "Behandlingsparametre", + "hint": { + "strategy": "Styrer, om resultatet behandles med det samme eller sendes til en regelkæde til yderligere behandling.", + "processing-options": "Behandlingsmuligheder", + "update-attribute-only-on-value-change": "Opdaterer attribut ved hver indgående besked, uanset om værdien har ændret sig. Dette øger API-forbrug og reducerer ydeevnen.", + "update-attribute-only-on-value-change-enabled": "Opdaterer attribut kun, når værdien ændrer sig. Hvis værdien er uændret, opdateres tidsstempler ikke, og der sendes ikke notifikationer.", + "send-attributes-updated-notification": "Sender en Attributter opdateret-hændelse til standardregel kæden.", + "save-time-series": "Gemmer tidsseriedata i tabellen ts_kv i databasen.", + "save-database": "Gemmer attributdata i databasen.", + "save-latest-values": "Opdaterer tidsseriedata i tabellen ts_kv_latest i databasen, hvis den nye værdi er nyere.", + "send-web-sockets-attribute": "Notificerer WebSocket-abonnementer om opdateringer af attributdata.", + "send-web-sockets-time-series": "Notificerer WebSocket-abonnementer om opdateringer af tidsseriedata.", + "save-calculated-fields-attribute": "Notificerer beregnede felter om opdateringer af attributdata.", + "save-calculated-fields-time-series": "Notificerer beregnede felter om opdateringer af tidsseriedata.", + "ttl": "Definerer opbevaringsperioden for tidsseriedata. Hvis deaktiveret, bruges Lejerprofil-TTL." + } + }, + "aggregate-interval-type": "Aggregeringsintervaltype", + "aggregate-interval-value": "Aggregeringsintervalværdi", + "aggregate-interval-value-required": "Aggregeringsintervalværdi er påkrævet.", + "aggregate-interval-value-min": "Aggregeringsintervalværdi skal være mindst { sec, plural, =0 {0 sekund} =1 {1 sekund} other {# sekunder} }.", + "aggregate-interval-value-step-multiple-of": "Aggregeringsintervalværdi skal være en divisor eller et multiplum af 1 dag.", + "aggregate-period": { + "hour": "Time", + "day": "Dag", + "week": "Uge (man - søn)", + "week-sun-sat": "Uge (søn - lør)", + "month": "Måned", + "quarter": "Kvartal", + "year": "År", + "custom": "Tilpasset" + }, + "aggregate-period-hint-offset": "Dit aggregeringsinterval vil være: {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "Dit aggregeringsinterval vil være: {{ interval }} og så videre.", + "entity-aggregation": { + "argument-hint": "Data vil blive hentet fra den aktuelle entitet.", + "argument-title-hint": "Definerer de inputargumenter, der bruges til aggregering.", + "argument-setting-hint": "Seneste telemetri er den eneste tilgængelige argumenttype for dette beregnede felt.", + "aggregation-interval": "Aggregeringsinterval", + "aggregation-interval-hint": "Definerer, hvor ofte aggregering udføres. Eksempel: hver 1 time aggregerer data kl. 00:00, 01:00, 02:00 osv. Aggregeringsresultater lagres med tidsstemplet, der svarer til starten af aggregeringsintervallet.", + "apply-offset": "Anvend forskydning på aggregeringsinterval", + "apply-offset-hint": "Definerer, hvor meget starten af hver aggregeringsperiode skal forskydes (f.eks. +10 minutter - 00:10, 01:10).", + "offset-value": "Forskydningsværdi", + "offset-value-required": "Forskydningsværdi er påkrævet.", + "offset-value-min": "Forskydningsværdi skal være et positivt heltal.", + "offset-value-max": "Forskydningsværdi skal være mindre end aggregeringsintervalværdien.", + "wait-delay": "Anvend ventetimeout for forsinket telemetri", + "wait-delay-hint": "Definerer, hvor længe der skal ventes på forsinket telemetri, efter intervallet slutter. Hvis sådan telemetri ankommer, genberegnes resultatet for det interval.", + "duration": "Varighed", + "duration-required": "Varighed er påkrævet.", + "duration-min": "Varighed skal være mindst 1 minut.", + "duration-hint": "Hvor længe der skal ventes på forsinkede data, efter intervallet slutter.", + "produce-intermediate-result": "Generér mellemliggende resultat", + "produce-intermediate-result-hint": "Beregner metrikker i det aktuelle interval for at generere et mellemliggende resultat. Opdateringer sker ikke oftere end én gang hver {{ time }}." + }, "hint": { "arguments-simple-with-rolling": "Beregnet felt af typen simpel må ikke indeholde nøgler med tidsserieglidningstype.", + "arguments-propagate-arguments-with-rolling": "Typen 'Time series rolling' er inkompatibel med propagering af 'Kun argumenter'.", + "arguments-propagate-argument-entity-type": "Entitetstypen er inkompatibel med propagering af 'Kun argumenter'.", + "arguments-propagate-argument-must-current-entity": "Mindst ét argument skal konfigureres med kildenhedstypen 'Aktuel entitet'.", "arguments-empty": "Argumenter må ikke være tomme.", "expression-required": "Udtryk er påkrævet.", "expression-invalid": "Udtryk er ugyldigt", @@ -1081,12 +1341,218 @@ "argument-name-duplicate": "Argument med dette navn findes allerede.", "argument-name-max-length": "Argumentnavn skal være under 256 tegn.", "argument-name-forbidden": "Argumentnavn er reserveret og kan ikke anvendes.", + "output-key-required": "Outputnøgle er påkrævet.", + "output-key-pattern": "Outputnøgle er ugyldig.", + "output-key-duplicate": "Der findes allerede en nøgle med dette navn.", + "output-key-max-length": "Outputnøgle skal være under 256 tegn.", + "output-key-forbidden": "Outputnøgle er reserveret og kan ikke bruges.", + "entity-type-required": "Entitetstype er påkrævet", + "name-required": "Navn er påkrævet.", + "name-pattern": "Navn er ugyldigt.", + "name-duplicate": "Der findes allerede et navn med dette navn.", + "name-max-length": "Navn skal være under 256 tegn.", + "name-forbidden": "Navn er reserveret og kan ikke bruges.", "argument-type-required": "Argumenttype er påkrævet.", "max-args": "Maksimalt antal argumenter er nået.", "decimals-range": "Standard decimaler skal være et tal mellem 0 og 15.", "expression": "Standardudtryk demonstrerer, hvordan man omregner temperatur fra Fahrenheit til Celsius.", "arguments-entity-not-found": "Målentitet for argument ikke fundet.", - "use-latest-timestamp": "Hvis aktiveret, vil den beregnede værdi blive gemt med det nyeste tidsstempel fra argumenternes telemetri i stedet for serverens tid." + "use-latest-timestamp": "Hvis aktiveret, vil den beregnede værdi blive gemt med det nyeste tidsstempel fra argumenternes telemetri i stedet for serverens tid.", + "entity-coordinates": "Angiv de tidsserienøgler, der leverer entitetens GPS-koordinater (breddegrad og længdegrad).", + "geofencing-zone-groups": "Definér én eller flere geofencingzonegrupper, der skal kontrolleres (f.eks. 'allowedZones', 'restrictedZones'). Hver gruppe skal have et unikt navn, som bruges som præfiks for telemetrinøgler for beregnet felt-output.", + "perimeter-attribute-key": "Angiv attributnøglen, der indeholder definitionen af geofencingzonens perimeter. Perimeteren hentes altid fra serversideattributter for zoneentiteten.", + "report-strategy": "Tilstedeværelsesstatus rapporterer, om entiteten aktuelt er INDE i eller UDE af zonegruppen. Overgangshændelser rapporterer, hvornår entiteten GIK IND I eller FORLOD zonegruppen.", + "create-relation-with-matched-zones": "Opret og vedligehold automatisk relationer mellem entiteten og de zoner, den aktuelt er inde i. Relationer fjernes, når entiteten forlader en zone, og oprettes, når den går ind i en ny.", + "relation-type-required": "Relationstype er påkrævet.", + "relation-level-required": "Relationsniveau er påkrævet.", + "relation-level-min": "Minimumsværdi for relationsniveau er 1.", + "relation-level-max": "Maksimumværdi for relationsniveau er {{max}}.", + "geofencing-empty": "Mindst én zonegruppe skal være konfigureret.", + "geofencing-entity-not-found": "Geofencingmålsentitet blev ikke fundet.", + "max-geofencing-zone": "Maksimalt antal geofencingzoner er nået.", + "zone-group-refresh-interval": "Definerer, hvor ofte zonegrupper, der er konfigureret via relaterede entiteter, opdateres.", + "zone-group-refresh-interval-required": "Opdateringsinterval for zonegrupper er påkrævet.", + "zone-group-refresh-interval-min": "Opdateringsinterval for zonegrupper skal være mindst {{ min }} sekunder.", + "propagation-path-related-entities": "Definerer en direkte sti på ét enkelt niveau til en relateret entitet baseret på den valgte retning og relationstype.", + "data-propagate": "Definerer de data, der skal propageres fra argumenterne konfigureret nedenfor. 'Kun argumenter' bruger de hentede data direkte, mens 'Beregningsresultat' beregner en ny værdi ud fra disse data.", + "aggregation-path-related-entities": "Definerer en aggregeringssti på ét enkelt niveau via direkte relationer med overordnede eller underordnede entiteter, baseret på retning og relationstype. Kun relationer mellem enheds-, asset-, kunde- og lejerentiteter understøttes.", + "arguments-aggregation": "Definerer de inputargumenter, der bruges til filtrering og aggregering.", + "setting-arguments-aggregation": "Data vil blive hentet fra relaterede entiteter, der er konfigureret i aggregeringsstien.", + "metrics": "Definerer metrikker, der aggregeres baseret på konfigurerede argumenter.", + "entity-aggregation-metrics": "Definerer metrikker, der aggregeres baseret på konfigurerede argumenter over de angivne tidsintervaller.", + "import-invalid-calculated-field-type": "Kan ikke importere beregnet felt: Ugyldig struktur for beregnet felt.", + "simple-expression-title": "Aritmetisk udtryk, der definerer, hvordan den beregnede værdi beregnes.", + "script-title": "TBEL-script, der definerer beregningslogikken og outputværdierne.", + "simple-arguments": "Aritmetisk udtryk, der definerer, hvordan den beregnede værdi beregnes.", + "script-arguments": "Definerer de inputargumenter, der er tilgængelige for scriptet." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Alarmregler", + "alarm-rule": "Alarmregel", + "alarm-rules": "Alarmregler", + "alarm-rules-old": "Gammel", + "alarm-rules-actual": "Aktuel", + "severities": "Alvorlighedsgrader", + "cleared": "Rydningsbetingelse", + "delete-title": "Er du sikker på, at du vil slette alarmreglen '{{title}}'?", + "delete-text": "Vær forsigtig, efter bekræftelsen kan alarmreglen og alle relaterede data ikke gendannes.", + "delete-multiple-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 alarmregel} other {# alarmregler} }?", + "delete-multiple-text": "Vær forsigtig, efter bekræftelsen vil alle valgte alarmregler blive fjernet, og alle relaterede data kan ikke gendannes.", + "create": "Opret ny alarmregel", + "add": "Tilføj alarmregel", + "copy": "Kopiér alarmregelkonfiguration", + "details": "Detaljer for alarmregel", + "no-found": "Ingen alarmregler fundet", + "list": "{ count, plural, =1 {Én alarmregel} other {Liste over # alarmregler} }", + "selected-fields": "{ count, plural, =1 {1 alarmregel} other {# alarmregler} } valgt", + "import": "Importér alarmregel", + "file": "Alarmregelfil", + "export": "Eksportér alarmregel", + "export-failed-error": "Kan ikke eksportere alarmregel: {{error}}", + "entity-type": "Entitetstype", + "entity-type-required": "Entitetstype er påkrævet.", + "alarm-type": "Alarmtype", + "alarm-type-hint": "Unik identifikator (f.eks. HighTempAlarm) inden for alarmoprinderens omfang (Enhed, Asset osv.) for at undgå konflikter.", + "alarm-type-required": "Alarmtype er påkrævet.", + "alarm-type-pattern": "Alarmtype er ugyldig.", + "alarm-type-max-length": "Alarmtype skal være under 256 tegn.", + "clear-alarm": "Ryd alarm", + "value-argument": "Argument", + "value-argument-required": "Argument er påkrævet.", + "static-settings": "Statiske indstillinger", + "configuration": "Konfiguration", + "static-schedule": "Statisk", + "dynamic-schedule": "Dynamisk", + "operation-and": "OG", + "operation-or": "ELLER", + "condition-during": "I løbet af {{during}}", + "condition-during-dynamic": "I løbet af \"{{ attribute }}\"", + "condition-repeat-times": "Gentages { count, plural, =1 {1 gang} other {# gange} }", + "condition-repeat-times-dynamic": "Gentages \"{{ attribute }}\" gange", + "filter-preview": "Forhåndsvisning af filter", + "condition-settings": "Betingelsesindstillinger", + "static": "Statisk", + "dynamic": "Dynamisk", + "argument-filters": "Argumentfiltre", + "argument-name": "Argumentnavn", + "value-type": "Værditype", + "general": "Generelt", + "filters": "Filtre", + "date-time-hint": "Argumentet skal være i epoch-millisekunder. Eksempel: 1698839340000 svarer til 2023-11-01 12:49:00 UTC.", + "operation": "Operation", + "value-source": "Værdikilde", + "value": "Værdi", + "ignore-case": "Ignorer store/små bogstaver", + "condition": "Betingelse", + "script": "Script", + "add-filter": "Tilføj argumentfilter", + "edit-filter": "Argumentfilter", + "remove-filter": "Fjern argumentfilter", + "no-filter": "Mindst ét filter er påkrævet.", + "conditions": { + "simple": "Simpel", + "duration": "Varighed", + "repeating": "Gentagende" + }, + "schedule-title": "Tidsplan", + "edit-schedule": "Rediger alarmtidsplan", + "schedule-type": "Tidsplanlægger-type", + "schedule-type-required": "Tidsplanlægger-type er påkrævet.", + "schedule": { + "any-time": "Aktiv hele tiden", + "specific-time": "Aktiv på et bestemt tidspunkt", + "custom": "Tilpasset" + }, + "schedule-day": { + "monday": "Mandag", + "tuesday": "Tirsdag", + "wednesday": "Onsdag", + "thursday": "Torsdag", + "friday": "Fredag", + "saturday": "Lørdag", + "sunday": "Søndag" + }, + "schedule-days": "Dage", + "schedule-time": "Tid", + "schedule-time-from": "Fra", + "schedule-time-to": "Til", + "schedule-days-of-week-required": "Mindst én ugedag skal vælges.", + "tbel": "TBEL", + "expression-type": { + "simple": "Simpel", + "script": "Script" + }, + "operation-type": { + "and": "Og", + "or": "Eller" + }, + "filter-predicate-type": { + "string": "Streng", + "numeric": "Numerisk", + "boolean": "Boolesk", + "complex": "Kompleks" + }, + "alarm-rule-additional-info": "Yderligere info", + "edit-alarm-rule-additional-info": "Rediger yderligere info", + "alarm-rule-additional-info-placeholder": "Angiv venligst dine kommentarer og justeringer her for at vise dem i Alarmdetaljer under Yderligere info", + "alarm-rule-additional-info-hint": "Tip: brug ${Argumentnavn} til at erstatte værdier for de argumenter, der bruges i alarmregelbetingelsen.", + "alarm-rule-additional-info-icon-hint": "Brug Argumentnavn til at erstatte værdier for de argumenter, der bruges i alarmregelbetingelsen.", + "alarm-rule-mobile-dashboard": "Mobilt Dashboard", + "alarm-rule-mobile-dashboard-hint": "Bruges af mobilapplikationen som et Dashboard til alarmdetaljer.", + "alarm-rule-no-mobile-dashboard": "Intet Dashboard valgt", + "alarm-rule-condition": "Alarmregelbetingelse", + "enter-alarm-rule-condition-prompt": "Tilføj betingelse", + "enter-alarm-rule-clear-condition-prompt": "Tilføj rydningsbetingelse", + "edit-alarm-rule-condition": "Alarmbetingelse", + "condition-type": "Betingelsestype", + "condition-type-hint": "\"Varighed\"- og \"Gentagende\"-mulighederne er ikke tilgængelige, når operationen \"Mangler i\" bruges i filtret.", + "select-alarm-severity": "Vælg alarmens alvorlighedsgrad", + "add-create-alarm-rule-prompt": "Mindst én udløserbetingelse er påkrævet.", + "add-create-alarm-rule": "Tilføj udløserbetingelse", + "add-clear-alarm-rule": "Tilføj rydningsbetingelse", + "condition-duration": "Betingelsesvarighed", + "condition-duration-value": "Varighedsværdi", + "condition-duration-time-unit": "Tidsenhed", + "condition-duration-value-range": "Varighedsværdi skal være i intervallet fra 1 til 2147483647.", + "condition-duration-value-pattern": "Varighedsværdi skal være heltal.", + "condition-duration-value-required": "Varighedsværdi er påkrævet.", + "condition-duration-time-unit-required": "Tidsenhed er påkrævet.", + "condition-repeating-value": "Antal hændelser", + "condition-repeating-value-hint": "Opdatering af ethvert alarmregelargument vil blive talt som en hændelse", + "condition-repeating-value-range": "Antal hændelser skal være i intervallet fra 1 til 2147483647.", + "condition-repeating-value-pattern": "Antal hændelser skal være heltal.", + "condition-repeating-value-required": "Antal hændelser er påkrævet.", + "create-conditions": "Udløserbetingelser", + "clear-condition": "Rydningsbetingelse", + "no-clear-alarm-rule": "Ingen rydningsbetingelse konfigureret.", + "advanced-settings": "Avancerede indstillinger", + "propagate-alarm": "Propagér alarm til relaterede entiteter", + "alarm-rule-relation-types-list": "Relationstyper", + "alarm-rule-relation-types-list-hint": "Definerer relationstyper til filtrering af de relaterede entiteter. Hvis ikke angivet, vil alarmen blive propageret til alle relaterede entiteter.", + "propagate-alarm-to-owner": "Propagér alarm til entitetens ejer (Kunde eller Lejer)", + "propagate-alarm-to-tenant": "Propagér alarm til Lejer", + "alarm-rule-filter-title": "Alarmregelfilter", + "filter-title": "Filter", + "debugging": "Fejlfinding af alarmregel", + "any-type": "Enhver type", + "enter-alarm-rule-type": "Angiv alarmtype", + "no-alarm-rule-types-matching": "Der blev ikke fundet nogen alarmtyper, der matcher '{{entitySubtype}}'.", + "alarm-rule-type-list-empty": "Ingen alarmtyper valgt.", + "alarm-rule-type-list": "Liste over alarmtyper", + "alarm-rule-entity-list": "Entitetsliste", + "missing-for": "mangler i", + "time-unit": "Enhed", + "mode": "Tilstand", + "type": "Type", + "value-required": "Værdi er påkrævet.", + "min-value": "Værdien skal være 1 eller højere.", + "argument-in-use": "Argumentet bruges som generelt argument.", + "import-invalid-alarm-rule-type": "Kan ikke importere alarmregel: Ugyldig struktur for alarmregel.", + "no-filter-preview": "Intet filter angivet", + "filter-operation": { + "and": "Og", + "or": "Eller" } }, "ai-models": { @@ -1193,6 +1659,7 @@ "contact": { "country": "Land", "country-required": "Land er påkrævet.", + "country-object-required": "Vælg venligst et gyldigt land fra listen.", "city": "By", "state": "Stat / Provins", "postal-code": "Postnummer", @@ -1229,6 +1696,8 @@ "documentation": "Dokumentation", "time-left": "{{time}} tilbage", "output": "Output", + "sort-asc": "Stigende", + "sort-desc": "Faldende", "suffix": { "s": "s", "ms": "ms" @@ -1365,6 +1834,8 @@ "mobile-order": "Dashboardrækkefølge i mobilapplikation", "mobile-hide": "Skjul dashboard i mobilapplikation", "update-image": "Opdatér dashboardbillede", + "update-new-version": "Upload ny version", + "upload-file-to-update": "Upload fil for at opdatere", "take-screenshot": "Tag skærmbillede", "select-widget-title": "Vælg widget", "select-widget-value": "{{title}}: vælg widget", @@ -1733,6 +2204,8 @@ "bootstrap-tab": "Bootstrap-klient", "bootstrap-server": "Bootstrap-server", "lwm2m-server": "LwM2M-server", + "client-reboot": "Trigger for registreringsopdatering", + "bootstrap-reboot": "Trigger for bootstrap-anmodning", "client-publicKey-or-id": "Klientens offentlige nøgle eller ID", "client-publicKey-or-id-required": "Klientens offentlige nøgle eller ID er påkrævet.", "client-publicKey-or-id-tooltip-psk": "PSK-identifikator er en vilkårlig identifikator op til 128 bytes som beskrevet i standarden [RFC7925].\nDen SKAL konverteres til en streng og kodes med UTF-8.", @@ -1780,7 +2253,6 @@ "unable-delete-device-alias-text": "Enhedsalias '{{deviceAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
    {{widgetsList}}", "is-gateway": "Er gateway", "overwrite-activity-time": "Overskriv aktivitetstid for tilsluttet enhed", - "device-filter": "Enhedsfilter", "device-filter-title": "Enhedsfilter", "filter-title": "Filter", "device-state": "Enhedstilstand", @@ -2234,6 +2706,7 @@ "short-id-required": "Kort server-ID er påkrævet.", "short-id-range": "Kort server-ID skal være mellem {{ min }} og {{ max }}.", "short-id-pattern": "Kort server-ID skal være et positivt heltal.", + "short-id-pattern-bs": "Kort server-ID må kun være null", "lifetime": "Klientens registreringslevetid", "lifetime-required": "Registreringslevetid er påkrævet.", "lifetime-pattern": "Registreringslevetid skal være et positivt heltal.", @@ -2328,7 +2801,9 @@ "composite-all-description": "Alle ressourcer observeres med en enkelt sammensat Observe-anmodning (mere effektivt, mindre fleksibelt)", "composite-by-object": "Sammensat efter objekt", "composite-by-object-description": "Ressourcer grupperes efter objekttype og observeres via separate sammensatte Observe-anmodninger (balanceret tilgang)" - } + }, + "init-attr-tel-as-obs-strategy": "Initialisér attributter og telemetri ved hjælp af Observe-strategien", + "init-attr-tel-as-obs-strategy-hint": "Hvis false - initialiseres attributter og telemetri ved at læse deres værdier én ad gangen.\\nHvis true - initialiseres attributter og telemetri ved at abonnere på deres værdier ved hjælp af Observe-strategien." }, "snmp": { "add-communication-config": "Tilføj kommunikationskonfiguration", @@ -2644,6 +3119,8 @@ "type-rulenodes": "Regelnoder", "list-of-rulenodes": "{ count, plural, =1 {Én regelnode} other {Liste over # regelnoder} }", "rulenode-name-starts-with": "Regelnoder, hvis navne starter med '{{prefix}}'", + "type-api-key": "API-nøgle", + "type-api-keys": "API-nøgler", "type-current-customer": "Aktuel kunde", "type-current-tenant": "Aktuel lejer", "type-current-user": "Aktuel bruger", @@ -2665,6 +3142,7 @@ "details": "Enhedsdetaljer", "no-entities-prompt": "Ingen enheder fundet", "no-data": "Ingen data at vise", + "show-all-columns": "Vis alle", "columns-to-display": "Kolonner at vise", "type-api-usage-state": "API-brugsstatus", "type-edge": "Edge", @@ -2710,7 +3188,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {Én mobilapplikation} other {Liste over # mobilapplikationer} }", "type-mobile-app-bundle": "Mobilpakke", "type-mobile-app-bundles": "Mobilpakker", - "list-of-mobile-app-bundles": "{ count, plural, =1 {Én mobilpakke} other {Liste over # mobilpakker} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {Én mobilpakke} other {Liste over # mobilpakker} }", + "limit-reached": "Grænse nået", + "limit-reached-text": "Du har nået grænsen på {{ entities }}. For at tilføje flere skal du bede din systemadministrator om at øge din {{ entity }}-grænse.", + "request-limit-increase": "Anmod om øgning af grænse", + "request-sysadmin-text": "Er du systemadministratoren?", + "login-here": "Log ind her", + "to-increase-limit": "for at øge grænsen.", + "increase-limit-request-sent-title": "Vi har sendt en automatisk anmodning til din systemadministrator om at øge grænsen", + "increase-limit-request-sent-text": "Giv dem venligst lidt tid til at gennemgå anmodningen og opdatere indstillingerne. Du skal muligvis genindlæse denne side for at se ændringerne." }, "entity-field": { "created-time": "Oprettelsestidspunkt", @@ -3798,7 +4284,8 @@ "password-link-sent-message": "Nulstillingslink er sendt", "email": "Email", "invalid-email-format": "Ugyldigt emailformat.", - "login-with": "Log ind med {{name}}", + "sign-in-with": "Log ind med {{name}}", + "sign-in-to-your-account": "Log ind på din konto", "or": "eller", "error": "Loginfejl", "verify-your-identity": "Bekræft din identitet", @@ -3817,7 +4304,51 @@ "activation-link-expired": "Aktiveringslinket er udløbet", "activation-link-expired-message": "Linket til at aktivere din profil er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email.", "reset-password-link-expired": "Nulstillingslinket for adgangskode er udløbet", - "reset-password-link-expired-message": "Linket til at nulstille din adgangskode er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email." + "reset-password-link-expired-message": "Linket til at nulstille din adgangskode er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email.", + "two-fa": "Tofaktorgodkendelse", + "two-fa-required": "Tofaktorgodkendelse er påkrævet", + "set-up-verification-method": "Opsæt en verifikationsmetode for at fortsætte", + "set-up-verification-method-login": "Opsæt en verifikationsmetode eller log ind", + "enable-authenticator-app": "Aktivér godkendelsesapp", + "enable-authenticator-app-description": "Indtast venligst sikkerhedskoden fra din godkendelsesapp", + "enable-authenticator-sms": "Aktivér SMS-godkendelse", + "enable-authenticator-sms-description": "Indtast den 6-cifrede kode, vi lige har sendt til ", + "enable-authenticator-email": "Aktivér email-godkendelse", + "enable-authenticator-email-description": "En sikkerhedskode er blevet sendt til din emailadresse på ", + "enter-key-manually": "eller indtast denne 32-cifrede nøgle manuelt:", + "continue": "Fortsæt", + "confirm": "Bekræft", + "authenticator-app-success": "Godkendelsesapp blev aktiveret", + "authenticator-app-success-description": "Næste gang du logger ind, skal du angive en tofaktorgodkendelseskode", + "authenticator-sms-success": "SMS-godkendelse blev aktiveret", + "authenticator-sms-success-description": "Næste gang du logger ind, bliver du bedt om at indtaste sikkerhedskoden, som sendes til telefonnummeret", + "authenticator-email-success": "Email-godkendelse blev aktiveret", + "authenticator-email-success-description": "Næste gang du logger ind, bliver du bedt om at indtaste sikkerhedskoden, som sendes til din emailadresse", + "authenticator-backup-code-success": "Backupkode blev aktiveret", + "authenticator-backup-code-success-description": "Næste gang du logger ind, bliver du bedt om at indtaste sikkerhedskoden eller bruge en af backupkoderne.", + "add-verification-method": "Tilføj verifikationsmetode", + "get-backup-code": "Hent backupkode", + "copy-key": "Kopiér nøgle", + "send-code": "Send kode", + "email-label": "Email", + "email-description": "Indtast en email, der skal bruges som din godkendelse.", + "sms-description": "Indtast et telefonnummer, der skal bruges som din godkendelse.", + "backup-code-description": "Print koderne, så du har dem ved hånden, når du skal bruge dem til at logge ind på din konto. Du kan bruge hver backupkode én gang.", + "backup-code-warn": "Når du forlader denne side, kan disse koder ikke vises igen. Opbevar dem sikkert ved hjælp af mulighederne nedenfor.", + "download-txt": "Download (txt)", + "print": "Udskriv", + "verification-code": "6-cifret kode", + "verification-code-invalid": "Ugyldigt format for verifikationskode", + "verification-code-incorrect": "Verifikationskoden er forkert", + "verification-code-many-request": "For mange anmodninger om at kontrollere verifikationskode", + "scan-qr-code": "Scan denne QR-kode med din verifikationsapp", + "phone-input": { + "phone-input-label": "Telefonnummer", + "phone-input-required": "Telefonnummer er påkrævet", + "phone-input-validation": "Telefonnummer er ugyldigt eller ikke muligt", + "phone-input-pattern": "Ugyldigt telefonnummer. Skal være i E.164-format, f.eks. {{phoneNumber}}", + "phone-input-hint": "Telefonnummer i E.164-format, f.eks. {{phoneNumber}}" + } }, "mobile": { "add-application": "Tilføj applikation", @@ -4185,6 +4716,7 @@ "api-usage-limit": "API-brugsgrænse", "device-activity": "Enhedsaktivitet", "entities-limit": "Enhedsgrænse", + "entities-limit-increase-request": "Anmodning om øgning af entitetsgrænse", "entity-action": "Enhedshandling", "general": "Generelt", "rule-engine-lifecycle-event": "Livscyklushændelse for Rule Engine", @@ -4402,6 +4934,12 @@ "at-least": "Mindst:", "character": "{ count, plural, =1 {1 tegn} other {# tegn} }", "digit": "{ count, plural, =1 {1 ciffer} other {# cifre} }", + "password-tooltip-min-length": "Mindst {{minimumLength}} tegn langt", + "password-tooltip-max-length": "Højst {{maximumLength}} tegn langt", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} stort bogstav", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} lille bogstav", + "password-tooltip-digit": "{{minimumDigits}} tal", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} specialtegn", "incorrect-password-try-again": "Forkert adgangskode. Prøv igen", "lowercase-letter": "{ count, plural, =1 {1 lille bogstav} other {# små bogstaver} }", "new-passwords-not-match": "De nye adgangskoder matcher ikke", @@ -4460,7 +4998,8 @@ "additional-info": "Yderligere info (JSON)", "invalid-additional-info": "Kunne ikke fortolke JSON for yderligere info.", "no-relations-text": "Ingen relationer fundet", - "not": "Ikke" + "not": "Ikke", + "copy-type": "Kopiér type" }, "resource": { "add": "Tilføj ressource", @@ -5411,7 +5950,7 @@ "time-series": "Tidsserier", "latest": "Seneste værdier", "web-sockets": "WebSockets", - "calculated-fields": "Beregnet felter" + "calculated-fields-and-alarm-rules": "Beregnede felter og alarmregler" }, "save-attribute": { "processing-settings": "Behandlingsindstillinger", @@ -5606,7 +6145,8 @@ "bad-request-params": "Ugyldige forespørgselsparametre", "item-not-found": "Element ikke fundet", "too-many-requests": "For mange forespørgsler", - "too-many-updates": "For mange opdateringer" + "too-many-updates": "For mange opdateringer", + "entities-limit-exceeded": "Entitetsgrænse overskredet" }, "tenant": { "tenant": "Lejer", @@ -5744,6 +6284,27 @@ "max-arguments-per-cf": "Maksimalt antal argumenter pr. beregnet felt", "max-arguments-per-cf-range": "Maksimalt antal argumenter pr. beregnet felt kan ikke være negativt", "max-arguments-per-cf-required": "Maksimalt antal argumenter pr. beregnet felt er påkrævet", + "max-related-level-per-argument": "Maksimalt relationsniveau pr. argumentet 'Relaterede entiteter'", + "max-related-level-per-argument-range": "Maks. antal for relationsniveau pr. argumentet 'Relaterede entiteter' kan ikke være mindre end '1'", + "max-related-level-per-argument-required": "Maks. antal for relationsniveau pr. argumentet 'Relaterede entiteter' er påkrævet", + "min-allowed-scheduled-update-interval": "Min. tilladt opdateringsinterval for argumenter af typen 'Relaterede entiteter' (sekunder)", + "min-allowed-scheduled-update-interval-range": "Min. tilladt opdateringsinterval kan ikke være negativt", + "min-allowed-deduplication-interval": "Min. tilladt deduplikeringsinterval (sekunder)", + "min-allowed-deduplication-interval-range": "Min. tilladt deduplikeringsintervalværdi kan ikke være negativ", + "min-allowed-deduplication-interval-required": "Min. tilladt deduplikeringsinterval er påkrævet", + "intermediate-aggregation-interval": "Mellemliggende aggregeringsinterval (sekunder)", + "intermediate-aggregation-interval-range": "Mellemliggende aggregeringsintervalværdi kan ikke være mindre end '1'", + "intermediate-aggregation-interval-required": "Mellemliggende aggregeringsinterval er påkrævet", + "reevaluation-check-interval": "Kontrolinterval for revurdering (sekunder)", + "reevaluation-check-interval-range": "Kontrolinterval for revurdering kan ikke være mindre end '1'", + "reevaluation-check-interval-required": "Kontrolinterval for revurdering er påkrævet", + "alarms-reevaluation-interval": "Revurderingsinterval for alarmer (sekunder)", + "alarms-reevaluation-interval-range": "Revurderingsinterval for alarmer kan ikke være mindre end '1'", + "alarms-reevaluation-interval-required": "Revurderingsinterval for alarmer er påkrævet", + "min-allowed-aggregation-interval": "Min. tilladt aggregeringsinterval (sekunder)", + "min-allowed-aggregation-interval-range": "Min. tilladt aggregeringsintervalværdi kan ikke være negativ", + "min-allowed-aggregation-interval-required": "Min. tilladt aggregeringsinterval er påkrævet", + "min-allowed-scheduled-update-interval-required": "Min. tilladt opdateringsintervals minimumsværdi er påkrævet", "max-state-size": "Maksimal tilstandsstørrelse i KB", "max-state-size-range": "Maksimal tilstandsstørrelse i KB kan ikke være negativ", "max-state-size-required": "Maksimal tilstandsstørrelse i KB er påkrævet", @@ -5819,6 +6380,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Maksimalt antal abonnementer pr. almindelig bruger", "ws-limit-max-subscriptions-per-public-user": "Maksimalt antal abonnementer pr. offentlig bruger", "ws-limit-updates-per-session": "WS-opdateringer pr. session", + "relation-search-entity-limit": "Entitetsgrænse for relationssøgning", + "relation-search-entity-limit-hint": "Begrænser antallet af entiteter, der opløses på det sidste niveau i relationsstien. Gælder for argumenter af typen 'Relaterede entiteter' og propageringsfelter.", + "relation-search-entity-limit-required": "Entitetsgrænse for relationssøgning er påkrævet", + "relation-search-entity-limit-range": "Entitetsgrænse for relationssøgning kan ikke være mindre end '1'", "rate-limits": { "add-limit": "Tilføj begrænsning", "and-also-less-than": "og også mindre end", @@ -6004,7 +6569,9 @@ "default-agg-interval": "Standard grupperingsinterval", "edit-intervals-list-hint": "Liste over tilgængelige intervalmuligheder kan specificeres.", "edit-grouping-intervals-list-hint": "Det er muligt at konfigurere listen over grupperingsintervaller og standard grupperingsinterval.", - "all": "Alle" + "all": "Alle", + "save-current-settings-as-default": "Gem aktuelle indstillinger som standardtidsvindue", + "hide-option-from-end-users": "Skjul indstillingen for slutbrugere" }, "tooltip": { "trigger": "Udløser", @@ -6658,7 +7225,8 @@ "export-relations": "Eksporter relationer", "export-attributes": "Eksporter attributter", "export-credentials": "Eksporter legitimationsoplysninger", - "export-calculated-fields": "Eksporter beregnede felter", + "export-calculated-fields": "Eksportér beregnede felter \nog alarmregler", + "export-alarm-rules": "Eksportér alarmregler", "entity-versions": "Entitetsversioner", "versions": "Versioner", "created-time": "Oprettelsestidspunkt", @@ -6675,7 +7243,8 @@ "load-relations": "Indlæs relationer", "load-attributes": "Indlæs attributter", "load-credentials": "Indlæs legitimationsoplysninger", - "load-calculated-fields": "Indlæs beregnede felter", + "load-calculated-fields": "Indlæs beregnede felter og alarmregler", + "load-alarm-rules": "Indlæs alarmregler", "compare-with-current": "Sammenlign med nuværende", "diff-entity-with-version": "Forskel fra entitetsversion '{{versionName}}'", "previous-difference": "Forrige forskel", @@ -6885,7 +7454,23 @@ "scan-qr-code": "Scan QR-kode", "make-phone-call": "Foretag telefonopkald", "get-location": "Hent telefonplacering", - "take-screenshot": "Tag skærmbillede" + "take-screenshot": "Tag skærmbillede", + "handle-provision-success-function": "Håndtér funktion ved vellykket provisionering", + "get-location-function": "Hent placeringsfunktion", + "process-launch-result-function": "Behandl funktion for startresultat", + "get-phone-number-function": "Hent telefonnummerfunktion", + "process-image-function": "Behandl billedfunktion", + "process-qr-code-function": "Behandl QR-kodefunktion", + "process-location-function": "Behandl placeringsfunktion", + "handle-empty-result-function": "Håndtér funktion for tomt resultat", + "handle-error-function": "Håndtér fejlfunktion", + "handle-non-mobile-fallback-function": "Håndtér fallbackfunktion for ikke-mobil", + "save-to-gallery": "Gem i galleri", + "provision-type": "Provisioneringstype", + "auto": "Auto", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Brugerdefineret handlingsfunktion", "custom-pretty-function": "Brugerdefineret handling (med HTML-skabelon) funktion", @@ -6894,7 +7479,8 @@ "marker": "Markør", "polygon": "Polygon", "rectangle": "Rektangel", - "circle": "Cirkel" + "circle": "Cirkel", + "polyline": "Polylinje" }, "place-map-item": "Placér kortelement", "map-item-tooltip": { @@ -6906,7 +7492,9 @@ "continue-draw-polygon": "Fortsæt med at tegne polygon", "finish-draw-polygon": "Afslut tegning af polygon", "start-draw-circle": "Start med at tegne cirkel", - "finish-draw-circle": "Afslut tegning af cirkel" + "finish-draw-circle": "Afslut tegning af cirkel", + "start-draw-polyline": "Start tegning af polylinje", + "finish-draw-polyline": "Afslut tegning af polylinje" } }, "widgets-bundle": { @@ -7472,6 +8060,14 @@ "update-animation-delay": "Opdater animationsforsinkelse" }, "chart-axis": { + "limit": "Grænse", + "source": "Kilde", + "key-value": "Nøgle / Værdi", + "value-required": "Værdi er påkrævet.", + "entity-key-required": "Entitetsnøgle er påkrævet.", + "key-required": "Nøgle er påkrævet.", + "scale-limits": "Skalagrænser", + "scale-appearance": "Skalaudseende", "scale": "Skala", "scale-min": "min", "scale-max": "maks", @@ -8016,7 +8612,10 @@ "add-radio-option": "Tilføj radiomulighed", "radio-label-position": "Etiketposition", "radio-label-position-before": "Før", - "radio-label-position-after": "Efter" + "radio-label-position-after": "Efter", + "save-image": "Gem billede", + "save-to-gallery": "Gem automatisk optagne billeder i Billedgalleriet", + "public-image": "Gør billedet tilgængeligt for enhver uautoriseret bruger" }, "invalid-qr-code-text": "Ugyldig inputtekst til QR-kode. Input skal være af typen streng", "qr-code": { @@ -8311,7 +8910,8 @@ "trips": "Rejser", "markers": "Markører", "polygons": "Polygoner", - "circles": "Cirkler" + "circles": "Cirkler", + "polylines": "Polylinjer" }, "data-layer": { "source": "Kilde", @@ -8508,6 +9108,25 @@ "finish-circle-hint-with-entity": "Cirkel for '{{entityName}}': klik for at afslutte og gemme cirkel", "finish-circle-hint": "Cirkel: klik for at afslutte tegning" }, + "polyline": { + "polyline-key": "Polylinjenøgle", + "polyline-key-required": "Polylinjenøgle er påkrævet", + "no-polylines": "Ingen polylinjer konfigureret", + "add-polylines": "Tilføj polylinje", + "polyline-configuration": "Polylinjekonfiguration", + "remove-polyline": "Fjern polylinje", + "edit": "Rediger polylinje", + "cut": "Beskær polylinjeområde", + "rotate": "Rotér polylinje", + "remove-polyline-for": "Fjern polylinje for '{{entityName}}'", + "draw-polyline": "Tegn polylinje", + "polyline-place-first-point-hint-with-entity": "Polylinje for '{{entityName}}': klik for at placere første punkt", + "polyline-place-first-point-hint": "Polylinje: klik for at placere første punkt", + "finish-polyline-hint-with-entity": "Polylinje for '{{entityName}}': klik for at afslutte tegningen", + "finish-polyline-hint": "Polylinje: klik for at afslutte tegningen", + "polyline-place-first-point-cut-hint": "Klik for at placere første punkt", + "finish-polyline-cut-hint": "Klik på den første markør for at afslutte og gemme" + }, "select-entity": "Vælg enhed", "select-entity-hint": "Tip: klik på kortet efter valg for at angive position" }, @@ -8949,6 +9568,7 @@ "show-empty-space-hidden-action": "Vis tomt mellemrum i stedet for skjult cellehandling", "dont-reserve-space-hidden-action": "Reserver ikke plads til skjulte handlingsknapper", "display-timestamp": "Tidsstempel", + "timestamp-column-name": "Tidsstempel", "display-pagination": "Vis sidetal", "default-page-size": "Standard sidestørrelse", "page-step-settings": "Indstillinger for sidetrin", @@ -9010,7 +9630,9 @@ "alarm-column-error": "Mindst én alarmkolonne skal angives", "table-tabs": "Tabeller i faner", "show-cell-actions-menu-mobile": "Vis cellehandlingsmenu i mobiltilstand", - "disable-sorting": "Deaktiver sortering" + "disable-sorting": "Deaktiver sortering", + "sort-by": "Sortér faner efter", + "sort-timestamp-option": "Oprettelsestid" }, "latest-chart": { "total": "Total", @@ -9502,11 +10124,28 @@ "content": "

    Ved at oprette dashboards til slutbrugere kan en kundebruger kun se sine egne enheder, mens data fra andre kunder skjules.

    Følg dokumentationen for at lære, hvordan du gør det:

    " } } + }, + "api-usage": { + "api-usage": "API-forbrug", + "label": "Etiket", + "state-name": "Tilstandsnavn", + "status": "Status", + "status-required": "Status er påkrævet.", + "limit": "Maks. grænse", + "limit-required": "Maks. grænse er påkrævet.", + "current-number": "Aktuelt antal", + "current-number-required": "Aktuelt antal er påkrævet.", + "add-key": "Tilføj nøgle", + "no-key": "Ingen nøgle", + "delete-key": "Slet nøgle", + "target-dashboard-state": "Mål-dashboardtilstand", + "go-to-main-state": "Gå til standardvisning" } }, "icon": { "icon": "Ikon", "icons": "Ikoner", + "custom": "Tilpasset", "select-icon": "Vælg ikon", "material-icons": "Materialeikoner", "show-all": "Vis alle ikoner", @@ -9547,6 +10186,7 @@ "items-per-page-separator": "af" }, "language": { + "auto": "Auto", "language": "Sprog" } } \ No newline at end of file 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 4e3a41e488..78a744a68d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -78,6 +78,7 @@ "show-more": "Mehr anzeigen", "dont-show-again": "Nicht erneut anzeigen", "see-documentation": "Dokumentation ansehen", + "see-debug-events": "Debug-Ereignisse ansehen", "clear": "Löschen", "upload": "Hochladen", "delete-anyway": "Trotzdem löschen", @@ -167,10 +168,10 @@ "number-from-required": "Telefonnummer Von ist erforderlich.", "number-to": "Telefonnummer An", "number-to-required": "Telefonnummer An ist erforderlich.", - "phone-number-hint": "Telefonnummer im E.164-Format, z. B. +19995550123", - "phone-number-hint-twilio": "Telefonnummer im E.164-Format/SID der Telefonnummer/SID des Messaging-Dienstes, z. B. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "Ungültige Telefonnummer. Sollte im E.164-Format vorliegen, z. B. +19995550123.", - "phone-number-pattern-twilio": "Ungültige Telefonnummer. Sollte im E.164-Format/SID der Telefonnummer/SID des Messaging-Dienstes vorliegen, z. B. +19995550123/PNXXX/MGXXX.", + "phone-number-hint": "Telefonnummer im E.164-Format, z.B. +19995550123", + "phone-number-hint-twilio": "Telefonnummer im E.164-Format/SID der Telefonnummer/SID des Messaging-Dienstes, z.B. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Ungültige Telefonnummer. Sollte im E.164-Format vorliegen, z.B. +19995550123.", + "phone-number-pattern-twilio": "Ungültige Telefonnummer. Sollte im E.164-Format/SID der Telefonnummer/SID des Messaging-Dienstes vorliegen, z.B. +19995550123/PNXXX/MGXXX.", "sms-message": "SMS-Nachricht", "sms-message-required": "SMS-Nachricht ist erforderlich.", "sms-message-max-length": "SMS-Nachricht darf nicht länger als 1600 Zeichen sein", @@ -485,6 +486,7 @@ "2fa": { "2fa": "Zwei-Faktor-Authentifizierung", "available-providers": "Verfügbare Anbieter", + "available-providers-required": "Mindestens ein 2FA-Anbieter muss konfiguriert werden.", "issuer-name": "Ausstellername", "issuer-name-required": "Ausstellername ist erforderlich.", "max-verification-failures-before-user-lockout": "Maximale Verifizierungsfehler vor Kontosperrung", @@ -513,7 +515,9 @@ "verification-message-template-required": "Nachrichtenvorlage ist erforderlich.", "within-time": "Innerhalb von (Sek.)", "within-time-pattern": "Zeit muss eine positive Ganzzahl sein.", - "within-time-required": "Zeit ist erforderlich." + "within-time-required": "Zeit ist erforderlich.", + "force-2fa": "Zwei-Faktor-Authentifizierung erzwingen", + "enforce-for": "Erzwingen für" }, "jwt": { "security-settings": "JWT-Sicherheitseinstellungen", @@ -545,16 +549,11 @@ "slack-settings": "Slack-Einstellungen", "mobile-settings": "Mobile Einstellungen", "firebase-service-account-file": "Firebase-Service-Konto-Anmeldeinformationen (JSON-Datei)", - "select-firebase-service-account-file": "Ziehen Sie Ihre Firebase-Service-Konto-Datei hierher oder ", - "trendz": "Trendz", - "trendz-settings": "Trendz-Einstellungen", - "trendz-url": "Trendz-URL", - "trendz-url-required": "Trendz-URL ist erforderlich", - "trendz-api-key": "Trendz-API-Schlüssel", - "trendz-enable": "Trendz aktivieren" + "select-firebase-service-account-file": "Ziehen Sie Ihre Firebase-Service-Konto-Datei hierher oder " }, "alarm": { "alarm": "Alarm", + "alarm-list": "Alarmliste", "alarms": "Alarme", "all-alarms": "Alle Alarme", "select-alarm": "Alarm auswählen", @@ -655,7 +654,16 @@ "alarm-type": "Alarmtyp", "enter-alarm-type": "Alarmtyp eingeben", "no-alarm-types-matching": "Keine Alarmtypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", - "alarm-type-list-empty": "Keine Alarmtypen ausgewählt." + "alarm-type-list-empty": "Keine Alarmtypen ausgewählt.", + "system-comments": { + "acked-by-user": "Alarm wurde von Benutzer {{userName}} bestätigt", + "cleared-by-user": "Alarm wurde von Benutzer {{userName}} gelöscht", + "assigned-to-user": "Alarm wurde von Benutzer {{userName}} Benutzer {{assigneeName}} zugewiesen", + "unassigned-to-user": "Zuweisung des Alarms wurde von Benutzer {{userName}} aufgehoben", + "unassigned-from-deleted-user": "Zuweisung des Alarms wurde aufgehoben, weil Benutzer {{userName}} gelöscht wurde", + "comment-deleted": "Benutzer {{userName}} hat seinen Kommentar gelöscht", + "severity-changed": "Alarmschweregrad wurde von {{oldSeverity}} auf {{newSeverity}} aktualisiert" + } }, "alarm-activity": { "add": "Kommentar hinzufügen...", @@ -760,6 +768,7 @@ "name-max-length": "Name sollte weniger als 256 Zeichen haben", "label-max-length": "Label sollte weniger als 256 Zeichen haben", "description": "Beschreibung", + "description-required": "Beschreibung ist erforderlich.", "type": "Typ", "type-required": "Typ ist erforderlich.", "details": "Details", @@ -873,6 +882,9 @@ "alarms-created-monthly-activity": "Monatliche Aktivität der Alarm-Erstellung", "data-points": "Datenpunkte", "data-points-storage-days": "Speicherzeitraum der Datenpunkte (in Tagen)", + "data-points-storage-days-hourly-activity": "Stichpunkt-Speichertage stündliche Aktivität", + "data-points-storage-days-daily-activity": "Stichpunkt-Speichertage tägliche Aktivität", + "data-points-storage-days-monthly-activity": "Stichpunkt-Speichertage monatliche Aktivität", "device-api": "Geräte-API", "email": "E-Mail", "email-messages": "E-Mail-Nachrichten", @@ -906,6 +918,7 @@ "rule-node": "Regelknoten", "sms": "SMS", "sms-messages": "SMS-Nachrichten", + "sms-messages-hourly-activity": "SMS-Nachrichten stündliche Aktivität", "sms-messages-daily-activity": "Tägliche Aktivität der SMS-Nachrichten", "sms-messages-monthly-activity": "Monatliche Aktivität der SMS-Nachrichten", "successful": "${entityName} Erfolgreich", @@ -915,13 +928,40 @@ "telemetry-persistence-hourly-activity": "Stündliche Aktivität der Telemetriepersistenz", "telemetry-persistence-monthly-activity": "Monatliche Aktivität der Telemetriepersistenz", "transport": "Transport", + "transport-msg-hourly-activity": "Transportnachrichten stündliche Aktivität", + "transport-msg-daily-activity": "Transportnachrichten tägliche Aktivität", + "transport-msg-monthly-activity": "Transportnachrichten monatliche Aktivität", "transport-daily-activity": "Tägliche Transportaktivität", "transport-data-points": "Transportierte Datenpunkte", - "transport-hourly-activity": "Stündliche Transportaktivität", - "transport-messages": "Transportnachrichten", - "transport-monthly-activity": "Monatliche Transportaktivität", + "transport-data-points-hourly-activity": "Transportdatenpunkte stündliche Aktivität", + "transport-data-points-daily-activity": "Transportdatenpunkte tägliche Aktivität", + "transport-data-points-monthly-activity": "Transportdatenpunkte monatliche Aktivität", "view-details": "Details anzeigen", - "view-statistics": "Statistiken anzeigen" + "view-statistics": "Statistiken anzeigen", + "transport-messages": "Transportnachrichten", + "transport-messages-hourly-activity": "Transportnachrichten stündliche Aktivität", + "transport-data-point-hourly-activity": "Transportdatenpunkt stündliche Aktivität", + "javascript-function-executions": "Ausführungen von JavaScript-Funktionen", + "javascript-function-executions-hourly-activity": "Ausführungen von JavaScript-Funktionen stündliche Aktivität", + "javascript-function-executions-daily-activity": "Ausführungen von JavaScript-Funktionen tägliche Aktivität", + "javascript-function-executions-monthly-activity": "Ausführungen von JavaScript-Funktionen monatliche Aktivität", + "tbel-function-executions": "Ausführungen von TBEL-Funktionen", + "tbel-function-executions-hourly-activity": "Ausführungen von TBEL-Funktionen stündliche Aktivität", + "tbel-function-executions-daily-activity": "Ausführungen von TBEL-Funktionen tägliche Aktivität", + "tbel-function-executions-monthly-activity": "Ausführungen von TBEL-Funktionen monatliche Aktivität", + "created-reports": "Erstellte Berichte", + "created-reports-hourly-activity": "Erstellte Berichte stündliche Aktivität", + "created-reports-daily-activity": "Erstellte Berichte tägliche Aktivität", + "created-reports-monthly-activity": "Erstellte Berichte monatliche Aktivität", + "emails": "E-Mails", + "emails-hourly-activity": "E-Mails stündliche Aktivität", + "emails-daily-activity": "E-Mails tägliche Aktivität", + "emails-monthly-activity": "E-Mails monatliche Aktivität", + "status": { + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "warning": "Warnung" + } }, "api-limit": { "cassandra-write-queries-core": "REST-API Cassandra-Schreibabfragen", @@ -946,6 +986,40 @@ "edge-uplink-messages": "Edge-Uplink-Nachrichten", "edge-uplink-messages-per-edge": "Edge-Uplink-Nachrichten pro Edge" }, + "api-key": { + "api-key": "API-Schlüssel", + "api-keys": "API-Schlüssel", + "delete-api-key-title": "Möchten Sie den API-Schlüssel '{{name}}' wirklich löschen?", + "delete-api-key-text": "Achtung, nach der Bestätigung kann der Schlüssel nicht wiederhergestellt werden.", + "delete-api-keys-title": "Möchten Sie { count, plural, =1 {1 API-Schlüssel} other {# API-Schlüssel} } wirklich löschen?", + "delete-api-keys-text": "Achtung, nach der Bestätigung können alle ausgewählten Schlüssel nicht wiederhergestellt werden.", + "expiration-date": "Ablaufdatum", + "date": "Datum", + "description": "Beschreibung", + "disable": "Deaktivieren", + "edit-description": "Beschreibung bearbeiten", + "enable": "API-Schlüssel aktivieren ", + "expiration-time": "Ablaufzeit", + "expiration-time-never": "Nie", + "expiration-time-custom": "Benutzerdefiniert", + "generate": "Generieren", + "generate-title": "API-Schlüssel generieren", + "generate-text": "Hinweis: Der API-Schlüssel übernimmt die Berechtigungen des Benutzers, für den er erstellt wird.", + "generated-api-key-title": "API-Schlüssel generiert. Lassen Sie uns die Verbindung prüfen!", + "generated-api-key-copy": "Stellen Sie sicher, dass Sie Ihren API-Schlüssel jetzt kopieren und speichern, da Sie ihn nicht erneut anzeigen können.", + "generated-api-key-command": "Verwenden Sie die folgenden Anweisungen, um die Verbindung zu prüfen. Als Ergebnis sollten Sie die aktuellen Benutzerinformationen erhalten:", + "generated-api-key-insecure-url": "Das Ausführen von Befehlen über eine unsichere HTTP-Verbindung sendet Ihren API-Schlüssel unverschlüsselt und macht ihn anfällig für Abfangen.", + "list": "{ count, plural, =1 {Ein API-Schlüssel} other {Liste von # API-Schlüsseln} }", + "manage": "Verwalten", + "manage-api-keys": "API-Schlüssel verwalten", + "no-found": "Keine API-Schlüssel gefunden", + "selected-api-keys": "{ count, plural, =1 {1 API-Schlüssel} other {# API-Schlüssel} } ausgewählt", + "search": "API-Schlüssel suchen", + "status": "Status", + "status-active": "Aktiv", + "status-inactive": "Inaktiv", + "status-expired": "Abgelaufen" + }, "audit-log": { "audit": "Audit", "audit-logs": "Audit-Protokolle", @@ -999,7 +1073,11 @@ "type-provision-failure": "Gerätebereitstellung fehlgeschlagen", "type-timeseries-updated": "Telemetrie aktualisiert", "type-timeseries-deleted": "Telemetrie gelöscht", - "type-sms-sent": "SMS gesendet" + "type-sms-sent": "SMS gesendet", + "any-type": "Beliebiger Typ", + "audit-log-filter-title": "Audit-Protokoll-Filter", + "filter-title": "Filter", + "filter-types": "Audit-Protokoll-Typen" }, "debug-settings": { "label": "Debug-Konfiguration", @@ -1020,12 +1098,25 @@ "selected-fields": "{ count, plural, =1 {1 berechnetes Feld} other {# berechnete Felder} } ausgewählt", "type": { "simple": "Einfach", - "script": "Skript" + "simple-hint": "Einfache arithmetische Berechnung basierend auf Eingabeargumenten.", + "script": "Skript", + "script-hint": "Berechnung über definierte Argumente mithilfe eines TBEL-Skripts.", + "geofencing": "Geofencing", + "geofencing-hint": "Auswertung der GPS-Position der Entität und von Übergängen gegenüber konfigurierten Geofencing-Zonengruppen.", + "propagation": "Weitergabe", + "propagation-hint": "Weitergabe von Daten an übergeordnete oder untergeordnete Entitäten basierend auf Beziehungsrichtung und -typ.", + "related-entities-aggregation": "Aggregation verwandter Entitäten", + "related-entities-aggregation-hint": "Aggregation der neuesten Daten aus verwandten Entitäten.", + "time-series-data-aggregation": "Aggregation von Zeitreihendaten", + "time-series-data-aggregation-hint": "Aggregation historischer Daten aus einer aktuellen Entität." }, + "preview": "Vorschau", "arguments": "Argumente", "decimals-by-default": "Standard-Dezimalstellen", "debugging": "Berechnetes Feld Debugging", + "calculated-field-details": "Details zum berechneten Feld", "argument-name": "Argumentname", + "name": "Name", "datasource": "Datenquelle", "add-argument": "Argument hinzufügen", "test-script-function": "Skriptfunktion testen", @@ -1037,8 +1128,9 @@ "argument-asset": "Asset", "argument-customer": "Kunde", "argument-tenant": "Aktueller Mandant", + "argument-owner": "Aktueller Eigentümer", + "argument-relation-query": "Verwandte Entitäten", "argument-type": "Argumenttyp", - "see-debug-events": "Debug-Ereignisse anzeigen", "attribute": "Attribut", "copy-argument-name": "Argumentnamen kopieren", "timeseries-key": "Zeitreihen-Schlüssel", @@ -1051,12 +1143,14 @@ "shared-attributes": "Geteilte Attribute", "attribute-key": "Attribut-Schlüssel", "default-value": "Standardwert", + "default-value-required": "Standardwert ist erforderlich.", "limit": "Maximale Werte", "time-window": "Zeitfenster", "customer-name": "Kundenname", "asset-name": "Asset-Name", "timeseries": "Zeitreihe", "output": "Ausgabe", + "output-hint": "Legt fest, wie die Ausgabe verarbeitet wird.", "create": "Neues berechnetes Feld erstellen", "file": "Berechnetes Feld-Datei", "invalid-file-error": "Ungültiges Dateiformat. Bitte stellen Sie sicher, dass die Datei eine gültige JSON-Datei ist.", @@ -1070,9 +1164,175 @@ "delete-multiple-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten berechneten Felder entfernt und alle zugehörigen Daten unwiederbringlich gelöscht.", "test-with-this-message": "Mit dieser Nachricht testen", "use-latest-timestamp": "Letzten Zeitstempel verwenden", + "entity-coordinates": "Entitätskoordinaten", + "latitude-time-series-key": "Breitengrad-Zeitreihenschlüssel", + "latitude-time-series-key-required": "Breitengrad-Zeitreihenschlüssel ist erforderlich.", + "longitude-time-series-key": "Längengrad-Zeitreihenschlüssel", + "longitude-time-series-key-required": "Längengrad-Zeitreihenschlüssel ist erforderlich.", + "geofencing-zone-groups": "Geofencing-Zonengruppen", + "geofencing-zone-groups-settings": "Einstellungen der Geofencing-Zonengruppe", + "target-zone": "Zielzone", + "perimeter-key": "Umfangsschlüssel", + "report-strategy": "Meldestrategie", + "no-zone-configured": "Mindestens eine Zone ist erforderlich.", + "no-zone-configured-required": "Mindestens eine Zonengruppe muss konfiguriert werden.", + "add-zone-group": "Zonengruppe hinzufügen", + "report-transition-event-only": "Nur Übergangsereignisse", + "report-presence-status-only": "Nur Anwesenheitsstatus", + "report-transition-event-and-presence": "Anwesenheitsstatus und Übergangsereignisse", + "perimeter-attribute-key": "Umfangsattributschlüssel", + "perimeter-attribute-key-required": "Umfangsattributschlüssel ist erforderlich.", + "perimeter-attribute-key-pattern": "Umfangsattributschlüssel ist ungültig.", + "entity-zone-relationship": "Pfad von der Entität zu den Zonen", + "direction": "Beziehungsrichtung", + "direction-from": "Von der Entität zur Zone", + "direction-to": "Von der Zone zur Entität", + "relation-type": "Beziehungstyp", + "create-relation-with-matched-zones": "Beziehungen für die Quellentität mit übereinstimmenden Zonen erstellen", + "relation-level": "Beziehungsebene", + "fetch-last-available-level": "Nur letzte verfügbare Ebene abrufen", + "zone-group-refresh-interval": "Aktualisierungsintervall der Zonengruppen", + "copy-zone-group-name": "Name der Zonengruppe kopieren", + "open-details-page": "Detailseite der Entität öffnen", + "level": "Ebene", + "direction-level": "Richtung", + "direction-up": "Nach oben", + "direction-up-parent": "Nach oben zum übergeordneten Element", + "direction-down": "Nach unten", + "direction-down-child": "Nach unten zum untergeordneten Element", + "add-level": "Ebene hinzufügen", + "delete-level": "Ebene löschen", + "no-level": "Keine Ebene konfiguriert", + "levels-required": "Mindestens eine Ebene muss konfiguriert werden.", + "max-allowed-levels-error": "Beziehungsebene überschreitet das maximal Zulässige.", + "propagation-path-related-entities": "Weitergabepfad zu verwandten Entitäten", + "propagate-type": { + "arguments-only": "Nur Argumente", + "expression-result": "Berechnungsergebnis" + }, + "script": "Skript", + "data-propagate": "Weiterzugebende Daten", + "output-key": "Ausgabeschlüssel", + "copy-output-key": "Ausgabeschlüssel kopieren", + "aggregation-path-related-entities": "Aggregationspfad zu verwandten Entitäten", + "deduplication-interval": "Deduplizierungsintervall", + "deduplication-interval-min": "Deduplizierungsintervall muss mindestens {{ sec }} Sekunden betragen.", + "deduplication-interval-hint": "Mindestzeit zwischen Telemetrieaggregationen.", + "deduplication-interval-required": "Deduplizierungsintervall ist erforderlich.", + "calculated-field-filter-title": "Filter für berechnete Felder", + "filter-title": "Filter", + "calculated-field-types": "Typen berechneter Felder", + "events": "Ereignisse", + "any-type": "Beliebiger Typ", + "metrics": { + "metrics": "Metriken", + "metrics-empty": "Mindestens eine Metrik muss konfiguriert werden.", + "metric-name": "Metrikname", + "metric-name-required": "Metrikname ist erforderlich.", + "metric-name-pattern": "Metrikname ist ungültig.", + "metric-name-duplicate": "Eine Metrik mit diesem Namen existiert bereits.", + "metric-name-max-length": "Der Metrikname sollte kürzer als 256 Zeichen sein.", + "metric-name-forbidden": "Der Metrikname ist reserviert und kann nicht verwendet werden.", + "copy-metric-name": "Metrikname kopieren", + "argument-name": "Argumentname", + "aggregation": "Aggregation", + "aggregation-type": { + "avg": "Durchschnitt", + "min": "Minimum", + "max": "Maximum", + "sum": "Summe", + "count": "Anzahl", + "count-unique": "Eindeutige Anzahl" + }, + "filtered": "Gefiltert", + "value-source": "Wertquelle", + "value-source-hint": "Legt fest, wie der Wert für die Aggregation ermittelt wird.", + "value-source-type": { + "key": "Schlüssel", + "function": "Funktion" + }, + "no-metrics-configured": "Mindestens eine Metrik ist erforderlich.", + "add-metric": "Metrik hinzufügen", + "max-metrics": "Maximale Anzahl an Metriken erreicht.", + "metric-settings": "Metrikeinstellungen", + "filter": "Filter", + "filter-hint": "Aktiviert die Filterung von Entitäten während der Aggregation. Die Filterfunktion muss einen booleschen Wert zurückgeben und kann alle konfigurierten Argumente verwenden." + }, + "output-strategy": { + "strategy": "Strategie", + "process-right-away": "Sofort verarbeiten", + "process-rule-chains": "Über Regelketten verarbeiten", + "save-time-series": "In Zeitreihe speichern", + "save-database": "In Datenbank speichern", + "save-latest-values": "In neueste Werte speichern", + "send-web-sockets": "An WebSockets senden", + "save-calculated-fields": "An Berechnete Felder senden", + "update-attribute-only-on-value-change": "Attribut nur bei Wertänderung aktualisieren", + "send-attributes-updated-notification": "Benachrichtigung über aktualisierte Attribute senden", + "ttl": "Benutzerdefinierte TTL", + "ttl-required": "TTL ist erforderlich", + "ttl-min": "Als minimales TTL ist nur 0 zulässig", + "processing-parameters": "Verarbeitungsparameter", + "hint": { + "strategy": "Steuert, ob das Ergebnis sofort verarbeitet oder zur zusätzlichen Verarbeitung an eine Regelkette gesendet wird.", + "processing-options": "Verarbeitungsoptionen", + "update-attribute-only-on-value-change": "Aktualisiert das Attribut bei jeder eingehenden Nachricht, unabhängig davon, ob sich der Wert geändert hat. Dies erhöht die API-Nutzung und verringert die Performance.", + "update-attribute-only-on-value-change-enabled": "Aktualisiert das Attribut nur, wenn sich der Wert ändert. Wenn der Wert unverändert ist, werden Zeitstempel nicht aktualisiert und Benachrichtigungen nicht gesendet.", + "send-attributes-updated-notification": "Sendet ein Ereignis „Attribute aktualisiert“ an die Standard-Regelkette.", + "save-time-series": "Speichert Zeitreihendaten in der Tabelle ts_kv in der Datenbank.", + "save-database": "Speichert Attributdaten in der Datenbank.", + "save-latest-values": "Aktualisiert Zeitreihendaten in der Tabelle ts_kv_latest in der Datenbank, wenn der neue Wert aktueller ist.", + "send-web-sockets-attribute": "Benachrichtigt WebSocket-Abonnements über Aktualisierungen der Attributdaten.", + "send-web-sockets-time-series": "Benachrichtigt WebSocket-Abonnements über Aktualisierungen der Zeitreihendaten.", + "save-calculated-fields-attribute": "Benachrichtigt Berechnete Felder über Aktualisierungen der Attributdaten.", + "save-calculated-fields-time-series": "Benachrichtigt Berechnete Felder über Aktualisierungen der Zeitreihendaten.", + "ttl": "Definiert die Aufbewahrungsdauer für Zeitreihendaten. Wenn deaktiviert, wird die TTL des Mandantenprofils verwendet." + } + }, + "aggregate-interval-type": "Aggregationsintervalltyp", + "aggregate-interval-value": "Aggregationsintervallwert", + "aggregate-interval-value-required": "Aggregationsintervallwert ist erforderlich.", + "aggregate-interval-value-min": "Aggregationsintervallwert muss mindestens { sec, plural, =0 {0 Sekunde} =1 {1 Sekunde} other {# Sekunden} } betragen.", + "aggregate-interval-value-step-multiple-of": "Aggregationsintervallwert muss ein Teiler oder ein Vielfaches von 1 Tag sein.", + "aggregate-period": { + "hour": "Stunde", + "day": "Tag", + "week": "Woche (Mo - So)", + "week-sun-sat": "Woche (So - Sa)", + "month": "Monat", + "quarter": "Quartal", + "year": "Jahr", + "custom": "Benutzerdefiniert" + }, + "aggregate-period-hint-offset": "Ihr Aggregationsintervall wird sein: {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "Ihr Aggregationsintervall wird sein: {{ interval }} und so weiter.", + "entity-aggregation": { + "argument-hint": "Daten werden von der aktuellen Entität abgerufen.", + "argument-title-hint": "Legt die für die Aggregation verwendeten Eingabeargumente fest.", + "argument-setting-hint": "Neueste Telemetrie ist der einzige verfügbare Argumenttyp für dieses berechnete Feld.", + "aggregation-interval": "Aggregationsintervall", + "aggregation-interval-hint": "Legt fest, wie oft die Aggregation durchgeführt wird. Beispiel: Alle 1 Stunde werden Daten um 00:00, 01:00, 02:00 usw. aggregiert. Aggregationsergebnisse werden mit dem Zeitstempel gespeichert, der dem Beginn des Aggregationsintervalls entspricht.", + "apply-offset": "Offset auf Aggregationsintervall anwenden", + "apply-offset-hint": "Legt fest, um wie viel der Beginn jeder Aggregationsperiode verschoben wird (z. B. +10 Minuten – 00:10, 01:10).", + "offset-value": "Offset-Wert", + "offset-value-required": "Offset-Wert ist erforderlich.", + "offset-value-min": "Offset-Wert muss eine positive ganze Zahl sein.", + "offset-value-max": "Offset-Wert sollte kleiner als der Aggregationsintervallwert sein.", + "wait-delay": "Await-Timeout für verzögerte Telemetrie anwenden", + "wait-delay-hint": "Legt fest, wie lange nach dem Ende des Intervalls auf verzögerte Telemetrie gewartet wird. Wenn solche Telemetrie eintrifft, wird das Ergebnis für dieses Intervall neu berechnet.", + "duration": "Dauer", + "duration-required": "Dauer ist erforderlich.", + "duration-min": "Dauer sollte mindestens 1 Minute betragen.", + "duration-hint": "Wie lange nach dem Ende des Intervalls auf verzögerte Daten gewartet wird.", + "produce-intermediate-result": "Zwischenergebnis erzeugen", + "produce-intermediate-result-hint": "Berechnet Metriken während des aktuellen Intervalls, um ein Zwischenergebnis zu erzeugen. Aktualisierungen erfolgen nicht häufiger als einmal alle {{ time }}." + }, "hint": { - "arguments-simple-with-rolling": "Einfacher Feldtyp darf keine Schlüssel mit Zeitreihen-Rollup-Typ enthalten.", - "arguments-empty": "Argumente dürfen nicht leer sein.", + "arguments-simple-with-rolling": "Ein berechnetes Feld vom Typ „Einfach“ sollte keine Schlüssel mit dem Zeitreihen-Rolling-Typ enthalten.", + "arguments-propagate-arguments-with-rolling": "Der Typ „Zeitreihen-Rolling“ ist nicht kompatibel mit der Weitergabe „Nur Argumente“.", + "arguments-propagate-argument-entity-type": "Der Entitätstyp ist nicht kompatibel mit der Weitergabe „Nur Argumente“.", + "arguments-propagate-argument-must-current-entity": "Mindestens ein Argument muss mit dem Quellentitätstyp „Aktuelle Entität“ konfiguriert werden.", + "arguments-empty": "Mindestens ein Argument sollte angegeben werden.", "expression-required": "Ausdruck ist erforderlich.", "expression-invalid": "Ausdruck ist ungültig", "expression-max-length": "Ausdruck sollte weniger als 255 Zeichen enthalten.", @@ -1081,12 +1341,218 @@ "argument-name-duplicate": "Ein Argument mit diesem Namen existiert bereits.", "argument-name-max-length": "Argumentname sollte weniger als 256 Zeichen enthalten.", "argument-name-forbidden": "Argumentname ist reserviert und darf nicht verwendet werden.", + "output-key-required": "Ausgabeschlüssel ist erforderlich.", + "output-key-pattern": "Ausgabeschlüssel ist ungültig.", + "output-key-duplicate": "Ein Schlüssel mit diesem Namen existiert bereits.", + "output-key-max-length": "Ausgabeschlüssel sollte kürzer als 256 Zeichen sein.", + "output-key-forbidden": "Ausgabeschlüssel ist reserviert und kann nicht verwendet werden.", + "entity-type-required": "Entitätstyp ist erforderlich", + "name-required": "Name ist erforderlich.", + "name-pattern": "Name ist ungültig.", + "name-duplicate": "Ein Name mit diesem Namen existiert bereits.", + "name-max-length": "Name sollte kürzer als 256 Zeichen sein.", + "name-forbidden": "Name ist reserviert und kann nicht verwendet werden.", "argument-type-required": "Argumenttyp ist erforderlich.", "max-args": "Maximale Anzahl an Argumenten erreicht.", "decimals-range": "Standard-Dezimalstellen sollten eine Zahl zwischen 0 und 15 sein.", "expression": "Standardausdruck demonstriert, wie eine Temperatur von Fahrenheit in Celsius umgewandelt wird.", "arguments-entity-not-found": "Zielentität des Arguments nicht gefunden.", - "use-latest-timestamp": "Wenn aktiviert, wird der berechnete Wert mit dem neuesten Zeitstempel aus der Telemetrie der Argumente gespeichert, anstatt mit der Serverzeit." + "use-latest-timestamp": "Wenn aktiviert, wird der berechnete Wert mit dem neuesten Zeitstempel aus der Telemetrie der Argumente gespeichert, anstatt mit der Serverzeit.", + "entity-coordinates": "Geben Sie die Zeitreihenschlüssel an, die die GPS-Koordinaten der Entität (Breitengrad und Längengrad) bereitstellen.", + "geofencing-zone-groups": "Definieren Sie eine oder mehrere Geofencing-Zonengruppen zur Prüfung (z. B. 'allowedZones', 'restrictedZones'). Jede Gruppe muss einen eindeutigen Namen haben, der als Präfix für Telemetrieschlüssel der Ausgabe des berechneten Feldes verwendet wird.", + "perimeter-attribute-key": "Legen Sie den Attributschlüssel fest, der die Definition des Geofencing-Zonenumfangs enthält. Der Umfang wird immer aus serverseitigen Attributen der Zonenentität übernommen.", + "report-strategy": "Der Anwesenheitsstatus meldet, ob sich die Entität aktuell INNERHALB oder AUSSERHALB der Zonengruppe befindet. Übergangsereignisse melden, wann die Entität die Zonengruppe BETRETEN oder VERLASSEN hat.", + "create-relation-with-matched-zones": "Erstellt und pflegt automatisch Beziehungen zwischen der Entität und den Zonen, in denen sie sich aktuell befindet. Beziehungen werden entfernt, wenn die Entität eine Zone verlässt, und erstellt, wenn sie eine neue betritt.", + "relation-type-required": "Beziehungstyp ist erforderlich.", + "relation-level-required": "Beziehungsebene ist erforderlich.", + "relation-level-min": "Der minimale Wert der Beziehungsebene ist 1.", + "relation-level-max": "Der maximale Wert der Beziehungsebene ist {{max}}.", + "geofencing-empty": "Mindestens eine Zonengruppe muss konfiguriert werden.", + "geofencing-entity-not-found": "Geofencing-Zielentität nicht gefunden.", + "max-geofencing-zone": "Maximale Anzahl an Geofencing-Zonen erreicht.", + "zone-group-refresh-interval": "Legt fest, wie oft Zonengruppen, die über verwandte Entitäten konfiguriert sind, aktualisiert werden.", + "zone-group-refresh-interval-required": "Aktualisierungsintervall der Zonengruppen ist erforderlich.", + "zone-group-refresh-interval-min": "Aktualisierungsintervall der Zonengruppe sollte mindestens {{ min }} Sekunden betragen.", + "propagation-path-related-entities": "Definiert einen direkten, einstufigen Pfad zu einer verwandten Entität basierend auf der ausgewählten Richtung und dem Beziehungstyp. Es werden nur Beziehungen zwischen Geräte-, Asset-, Kunden- und Mandantenentitäten unterstützt. Maximale Anzahl der über den Beziehungspfad aufgelösten Entitäten ist {{ max }}.", + "data-propagate": "Legt die Daten fest, die aus den unten konfigurierten Argumenten weitergegeben werden. 'Nur Argumente' verwendet die abgerufenen Daten direkt, während 'Berechnungsergebnis' aus diesen Daten einen neuen Wert berechnet.", + "aggregation-path-related-entities": "Definiert einen einstufigen Aggregationspfad über direkte Beziehungen zu übergeordneten oder untergeordneten Entitäten, basierend auf Richtung und Beziehungstyp. Es werden nur Beziehungen zwischen Geräte-, Asset-, Kunden- und Mandantenentitäten unterstützt. Maximale Anzahl der über den Beziehungspfad aufgelösten Entitäten ist {{ max }}.", + "arguments-aggregation": "Legt die Eingabeargumente fest, die für Filterung und Aggregation verwendet werden.", + "setting-arguments-aggregation": "Daten werden von verwandten Entitäten abgerufen, die im Aggregationspfad konfiguriert sind.", + "metrics": "Legt Metriken fest, die basierend auf konfigurierten Argumenten aggregiert werden.", + "entity-aggregation-metrics": "Legt Metriken fest, die basierend auf konfigurierten Argumenten über die angegebenen Zeitintervalle aggregiert werden.", + "import-invalid-calculated-field-type": "Berechnetes Feld kann nicht importiert werden: Ungültige Struktur des berechneten Feldes.", + "simple-expression-title": "Arithmetischer Ausdruck, der definiert, wie der berechnete Wert ermittelt wird.", + "script-title": "TBEL-Skript, das die Berechnungslogik und Ausgabewerte definiert.", + "simple-arguments": "Arithmetischer Ausdruck, der definiert, wie der berechnete Wert ermittelt wird.", + "script-arguments": "Legt die Eingabeargumente fest, die dem Skript zur Verfügung stehen." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Alarmregeln", + "alarm-rule": "Alarmregel", + "alarm-rules": "Alarmregeln", + "alarm-rules-old": "Alt", + "alarm-rules-actual": "Aktuell", + "severities": "Schweregrade", + "cleared": "Löschbedingung", + "delete-title": "Möchten Sie die Alarmregel '{{title}}' wirklich löschen?", + "delete-text": "Achtung, nach der Bestätigung können die Alarmregel und alle zugehörigen Daten nicht wiederhergestellt werden.", + "delete-multiple-title": "Möchten Sie { count, plural, =1 {1 Alarmregel} other {# Alarmregeln} } wirklich löschen?", + "delete-multiple-text": "Achtung, nach der Bestätigung werden alle ausgewählten Alarmregeln entfernt und alle zugehörigen Daten können nicht wiederhergestellt werden.", + "create": "Neue Alarmregel erstellen", + "add": "Alarmregel hinzufügen", + "copy": "Alarmregelkonfiguration kopieren", + "details": "Details zur Alarmregel", + "no-found": "Keine Alarmregeln gefunden", + "list": "{ count, plural, =1 {Eine Alarmregel} other {Liste von # Alarmregeln} }", + "selected-fields": "{ count, plural, =1 {1 Alarmregel} other {# Alarmregeln} } ausgewählt", + "import": "Alarmregel importieren", + "file": "Alarmregeldatei", + "export": "Alarmregel exportieren", + "export-failed-error": "Alarmregel kann nicht exportiert werden: {{error}}", + "entity-type": "Entitätstyp", + "entity-type-required": "Entitätstyp ist erforderlich.", + "alarm-type": "Alarmtyp", + "alarm-type-hint": "Eindeutiger Bezeichner (z. B. HighTempAlarm) innerhalb des Gültigkeitsbereichs des Alarmerstellers (Gerät, Asset usw.), um Konflikte zu vermeiden.", + "alarm-type-required": "Alarmtyp ist erforderlich.", + "alarm-type-pattern": "Alarmtyp ist ungültig.", + "alarm-type-max-length": "Alarmtyp sollte kürzer als 256 Zeichen sein.", + "clear-alarm": "Alarm löschen", + "value-argument": "Argument", + "value-argument-required": "Argument ist erforderlich.", + "static-settings": "Statische Einstellungen", + "configuration": "Konfiguration", + "static-schedule": "Statisch", + "dynamic-schedule": "Dynamisch", + "operation-and": "UND", + "operation-or": "ODER", + "condition-during": "Während {{during}}", + "condition-during-dynamic": "Während \"{{ attribute }}\"", + "condition-repeat-times": "Wiederholt sich { count, plural, =1 {1 Mal} other {# Mal} }", + "condition-repeat-times-dynamic": "Wiederholt sich \"{{ attribute }}\"-mal", + "filter-preview": "Filtervorschau", + "condition-settings": "Bedingungseinstellungen", + "static": "Statisch", + "dynamic": "Dynamisch", + "argument-filters": "Argumentfilter", + "argument-name": "Argumentname", + "value-type": "Werttyp", + "general": "Allgemein", + "filters": "Filter", + "date-time-hint": "Das Argument muss in Epoch-Millisekunden angegeben werden. Beispiel: 1698839340000 entspricht 2023-11-01 12:49:00 UTC.", + "operation": "Operation", + "value-source": "Wertquelle", + "value": "Wert", + "ignore-case": "Groß-/Kleinschreibung ignorieren", + "condition": "Bedingung", + "script": "Skript", + "add-filter": "Argumentfilter hinzufügen", + "edit-filter": "Argumentfilter", + "remove-filter": "Argumentfilter entfernen", + "no-filter": "Mindestens ein Filter ist erforderlich.", + "conditions": { + "simple": "Einfach", + "duration": "Dauer", + "repeating": "Wiederholend" + }, + "schedule-title": "Zeitplan", + "edit-schedule": "Alarmzeitplan bearbeiten", + "schedule-type": "Zeitplanertyp", + "schedule-type-required": "Zeitplanertyp ist erforderlich.", + "schedule": { + "any-time": "Immer aktiv", + "specific-time": "Zu einer bestimmten Zeit aktiv", + "custom": "Benutzerdefiniert" + }, + "schedule-day": { + "monday": "Montag", + "tuesday": "Dienstag", + "wednesday": "Mittwoch", + "thursday": "Donnerstag", + "friday": "Freitag", + "saturday": "Samstag", + "sunday": "Sonntag" + }, + "schedule-days": "Tage", + "schedule-time": "Zeit", + "schedule-time-from": "Von", + "schedule-time-to": "Bis", + "schedule-days-of-week-required": "Mindestens ein Wochentag muss ausgewählt werden.", + "tbel": "TBEL", + "expression-type": { + "simple": "Einfach", + "script": "Skript" + }, + "operation-type": { + "and": "Und", + "or": "Oder" + }, + "filter-predicate-type": { + "string": "String", + "numeric": "Numerisch", + "boolean": "Boolesch", + "complex": "Komplex" + }, + "alarm-rule-additional-info": "Zusätzliche Informationen", + "edit-alarm-rule-additional-info": "Zusätzliche Informationen bearbeiten", + "alarm-rule-additional-info-placeholder": "Bitte geben Sie hier Ihre Kommentare und Anpassungen ein, um sie in den Alarmdetails unter „Zusätzliche Informationen“ anzuzeigen", + "alarm-rule-additional-info-hint": "Hinweis: Verwenden Sie ${Argumentname}, um Werte der Argumente zu ersetzen, die in der Alarmregelbedingung verwendet werden.", + "alarm-rule-additional-info-icon-hint": "Verwenden Sie den Argumentnamen, um Werte der Argumente zu ersetzen, die in der Alarmregelbedingung verwendet werden.", + "alarm-rule-mobile-dashboard": "Mobiles Dashboard", + "alarm-rule-mobile-dashboard-hint": "Wird von der mobilen Anwendung als Dashboard für Alarmdetails verwendet.", + "alarm-rule-no-mobile-dashboard": "Kein Dashboard ausgewählt", + "alarm-rule-condition": "Alarmregelbedingung", + "enter-alarm-rule-condition-prompt": "Bedingung hinzufügen", + "enter-alarm-rule-clear-condition-prompt": "Löschbedingung hinzufügen", + "edit-alarm-rule-condition": "Alarmbedingung", + "condition-type": "Bedingungstyp", + "condition-type-hint": "Die Optionen „Dauer“ und „Wiederholend“ sind nicht verfügbar, wenn im Filter die Operation „Missing for“ verwendet wird.", + "select-alarm-severity": "Alarmschweregrad auswählen", + "add-create-alarm-rule-prompt": "Mindestens eine Auslösebedingung ist erforderlich.", + "add-create-alarm-rule": "Auslösebedingung hinzufügen", + "add-clear-alarm-rule": "Löschbedingung hinzufügen", + "condition-duration": "Bedingungsdauer", + "condition-duration-value": "Dauerwert", + "condition-duration-time-unit": "Zeiteinheit", + "condition-duration-value-range": "Der Dauerwert muss im Bereich von 1 bis 2147483647 liegen.", + "condition-duration-value-pattern": "Der Dauerwert muss eine ganze Zahl sein.", + "condition-duration-value-required": "Dauerwert ist erforderlich.", + "condition-duration-time-unit-required": "Zeiteinheit ist erforderlich.", + "condition-repeating-value": "Anzahl der Ereignisse", + "condition-repeating-value-hint": "Die Aktualisierung eines beliebigen Alarmregelarguments wird als Ereignis gezählt", + "condition-repeating-value-range": "Die Anzahl der Ereignisse muss im Bereich von 1 bis 2147483647 liegen.", + "condition-repeating-value-pattern": "Die Anzahl der Ereignisse muss eine ganze Zahl sein.", + "condition-repeating-value-required": "Anzahl der Ereignisse ist erforderlich.", + "create-conditions": "Auslösebedingungen", + "clear-condition": "Löschbedingung", + "no-clear-alarm-rule": "Keine Löschbedingung konfiguriert.", + "advanced-settings": "Erweiterte Einstellungen", + "propagate-alarm": "Alarm an verwandte Entitäten weitergeben", + "alarm-rule-relation-types-list": "Beziehungstypen", + "alarm-rule-relation-types-list-hint": "Legt Beziehungstypen fest, um die verwandten Entitäten zu filtern. Wenn nicht festgelegt, wird der Alarm an alle verwandten Entitäten weitergegeben.", + "propagate-alarm-to-owner": "Alarm an den Entitätseigentümer weitergeben (Kunde oder Mandant)", + "propagate-alarm-to-tenant": "Alarm an den Mandanten weitergeben", + "alarm-rule-filter-title": "Alarmregel-Filter", + "filter-title": "Filter", + "debugging": "Alarmregel-Debugging", + "any-type": "Beliebiger Typ", + "enter-alarm-rule-type": "Alarmtyp eingeben", + "no-alarm-rule-types-matching": "Keine Alarmtypen gefunden, die zu '{{entitySubtype}}' passen.", + "alarm-rule-type-list-empty": "Keine Alarmtypen ausgewählt.", + "alarm-rule-type-list": "Liste der Alarmtypen", + "alarm-rule-entity-list": "Entitätsliste", + "missing-for": "fehlt seit", + "time-unit": "Einheit", + "mode": "Modus", + "type": "Typ", + "value-required": "Wert ist erforderlich.", + "min-value": "Wert muss 1 oder größer sein.", + "argument-in-use": "Argument wird als allgemeines Argument verwendet.", + "import-invalid-alarm-rule-type": "Alarmregel kann nicht importiert werden: Ungültige Alarmregelstruktur.", + "no-filter-preview": "Kein Filter angegeben", + "filter-operation": { + "and": "Und", + "or": "Oder" } }, "ai-models": { @@ -1193,6 +1659,7 @@ "contact": { "country": "Land", "country-required": "Land ist erforderlich.", + "country-object-required": "Bitte wählen Sie ein gültiges Land aus der Liste aus.", "city": "Stadt", "state": "Bundesland / Provinz", "postal-code": "PLZ / Postleitzahl", @@ -1229,6 +1696,8 @@ "documentation": "Dokumentation", "time-left": "{{time}} verbleibend", "output": "Ausgabe", + "sort-asc": "Aufsteigend", + "sort-desc": "Absteigend", "suffix": { "s": "s", "ms": "ms" @@ -1365,6 +1834,8 @@ "mobile-order": "Reihenfolge des Dashboards in mobiler App", "mobile-hide": "Dashboard in mobiler App ausblenden", "update-image": "Dashboard-Bild aktualisieren", + "update-new-version": "Neue Version hochladen", + "upload-file-to-update": "Datei zum Aktualisieren hochladen", "take-screenshot": "Screenshot erstellen", "select-widget-title": "Widget auswählen", "select-widget-value": "{{title}}: Widget auswählen", @@ -1733,6 +2204,8 @@ "bootstrap-tab": "Bootstrap-Client", "bootstrap-server": "Bootstrap-Server", "lwm2m-server": "LwM2M-Server", + "client-reboot": "Registrierungsupdate-Trigger", + "bootstrap-reboot": "Bootstrap-Anforderungs-Trigger", "client-publicKey-or-id": "Öffentlicher Client-Schlüssel oder ID", "client-publicKey-or-id-required": "Öffentlicher Client-Schlüssel oder ID ist erforderlich.", "client-publicKey-or-id-tooltip-psk": "Die PSK-Kennung ist eine beliebige PSK-Kennung bis zu 128 Byte, wie im Standard [RFC7925] beschrieben.\nDie PSK-Kennung MUSS zuerst in eine Zeichenkette umgewandelt und dann mittels UTF-8 in Oktetten codiert werden.", @@ -1780,7 +2253,6 @@ "unable-delete-device-alias-text": "Gerätealias '{{deviceAlias}}' kann nicht gelöscht werden, da er in folgenden Widgets verwendet wird:
    {{widgetsList}}", "is-gateway": "Ist Gateway", "overwrite-activity-time": "Aktivitätszeit für verbundenes Gerät überschreiben", - "device-filter": "Gerätefilter", "device-filter-title": "Gerätefilter", "filter-title": "Filter", "device-state": "Gerätestatus", @@ -2234,7 +2706,8 @@ "short-id-required": "Kurze Server-ID ist erforderlich.", "short-id-range": "Kurze Server-ID sollte im Bereich von {{ min }} bis {{ max }} liegen.", "short-id-pattern": "Kurze Server-ID muss eine positive Ganzzahl sein.", - "lifetime": "Registrierungslebensdauer des Clients", + "short-id-pattern-bs": "Kurze Server-ID darf nur null sein", + "lifetime": "Lebensdauer der Client-Registrierung", "lifetime-required": "Registrierungslebensdauer des Clients ist erforderlich.", "lifetime-pattern": "Registrierungslebensdauer muss eine positive Ganzzahl sein.", "default-min-period": "Minimale Periode zwischen zwei Benachrichtigungen (s)", @@ -2328,7 +2801,9 @@ "composite-all-description": "Alle Ressourcen werden mit einer einzigen zusammengefassten Beobachtungsanfrage überwacht (effizienter, weniger flexibel)", "composite-by-object": "Nach Objekten zusammengefasst", "composite-by-object-description": "Ressourcen werden nach Objekttyp gruppiert und mit separaten zusammengefassten Beobachtungsanfragen überwacht (ausgewogener Ansatz)" - } + }, + "init-attr-tel-as-obs-strategy": "Attribute und Telemetrie mithilfe der Observe-Strategie initialisieren", + "init-attr-tel-as-obs-strategy-hint": "Wenn false – werden Attribute und Telemetrie initialisiert, indem ihre Werte einzeln gelesen werden.\\nWenn true – werden Attribute und Telemetrie initialisiert, indem ihre Werte über die Observe-Strategie abonniert werden." }, "snmp": { "add-communication-config": "Kommunikationskonfiguration hinzufügen", @@ -2644,6 +3119,8 @@ "type-rulenodes": "Regelknoten", "list-of-rulenodes": "{ count, plural, =1 {Ein Regelknoten} other {Liste von # Regelknoten} }", "rulenode-name-starts-with": "Regelknoten, deren Name mit '{{prefix}}' beginnt", + "type-api-key": "API-Schlüssel", + "type-api-keys": "API-Schlüssel", "type-current-customer": "Aktueller Kunde", "type-current-tenant": "Aktueller Tenant", "type-current-user": "Aktueller Benutzer", @@ -2665,6 +3142,7 @@ "details": "Entitätsdetails", "no-entities-prompt": "Keine Entitäten gefunden", "no-data": "Keine Daten zur Anzeige vorhanden", + "show-all-columns": "Alle anzeigen", "columns-to-display": "Anzuzeigende Spalten", "type-api-usage-state": "API-Nutzungsstatus", "type-edge": "Edge", @@ -2710,7 +3188,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {Eine mobile Anwendung} other {Liste von # mobilen Anwendungen} }", "type-mobile-app-bundle": "Mobile-Bundle", "type-mobile-app-bundles": "Mobile-Bundles", - "list-of-mobile-app-bundles": "{ count, plural, =1 {Ein Mobile-Bundle} other {Liste von # Mobile-Bundles} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {Ein Mobile-Bundle} other {Liste von # Mobile-Bundles} }", + "limit-reached": "Limit erreicht", + "limit-reached-text": "Sie haben das Limit von {{ entities }} erreicht. Um weitere hinzuzufügen, bitten Sie bitte Ihren Systemadministrator, Ihr {{ entity }}-Limit zu erhöhen.", + "request-limit-increase": "Limiterhöhung anfordern", + "request-sysadmin-text": "Sind Sie der Systemadministrator?", + "login-here": "Hier anmelden", + "to-increase-limit": "um das Limit zu erhöhen.", + "increase-limit-request-sent-title": "Wir haben eine automatische Anfrage an Ihren Systemadministrator gesendet, um das Limit zu erhöhen", + "increase-limit-request-sent-text": "Bitte geben Sie ihm etwas Zeit, die Anfrage zu prüfen und die Einstellungen zu aktualisieren. Möglicherweise müssen Sie diese Seite aktualisieren, um die Änderungen zu sehen." }, "entity-field": { "created-time": "Erstellungszeit", @@ -3060,7 +3546,7 @@ "edit": "Filter bearbeiten", "name": "Filtername", "name-required": "Filtername ist erforderlich.", - "duplicate-filter": "Ein Filter mit demselben Namen ist bereits vorhanden.", + "duplicate-filter": "Ein Filter mit demselben Namen existiert bereits.", "filters": "Filter", "unable-delete-filter-title": "Filter kann nicht gelöscht werden", "unable-delete-filter-text": "Filter '{{filter}}' kann nicht gelöscht werden, da er von folgendem/n Widget(s) verwendet wird:
    {{widgetsList}}", @@ -3798,7 +4284,8 @@ "password-link-sent-message": "Link zum Zurücksetzen wurde gesendet", "email": "E-Mail", "invalid-email-format": "Ungültiges E-Mail-Format.", - "login-with": "Anmelden mit {{name}}", + "sign-in-with": "Anmelden mit {{name}}", + "sign-in-to-your-account": "Bei Ihrem Konto anmelden", "or": "oder", "error": "Anmeldefehler", "verify-your-identity": "Identität bestätigen", @@ -3817,7 +4304,51 @@ "activation-link-expired": "Aktivierungslink ist abgelaufen", "activation-link-expired-message": "Der Link zur Aktivierung Ihres Profils ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten.", "reset-password-link-expired": "Link zum Zurücksetzen des Passworts ist abgelaufen", - "reset-password-link-expired-message": "Der Link zum Zurücksetzen Ihres Passworts ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten." + "reset-password-link-expired-message": "Der Link zum Zurücksetzen Ihres Passworts ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten.", + "two-fa": "Zwei-Faktor-Authentifizierung", + "two-fa-required": "Zwei-Faktor-Authentifizierung ist erforderlich", + "set-up-verification-method": "Richten Sie eine Verifizierungsmethode ein, um fortzufahren", + "set-up-verification-method-login": "Richten Sie eine Verifizierungsmethode ein oder melden Sie sich an", + "enable-authenticator-app": "Authenticator-App aktivieren", + "enable-authenticator-app-description": "Bitte geben Sie den Sicherheitscode aus Ihrer Authenticator-App ein", + "enable-authenticator-sms": "SMS-Authenticator aktivieren", + "enable-authenticator-sms-description": "Geben Sie den 6-stelligen Code ein, den wir gerade gesendet haben an ", + "enable-authenticator-email": "E-Mail-Authenticator aktivieren", + "enable-authenticator-email-description": "Ein Sicherheitscode wurde an Ihre E-Mail-Adresse gesendet an ", + "enter-key-manually": "oder geben Sie diesen 32-stelligen Schlüssel manuell ein:", + "continue": "Weiter", + "confirm": "Bestätigen", + "authenticator-app-success": "Authenticator-App erfolgreich aktiviert", + "authenticator-app-success-description": "Beim nächsten Anmelden müssen Sie einen Code zur Zwei-Faktor-Authentifizierung angeben", + "authenticator-sms-success": "SMS-Authenticator erfolgreich aktiviert", + "authenticator-sms-success-description": "Beim nächsten Anmelden werden Sie aufgefordert, den Sicherheitscode einzugeben, der an die Telefonnummer gesendet wird", + "authenticator-email-success": "E-Mail-Authenticator erfolgreich aktiviert", + "authenticator-email-success-description": "Beim nächsten Anmelden werden Sie aufgefordert, den Sicherheitscode einzugeben, der an Ihre E-Mail-Adresse gesendet wird", + "authenticator-backup-code-success": "Backup-Code erfolgreich aktiviert", + "authenticator-backup-code-success-description": "Beim nächsten Anmelden werden Sie aufgefordert, den Sicherheitscode einzugeben oder einen der Backup-Codes zu verwenden.", + "add-verification-method": "Verifizierungsmethode hinzufügen", + "get-backup-code": "Backup-Code abrufen", + "copy-key": "Schlüssel kopieren", + "send-code": "Code senden", + "email-label": "E-Mail", + "email-description": "Geben Sie eine E-Mail-Adresse ein, die Sie als Authenticator verwenden möchten.", + "sms-description": "Geben Sie eine Telefonnummer ein, die Sie als Authenticator verwenden möchten.", + "backup-code-description": "Drucken Sie die Codes aus, damit Sie sie griffbereit haben, wenn Sie sie zum Anmelden bei Ihrem Konto benötigen. Sie können jeden Backup-Code einmal verwenden.", + "backup-code-warn": "Sobald Sie diese Seite verlassen, können diese Codes nicht erneut angezeigt werden. Bewahren Sie sie sicher auf, indem Sie die folgenden Optionen verwenden.", + "download-txt": "Herunterladen (txt)", + "print": "Drucken", + "verification-code": "6-stelliger Code", + "verification-code-invalid": "Ungültiges Format des Verifizierungscodes", + "verification-code-incorrect": "Verifizierungscode ist falsch", + "verification-code-many-request": "Zu viele Anfragen zur Prüfung des Verifizierungscodes", + "scan-qr-code": "Scannen Sie diesen QR-Code mit Ihrer Verifizierungs-App", + "phone-input": { + "phone-input-label": "Telefonnummer", + "phone-input-required": "Telefonnummer ist erforderlich", + "phone-input-validation": "Telefonnummer ist ungültig oder nicht möglich", + "phone-input-pattern": "Ungültige Telefonnummer. Muss im E.164-Format sein, z. B. {{phoneNumber}}", + "phone-input-hint": "Telefonnummer im E.164-Format, z. B. {{phoneNumber}}" + } }, "mobile": { "add-application": "Anwendung hinzufügen", @@ -4185,6 +4716,7 @@ "api-usage-limit": "API-Nutzungslimit", "device-activity": "Geräteaktivität", "entities-limit": "Entitätenlimit", + "entities-limit-increase-request": "Anfrage zur Erhöhung des Entitätenlimits", "entity-action": "Entitätsaktion", "general": "Allgemein", "rule-engine-lifecycle-event": "Lebenszyklusereignis der Rule Engine", @@ -4402,6 +4934,12 @@ "at-least": "Mindestens:", "character": "{ count, plural, =1 {1 Zeichen} other {# Zeichen} }", "digit": "{ count, plural, =1 {1 Ziffer} other {# Ziffern} }", + "password-tooltip-min-length": "Mindestens {{minimumLength}} Zeichen lang", + "password-tooltip-max-length": "Höchstens {{maximumLength}} Zeichen lang", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} Großbuchstabe", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} Kleinbuchstabe", + "password-tooltip-digit": "{{minimumDigits}} Zahl", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} Sonderzeichen", "incorrect-password-try-again": "Falsches Passwort. Bitte versuchen Sie es erneut", "lowercase-letter": "{ count, plural, =1 {1 Kleinbuchstabe} other {# Kleinbuchstaben} }", "new-passwords-not-match": "Die neuen Passwörter stimmen nicht überein", @@ -4460,7 +4998,8 @@ "additional-info": "Zusätzliche Informationen (JSON)", "invalid-additional-info": "Zusätzliche Info-JSON kann nicht geparst werden.", "no-relations-text": "Keine Beziehungen gefunden", - "not": "Nicht" + "not": "Nicht", + "copy-type": "Typ kopieren" }, "resource": { "add": "Ressource hinzufügen", @@ -5411,7 +5950,7 @@ "time-series": "Zeitreihe", "latest": "Neueste Werte", "web-sockets": "WebSockets", - "calculated-fields": "Berechnete Felder" + "calculated-fields-and-alarm-rules": "Berechnete Felder und Alarmregeln" }, "save-attribute": { "processing-settings": "Verarbeitungseinstellungen", @@ -5606,7 +6145,8 @@ "bad-request-params": "Ungültige Anfrageparameter", "item-not-found": "Element nicht gefunden", "too-many-requests": "Zu viele Anfragen", - "too-many-updates": "Zu viele Aktualisierungen" + "too-many-updates": "Zu viele Aktualisierungen", + "entities-limit-exceeded": "Entitätenlimit überschritten" }, "tenant": { "tenant": "Mieter", @@ -5744,6 +6284,27 @@ "max-arguments-per-cf": "Maximale Argumente pro berechnetem Feld", "max-arguments-per-cf-range": "Maximale Argumente pro berechnetem Feld dürfen nicht negativ sein", "max-arguments-per-cf-required": "Maximale Argumente pro berechnetem Feld sind erforderlich", + "max-related-level-per-argument": "Maximale Beziehungsebene pro Argument „Verwandte Entitäten“", + "max-related-level-per-argument-range": "Die maximale Beziehungsebene pro Argument „Verwandte Entitäten“ darf nicht kleiner als „1“ sein", + "max-related-level-per-argument-required": "Die maximale Beziehungsebene pro Argument „Verwandte Entitäten“ ist erforderlich", + "min-allowed-scheduled-update-interval": "Minimal zulässiges Aktualisierungsintervall für Argumente „Verwandte Entitäten“ (Sekunden)", + "min-allowed-scheduled-update-interval-range": "Der Mindestwert für das minimal zulässige Aktualisierungsintervall darf nicht negativ sein", + "min-allowed-deduplication-interval": "Minimal zulässiges Deduplizierungsintervall (Sekunden)", + "min-allowed-deduplication-interval-range": "Der Wert des minimal zulässigen Deduplizierungsintervalls darf nicht negativ sein", + "min-allowed-deduplication-interval-required": "Minimal zulässiges Deduplizierungsintervall ist erforderlich", + "intermediate-aggregation-interval": "Intermediäres Aggregationsintervall (Sekunden)", + "intermediate-aggregation-interval-range": "Der Wert des intermediären Aggregationsintervalls darf nicht kleiner als „1“ sein", + "intermediate-aggregation-interval-required": "Intermediäres Aggregationsintervall ist erforderlich", + "reevaluation-check-interval": "Intervall für Neubewertungsprüfung (Sekunden)", + "reevaluation-check-interval-range": "Der Wert des Intervalls für die Neubewertungsprüfung darf nicht kleiner als „1“ sein", + "reevaluation-check-interval-required": "Intervall für Neubewertungsprüfung ist erforderlich", + "alarms-reevaluation-interval": "Intervall für Alarmneubewertung (Sekunden)", + "alarms-reevaluation-interval-range": "Der Wert des Intervalls für Alarmneubewertung darf nicht kleiner als „1“ sein", + "alarms-reevaluation-interval-required": "Intervall für Alarmneubewertung ist erforderlich", + "min-allowed-aggregation-interval": "Minimal zulässiges Aggregationsintervall (Sekunden)", + "min-allowed-aggregation-interval-range": "Der Wert des minimal zulässigen Aggregationsintervalls darf nicht negativ sein", + "min-allowed-aggregation-interval-required": "Minimal zulässiges Aggregationsintervall ist erforderlich", + "min-allowed-scheduled-update-interval-required": "Der Mindestwert für das minimal zulässige Aktualisierungsintervall ist erforderlich", "max-state-size": "Maximale Zustandsgröße in KB", "max-state-size-range": "Maximale Zustandsgröße darf nicht negativ sein", "max-state-size-required": "Maximale Zustandsgröße ist erforderlich", @@ -5819,6 +6380,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Maximale Abonnements pro normalem Benutzer", "ws-limit-max-subscriptions-per-public-user": "Maximale Abonnements pro öffentlichem Benutzer", "ws-limit-updates-per-session": "WebSocket-Aktualisierungen pro Sitzung", + "relation-search-entity-limit": "Entitätslimit für Beziehungssuche", + "relation-search-entity-limit-hint": "Begrenzt die Anzahl der Entitäten, die auf der letzten Ebene des Beziehungspfads aufgelöst werden. Gilt für Argumente „Verwandte Entitäten“ und Weitergabefelder.", + "relation-search-entity-limit-required": "Entitätslimit für Beziehungssuche ist erforderlich", + "relation-search-entity-limit-range": "Entitätslimit für Beziehungssuche darf nicht kleiner als „1“ sein", "rate-limits": { "add-limit": "Limit hinzufügen", "and-also-less-than": "und außerdem kleiner als", @@ -6004,7 +6569,9 @@ "default-agg-interval": "Standard-Gruppierungsintervall", "edit-intervals-list-hint": "Liste verfügbarer Intervalloptionen kann angegeben werden.", "edit-grouping-intervals-list-hint": "Gruppierungsintervallliste und Standardintervall konfigurierbar.", - "all": "Alle" + "all": "Alle", + "save-current-settings-as-default": "Aktuelle Einstellungen als Standard-Zeitfenster speichern", + "hide-option-from-end-users": "Option vor Endbenutzern ausblenden" }, "tooltip": { "trigger": "Auslöser", @@ -6658,7 +7225,8 @@ "export-relations": "Beziehungen exportieren", "export-attributes": "Attribute exportieren", "export-credentials": "Zugangsdaten exportieren", - "export-calculated-fields": "Berechnete Felder exportieren", + "export-calculated-fields": "Berechnete Felder \nund Alarmregeln exportieren", + "export-alarm-rules": "Alarmregeln exportieren", "entity-versions": "Entitätsversionen", "versions": "Versionen", "created-time": "Erstellungszeit", @@ -6676,6 +7244,7 @@ "load-attributes": "Attribute laden", "load-credentials": "Zugangsdaten laden", "load-calculated-fields": "Berechnete Felder laden", + "load-alarm-rules": "Alarmregeln laden", "compare-with-current": "Mit aktueller vergleichen", "diff-entity-with-version": "Unterschiede mit Entitätsversion '{{versionName}}'", "previous-difference": "Vorheriger Unterschied", @@ -6885,7 +7454,23 @@ "scan-qr-code": "QR-Code scannen", "make-phone-call": "Anruf tätigen", "get-location": "Standort des Telefons abrufen", - "take-screenshot": "Screenshot erstellen" + "take-screenshot": "Screenshot erstellen", + "handle-provision-success-function": "Funktion zum Verarbeiten eines erfolgreichen Provisionings", + "get-location-function": "Funktion zum Abrufen des Standorts", + "process-launch-result-function": "Funktion zum Verarbeiten des Start-Ergebnisses", + "get-phone-number-function": "Funktion zum Abrufen der Telefonnummer", + "process-image-function": "Funktion zum Verarbeiten des Bildes", + "process-qr-code-function": "Funktion zum Verarbeiten des QR-Codes", + "process-location-function": "Funktion zum Verarbeiten des Standorts", + "handle-empty-result-function": "Funktion zum Verarbeiten eines leeren Ergebnisses", + "handle-error-function": "Funktion zum Verarbeiten eines Fehlers", + "handle-non-mobile-fallback-function": "Fallback-Funktion für Nicht-Mobile", + "save-to-gallery": "In Galerie speichern", + "provision-type": "Provisioning-Typ", + "auto": "Auto", + "wi-fi": "WLAN", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Benutzerdefinierte Aktionsfunktion", "custom-pretty-function": "Benutzerdefinierte Aktion (mit HTML-Vorlage) Funktion", @@ -6894,7 +7479,8 @@ "marker": "Markierung", "polygon": "Polygon", "rectangle": "Rechteck", - "circle": "Kreis" + "circle": "Kreis", + "polyline": "Polylinie" }, "place-map-item": "Kartenelement platzieren", "map-item-tooltip": { @@ -6906,7 +7492,9 @@ "continue-draw-polygon": "Polygon weiterzeichnen", "finish-draw-polygon": "Polygonzeichnen beenden", "start-draw-circle": "Kreis zeichnen starten", - "finish-draw-circle": "Kreiszeichnen beenden" + "finish-draw-circle": "Kreiszeichnen beenden", + "start-draw-polyline": "Polylinie zeichnen starten", + "finish-draw-polyline": "Polylinie zeichnen abschließen" } }, "widgets-bundle": { @@ -7472,7 +8060,15 @@ "update-animation-delay": "Verzögerung der Aktualisierungsanimation" }, "chart-axis": { - "scale": "Skalierung", + "limit": "Limit", + "source": "Quelle", + "key-value": "Schlüssel / Wert", + "value-required": "Wert ist erforderlich.", + "entity-key-required": "Entitätsschlüssel ist erforderlich.", + "key-required": "Schlüssel ist erforderlich.", + "scale-limits": "Skalengrenzen", + "scale-appearance": "Skalendesign", + "scale": "Skala", "scale-min": "min", "scale-max": "max", "scale-auto": "Auto" @@ -8016,19 +8612,22 @@ "add-radio-option": "Optionsfeldoption hinzufügen", "radio-label-position": "Labelposition", "radio-label-position-before": "Vorher", - "radio-label-position-after": "Nachher" + "radio-label-position-after": "Nachher", + "save-image": "Bild speichern", + "save-to-gallery": "Aufgenommene Bilder automatisch in der Bildergalerie speichern", + "public-image": "Macht das Bild für jeden nicht autorisierten Benutzer verfügbar" }, "invalid-qr-code-text": "Ungültiger Eingabetext für QR-Code. Eingabe sollte vom Typ String sein", "qr-code": { "use-qr-code-text-function": "QR-Code-Textfunktion verwenden", - "qr-code-text-pattern": "QR-Code-Textmuster (z. B. '${entityName} | ${keyName} - ein Text.')", + "qr-code-text-pattern": "QR-Code-Textmuster (z.B. '${entityName} | ${keyName} - ein Text.')", "qr-code-text-pattern-hint": "QR-Code-Textmuster verwendet den Wert des zuerst gefundenen Schlüssels in den Entitäten im Entitätsalias.", "qr-code-text-pattern-required": "QR-Code-Textmuster ist erforderlich.", "qr-code-text-function": "QR-Code-Textfunktion" }, "label-widget": { "label-pattern": "Muster", - "label-pattern-hint": "Hinweis: z. B. 'Text ${keyName} Einheiten.' oder ${#<key index>} Einheiten", + "label-pattern-hint": "Hinweis: z.B. 'Text ${keyName} Einheiten.' oder ${#<key index>} Einheiten", "label-pattern-required": "Muster ist erforderlich", "label-position": "Position (Prozent relativ zum Hintergrund)", "x-pos": "X", @@ -8311,7 +8910,8 @@ "trips": "Reisen", "markers": "Marker", "polygons": "Polygone", - "circles": "Kreise" + "circles": "Kreise", + "polylines": "Polylinien" }, "data-layer": { "source": "Quelle", @@ -8508,8 +9108,27 @@ "finish-circle-hint-with-entity": "Kreis für '{{entityName}}': Klicken, um Kreis zu beenden und zu speichern", "finish-circle-hint": "Kreis: Klicken, um Zeichnung zu beenden" }, + "polyline": { + "polyline-key": "Polylinienschlüssel", + "polyline-key-required": "Polylinienschlüssel ist erforderlich", + "no-polylines": "Keine Polylinien konfiguriert", + "add-polylines": "Polylinie hinzufügen", + "polyline-configuration": "Polylinienkonfiguration", + "remove-polyline": "Polylinie entfernen", + "edit": "Polylinie bearbeiten", + "cut": "Polylinienbereich ausschneiden", + "rotate": "Polylinie drehen", + "remove-polyline-for": "Polylinie für '{{entityName}}' entfernen", + "draw-polyline": "Polylinie zeichnen", + "polyline-place-first-point-hint-with-entity": "Polylinie für '{{entityName}}': Klicken, um den ersten Punkt zu setzen", + "polyline-place-first-point-hint": "Polylinie: Klicken, um den ersten Punkt zu setzen", + "finish-polyline-hint-with-entity": "Polylinie für '{{entityName}}': Klicken, um das Zeichnen abzuschließen", + "finish-polyline-hint": "Polylinie: Klicken, um das Zeichnen abzuschließen", + "polyline-place-first-point-cut-hint": "Klicken, um den ersten Punkt zu setzen", + "finish-polyline-cut-hint": "Klicken Sie auf den ersten Marker, um abzuschließen und zu speichern" + }, "select-entity": "Entität auswählen", - "select-entity-hint": "Hinweis: Nach Auswahl auf die Karte klicken, um Position festzulegen" + "select-entity-hint": "Hinweis: Nach der Auswahl auf die Karte klicken, um die Position festzulegen" }, "select-entity": "Entität auswählen", "select-entity-hint": "Hinweis: Nach der Auswahl auf die Karte klicken, um die Position festzulegen", @@ -8949,6 +9568,7 @@ "show-empty-space-hidden-action": "Leeren Platz anstelle der versteckten Zellaktion anzeigen", "dont-reserve-space-hidden-action": "Keinen Platz für versteckte Aktionsschaltflächen reservieren", "display-timestamp": "Zeitstempel anzeigen", + "timestamp-column-name": "Zeitstempel", "display-pagination": "Seitennummerierung anzeigen", "default-page-size": "Standard-Seitengröße", "page-step-settings": "Seitenschritteinstellungen", @@ -9010,7 +9630,9 @@ "alarm-column-error": "Mindestens eine Alarmspalte muss angegeben werden", "table-tabs": "Tabellen-Tabs", "show-cell-actions-menu-mobile": "Zellenaktions-Dropdownmenü im mobilen Modus anzeigen", - "disable-sorting": "Sortierung deaktivieren" + "disable-sorting": "Sortierung deaktivieren", + "sort-by": "Tabs sortieren nach", + "sort-timestamp-option": "Erstellungszeit" }, "latest-chart": { "total": "Gesamt", @@ -9502,11 +10124,28 @@ "content": "

    Durch die Erstellung von Dashboards für Endbenutzer kann ein Kundenbenutzer nur seine eigenen Geräte sehen, während Daten anderer Kunden verborgen bleiben.

    Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

    " } } + }, + "api-usage": { + "api-usage": "API-Nutzung", + "label": "Label", + "state-name": "Statusname", + "status": "Status", + "status-required": "Status ist erforderlich.", + "limit": "Maximales Limit", + "limit-required": "Maximales Limit ist erforderlich.", + "current-number": "Aktuelle Anzahl", + "current-number-required": "Aktuelle Anzahl ist erforderlich.", + "add-key": "Schlüssel hinzufügen", + "no-key": "Kein Schlüssel", + "delete-key": "Schlüssel löschen", + "target-dashboard-state": "Ziel-Dashboardstatus", + "go-to-main-state": "Zur Standardansicht wechseln" } }, "icon": { "icon": "Symbol", "icons": "Symbole", + "custom": "Benutzerdefiniert", "select-icon": "Symbol auswählen", "material-icons": "Materialsymbole", "show-all": "Alle Symbole anzeigen", @@ -9547,6 +10186,7 @@ "items-per-page-separator": "von" }, "language": { + "auto": "Auto", "language": "Sprache" } } \ No newline at end of file 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 9c2a7edc23..31f5d49666 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -3787,7 +3787,6 @@ "two-factor-authentication": "Έλεγχος ταυτότητας δύο παραγόντων", "passwords-mismatch-error": "Οι κωδικοί πρέπει να είναι ίδιοι!", "password-again": "Επαλήθευση κωδικού", - "sign-in": "Παρακαλώ συνδεθείτε", "username": "Όνομα χρήστη (email)", "remember-me": "Να με θυμάσαι", "forgot-password": "Ξεχάσατε τον κωδικό;", 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 ea11fff5a0..116ff4b4c4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -78,6 +78,7 @@ "show-more": "Show more", "dont-show-again": "Do not show again", "see-documentation": "See documentation", + "see-debug-events": "See debug events", "clear": "Clear", "upload": "Upload", "delete-anyway": "Delete anyway", @@ -485,6 +486,7 @@ "2fa": { "2fa": "Two-factor authentication", "available-providers": "Available providers", + "available-providers-required": "At least one 2FA provider should be configured.", "issuer-name": "Issuer name", "issuer-name-required": "Issuer name is required.", "max-verification-failures-before-user-lockout": "Max verification failures before user lockout", @@ -496,24 +498,26 @@ "number-of-codes-pattern": "Number of codes must be a positive integer.", "number-of-codes-required": "Number of codes is required.", "provider": "Provider", - "retry-verification-code-period": "Retry verification code period (sec)", + "retry-verification-code-period": "Retry verification code period", "retry-verification-code-period-pattern": "Minimal period time is 5 sec", "retry-verification-code-period-required": "Retry verification code period is required.", - "total-allowed-time-for-verification": "Total allowed time for verification (sec)", + "total-allowed-time-for-verification": "Total allowed time for verification", "total-allowed-time-for-verification-pattern": "Minimal total allowed time is 60 sec", "total-allowed-time-for-verification-required": "Total allowed time is required.", "use-system-two-factor-auth-settings": "Use system two factor auth settings", "verification-code-check-rate-limit": "Verification code check rate limit", - "verification-code-lifetime": "Verification code lifetime (sec)", + "verification-code-lifetime": "Verification code lifetime", "verification-code-lifetime-pattern": "Verification code lifetime must be a positive integer.", "verification-code-lifetime-required": "Verification code lifetime is required.", "verification-message-template": "Verification message template", "verification-limitations": "Verification limitations", "verification-message-template-pattern": "Verification message need to contains pattern: ${code}", "verification-message-template-required": "Verification message template is required.", - "within-time": "Within time (sec)", + "within-time": "Within time", "within-time-pattern": "Time must be a positive integer.", - "within-time-required": "Time is required." + "within-time-required": "Time is required.", + "force-2fa": "Enforce two-factor authentication", + "enforce-for": "Enforce for" }, "jwt": { "security-settings": "JWT security settings", @@ -555,6 +559,7 @@ }, "alarm": { "alarm": "Alarm", + "alarm-list": "Alarm list", "alarms": "Alarms", "all-alarms": "All alarms", "select-alarm": "Select alarm", @@ -634,7 +639,7 @@ "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", + "alarm-filter-title": "Alarm filter", "assigned": "Assigned", "filter-title": "Filter", "max-count-load": "Maximum number of alarms to load (0 - unlimited)", @@ -655,7 +660,16 @@ "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-type-list-empty": "No alarm types selected.", + "system-comments": { + "acked-by-user": "Alarm was acknowledged by user {{userName}}", + "cleared-by-user": "Alarm was cleared by user {{userName}}", + "assigned-to-user": "Alarm was assigned by user {{userName}} to user {{assigneeName}}", + "unassigned-to-user": "Alarm was unassigned by user {{userName}}", + "unassigned-from-deleted-user": "Alarm was unassigned because user {{userName}} - was deleted", + "comment-deleted": "User {{userName}} deleted his comment", + "severity-changed": "Alarm severity was updated from {{oldSeverity}} to {{newSeverity}}" + } }, "alarm-activity": { "add": "Add a comment...", @@ -760,6 +774,7 @@ "name-max-length": "Name should be less than 256", "label-max-length": "Label should be less than 256", "description": "Description", + "description-required": "Description is required.", "type": "Type", "type-required": "Type is required.", "details": "Details", @@ -793,7 +808,7 @@ "idCopiedMessage": "Asset Id has been copied to clipboard", "select-asset": "Select asset", "no-assets-matching": "No assets matching '{{entity}}' were found.", - "asset-required": "Asset is required", + "asset-required": "Asset is required.", "name-starts-with": "Asset name expression", "help-text": "Use '%' according to need: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with%'.", "import": "Import assets", @@ -865,15 +880,18 @@ "api-features": "API features", "api-usage": "API usage", "alarm": "Alarm", - "alarms-created": "Alarms created", + "alarms-created": "Created alarms", "queue-stats": "Queue Stats", "processing-failures-and-timeouts": "Processing Failures and Timeouts", "exceptions": "Exceptions", - "alarms-created-daily-activity": "Alarms created daily activity", - "alarms-created-hourly-activity": "Alarms created hourly activity", - "alarms-created-monthly-activity": "Alarms created monthly activity", + "alarms-created-daily-activity": "Created alarms daily activity", + "alarms-created-hourly-activity": "Created alarms hourly activity", + "alarms-created-monthly-activity": "Created alarms monthly activity", "data-points": "Data points", "data-points-storage-days": "Data points storage days", + "data-points-storage-days-hourly-activity": "Data points storage days hourly activity", + "data-points-storage-days-daily-activity": "Data points storage days daily activity", + "data-points-storage-days-monthly-activity": "Data points storage days monthly activity", "device-api": "Device API", "email": "Email", "email-messages": "Email messages", @@ -899,14 +917,15 @@ "processing-timeouts": "${entityName} Processing Timeouts", "rule-chain": "Rule Chain", "rule-engine": "Rule Engine", - "rule-engine-daily-activity": "Rule Engine daily activity", "rule-engine-executions": "Rule Engine executions", "rule-engine-hourly-activity": "Rule Engine hourly activity", + "rule-engine-daily-activity": "Rule Engine daily activity", "rule-engine-monthly-activity": "Rule Engine monthly activity", "rule-engine-statistics": "Rule Engine Statistics", "rule-node": "Rule Node", "sms": "SMS", "sms-messages": "SMS messages", + "sms-messages-hourly-activity": "SMS messages hourly activity", "sms-messages-daily-activity": "SMS messages daily activity", "sms-messages-monthly-activity": "SMS messages monthly activity", "successful": "${entityName} Successful", @@ -916,13 +935,40 @@ "telemetry-persistence-hourly-activity": "Telemetry persistence hourly activity", "telemetry-persistence-monthly-activity": "Telemetry persistence monthly activity", "transport": "Transport", + "transport-msg-hourly-activity": "Transport messages hourly activity", + "transport-msg-daily-activity": "Transport messages daily activity", + "transport-msg-monthly-activity": "Transport messages monthly activity", "transport-daily-activity": "Transport daily activity", "transport-data-points": "Transport data points", - "transport-hourly-activity": "Transport hourly activity", - "transport-messages": "Transport messages", - "transport-monthly-activity": "Transport monthly activity", + "transport-data-points-hourly-activity": "Transport data points hourly activity", + "transport-data-points-daily-activity": "Transport data points daily activity", + "transport-data-points-monthly-activity": "Transport data points monthly activity", "view-details": "View details", - "view-statistics": "View statistics" + "view-statistics": "View statistics", + "transport-messages": "Transport messages", + "transport-messages-hourly-activity": "Transport messages hourly activity", + "transport-data-point-hourly-activity": "Transport data point hourly activity", + "javascript-function-executions": "JavaScript function executions", + "javascript-function-executions-hourly-activity": "JavaScript function executions hourly activity", + "javascript-function-executions-daily-activity": "JavaScript function executions daily activity", + "javascript-function-executions-monthly-activity": "JavaScript function executions monthly activity", + "tbel-function-executions": "TBEL function executions", + "tbel-function-executions-hourly-activity": "TBEL function executions hourly activity", + "tbel-function-executions-daily-activity": "TBEL function executions daily activity", + "tbel-function-executions-monthly-activity": "TBEL function executions monthly activity", + "created-reports": "Created reports", + "created-reports-hourly-activity": "Created reports hourly activity", + "created-reports-daily-activity": "Created reports daily activity", + "created-reports-monthly-activity": "Created reports monthly activity", + "emails": "Emails", + "emails-hourly-activity": "Emails hourly activity", + "emails-daily-activity": "Emails daily activity", + "emails-monthly-activity": "Emails monthly activity", + "status": { + "enabled": "Enabled", + "disabled": "Disabled", + "warning": "Warning" + } }, "api-limit": { "cassandra-write-queries-core": "Rest API Cassandra write queries", @@ -947,6 +993,40 @@ "edge-uplink-messages": "Edge uplink messages", "edge-uplink-messages-per-edge": "Edge uplink messages per edge" }, + "api-key": { + "api-key": "API key", + "api-keys": "API keys", + "delete-api-key-title": "Are you sure you want to delete the API key '{{name}}'?", + "delete-api-key-text": "Be careful, after the confirmation key will become unrecoverable.", + "delete-api-keys-title": "Are you sure you want to delete { count, plural, =1 {1 API key} other {# API keys} }?", + "delete-api-keys-text": "Be careful, after the confirmation all selected keys will become unrecoverable.", + "expiration-date": "Expiration date", + "date": "date", + "description": "Description", + "disable": "Disable", + "edit-description": "Edit description", + "enable": "Enable API key ", + "expiration-time": "Expiration time", + "expiration-time-never": "Never", + "expiration-time-custom": "Custom", + "generate": "Generate", + "generate-title": "Generate API key", + "generate-text": "Note: The API key inherits the permissions of the user it is created for.", + "generated-api-key-title": "API key generated. Let’s check connectivity!", + "generated-api-key-copy": "Make sure to copy and save your API key now as you will not be able to see it again.", + "generated-api-key-command": "Use the following instructions to check connectivity. As a result, you should receive the current user information:", + "generated-api-key-insecure-url": "Executing commands over an insecure HTTP connection will send your API key unencrypted, making it vulnerable to interception.", + "list": "{ count, plural, =1 {One API key} other {List of # API keys} }", + "manage": "Manage", + "manage-api-keys": "Manage API keys", + "no-found": "No API keys found", + "selected-api-keys": "{ count, plural, =1 {1 API key} other {# API keys} } selected", + "search": "Search API keys", + "status": "Status", + "status-active": "Active", + "status-inactive": "Inactive", + "status-expired": "Expired" + }, "audit-log": { "audit": "Audit", "audit-logs": "Audit logs", @@ -1000,10 +1080,14 @@ "type-provision-failure": "Device provisioning was failed", "type-timeseries-updated": "Telemetry updated", "type-timeseries-deleted": "Telemetry deleted", - "type-sms-sent": "SMS sent" + "type-sms-sent": "SMS sent", + "any-type":"Any type", + "audit-log-filter-title": "Audit log filter", + "filter-title": "Filter", + "filter-types": "Audit log types" }, "debug-settings": { - "label": "Debug Configuration", + "label": "Debug configuration", "on-failure": "Failures only (24/7)", "all-messages": "All messages ({{time}})", "failures": "Failures", @@ -1021,16 +1105,29 @@ "selected-fields": "{ count, plural, =1 {1 calculated field} other {# calculated fields} } selected", "type": { "simple": "Simple", - "script": "Script" + "simple-hint": "Simple arithmetic calculation based on input arguments.", + "script": "Script", + "script-hint": "Calculation over defined arguments using a TBEL script.", + "geofencing" : "Geofencing", + "geofencing-hint" : "Evaluation of entity GPS position and transitions against configured geofencing zone groups.", + "propagation": "Propagation", + "propagation-hint": "Propagation of data to parent or child entities based on relation direction and type.", + "related-entities-aggregation": "Related entities aggregation", + "related-entities-aggregation-hint": "Aggregation of latest data from related entities.", + "time-series-data-aggregation": "Time series data aggregation", + "time-series-data-aggregation-hint": "Aggregation of historical data from a current entity." }, + "preview": "Preview", "arguments": "Arguments", "decimals-by-default": "Decimals by default", "debugging": "Calculated field debugging", + "calculated-field-details": "Calculated field details", "argument-name": "Argument name", + "name": "Name", "datasource": "Datasource", "add-argument": "Add argument", "test-script-function": "Test script function", - "no-arguments": "No arguments configured", + "no-arguments": "At least one argument is required.", "argument-settings": "Argument settings", "argument-current": "Current entity", "argument-current-tenant": "Current tenant", @@ -1038,8 +1135,9 @@ "argument-asset": "Asset", "argument-customer": "Customer", "argument-tenant": "Current tenant", + "argument-owner": "Current owner", + "argument-relation-query": "Related entities", "argument-type": "Argument type", - "see-debug-events": "See debug events", "attribute": "Attribute", "copy-argument-name": "Copy argument name", "timeseries-key": "Time series key", @@ -1052,12 +1150,14 @@ "shared-attributes": "Shared attributes", "attribute-key": "Attribute key", "default-value": "Default value", + "default-value-required": "Default value is required.", "limit": "Max values", "time-window": "Time window", "customer-name": "Customer name", "asset-name": "Asset name", "timeseries": "Time series", "output": "Output", + "output-hint": "Defines how output is processed.", "create": "Create new calculated field", "file": "Calculated field file", "invalid-file-error": "Invalid file format. Please make sure the file is a valid JSON file.", @@ -1071,9 +1171,175 @@ "delete-multiple-text": "Be careful, after the confirmation all selected calculated fields will be removed and all related data will become unrecoverable.", "test-with-this-message": "Test with this message", "use-latest-timestamp": "Use latest timestamp", + "entity-coordinates": "Entity coordinates", + "latitude-time-series-key": "Latitude time series key", + "latitude-time-series-key-required": "Latitude time series key is required.", + "longitude-time-series-key": "Longitude time series key", + "longitude-time-series-key-required": "Longitude time series key is required.", + "geofencing-zone-groups": "Geofencing zone groups", + "geofencing-zone-groups-settings": "Geofencing zone group settings", + "target-zone": "Target zone", + "perimeter-key": "Perimeter key", + "report-strategy": "Report strategy", + "no-zone-configured": "At least one zone is required.", + "no-zone-configured-required": "At least one zone group must be configured.", + "add-zone-group": "Add zone group", + "report-transition-event-only": "Transition events only", + "report-presence-status-only": "Presence status only", + "report-transition-event-and-presence": "Presence status and transition events", + "perimeter-attribute-key": "Perimeter attribute key", + "perimeter-attribute-key-required": "Perimeter attribute key is required.", + "perimeter-attribute-key-pattern": "Perimeter attribute key is invalid.", + "entity-zone-relationship": "Path from Entity to Zones", + "direction": "Relation direction", + "direction-from": "From entity to zone", + "direction-to": "From zone to entity", + "relation-type": "Relation type", + "create-relation-with-matched-zones": "Create relations for source entity with matched zones", + "relation-level": "Relation level", + "fetch-last-available-level": "Fetch last available level only", + "zone-group-refresh-interval": "Zone groups refresh interval", + "copy-zone-group-name": "Copy zone group name", + "open-details-page": "Open entity details page", + "level": "Level", + "direction-level": "Direction", + "direction-up": "Up", + "direction-up-parent": "Up to parent", + "direction-down": "Down", + "direction-down-child": "Down to child", + "add-level": "Add level", + "delete-level": "Delete level", + "no-level": "No level configured", + "levels-required": "At least one level must be configured.", + "max-allowed-levels-error": "Relation level exceeds the maximum allowed.", + "propagation-path-related-entities": "Propagation path to related entities", + "propagate-type": { + "arguments-only": "Arguments only", + "expression-result": "Calculation result" + }, + "script": "Script", + "data-propagate": "Data to propagate", + "output-key": "Output key", + "copy-output-key": "Copy output key", + "aggregation-path-related-entities": "Aggregation path to related entities", + "deduplication-interval": "Deduplication interval", + "deduplication-interval-min": "Deduplication interval should be at least {{ sec }} seconds.", + "deduplication-interval-hint": "Minimum time between telemetry aggregations.", + "deduplication-interval-required": "Deduplication interval is required.", + "calculated-field-filter-title": "Calculated field filter", + "filter-title": "Filter", + "calculated-field-types": "Calculated field types", + "events": "Events", + "any-type": "Any type", + "metrics": { + "metrics": "Metrics", + "metrics-empty": "At least one metric must be configured.", + "metric-name": "Metric name", + "metric-name-required": "Metric name is required.", + "metric-name-pattern": "Metric name is invalid.", + "metric-name-duplicate": "Metric with such name already exists.", + "metric-name-max-length": "Metric name should be less than 256 characters.", + "metric-name-forbidden": "Metric name is reserved and cannot be used.", + "copy-metric-name": "Copy metric name", + "argument-name": "Argument name", + "aggregation": "Aggregation", + "aggregation-type": { + "avg": "Average", + "min": "Minimum", + "max": "Maximum", + "sum": "Sum", + "count": "Count", + "count-unique": "Count unique" + }, + "filtered": "Filtered", + "value-source": "Value source", + "value-source-hint": "Defines how the value for aggregation is obtained.", + "value-source-type": { + "key": "Key", + "function": "Function" + }, + "no-metrics-configured": "At least one metric is required.", + "add-metric": "Add metric", + "max-metrics": "Maximum number of metrics reached.", + "metric-settings": "Metric settings", + "filter": "Filter", + "filter-hint": "Enables filtering of entities during aggregation. The filter function must return a boolean value and can use all configured arguments." + }, + "output-strategy": { + "strategy": "Strategy", + "process-right-away": "Process right away", + "process-rule-chains": "Process via Rule Chains", + "save-time-series": "Save to time series", + "save-database": "Save to database", + "save-latest-values": "Save to latest values", + "send-web-sockets": "Send to WebSockets", + "save-calculated-fields": "Send to Calculated fields", + "update-attribute-only-on-value-change": "Update attribute only on value change", + "send-attributes-updated-notification": "Send attributes updated notification", + "ttl": "Custom TTL", + "ttl-required": "TTL is required", + "ttl-min": "Only 0 minimum TTL is allowed", + "processing-parameters": "Processing parameters", + "hint": { + "strategy": "Controls whether the result is processed immediately or sent to a rule chain for additional processing.", + "processing-options": "Processing options", + "update-attribute-only-on-value-change": "Updates attribute on every incoming message, regardless of whether the value has changed. This increases API usage and reduces performance.", + "update-attribute-only-on-value-change-enabled": "Updates attribute only when the value changes. If the value is unchanged, timestamps are not updated and notifications are not sent.", + "send-attributes-updated-notification": "Sends an Attributes Updated event to the default rule chain.", + "save-time-series": "Saves time series data to the ts_kv table in the database.", + "save-database": "Saves attribute data to the database.", + "save-latest-values": "Updates time series data in the ts_kv_latest table in the database if the new value is more recent.", + "send-web-sockets-attribute": "Notifies WebSocket subscriptions about updates to the attribute data.", + "send-web-sockets-time-series": "Notifies WebSocket subscriptions about updates to the time series data.", + "save-calculated-fields-attribute": "Notifies calculated fields about updates to the attribute data.", + "save-calculated-fields-time-series": "Notifies calculated fields about updates to the time series data.", + "ttl": "Defines the retention period for time series data. If disabled, the Tenant Profile TTL is used." + } + }, + "aggregate-interval-type": "Aggregate interval type", + "aggregate-interval-value": "Aggregate interval value", + "aggregate-interval-value-required": "Aggregate interval value is required.", + "aggregate-interval-value-min": "Aggregate interval value should be at least { sec, plural, =0 {0 second} =1 {1 second} other {# seconds} }.", + "aggregate-interval-value-step-multiple-of": "Aggregate interval value must be a divisor or multiple of 1 day.", + "aggregate-period": { + "hour": "Hour", + "day": "Day", + "week": "Week (Mon - Sun)", + "week-sun-sat": "Week (Sun - Sat)", + "month": "Month", + "quarter": "Quarter", + "year": "Year", + "custom": "Custom" + }, + "aggregate-period-hint-offset": "Your aggregation interval will be: {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "Your aggregation interval will be: {{ interval }} and so on.", + "entity-aggregation": { + "argument-hint": "Data will be fetched from current entity.", + "argument-title-hint": "Defines the input arguments used for aggregation.", + "argument-setting-hint": "Latest telemetry is the only available argument type for this calculated field.", + "aggregation-interval": "Aggregation interval", + "aggregation-interval-hint": "Defines how often aggregation is performed. Example: every 1 hour aggregates data at 00:00, 01:00, 02:00, etc. Aggregation results are stored with the timestamp corresponding to the start of the aggregation interval.", + "apply-offset": "Apply offset to aggregation interval", + "apply-offset-hint": "Defines how much to shift the start of each aggregation period (e.g., +10 minutes - 00:10, 01:10).", + "offset-value": "Offset value", + "offset-value-required": "Offset value is required.", + "offset-value-min": "Offset value must be a positive integer.", + "offset-value-max": "Offset value should be less than the aggregate interval value.", + "wait-delay": "Apply await timeout for delayed telemetry", + "wait-delay-hint": "Defines how long to wait for delayed telemetry after the interval ends. If such telemetry arrives, the result for that interval will be recalculated.", + "duration": "Duration", + "duration-required": "Duration is required.", + "duration-min": "Duration should be at least 1 minute.", + "duration-hint": "How long to wait for delayed data after the interval ends.", + "produce-intermediate-result": "Produce intermediate result", + "produce-intermediate-result-hint": "Calculates metrics during the current interval to produce an intermediate result. Updates occur no more often than once every {{ time }}." + }, "hint": { "arguments-simple-with-rolling": "Simple type calculated field should not contain keys with time series rolling type.", - "arguments-empty": "Arguments should not be empty.", + "arguments-propagate-arguments-with-rolling": "'Time series rolling' type is incompatible with 'Arguments only' propagation.", + "arguments-propagate-argument-entity-type": "Entity type is incompatible with 'Arguments only' propagation.", + "arguments-propagate-argument-must-current-entity": "At least one argument must be configured with the 'Current entity' source entity type.", + "arguments-empty": "At least one argument should be specified.", "expression-required": "Expression is required.", "expression-invalid": "Expression is invalid", "expression-max-length": "Expression length should be less than 255 characters.", @@ -1082,12 +1348,218 @@ "argument-name-duplicate": "Argument with such name already exists.", "argument-name-max-length": "Argument name should be less than 256 characters.", "argument-name-forbidden": "Argument name is reserved and cannot be used.", + "output-key-required": "Output key is required.", + "output-key-pattern": "Output key is invalid.", + "output-key-duplicate": "Key with such name already exists.", + "output-key-max-length": "Output key should be less than 256 characters.", + "output-key-forbidden": "Output key is reserved and cannot be used.", + "entity-type-required": "Entity type is required", + "name-required": "Name is required.", + "name-pattern": "Name is invalid.", + "name-duplicate": "Name with such name already exists.", + "name-max-length": "Name should be less than 256 characters.", + "name-forbidden": "Name is reserved and cannot be used.", "argument-type-required": "Argument type is required.", "max-args": "Maximum number of arguments reached.", "decimals-range": "Decimals by default should be a number between 0 and 15.", "expression": "Default expression demonstrates how to transform a temperature from Fahrenheit to Celsius.", "arguments-entity-not-found": "Argument target entity not found.", - "use-latest-timestamp": "If enabled, the calculated value will be persisted using the most recent timestamp from the arguments telemetry, instead of the server time." + "use-latest-timestamp": "If enabled, the calculated value will be persisted using the most recent timestamp from the arguments telemetry, instead of the server time.", + "entity-coordinates": "Specify the time series keys that provide entity GPS coordinates (latitude and longitude).", + "geofencing-zone-groups": "Define one or more geofencing zones groups to check (e.g. 'allowedZones', 'restrictedZones'). Each group must have a unique name, which is used as a prefix for calculated field output telemetry keys.", + "perimeter-attribute-key": "Set the attribute key that contains the geofencing zone perimeter definition. The perimeter is always taken from server-side attributes of the zone entity.", + "report-strategy": "Presence status reports whether the entity is currently INSIDE or OUTSIDE the zone group. Transition events report when the entity ENTERED or LEFT the zone group.", + "create-relation-with-matched-zones": "Automatically create and maintain relations between the entity and the zones it is currently inside. Relations are removed when the entity leaves a zone and created when it enters a new one.", + "relation-type-required": "Relation type is required.", + "relation-level-required": "Relation level is required.", + "relation-level-min": "Minimum relation level value is 1.", + "relation-level-max": "Maximum relation level value is {{max}}.", + "geofencing-empty": "At least one zone group must be configured.", + "geofencing-entity-not-found": "Geofencing target entity not found.", + "max-geofencing-zone": "Maximum number of geofencing zones reached.", + "zone-group-refresh-interval": "Defines how often zone groups configured via related entities are refreshed.", + "zone-group-refresh-interval-required": "Zone groups refresh interval is required.", + "zone-group-refresh-interval-min": "Zone group refresh interval should be at least {{ min }} seconds.", + "propagation-path-related-entities": "Defines a direct, single-level path to a related entity based on the selected direction and relation type. Only relations between device, asset, customer, and tenant entities are supported. Maximum entities resolved by relation path is {{ max }}.", + "data-propagate": "Defines the data to be propagated from the arguments configured below. 'Arguments only' uses the retrieved data directly, while 'Expression result' calculates a new value from that data.", + "aggregation-path-related-entities": "Defines a single-level aggregation path via direct relations with parent or child entities, based on direction and relation type. Only relations between device, asset, customer, and tenant entities are supported. Maximum entities resolved by relation path is {{ max }}.", + "arguments-aggregation": "Defines the input arguments used for filtering and aggregation.", + "setting-arguments-aggregation": "Data will be fetched from related entities configured in aggregation path.", + "metrics": "Defines metrics aggregated based on configured arguments.", + "entity-aggregation-metrics": "Defines metrics aggregated based on configured arguments over the specified time intervals.", + "import-invalid-calculated-field-type": "Unable to import calculated field: Invalid calculated field structure.", + "simple-expression-title": "Arithmetic expression that defines how the calculated value is computed.", + "script-title": "TBEL script that defines the calculation logic and output values.", + "simple-arguments": "Arithmetic expression that defines how the calculated value is computed.", + "script-arguments": "Defines the input arguments available to the script." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Alarm rules", + "alarm-rule": "Alarm rule", + "alarm-rules": "Alarm rules", + "alarm-rules-old": "Old", + "alarm-rules-actual": "Actual", + "severities": "Severities", + "cleared": "Clear condition", + "delete-title": "Are you sure you want to delete the alarm rule '{{title}}'?", + "delete-text": "Be careful, after the confirmation the alarm rule and all related data will become unrecoverable.", + "delete-multiple-title": "Are you sure you want to delete { count, plural, =1 {1 alarm rule} other {# alarm rules} }?", + "delete-multiple-text": "Be careful, after the confirmation all selected alarm rules will be removed and all related data will become unrecoverable.", + "create": "Create new alarm rule", + "add": "Add alarm rule", + "copy": "Copy alarm rule configuration", + "details": "Alarm rule details", + "no-found": "No alarm rules found", + "list": "{ count, plural, =1 {One alarm rule} other {List of # alarm rules} }", + "selected-fields": "{ count, plural, =1 {1 alarm rule} other {# alarm rules} } selected", + "import": "Import alarm rule", + "file": "Alarm rule file", + "export": "Export alarm rule", + "export-failed-error": "Unable to export alarm rule: {{error}}", + "entity-type": "Entity type", + "entity-type-required": "Entity type is required.", + "alarm-type": "Alarm type", + "alarm-type-hint": "Unique identifier (e.g., HighTempAlarm) across the scope of the alarm originator (Device, Asset, etc.) to prevent conflicts.", + "alarm-type-required": "Alarm type is required.", + "alarm-type-pattern": "Alarm type is invalid.", + "alarm-type-max-length": "Alarm type should be less than 256 characters.", + "clear-alarm": "Clear alarm", + "value-argument": "Argument", + "value-argument-required": "Argument is required.", + "static-settings": "Static settings", + "configuration": "Configuration", + "static-schedule": "Static", + "dynamic-schedule": "Dynamic", + "operation-and": "AND", + "operation-or": "OR", + "condition-during": "During {{during}}", + "condition-during-dynamic": "During \"{{ attribute }}\"", + "condition-repeat-times": "Repeats { count, plural, =1 {1 time} other {# times} }", + "condition-repeat-times-dynamic": "Repeats \"{{ attribute }}\" times", + "filter-preview": "Filter preview", + "condition-settings": "Condition settings", + "static": "Static", + "dynamic": "Dynamic", + "argument-filters": "Argument filters", + "argument-name": "Argument name", + "value-type": "Value type", + "general": "General", + "filters": "Filters", + "date-time-hint": "The argument must be in epoch milliseconds. Example: 1698839340000 equals 2023-11-01 12:49:00 UTC.", + "operation": "Operation", + "value-source": "Value source", + "value": "Value", + "ignore-case": "Ignore case", + "condition": "Condition", + "script": "Script", + "add-filter": "Add argument filter", + "edit-filter": "Argument filter", + "remove-filter": "Remove argument filter", + "no-filter": "At least one filter is required.", + "conditions": { + "simple": "Simple", + "duration": "Duration", + "repeating": "Repeating" + }, + "schedule-title": "Schedule", + "edit-schedule": "Edit alarm schedule", + "schedule-type": "Scheduler type", + "schedule-type-required": "Scheduler type is required.", + "schedule": { + "any-time": "Active all the time", + "specific-time": "Active at a specific time", + "custom": "Custom" + }, + "schedule-day": { + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday", + "sunday": "Sunday" + }, + "schedule-days": "Days", + "schedule-time": "Time", + "schedule-time-from": "From", + "schedule-time-to": "To", + "schedule-days-of-week-required": "At least one day of week should be selected.", + "tbel": "TBEL", + "expression-type": { + "simple": "Simple", + "script": "Script" + }, + "operation-type": { + "and": "And", + "or": "Or" + }, + "filter-predicate-type": { + "string": "String", + "numeric": "Numeric", + "boolean": "Boolean", + "complex": "Complex" + }, + "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 ${Argument name} to substitute values of the arguments that are used in alarm rule condition.", + "alarm-rule-additional-info-icon-hint": "Use Argument name to substitute values of the arguments 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", + "alarm-rule-condition": "Alarm rule condition", + "enter-alarm-rule-condition-prompt": "Add condition", + "enter-alarm-rule-clear-condition-prompt": "Add clearing condition", + "edit-alarm-rule-condition": "Alarm condition", + "condition-type": "Condition type", + "condition-type-hint": "\"Duration\" and \"Repeating\" options are not available when the \"Missing for\" operation is used in the filter.", + "select-alarm-severity": "Select alarm severity", + "add-create-alarm-rule-prompt": "At least one trigger condition is required.", + "add-create-alarm-rule": "Add trigger condition", + "add-clear-alarm-rule": "Add clear condition", + "condition-duration": "Condition duration", + "condition-duration-value": "Duration value", + "condition-duration-time-unit": "Time unit", + "condition-duration-value-range": "Duration value should be in a range from 1 to 2147483647.", + "condition-duration-value-pattern": "Duration value should be integers.", + "condition-duration-value-required": "Duration value is required.", + "condition-duration-time-unit-required": "Time unit is required.", + "condition-repeating-value": "Count of events", + "condition-repeating-value-hint": "Update of any alarm rule argument will be counted as event", + "condition-repeating-value-range": "Count of events should be in a range from 1 to 2147483647.", + "condition-repeating-value-pattern": "Count of events should be integers.", + "condition-repeating-value-required": "Count of events is required.", + "create-conditions": "Trigger conditions", + "clear-condition": "Clear condition", + "no-clear-alarm-rule": "No clear condition configured.", + "advanced-settings": "Advanced settings", + "propagate-alarm": "Propagate alarm to related entities", + "alarm-rule-relation-types-list": "Relation types", + "alarm-rule-relation-types-list-hint": "Defines relation types to filter the related entities. If not set, the alarm will be propagated to all related entities.", + "propagate-alarm-to-owner": "Propagate alarm to entity owner (Customer or Tenant)", + "propagate-alarm-to-tenant": "Propagate alarm to Tenant", + "alarm-rule-filter-title": "Alarm rule filter", + "filter-title": "Filter", + "debugging": "Alarm rule debugging", + "any-type": "Any type", + "enter-alarm-rule-type": "Enter alarm type", + "no-alarm-rule-types-matching": "No alarm types matching '{{entitySubtype}}' were found.", + "alarm-rule-type-list-empty": "No alarm types selected.", + "alarm-rule-type-list": "Alarm type list", + "alarm-rule-entity-list": "Entity list", + "missing-for": "missing for", + "time-unit": "Unit", + "mode": "Mode", + "type": "Type", + "value-required": "Value is required.", + "min-value": "Value must be 1 or greater.", + "argument-in-use": "Argument is used as general argument.", + "import-invalid-alarm-rule-type": "Unable to import alarm rule: Invalid alarm rule structure.", + "no-filter-preview": "No filter specified", + "filter-operation": { + "and": "And", + "or": "Or" } }, "ai-models": { @@ -1231,6 +1703,8 @@ "documentation": "Documentation", "time-left": "{{time}} left", "output": "Output", + "sort-asc": "Ascending", + "sort-desc": "Descending", "suffix": { "s": "s", "ms": "ms" @@ -1300,7 +1774,7 @@ "idCopiedMessage": "Customer Id has been copied to clipboard", "select-customer": "Select customer", "no-customers-matching": "No customers matching '{{entity}}' were found.", - "customer-required": "Customer is required", + "customer-required": "Customer is required.", "select-default-customer": "Select default customer", "default-customer": "Default customer", "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level", @@ -1367,6 +1841,8 @@ "mobile-order": "Dashboard order in mobile application", "mobile-hide": "Hide dashboard in mobile application", "update-image": "Update dashboard image", + "update-new-version": "Upload new version", + "upload-file-to-update": "Upload file to update", "take-screenshot": "Take screenshot", "select-widget-title": "Select widget", "select-widget-value": "{{title}}: select widget", @@ -1737,6 +2213,8 @@ "bootstrap-tab": "Bootstrap Client", "bootstrap-server": "Bootstrap Server", "lwm2m-server": "LwM2M Server", + "client-reboot": "Registration Update Trigger", + "bootstrap-reboot": "Bootstrap-Request Trigger", "client-publicKey-or-id": "Client Public Key or Id", "client-publicKey-or-id-required": "Client Public Key or Id is required.", "client-publicKey-or-id-tooltip-psk": "The PSK identifier is an arbitrary PSK identifier up to 128 bytes, as described in the standard [RFC7925].\nThe PSK identifier MUST first be converted to a character string and then encoded into octets using UTF-8.", @@ -1784,8 +2262,7 @@ "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):
    {{widgetsList}}", "is-gateway": "Is gateway", "overwrite-activity-time": "Overwrite activity time for connected device", - "device-filter": "Device filter", - "device-filter-title": "Device Filter", + "device-filter-title": "Device filter", "filter-title": "Filter", "device-state": "Device state", "state": "State", @@ -2238,6 +2715,7 @@ "short-id-required": "Short server ID is required.", "short-id-range": "Short server ID should be in a range from {{ min }} to {{ max }}.", "short-id-pattern": "Short server ID must be a positive integer.", + "short-id-pattern-bs": "Short server ID must be only null", "lifetime": "Client registration lifetime", "lifetime-required": "Client registration lifetime is required.", "lifetime-pattern": "Client registration lifetime must be a positive integer.", @@ -2332,7 +2810,9 @@ "composite-all-description": "All resources are observed with a single Composite Observe request (more efficient, less flexible)", "composite-by-object": "Composite by objects", "composite-by-object-description": "Resources are grouped by object type and observed using separate Composite Observe requests (balanced approach)" - } + }, + "init-attr-tel-as-obs-strategy": "Initialize attributes and telemetry using Observe strategy", + "init-attr-tel-as-obs-strategy-hint": "If false - attributes and telemetry are initialized by reading their values one by one.\\nIf true - attributes and telemetry are initialized by subscribing to their values using the Observe strategy." }, "snmp": { "add-communication-config": "Add communication config", @@ -2648,6 +3128,8 @@ "type-rulenodes": "Rule nodes", "list-of-rulenodes": "{ count, plural, =1 {One rule node} other {List of # rule nodes} }", "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'", + "type-api-key": "API key", + "type-api-keys": "API keys", "type-current-customer": "Current Customer", "type-current-tenant": "Current Tenant", "type-current-user": "Current User", @@ -2669,6 +3151,7 @@ "details": "Entity details", "no-entities-prompt": "No entities found", "no-data": "No data to display", + "show-all-columns":"Show All", "columns-to-display": "Columns to Display", "type-api-usage-state": "API Usage State", "type-edge": "Edge", @@ -2714,7 +3197,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {One Mobile application} other {List of # Mobile applications} }", "type-mobile-app-bundle": "Mobile bundle", "type-mobile-app-bundles": "Mobile bundles", - "list-of-mobile-app-bundles": "{ count, plural, =1 {One mobile bundle} other {List of # mobile bundles} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {One mobile bundle} other {List of # mobile bundles} }", + "limit-reached": "Limit reached", + "limit-reached-text": "You have reached the limit of {{ entities }}. To add more, please ask your System Administrator to increase your {{ entity }} limit.", + "request-limit-increase": "Request limit increase", + "request-sysadmin-text": "Are you the System Administrator?", + "login-here": "Login here", + "to-increase-limit": "to increase limit.", + "increase-limit-request-sent-title": "We have sent an automated request to your System Administrator to increase the limit", + "increase-limit-request-sent-text": "Please allow some time for them to review the request and update the settings. You may need to refresh this page to see the changes." }, "entity-field": { "created-time": "Created time", @@ -3786,24 +4277,25 @@ }, "login": { "login": "Login", - "request-password-reset": "Request Password Reset", - "reset-password": "Reset Password", - "create-password": "Create Password", + "request-password-reset": "Request password reset", + "reset-password": "Reset password", + "create-password": "Create password", "two-factor-authentication": "Two factor authentication", "passwords-mismatch-error": "Entered passwords must be same!", "password-again": "Password again", - "sign-in": "Please sign in", + "sign-in": "Sign in", "username": "Username (email)", "remember-me": "Remember me", - "forgot-password": "Forgot Password?", + "forgot-password": "Forgot your password?", "password-reset": "Password reset", - "expired-password-reset-message": "Your credentials has been expired! Please create new password.", + "expired-password-reset-message": "Your password has expired! \nPlease enter a new password.", "new-password": "New password", "new-password-again": "Confirm new password", "password-link-sent-message": "Reset link has been sent", "email": "Email", "invalid-email-format": "Invalid email format.", - "login-with": "Login with {{name}}", + "sign-in-with": "Sign in with {{name}}", + "sign-in-to-your-account": "Sign in to your account", "or": "or", "error": "Login error", "verify-your-identity": "Verify your identity", @@ -3822,7 +4314,51 @@ "activation-link-expired": "Activation link has expired", "activation-link-expired-message": "The link to activate your profile has expired. You can return to the login page to receive a new email.", "reset-password-link-expired": "Password reset link has expired", - "reset-password-link-expired-message": "The link to reset your password has expired. You can return to the login page to receive a new email." + "reset-password-link-expired-message": "The link to reset your password has expired. You can return to the login page to receive a new email.", + "two-fa": "Two-factor authentication", + "two-fa-required": "Two-factor authentication is required", + "set-up-verification-method": "Set up a verification method to continue", + "set-up-verification-method-login": "Set up a verification method or login", + "enable-authenticator-app": "Enable authenticator app", + "enable-authenticator-app-description": "Please enter the security code from your authenticator app", + "enable-authenticator-sms": "Enable SMS authenticator", + "enable-authenticator-sms-description": "Enter a 6-digit code we just sent to ", + "enable-authenticator-email": "Enable email authenticator", + "enable-authenticator-email-description": "A security code has been sent to your email address at ", + "enter-key-manually": "or enter this 32-digits key manually:", + "continue": "Continue", + "confirm": "Confirm", + "authenticator-app-success": "Authenticator app successfully enabled", + "authenticator-app-success-description": "The next time you log in, you will need to provide a two-factor authentication code", + "authenticator-sms-success": "SMS authenticator successfully enabled", + "authenticator-sms-success-description": "The next time you log in, you will be prompted to enter the security code that will be sent to the phone number", + "authenticator-email-success": "Email authenticator successfully enabled", + "authenticator-email-success-description": "The next time you log in, you will be prompted to enter the security code that will be sent to your email address", + "authenticator-backup-code-success": "Backup code successfully enabled", + "authenticator-backup-code-success-description": "The next time you log in, you will be prompted to enter the security code or use one of backup code.", + "add-verification-method": "Add verification method", + "get-backup-code": "Get backup code", + "copy-key": "Copy key", + "send-code": "Send code", + "email-label": "Email", + "email-description": "Enter an email to use as your authenticator.", + "sms-description": "Enter a phone number to use as your authenticator.", + "backup-code-description": "Print out the codes so you have them handy when you need to use them to log in to your account. You can use each backup code once.", + "backup-code-warn": "Once you leave this page, these codes cannot be shown again. Store them safely using the options below.", + "download-txt": "Download (txt)", + "print": "Print", + "verification-code": "6-digit code", + "verification-code-invalid": "Invalid verification code format", + "verification-code-incorrect": "Verification code is incorrect", + "verification-code-many-request": "Too many requests to check verification code", + "scan-qr-code": "Scan this QR code with your verification app", + "phone-input": { + "phone-input-label": "Phone number", + "phone-input-required": "Phone number is required", + "phone-input-validation": "Phone number is invalid or not possible", + "phone-input-pattern": "Invalid phone number. Should be in E.164 format, ex. {{phoneNumber}}", + "phone-input-hint": "Phone Number in E.164 format, ex. {{phoneNumber}}" + } }, "markdown": { "edit": "Edit", @@ -4196,6 +4732,7 @@ "api-usage-limit": "API usage limit", "device-activity": "Device activity", "entities-limit": "Entities limit", + "entities-limit-increase-request": "Entities limit increase request", "entity-action": "Entity action", "general": "General", "rule-engine-lifecycle-event": "Rule engine lifecycle event", @@ -4390,7 +4927,7 @@ "verification-code": "6-digit code", "verification-code-invalid": "Invalid verification code format", "verification-code-incorrect": "Verification code is incorrect", - "verification-code-many-request": "Too many requests check verification code", + "verification-code-many-request": "Too many requests to check verification code", "verification-step-description": "Enter a 6-digit code we just sent to {{address}}", "verification-step-label": "Verification" }, @@ -4413,6 +4950,12 @@ "at-least": "At least:", "character": "{ count, plural, =1 {1 character} other {# characters} }", "digit": "{ count, plural, =1 {1 digit} other {# digits} }", + "password-tooltip-min-length": "At least {{minimumLength}} characters long", + "password-tooltip-max-length": "At most {{maximumLength}} characters long", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} uppercase character", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} lowercase character", + "password-tooltip-digit": "{{minimumDigits}} number", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} special character", "incorrect-password-try-again": "Incorrect password. Try again", "lowercase-letter": "{ count, plural, =1 {1 lowercase letter} other {# lowercase letters} }", "new-passwords-not-match": "New password didn't match", @@ -4471,7 +5014,8 @@ "additional-info": "Additional info (JSON)", "invalid-additional-info": "Unable to parse additional info json.", "no-relations-text": "No relations found", - "not": "Not" + "not": "Not", + "copy-type": "Copy type" }, "resource": { "add": "Add resource", @@ -5422,7 +5966,7 @@ "time-series": "Time series", "latest": "Latest values", "web-sockets": "WebSockets", - "calculated-fields": "Calculated fields" + "calculated-fields-and-alarm-rules": "Calculated fields and alarm rules" }, "save-attribute": { "processing-settings": "Processing settings", @@ -5617,7 +6161,8 @@ "bad-request-params": "Bad request params", "item-not-found": "Item not found", "too-many-requests": "Too many requests", - "too-many-updates": "Too many updates" + "too-many-updates": "Too many updates", + "entities-limit-exceeded": "Entities limit exceeded" }, "tenant": { "tenant": "Tenant", @@ -5755,6 +6300,27 @@ "max-arguments-per-cf": "Arguments per calculated field max number", "max-arguments-per-cf-range": "Arguments per calculated field max number can't be negative", "max-arguments-per-cf-required": "Arguments per calculated field max number is required", + "max-related-level-per-argument": "Maximum relation level per 'Related entities' argument", + "max-related-level-per-argument-range": "Relation level per 'Related entities' argument max number can't be less than '1'", + "max-related-level-per-argument-required": "Relation level per 'Related entities' argument max number is required", + "min-allowed-scheduled-update-interval": "Min allowed update interval for 'Related entities' arguments (seconds)", + "min-allowed-scheduled-update-interval-range": "Min allowed update interval min number can't be negative", + "min-allowed-deduplication-interval": "Min allowed deduplication interval (seconds)", + "min-allowed-deduplication-interval-range": "Min allowed deduplication interval value can't be negative", + "min-allowed-deduplication-interval-required": "Min allowed deduplication interval is required", + "intermediate-aggregation-interval": "Intermediate aggregation interval (seconds)", + "intermediate-aggregation-interval-range": "Intermediate aggregation interval value can't be less than '1'", + "intermediate-aggregation-interval-required": "Intermediate aggregation interval is required", + "reevaluation-check-interval": "Reevaluation check interval (seconds)", + "reevaluation-check-interval-range": "Reevaluation check interval value can't be less than '1'", + "reevaluation-check-interval-required": "Reevaluation check interval is required", + "alarms-reevaluation-interval": "Alarms reevaluation interval (seconds)", + "alarms-reevaluation-interval-range": "Alarms reevaluation interval value can't be less than '1'", + "alarms-reevaluation-interval-required": "Alarms reevaluation interval is required", + "min-allowed-aggregation-interval": "Min allowed aggregation interval (seconds)", + "min-allowed-aggregation-interval-range": "Min allowed aggregation interval value can't be negative", + "min-allowed-aggregation-interval-required": "Min allowed aggregation interval is required", + "min-allowed-scheduled-update-interval-required": "Min allowed update interval min number is required", "max-state-size": "State maximum size in KB", "max-state-size-range": "State maximum size in KB can't be negative", "max-state-size-required": "State maximum size in KB is required", @@ -5830,6 +6396,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Subscriptions per regular user maximum number", "ws-limit-max-subscriptions-per-public-user": "Subscriptions per public user maximum number", "ws-limit-updates-per-session": "WS updates per session", + "relation-search-entity-limit": "Relation search entity limit", + "relation-search-entity-limit-hint": "Limits the number of entities resolved at the last level of the relation path. Applies to 'Related entities' arguments and Propagation fields.", + "relation-search-entity-limit-required": "Relation search entity limit", + "relation-search-entity-limit-range": "Relation search entity limit can't be less than '1'", "rate-limits": { "add-limit": "Add limit", "and-also-less-than": "and also less than", @@ -6015,7 +6585,9 @@ "default-agg-interval": "Default grouping interval", "edit-intervals-list-hint": "List of available interval options can be specified.", "edit-grouping-intervals-list-hint": "It is possible to configure the grouping intervals list and default grouping interval.", - "all": "All" + "all": "All", + "save-current-settings-as-default": "Save current settings as default time window", + "hide-option-from-end-users": "Hide option from end-users" }, "tooltip": { "trigger": "Trigger", @@ -6669,7 +7241,8 @@ "export-relations": "Export relations", "export-attributes": "Export attributes", "export-credentials": "Export credentials", - "export-calculated-fields": "Export calculated fields", + "export-calculated-fields": "Export calculated fields \nand alarm rules", + "export-alarm-rules": "Export alarm rules", "entity-versions": "Entity versions", "versions": "Versions", "created-time": "Created time", @@ -6686,7 +7259,8 @@ "load-relations": "Load relations", "load-attributes": "Load attributes", "load-credentials": "Load credentials", - "load-calculated-fields": "Load calculated fields", + "load-calculated-fields": "Load calculated fields and alarm rules", + "load-alarm-rules": "Load alarm rules", "compare-with-current": "Compare with current", "diff-entity-with-version": "Diff with entity version '{{versionName}}'", "previous-difference": "Previous Difference", @@ -6896,7 +7470,23 @@ "scan-qr-code": "Scan QR Code", "make-phone-call": "Make phone call", "get-location": "Get phone location", - "take-screenshot": "Take screenshot" + "take-screenshot": "Take screenshot", + "handle-provision-success-function": "Handle provision success function", + "get-location-function": "Get location function", + "process-launch-result-function": "Process launch result function", + "get-phone-number-function": "Get phone number function", + "process-image-function": "Process image function", + "process-qr-code-function": "Process QR code function", + "process-location-function": "Process location function", + "handle-empty-result-function": "Handle empty result function", + "handle-error-function": "Handle error function", + "handle-non-mobile-fallback-function": "Handle Non-Mobile fallback function", + "save-to-gallery": "Save to gallery", + "provision-type": "Provision type", + "auto": "Auto", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Custom action function", "custom-pretty-function": "Custom action (with HTML template) function", @@ -6905,7 +7495,8 @@ "marker": "Marker", "polygon": "Polygon", "rectangle": "Rectangle", - "circle": "Circle" + "circle": "Circle", + "polyline": "Polyline" }, "place-map-item": "Place map item", "map-item-tooltip": { @@ -6917,7 +7508,9 @@ "continue-draw-polygon": "Continue draw polygon", "finish-draw-polygon": "Finish draw polygon", "start-draw-circle": "Start draw circle", - "finish-draw-circle": "Finish draw circle" + "finish-draw-circle": "Finish draw circle", + "start-draw-polyline": "Start draw polyline", + "finish-draw-polyline": "Finish draw polyline" } }, "widgets-bundle": { @@ -7477,6 +8070,14 @@ "update-animation-delay": "Update animation delay" }, "chart-axis": { + "limit":"Limit", + "source": "Source", + "key-value": "Key / Value", + "value-required": "Value is required.", + "entity-key-required": "Entity key is required.", + "key-required": "Key is required.", + "scale-limits": "Scale limits", + "scale-appearance": "Scale appearance", "scale": "Scale", "scale-min": "min", "scale-max": "max", @@ -7933,14 +8534,14 @@ "attribute-scope-server": "Server attribute", "attribute-scope-shared": "Shared attribute", "value-required": "Value required", - "image-settings": "Image settings", + "image-settings": "Image output settings", "image-format": "Image format", "image-format-jpeg": "JPEG", "image-format-png": "PNG", "image-format-webp": "WEBP", - "image-quality": "Image quality that use lossy compression such as jpeg and webp", - "max-image-width": "Maximum image width", - "max-image-height": "Maximum image height", + "image-quality": "Image quality", + "max-image-width": "Max width", + "max-image-height": "Max height", "action-buttons": "Action buttons", "show-action-buttons": "Show action buttons", "update-all-values": "Update all values, not only modified", @@ -8021,7 +8622,10 @@ "add-radio-option": "Add radio option", "radio-label-position": "Label position", "radio-label-position-before": "Before", - "radio-label-position-after": "After" + "radio-label-position-after": "After", + "save-image": "Save image", + "save-to-gallery": "Automatically store captured images in Image Gallery", + "public-image": "Makes image avaliable for any unauthorized user" }, "invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type", "qr-code": { @@ -8316,7 +8920,8 @@ "trips": "Trips", "markers": "Markers", "polygons": "Polygons", - "circles": "Circles" + "circles": "Circles", + "polylines": "Polylines" }, "data-layer": { "source": "Source", @@ -8513,6 +9118,25 @@ "finish-circle-hint-with-entity": "Circle for '{{entityName}}': click to finish and save circle", "finish-circle-hint": "Circle: click to finish drawing" }, + "polyline": { + "polyline-key": "Polyline key", + "polyline-key-required": "Polyline key required", + "no-polylines": "No polylines configured", + "add-polylines": "Add polyline", + "polyline-configuration": "Polyline configuration", + "remove-polyline": "Remove polyline", + "edit": "Edit polyline", + "cut": "Cut polyline area", + "rotate": "Rotate polyline", + "remove-polyline-for": "Remove polyline for '{{entityName}}'", + "draw-polyline": "Draw polyline", + "polyline-place-first-point-hint-with-entity": "Polyline for '{{entityName}}': click to place first point", + "polyline-place-first-point-hint": "Polyline: click to place first point", + "finish-polyline-hint-with-entity": "Polyline for '{{entityName}}': click to finish drawing", + "finish-polyline-hint": "Polyline: click to finish drawing", + "polyline-place-first-point-cut-hint": "Click to place first point", + "finish-polyline-cut-hint": "Click first marker to finish and save" + }, "select-entity": "Select entity", "select-entity-hint": "Hint: after selection click at the map to set position" }, @@ -8954,6 +9578,7 @@ "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": "Timestamp", + "timestamp-column-name":"Timestamp", "display-pagination": "Display pagination", "default-page-size": "Default page size", "page-step-settings": "Page step settings", @@ -9015,7 +9640,9 @@ "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", - "disable-sorting": "Disable sorting" + "disable-sorting": "Disable sorting", + "sort-by": "Sort tabs by", + "sort-timestamp-option": "Created time" }, "latest-chart": { "total": "Total", @@ -9511,6 +10138,22 @@ "how-to-create-customer-and-assign-dashboard": "How to create Customer and assign Dashboard" } } + }, + "api-usage": { + "api-usage": "API usage", + "label": "Label", + "state-name": "State name", + "status": "Status", + "status-required": "Status is required.", + "limit": "Max limit", + "limit-required": "Max limit is required.", + "current-number": "Current number", + "current-number-required": "Current number is required.", + "add-key": "Add key", + "no-key": "No key", + "delete-key": "Delete key", + "target-dashboard-state": "Target dashboard state", + "go-to-main-state": "Go to default view" } }, "color": { @@ -9519,6 +10162,7 @@ "icon": { "icon": "Icon", "icons": "Icons", + "custom": "Custom", "select-icon": "Select icon", "material-icons": "Material icons", "show-all": "Show all icons", @@ -9559,6 +10203,7 @@ "items-per-page-separator": "of" }, "language": { + "auto": "Auto", "language": "Language", "locales": { "ar_AE": "العربية (الإمارات العربية المتحدة)", @@ -9571,6 +10216,7 @@ "es_ES": "español (España)", "fa_IR": "فارسی (ایران)", "fr_FR": "français (France)", + "hi_IN": "हिन्दी (भारत)", "it_IT": "italiano (Italia)", "ja_JP": "日本語 (日本)", "ka_GE": "ქართული (საქართველო)", 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 3abb65bec6..75ba33e6d3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3768,7 +3768,6 @@ "two-factor-authentication": "Autenticación de dos factores", "passwords-mismatch-error": "¡Las contraseñas ingresadas deben coincidir!", "password-again": "Repetir contraseña", - "sign-in": "Por favor inicia sesión", "username": "Nombre de usuario (correo electrónico)", "remember-me": "Recordarme", "forgot-password": "¿Olvidaste tu contraseña?", 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 4ab3e8a059..dd15a3d7d0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fa_IR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fa_IR.json @@ -1131,7 +1131,6 @@ "create-password": "ايجاد رمز عبور", "passwords-mismatch-error": "!رمزهاي عبور وارد شده بايد مشابه باشند", "password-again": "رمز عبور دوباره", - "sign-in": "لطفا وارد شويد", "username": "(نام کاربري (پست الکترونيک", "remember-me": "مرا به خاطر داشته باش", "forgot-password": "رمز عبور را فراموش کرده ايد؟", 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 91cd95bd41..989b252cc2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -78,6 +78,7 @@ "show-more": "Afficher plus", "dont-show-again": "Ne plus afficher", "see-documentation": "Voir la documentation", + "see-debug-events": "Voir les événements de débogage", "clear": "Effacer", "upload": "Téléverser", "delete-anyway": "Supprimer quand même", @@ -485,6 +486,7 @@ "2fa": { "2fa": "Authentification à deux facteurs", "available-providers": "Fournisseurs disponibles", + "available-providers-required": "Au moins un fournisseur 2FA doit être configuré.", "issuer-name": "Nom de l'émetteur", "issuer-name-required": "Le nom de l'émetteur est requis.", "max-verification-failures-before-user-lockout": "Nombre maximal d'échecs de vérification avant verrouillage de l'utilisateur", @@ -513,7 +515,9 @@ "verification-message-template-required": "Le modèle de message de vérification est requis.", "within-time": "Délai (sec)", "within-time-pattern": "Le délai doit être un entier positif.", - "within-time-required": "Le délai est requis." + "within-time-required": "Le délai est requis.", + "force-2fa": "Appliquer l’authentification à deux facteurs", + "enforce-for": "Appliquer à" }, "jwt": { "security-settings": "Paramètres de sécurité JWT", @@ -545,16 +549,11 @@ "slack-settings": "Paramètres Slack", "mobile-settings": "Paramètres mobiles", "firebase-service-account-file": "Fichier JSON des identifiants du compte de service Firebase", - "select-firebase-service-account-file": "Glissez-déposez votre fichier d'identifiants de compte de service Firebase ou ", - "trendz": "Trendz", - "trendz-settings": "Paramètres Trendz", - "trendz-url": "URL Trendz", - "trendz-url-required": "L'URL Trendz est requise", - "trendz-api-key": "Clé API Trendz", - "trendz-enable": "Activer Trendz" + "select-firebase-service-account-file": "Glissez-déposez votre fichier d'identifiants de compte de service Firebase ou " }, "alarm": { "alarm": "Alarme", + "alarm-list": "Liste des alarmes", "alarms": "Alarmes", "all-alarms": "Toutes les alarmes", "select-alarm": "Sélectionner une alarme", @@ -655,7 +654,16 @@ "alarm-type": "Type d'alarme", "enter-alarm-type": "Entrer le type d'alarme", "no-alarm-types-matching": "Aucun type d'alarme correspondant à '{{entitySubtype}}' n'a été trouvé.", - "alarm-type-list-empty": "Aucun type d'alarme sélectionné." + "alarm-type-list-empty": "Aucun type d'alarme sélectionné.", + "system-comments": { + "acked-by-user": "L’alarme a été acquittée par l’utilisateur {{userName}}", + "cleared-by-user": "L’alarme a été effacée par l’utilisateur {{userName}}", + "assigned-to-user": "L’alarme a été assignée par l’utilisateur {{userName}} à l’utilisateur {{assigneeName}}", + "unassigned-to-user": "L’alarme a été désassignée par l’utilisateur {{userName}}", + "unassigned-from-deleted-user": "L’alarme a été désassignée car l’utilisateur {{userName}} a été supprimé", + "comment-deleted": "L’utilisateur {{userName}} a supprimé son commentaire", + "severity-changed": "La sévérité de l’alarme a été mise à jour de {{oldSeverity}} à {{newSeverity}}" + } }, "alarm-activity": { "add": "Ajouter un commentaire...", @@ -760,6 +768,7 @@ "name-max-length": "Le nom doit contenir moins de 256 caractères", "label-max-length": "L'étiquette doit contenir moins de 256 caractères", "description": "Description", + "description-required": "La description est requise.", "type": "Type", "type-required": "Le type est requis.", "details": "Détails", @@ -873,6 +882,9 @@ "alarms-created-monthly-activity": "Activité mensuelle des alarmes créées", "data-points": "Points de données", "data-points-storage-days": "Durée de conservation des points de données (jours)", + "data-points-storage-days-hourly-activity": "Activité horaire des jours de stockage des points de données", + "data-points-storage-days-daily-activity": "Activité quotidienne des jours de stockage des points de données", + "data-points-storage-days-monthly-activity": "Activité mensuelle des jours de stockage des points de données", "device-api": "API des appareils", "email": "E-mail", "email-messages": "Messages e-mail", @@ -906,6 +918,7 @@ "rule-node": "Nœud de règle", "sms": "SMS", "sms-messages": "Messages SMS", + "sms-messages-hourly-activity": "Activité horaire des messages SMS", "sms-messages-daily-activity": "Activité quotidienne des SMS", "sms-messages-monthly-activity": "Activité mensuelle des SMS", "successful": "${entityName} Réussis", @@ -915,13 +928,40 @@ "telemetry-persistence-hourly-activity": "Activité horaire de persistance", "telemetry-persistence-monthly-activity": "Activité mensuelle de persistance", "transport": "Transport", + "transport-msg-hourly-activity": "Activité horaire des messages de transport", + "transport-msg-daily-activity": "Activité quotidienne des messages de transport", + "transport-msg-monthly-activity": "Activité mensuelle des messages de transport", "transport-daily-activity": "Activité quotidienne de transport", "transport-data-points": "Points de données de transport", - "transport-hourly-activity": "Activité horaire de transport", - "transport-messages": "Messages de transport", - "transport-monthly-activity": "Activité mensuelle de transport", + "transport-data-points-hourly-activity": "Activité horaire des points de données de transport", + "transport-data-points-daily-activity": "Activité quotidienne des points de données de transport", + "transport-data-points-monthly-activity": "Activité mensuelle des points de données de transport", "view-details": "Voir les détails", - "view-statistics": "Voir les statistiques" + "view-statistics": "Voir les statistiques", + "transport-messages": "Messages de transport", + "transport-messages-hourly-activity": "Activité horaire des messages de transport", + "transport-data-point-hourly-activity": "Activité horaire du point de données de transport", + "javascript-function-executions": "Exécutions de fonctions JavaScript", + "javascript-function-executions-hourly-activity": "Activité horaire des exécutions de fonctions JavaScript", + "javascript-function-executions-daily-activity": "Activité quotidienne des exécutions de fonctions JavaScript", + "javascript-function-executions-monthly-activity": "Activité mensuelle des exécutions de fonctions JavaScript", + "tbel-function-executions": "Exécutions de fonctions TBEL", + "tbel-function-executions-hourly-activity": "Activité horaire des exécutions de fonctions TBEL", + "tbel-function-executions-daily-activity": "Activité quotidienne des exécutions de fonctions TBEL", + "tbel-function-executions-monthly-activity": "Activité mensuelle des exécutions de fonctions TBEL", + "created-reports": "Rapports créés", + "created-reports-hourly-activity": "Activité horaire des rapports créés", + "created-reports-daily-activity": "Activité quotidienne des rapports créés", + "created-reports-monthly-activity": "Activité mensuelle des rapports créés", + "emails": "Emails", + "emails-hourly-activity": "Activité horaire des Emails", + "emails-daily-activity": "Activité quotidienne des Emails", + "emails-monthly-activity": "Activité mensuelle des Emails", + "status": { + "enabled": "Activé", + "disabled": "Désactivé", + "warning": "Avertissement" + } }, "api-limit": { "cassandra-write-queries-core": "Requêtes d’écriture Cassandra via l’API REST", @@ -946,6 +986,40 @@ "edge-uplink-messages": "Messages montants Edge", "edge-uplink-messages-per-edge": "Messages montants Edge par instance" }, + "api-key": { + "api-key": "Clé API", + "api-keys": "Clés API", + "delete-api-key-title": "Êtes-vous sûr de vouloir supprimer la clé API '{{name}}' ?", + "delete-api-key-text": "Attention : après confirmation, la clé deviendra irrécupérable.", + "delete-api-keys-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 clé API} other {# clés API} } ?", + "delete-api-keys-text": "Attention : après confirmation, toutes les clés sélectionnées deviendront irrécupérables.", + "expiration-date": "Date d’expiration", + "date": "date", + "description": "Description", + "disable": "Désactiver", + "edit-description": "Modifier la description", + "enable": "Activer la clé API ", + "expiration-time": "Heure d’expiration", + "expiration-time-never": "Jamais", + "expiration-time-custom": "Personnalisé", + "generate": "Générer", + "generate-title": "Générer une clé API", + "generate-text": "Remarque : la clé API hérite des autorisations de l’utilisateur pour lequel elle est créée.", + "generated-api-key-title": "Clé API générée. Vérifions la connectivité !", + "generated-api-key-copy": "Assurez-vous de copier et d’enregistrer votre clé API maintenant, car vous ne pourrez plus l’afficher.", + "generated-api-key-command": "Utilisez les instructions suivantes pour vérifier la connectivité. En résultat, vous devriez recevoir les informations de l’utilisateur actuel :", + "generated-api-key-insecure-url": "L’exécution de commandes via une connexion HTTP non sécurisée enverra votre clé API sans chiffrement, la rendant vulnérable à l’interception.", + "list": "{ count, plural, =1 {Une clé API} other {Liste de # clés API} }", + "manage": "Gérer", + "manage-api-keys": "Gérer les clés API", + "no-found": "Aucune clé API trouvée", + "selected-api-keys": "{ count, plural, =1 {1 clé API} other {# clés API} } sélectionné(s)", + "search": "Rechercher des clés API", + "status": "Statut", + "status-active": "Actif", + "status-inactive": "Inactif", + "status-expired": "Expiré" + }, "audit-log": { "audit": "Audit", "audit-logs": "Journaux d'audit", @@ -999,7 +1073,11 @@ "type-provision-failure": "Le provisionnement de l'appareil a échoué", "type-timeseries-updated": "Télémétrie mise à jour", "type-timeseries-deleted": "Télémétrie supprimée", - "type-sms-sent": "SMS envoyé" + "type-sms-sent": "SMS envoyé", + "any-type": "Tout type", + "audit-log-filter-title": "Filtre du journal d’audit", + "filter-title": "Filtre", + "filter-types": "Types de journal d’audit" }, "debug-settings": { "label": "Configuration de débogage", @@ -1020,12 +1098,25 @@ "selected-fields": "{ count, plural, =1 {1 champ calculé} other {# champs calculés} } sélectionné(s)", "type": { "simple": "Simple", - "script": "Script" + "simple-hint": "Calcul arithmétique simple basé sur les arguments d’entrée.", + "script": "Script", + "script-hint": "Calcul sur des arguments définis à l’aide d’un script TBEL.", + "geofencing": "Géorepérage", + "geofencing-hint": "Évaluation de la position GPS de l’entité et des transitions par rapport aux groupes de zones de géorepérage configurés.", + "propagation": "Propagation", + "propagation-hint": "Propagation des données vers les entités parentes ou enfants en fonction de la direction et du type de relation.", + "related-entities-aggregation": "Agrégation des entités associées", + "related-entities-aggregation-hint": "Agrégation des dernières données des entités associées.", + "time-series-data-aggregation": "Agrégation des données de séries temporelles", + "time-series-data-aggregation-hint": "Agrégation des données historiques d’une entité actuelle." }, + "preview": "Aperçu", "arguments": "Arguments", "decimals-by-default": "Décimales par défaut", "debugging": "Débogage du champ calculé", + "calculated-field-details": "Détails du champ calculé", "argument-name": "Nom de l'argument", + "name": "Nom", "datasource": "Source de données", "add-argument": "Ajouter un argument", "test-script-function": "Tester la fonction script", @@ -1037,8 +1128,9 @@ "argument-asset": "Actif", "argument-customer": "Client", "argument-tenant": "Tenant actuel", + "argument-owner": "Propriétaire actuel", + "argument-relation-query": "Entités associées", "argument-type": "Type d'argument", - "see-debug-events": "Voir les événements de débogage", "attribute": "Attribut", "copy-argument-name": "Copier le nom de l'argument", "timeseries-key": "Clé de série temporelle", @@ -1051,12 +1143,14 @@ "shared-attributes": "Attributs partagés", "attribute-key": "Clé d'attribut", "default-value": "Valeur par défaut", + "default-value-required": "La valeur par défaut est requise.", "limit": "Valeurs max.", "time-window": "Fenêtre temporelle", "customer-name": "Nom du client", "asset-name": "Nom de l’actif", "timeseries": "Séries temporelles", "output": "Sortie", + "output-hint": "Définit la façon dont la sortie est traitée.", "create": "Créer un nouveau champ calculé", "file": "Fichier de champ calculé", "invalid-file-error": "Format de fichier invalide. Veuillez vous assurer que le fichier est un JSON valide.", @@ -1070,9 +1164,175 @@ "delete-multiple-text": "Attention, après confirmation, tous les champs calculés sélectionnés seront supprimés et les données associées seront irrécupérables.", "test-with-this-message": "Tester avec ce message", "use-latest-timestamp": "Utiliser le dernier horodatage", + "entity-coordinates": "Coordonnées de l’entité", + "latitude-time-series-key": "Clé de série temporelle de latitude", + "latitude-time-series-key-required": "La clé de série temporelle de latitude est requise.", + "longitude-time-series-key": "Clé de série temporelle de longitude", + "longitude-time-series-key-required": "La clé de série temporelle de longitude est requise.", + "geofencing-zone-groups": "Groupes de zones de géorepérage", + "geofencing-zone-groups-settings": "Paramètres des groupes de zones de géorepérage", + "target-zone": "Zone cible", + "perimeter-key": "Clé de périmètre", + "report-strategy": "Stratégie de rapport", + "no-zone-configured": "Au moins une zone est requise.", + "no-zone-configured-required": "Au moins un groupe de zones doit être configuré.", + "add-zone-group": "Ajouter un groupe de zones", + "report-transition-event-only": "Événements de transition uniquement", + "report-presence-status-only": "Statut de présence uniquement", + "report-transition-event-and-presence": "Statut de présence et événements de transition", + "perimeter-attribute-key": "Clé d’attribut de périmètre", + "perimeter-attribute-key-required": "La clé d’attribut de périmètre est requise.", + "perimeter-attribute-key-pattern": "La clé d’attribut de périmètre n’est pas valide.", + "entity-zone-relationship": "Chemin de l’entité vers les zones", + "direction": "Direction de la relation", + "direction-from": "De l’entité vers la zone", + "direction-to": "De la zone vers l’entité", + "relation-type": "Type de relation", + "create-relation-with-matched-zones": "Créer des relations pour l’entité source avec les zones correspondantes", + "relation-level": "Niveau de relation", + "fetch-last-available-level": "Récupérer uniquement le dernier niveau disponible", + "zone-group-refresh-interval": "Intervalle d’actualisation des groupes de zones", + "copy-zone-group-name": "Copier le nom du groupe de zones", + "open-details-page": "Ouvrir la page des détails de l’entité", + "level": "Niveau", + "direction-level": "Direction", + "direction-up": "Vers le haut", + "direction-up-parent": "Vers le haut jusqu’au parent", + "direction-down": "Vers le bas", + "direction-down-child": "Vers le bas jusqu’à l’enfant", + "add-level": "Ajouter un niveau", + "delete-level": "Supprimer le niveau", + "no-level": "Aucun niveau configuré", + "levels-required": "Au moins un niveau doit être configuré.", + "max-allowed-levels-error": "Le niveau de relation dépasse le maximum autorisé.", + "propagation-path-related-entities": "Chemin de propagation vers les entités associées", + "propagate-type": { + "arguments-only": "Arguments uniquement", + "expression-result": "Résultat du calcul" + }, + "script": "Script", + "data-propagate": "Données à propager", + "output-key": "Clé de sortie", + "copy-output-key": "Copier la clé de sortie", + "aggregation-path-related-entities": "Chemin d’agrégation vers les entités associées", + "deduplication-interval": "Intervalle de déduplication", + "deduplication-interval-min": "L’intervalle de déduplication doit être d’au moins {{ sec }} secondes.", + "deduplication-interval-hint": "Temps minimum entre les agrégations de télémétrie.", + "deduplication-interval-required": "L’intervalle de déduplication est requis.", + "calculated-field-filter-title": "Filtre des champs calculés", + "filter-title": "Filtre", + "calculated-field-types": "Types de champs calculés", + "events": "Événements", + "any-type": "Tout type", + "metrics": { + "metrics": "Métriques", + "metrics-empty": "Au moins une métrique doit être configurée.", + "metric-name": "Nom de la métrique", + "metric-name-required": "Le nom de la métrique est requis.", + "metric-name-pattern": "Le nom de la métrique n’est pas valide.", + "metric-name-duplicate": "Une métrique portant ce nom existe déjà.", + "metric-name-max-length": "Le nom de la métrique doit comporter moins de 256 caractères.", + "metric-name-forbidden": "Le nom de la métrique est réservé et ne peut pas être utilisé.", + "copy-metric-name": "Copier le nom de la métrique", + "argument-name": "Nom de l’argument", + "aggregation": "Agrégation", + "aggregation-type": { + "avg": "Moyenne", + "min": "Minimum", + "max": "Maximum", + "sum": "Somme", + "count": "Nombre", + "count-unique": "Nombre d’éléments uniques" + }, + "filtered": "Filtré", + "value-source": "Source de la valeur", + "value-source-hint": "Définit comment la valeur à agréger est obtenue.", + "value-source-type": { + "key": "Clé", + "function": "Fonction" + }, + "no-metrics-configured": "Au moins une métrique est requise.", + "add-metric": "Ajouter une métrique", + "max-metrics": "Nombre maximum de métriques atteint.", + "metric-settings": "Paramètres de la métrique", + "filter": "Filtre", + "filter-hint": "Permet de filtrer les entités lors de l’agrégation. La fonction de filtre doit renvoyer une valeur booléenne et peut utiliser tous les arguments configurés." + }, + "output-strategy": { + "strategy": "Stratégie", + "process-right-away": "Traiter immédiatement", + "process-rule-chains": "Traiter via les chaînes de règles", + "save-time-series": "Enregistrer dans les séries temporelles", + "save-database": "Enregistrer dans la base de données", + "save-latest-values": "Enregistrer dans les dernières valeurs", + "send-web-sockets": "Envoyer vers WebSockets", + "save-calculated-fields": "Envoyer vers les champs calculés", + "update-attribute-only-on-value-change": "Mettre à jour l’attribut uniquement en cas de changement de valeur", + "send-attributes-updated-notification": "Envoyer une notification de mise à jour des attributs", + "ttl": "TTL personnalisé", + "ttl-required": "Le TTL est requis", + "ttl-min": "Seul un TTL minimum de 0 est autorisé", + "processing-parameters": "Paramètres de traitement", + "hint": { + "strategy": "Contrôle si le résultat est traité immédiatement ou envoyé à une chaîne de règles pour un traitement supplémentaire.", + "processing-options": "Options de traitement", + "update-attribute-only-on-value-change": "Met à jour l’attribut à chaque message entrant, que la valeur ait changé ou non. Cela augmente l’utilisation de l’API et réduit les performances.", + "update-attribute-only-on-value-change-enabled": "Met à jour l’attribut uniquement lorsque la valeur change. Si la valeur est inchangée, les horodatages ne sont pas mis à jour et les notifications ne sont pas envoyées.", + "send-attributes-updated-notification": "Envoie un événement « Attributes Updated » à la chaîne de règles par défaut.", + "save-time-series": "Enregistre les données de séries temporelles dans la table ts_kv de la base de données.", + "save-database": "Enregistre les données d’attribut dans la base de données.", + "save-latest-values": "Met à jour les données de séries temporelles dans la table ts_kv_latest de la base de données si la nouvelle valeur est plus récente.", + "send-web-sockets-attribute": "Notifie les abonnements WebSocket des mises à jour des données d’attribut.", + "send-web-sockets-time-series": "Notifie les abonnements WebSocket des mises à jour des données de séries temporelles.", + "save-calculated-fields-attribute": "Notifie les champs calculés des mises à jour des données d’attribut.", + "save-calculated-fields-time-series": "Notifie les champs calculés des mises à jour des données de séries temporelles.", + "ttl": "Définit la durée de conservation des données de séries temporelles. Si désactivé, le TTL du profil de locataire est utilisé." + } + }, + "aggregate-interval-type": "Type d’intervalle d’agrégation", + "aggregate-interval-value": "Valeur de l’intervalle d’agrégation", + "aggregate-interval-value-required": "La valeur de l’intervalle d’agrégation est requise.", + "aggregate-interval-value-min": "La valeur de l’intervalle d’agrégation doit être d’au moins { sec, plural, =0 {0 seconde} =1 {1 seconde} other {# secondes} }.", + "aggregate-interval-value-step-multiple-of": "La valeur de l’intervalle d’agrégation doit être un diviseur ou un multiple d’un jour.", + "aggregate-period": { + "hour": "Heure", + "day": "Jour", + "week": "Semaine (lun. - dim.)", + "week-sun-sat": "Semaine (dim. - sam.)", + "month": "Mois", + "quarter": "Trimestre", + "year": "Année", + "custom": "Personnalisé" + }, + "aggregate-period-hint-offset": "Votre intervalle d’agrégation sera : {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "Votre intervalle d’agrégation sera : {{ interval }} et ainsi de suite.", + "entity-aggregation": { + "argument-hint": "Les données seront récupérées à partir de l’entité actuelle.", + "argument-title-hint": "Définit les arguments d’entrée utilisés pour l’agrégation.", + "argument-setting-hint": "La dernière télémétrie est le seul type d’argument disponible pour ce champ calculé.", + "aggregation-interval": "Intervalle d’agrégation", + "aggregation-interval-hint": "Définit la fréquence d’exécution de l’agrégation. Exemple : toutes les 1 heures, les données sont agrégées à 00:00, 01:00, 02:00, etc. Les résultats de l’agrégation sont stockés avec l’horodatage correspondant au début de l’intervalle d’agrégation.", + "apply-offset": "Appliquer un décalage à l’intervalle d’agrégation", + "apply-offset-hint": "Définit de combien décaler le début de chaque période d’agrégation (p. ex., +10 minutes – 00:10, 01:10).", + "offset-value": "Valeur du décalage", + "offset-value-required": "La valeur du décalage est requise.", + "offset-value-min": "La valeur du décalage doit être un entier positif.", + "offset-value-max": "La valeur du décalage doit être inférieure à la valeur de l’intervalle d’agrégation.", + "wait-delay": "Appliquer un délai d’attente pour la télémétrie retardée", + "wait-delay-hint": "Définit combien de temps attendre la télémétrie retardée après la fin de l’intervalle. Si une telle télémétrie arrive, le résultat de cet intervalle sera recalculé.", + "duration": "Durée", + "duration-required": "La durée est requise.", + "duration-min": "La durée doit être d’au moins 1 minute.", + "duration-hint": "Durée d’attente des données retardées après la fin de l’intervalle.", + "produce-intermediate-result": "Produire un résultat intermédiaire", + "produce-intermediate-result-hint": "Calcule les métriques pendant l’intervalle actuel afin de produire un résultat intermédiaire. Les mises à jour n’ont lieu pas plus d’une fois toutes les {{ time }}." + }, "hint": { - "arguments-simple-with-rolling": "Un champ calculé de type simple ne doit pas contenir de clés avec déroulement de séries temporelles.", - "arguments-empty": "Les arguments ne doivent pas être vides.", + "arguments-simple-with-rolling": "Un champ calculé de type simple ne doit pas contenir de clés avec le type « Série temporelle glissante ».", + "arguments-propagate-arguments-with-rolling": "Le type « Série temporelle glissante » est incompatible avec la propagation « Arguments uniquement ».", + "arguments-propagate-argument-entity-type": "Le type d’entité est incompatible avec la propagation « Arguments uniquement ».", + "arguments-propagate-argument-must-current-entity": "Au moins un argument doit être configuré avec le type d’entité source « Entité actuelle ».", + "arguments-empty": "Au moins un argument doit être spécifié.", "expression-required": "L'expression est requise.", "expression-invalid": "L'expression est invalide.", "expression-max-length": "La longueur de l'expression doit être inférieure à 255 caractères.", @@ -1081,12 +1341,218 @@ "argument-name-duplicate": "Un argument avec ce nom existe déjà.", "argument-name-max-length": "Le nom de l'argument doit contenir moins de 256 caractères.", "argument-name-forbidden": "Ce nom d'argument est réservé et ne peut pas être utilisé.", + "output-key-required": "La clé de sortie est requise.", + "output-key-pattern": "La clé de sortie n’est pas valide.", + "output-key-duplicate": "Une clé portant ce nom existe déjà.", + "output-key-max-length": "La clé de sortie doit comporter moins de 256 caractères.", + "output-key-forbidden": "La clé de sortie est réservée et ne peut pas être utilisée.", + "entity-type-required": "Le type d’entité est requis.", + "name-required": "Le nom est requis.", + "name-pattern": "Le nom n’est pas valide.", + "name-duplicate": "Un nom identique existe déjà.", + "name-max-length": "Le nom doit comporter moins de 256 caractères.", + "name-forbidden": "Le nom est réservé et ne peut pas être utilisé.", "argument-type-required": "Le type d'argument est requis.", "max-args": "Nombre maximal d'arguments atteint.", "decimals-range": "Le nombre de décimales par défaut doit être compris entre 0 et 15.", "expression": "L'expression par défaut montre comment convertir une température de Fahrenheit en Celsius.", "arguments-entity-not-found": "L'entité cible de l'argument est introuvable.", - "use-latest-timestamp": "Si activé, la valeur calculée sera enregistrée avec l’horodatage le plus récent parmi la télémétrie des arguments, au lieu de celui du serveur." + "use-latest-timestamp": "Si activé, la valeur calculée sera enregistrée avec l’horodatage le plus récent parmi la télémétrie des arguments, au lieu de celui du serveur.", + "entity-coordinates": "Spécifiez les clés de série temporelle qui fournissent les coordonnées GPS de l’entité (latitude et longitude).", + "geofencing-zone-groups": "Définissez un ou plusieurs groupes de zones de géorepérage à vérifier (p. ex. « allowedZones », « restrictedZones »). Chaque groupe doit avoir un nom unique, utilisé comme préfixe pour les clés de télémétrie de sortie du champ calculé.", + "perimeter-attribute-key": "Définissez la clé d’attribut qui contient la définition du périmètre de la zone de géorepérage. Le périmètre est toujours récupéré à partir des attributs côté serveur de l’entité de zone.", + "report-strategy": "Le statut de présence indique si l’entité est actuellement À L’INTÉRIEUR ou À L’EXTÉRIEUR du groupe de zones. Les événements de transition indiquent quand l’entité est ENTRÉE dans le groupe de zones ou l’a QUITTÉ.", + "create-relation-with-matched-zones": "Créer et maintenir automatiquement des relations entre l’entité et les zones dans lesquelles elle se trouve actuellement. Les relations sont supprimées lorsque l’entité quitte une zone et créées lorsqu’elle entre dans une nouvelle zone.", + "relation-type-required": "Le type de relation est requis.", + "relation-level-required": "Le niveau de relation est requis.", + "relation-level-min": "La valeur minimale du niveau de relation est 1.", + "relation-level-max": "La valeur maximale du niveau de relation est {{max}}.", + "geofencing-empty": "Au moins un groupe de zones doit être configuré.", + "geofencing-entity-not-found": "L’entité cible de géorepérage est introuvable.", + "max-geofencing-zone": "Nombre maximum de zones de géorepérage atteint.", + "zone-group-refresh-interval": "Définit la fréquence d’actualisation des groupes de zones configurés via des entités associées.", + "zone-group-refresh-interval-required": "L’intervalle d’actualisation des groupes de zones est requis.", + "zone-group-refresh-interval-min": "L’intervalle d’actualisation des groupes de zones doit être d’au moins {{ min }} secondes.", + "propagation-path-related-entities": "Définit un chemin direct à un seul niveau vers une entité associée, selon la direction sélectionnée et le type de relation. Seules les relations entre les entités d’appareil, d’actif, de client et de locataire sont prises en charge. Le nombre maximal d’entités résolues par le chemin de relation est de {{ max }}.", + "data-propagate": "Définit les données à propager à partir des arguments configurés ci-dessous. « Arguments uniquement » utilise directement les données récupérées, tandis que « Résultat de l’expression » calcule une nouvelle valeur à partir de ces données.", + "aggregation-path-related-entities": "Définit un chemin d’agrégation à un seul niveau via des relations directes avec des entités parentes ou enfants, selon la direction et le type de relation. Seules les relations entre les entités d’appareil, d’actif, de client et de locataire sont prises en charge. Le nombre maximal d’entités résolues par le chemin de relation est de {{ max }}.", + "arguments-aggregation": "Définit les arguments d’entrée utilisés pour le filtrage et l’agrégation.", + "setting-arguments-aggregation": "Les données seront récupérées à partir des entités associées configurées dans le chemin d’agrégation.", + "metrics": "Définit les métriques agrégées en fonction des arguments configurés.", + "entity-aggregation-metrics": "Définit les métriques agrégées en fonction des arguments configurés sur les intervalles de temps spécifiés.", + "import-invalid-calculated-field-type": "Impossible d’importer le champ calculé : structure de champ calculé non valide.", + "simple-expression-title": "Expression arithmétique qui définit la manière dont la valeur calculée est déterminée.", + "script-title": "Script TBEL qui définit la logique de calcul et les valeurs de sortie.", + "simple-arguments": "Expression arithmétique qui définit la manière dont la valeur calculée est déterminée.", + "script-arguments": "Définit les arguments d’entrée disponibles pour le script." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Règles d’alarme", + "alarm-rule": "Règle d’alarme", + "alarm-rules": "Règles d’alarme", + "alarm-rules-old": "Anciennes", + "alarm-rules-actual": "Actuelles", + "severities": "Sévérités", + "cleared": "Condition d’effacement", + "delete-title": "Êtes-vous sûr de vouloir supprimer la règle d’alarme « {{title}} » ?", + "delete-text": "Attention : après confirmation, la règle d’alarme et toutes les données associées deviendront irrécupérables.", + "delete-multiple-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 règle d’alarme} other {# règles d’alarme} } ?", + "delete-multiple-text": "Attention : après confirmation, toutes les règles d’alarme sélectionnées seront supprimées et toutes les données associées deviendront irrécupérables.", + "create": "Créer une nouvelle règle d’alarme", + "add": "Ajouter une règle d’alarme", + "copy": "Copier la configuration de la règle d’alarme", + "details": "Détails de la règle d’alarme", + "no-found": "Aucune règle d’alarme trouvée", + "list": "{ count, plural, =1 {Une règle d’alarme} other {Liste de # règles d’alarme} }", + "selected-fields": "{ count, plural, =1 {1 règle d’alarme} other {# règles d’alarme} } sélectionné(s)", + "import": "Importer une règle d’alarme", + "file": "Fichier de règle d’alarme", + "export": "Exporter la règle d’alarme", + "export-failed-error": "Impossible d’exporter la règle d’alarme : {{error}}", + "entity-type": "Type d’entité", + "entity-type-required": "Le type d’entité est requis.", + "alarm-type": "Type d’alarme", + "alarm-type-hint": "Identifiant unique (p. ex. HighTempAlarm) dans le périmètre de l’émetteur de l’alarme (Appareil, Actif, etc.) afin d’éviter les conflits.", + "alarm-type-required": "Le type d’alarme est requis.", + "alarm-type-pattern": "Le type d’alarme n’est pas valide.", + "alarm-type-max-length": "Le type d’alarme doit comporter moins de 256 caractères.", + "clear-alarm": "Effacer l’alarme", + "value-argument": "Argument", + "value-argument-required": "L’argument est requis.", + "static-settings": "Paramètres statiques", + "configuration": "Configuration", + "static-schedule": "Statique", + "dynamic-schedule": "Dynamique", + "operation-and": "ET", + "operation-or": "OU", + "condition-during": "Pendant {{during}}", + "condition-during-dynamic": "Pendant « {{ attribute }} »", + "condition-repeat-times": "Répète { count, plural, =1 {1 fois} other {# fois} }", + "condition-repeat-times-dynamic": "Répète « {{ attribute }} » fois", + "filter-preview": "Aperçu du filtre", + "condition-settings": "Paramètres de condition", + "static": "Statique", + "dynamic": "Dynamique", + "argument-filters": "Filtres d’arguments", + "argument-name": "Nom de l’argument", + "value-type": "Type de valeur", + "general": "Général", + "filters": "Filtres", + "date-time-hint": "L’argument doit être en millisecondes epoch. Exemple : 1698839340000 correspond à 2023-11-01 12:49:00 UTC.", + "operation": "Opération", + "value-source": "Source de la valeur", + "value": "Valeur", + "ignore-case": "Ignorer la casse", + "condition": "Condition", + "script": "Script", + "add-filter": "Ajouter un filtre d’argument", + "edit-filter": "Filtre d’argument", + "remove-filter": "Supprimer le filtre d’argument", + "no-filter": "Au moins un filtre est requis.", + "conditions": { + "simple": "Simple", + "duration": "Durée", + "repeating": "Répétition" + }, + "schedule-title": "Planification", + "edit-schedule": "Modifier la planification de l’alarme", + "schedule-type": "Type de planificateur", + "schedule-type-required": "Le type de planificateur est requis.", + "schedule": { + "any-time": "Active en permanence", + "specific-time": "Active à une heure précise", + "custom": "Personnalisé" + }, + "schedule-day": { + "monday": "Lundi", + "tuesday": "Mardi", + "wednesday": "Mercredi", + "thursday": "Jeudi", + "friday": "Vendredi", + "saturday": "Samedi", + "sunday": "Dimanche" + }, + "schedule-days": "Jours", + "schedule-time": "Heure", + "schedule-time-from": "De", + "schedule-time-to": "À", + "schedule-days-of-week-required": "Au moins un jour de la semaine doit être sélectionné.", + "tbel": "TBEL", + "expression-type": { + "simple": "Simple", + "script": "Script" + }, + "operation-type": { + "and": "ET", + "or": "OU" + }, + "filter-predicate-type": { + "string": "Chaîne", + "numeric": "Numérique", + "boolean": "Booléen", + "complex": "Complexe" + }, + "alarm-rule-additional-info": "Informations supplémentaires", + "edit-alarm-rule-additional-info": "Modifier les informations supplémentaires", + "alarm-rule-additional-info-placeholder": "Veuillez fournir ici vos commentaires et ajustements afin de les afficher dans les détails de l’alarme, sous « Informations supplémentaires »", + "alarm-rule-additional-info-hint": "Astuce : utiliser ${Nom de l’argument} pour substituer les valeurs des arguments utilisés dans la condition de la règle d’alarme.", + "alarm-rule-additional-info-icon-hint": "Utiliser le nom de l’argument pour substituer les valeurs des arguments utilisés dans la condition de la règle d’alarme.", + "alarm-rule-mobile-dashboard": "Tableau de bord mobile", + "alarm-rule-mobile-dashboard-hint": "Utilisé par l’application mobile comme tableau de bord des détails de l’alarme.", + "alarm-rule-no-mobile-dashboard": "Aucun tableau de bord sélectionné", + "alarm-rule-condition": "Condition de la règle d’alarme", + "enter-alarm-rule-condition-prompt": "Ajouter une condition", + "enter-alarm-rule-clear-condition-prompt": "Ajouter une condition d’effacement", + "edit-alarm-rule-condition": "Condition d’alarme", + "condition-type": "Type de condition", + "condition-type-hint": "Les options \"Durée\" et \"Répétition\" ne sont pas disponibles lorsque l’opération \"Manquant pendant\" est utilisée dans le filtre.", + "select-alarm-severity": "Sélectionner la sévérité de l’alarme", + "add-create-alarm-rule-prompt": "Au moins une condition de déclenchement est requise.", + "add-create-alarm-rule": "Ajouter une condition de déclenchement", + "add-clear-alarm-rule": "Ajouter une condition d’effacement", + "condition-duration": "Durée de la condition", + "condition-duration-value": "Valeur de la durée", + "condition-duration-time-unit": "Unité de temps", + "condition-duration-value-range": "La valeur de la durée doit être comprise entre 1 et 2147483647.", + "condition-duration-value-pattern": "La valeur de la durée doit être un entier.", + "condition-duration-value-required": "La valeur de la durée est requise.", + "condition-duration-time-unit-required": "L’unité de temps est requise.", + "condition-repeating-value": "Nombre d’événements", + "condition-repeating-value-hint": "La mise à jour de n’importe quel argument de la règle d’alarme sera comptabilisée comme un événement", + "condition-repeating-value-range": "Le nombre d’événements doit être compris entre 1 et 2147483647.", + "condition-repeating-value-pattern": "Le nombre d’événements doit être un entier.", + "condition-repeating-value-required": "Le nombre d’événements est requis.", + "create-conditions": "Conditions de déclenchement", + "clear-condition": "Condition d’effacement", + "no-clear-alarm-rule": "Aucune condition d’effacement configurée.", + "advanced-settings": "Paramètres avancés", + "propagate-alarm": "Propager l’alarme vers les entités associées", + "alarm-rule-relation-types-list": "Types de relation", + "alarm-rule-relation-types-list-hint": "Définit les types de relation pour filtrer les entités associées. Si non défini, l’alarme sera propagée à toutes les entités associées.", + "propagate-alarm-to-owner": "Propager l’alarme vers le propriétaire de l’entité (Client ou Locataire)", + "propagate-alarm-to-tenant": "Propager l’alarme vers le Locataire", + "alarm-rule-filter-title": "Filtre de règle d’alarme", + "filter-title": "Filtre", + "debugging": "Débogage des règles d’alarme", + "any-type": "Tout type", + "enter-alarm-rule-type": "Saisir le type d’alarme", + "no-alarm-rule-types-matching": "Aucun type d’alarme correspondant à « {{entitySubtype}} » n’a été trouvé.", + "alarm-rule-type-list-empty": "Aucun type d’alarme sélectionné.", + "alarm-rule-type-list": "Liste des types d’alarme", + "alarm-rule-entity-list": "Liste des entités", + "missing-for": "manquant pendant", + "time-unit": "Unité", + "mode": "Mode", + "type": "Type", + "value-required": "La valeur est requise.", + "min-value": "La valeur doit être supérieure ou égale à 1.", + "argument-in-use": "L’argument est utilisé comme argument général.", + "import-invalid-alarm-rule-type": "Impossible d’importer la règle d’alarme : structure de règle d’alarme non valide.", + "no-filter-preview": "Aucun filtre spécifié", + "filter-operation": { + "and": "ET", + "or": "OU" } }, "ai-models": { @@ -1193,6 +1659,7 @@ "contact": { "country": "Pays", "country-required": "Le pays est requis.", + "country-object-required": "Veuillez sélectionner un pays valide dans la liste.", "city": "Ville", "state": "État / Province", "postal-code": "Code postal / ZIP", @@ -1229,6 +1696,8 @@ "documentation": "Documentation", "time-left": "{{time}} restantes", "output": "Sortie", + "sort-asc": "Croissant", + "sort-desc": "Décroissant", "suffix": { "s": "s", "ms": "ms" @@ -1365,6 +1834,8 @@ "mobile-order": "Ordre du tableau de bord dans l'application mobile", "mobile-hide": "Masquer le tableau de bord dans l'application mobile", "update-image": "Mettre à jour l'image du tableau de bord", + "update-new-version": "Téléverser une nouvelle version", + "upload-file-to-update": "Téléverser le fichier pour mettre à jour", "take-screenshot": "Prendre une capture d’écran", "select-widget-title": "Sélectionner un widget", "select-widget-value": "{{title}} : sélectionner un widget", @@ -1733,6 +2204,8 @@ "bootstrap-tab": "Client Bootstrap", "bootstrap-server": "Serveur Bootstrap", "lwm2m-server": "Serveur LwM2M", + "client-reboot": "Déclencheur de mise à jour d’enregistrement", + "bootstrap-reboot": "Déclencheur de requête de bootstrap", "client-publicKey-or-id": "Clé publique ou ID du client", "client-publicKey-or-id-required": "La clé publique ou l'ID du client est requis.", "client-publicKey-or-id-tooltip-psk": "L'identifiant PSK est un identifiant PSK arbitraire jusqu'à 128 octets, comme décrit dans la norme [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en chaîne de caractères, puis encodé en octets en utilisant UTF-8.", @@ -1780,7 +2253,6 @@ "unable-delete-device-alias-text": "L'alias de l'appareil '{{deviceAlias}}' ne peut pas être supprimé car il est utilisé par le(s) widget(s) suivant(s) :
    {{widgetsList}}", "is-gateway": "Est une passerelle", "overwrite-activity-time": "Écraser l'heure d'activité de l'appareil connecté", - "device-filter": "Filtre d'appareil", "device-filter-title": "Filtre d'appareil", "filter-title": "Filtre", "device-state": "État de l'appareil", @@ -2234,6 +2706,7 @@ "short-id-required": "L’ID court du serveur est requis.", "short-id-range": "L’ID court du serveur doit être compris entre {{ min }} et {{ max }}.", "short-id-pattern": "L’ID court doit être un entier positif.", + "short-id-pattern-bs": "L’ID court du serveur doit être uniquement null", "lifetime": "Durée d’enregistrement du client", "lifetime-required": "La durée d’enregistrement du client est requise.", "lifetime-pattern": "La durée d’enregistrement doit être un entier positif.", @@ -2328,7 +2801,9 @@ "composite-all-description": "Toutes les ressources sont observées avec une seule requête Observe Composite (plus efficace, moins flexible)", "composite-by-object": "Composite par objets", "composite-by-object-description": "Les ressources sont regroupées par type d’objet et observées via des requêtes Observe Composite distinctes (approche équilibrée)" - } + }, + "init-attr-tel-as-obs-strategy": "Initialiser les attributs et la télémétrie en utilisant la stratégie Observe", + "init-attr-tel-as-obs-strategy-hint": "Si false - les attributs et la télémétrie sont initialisés en lisant leurs valeurs une par une.\\nSi true - les attributs et la télémétrie sont initialisés en s’abonnant à leurs valeurs via la stratégie Observe." }, "snmp": { "add-communication-config": "Ajouter une configuration de communication", @@ -2644,6 +3119,8 @@ "type-rulenodes": "Nœuds de règle", "list-of-rulenodes": "{ count, plural, =1 {Un nœud de règle} other {Liste de # nœuds de règle} }", "rulenode-name-starts-with": "Nœuds de règle dont le nom commence par '{{prefix}}'", + "type-api-key": "Clé API", + "type-api-keys": "Clés API", "type-current-customer": "Client actuel", "type-current-tenant": "Locataire actuel", "type-current-user": "Utilisateur actuel", @@ -2665,6 +3142,7 @@ "details": "Détails de l'entité", "no-entities-prompt": "Aucune entité trouvée", "no-data": "Aucune donnée à afficher", + "show-all-columns": "Tout afficher", "columns-to-display": "Colonnes à afficher", "type-api-usage-state": "État d'utilisation de l'API", "type-edge": "Edge", @@ -2710,7 +3188,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {Une application mobile} other {Liste de # applications mobiles} }", "type-mobile-app-bundle": "Pack mobile", "type-mobile-app-bundles": "Packs mobiles", - "list-of-mobile-app-bundles": "{ count, plural, =1 {Un bundle mobile} other {Liste de # bundles mobiles} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {Un bundle mobile} other {Liste de # bundles mobiles} }", + "limit-reached": "Limite atteinte", + "limit-reached-text": "Vous avez atteint la limite de {{ entities }}. Pour en ajouter davantage, veuillez demander à votre administrateur système d’augmenter votre limite de {{ entity }}.", + "request-limit-increase": "Demander une augmentation de limite", + "request-sysadmin-text": "Êtes-vous l’administrateur système ?", + "login-here": "Se connecter ici", + "to-increase-limit": "pour augmenter la limite.", + "increase-limit-request-sent-title": "Nous avons envoyé une demande automatisée à votre administrateur système pour augmenter la limite", + "increase-limit-request-sent-text": "Veuillez leur laisser un peu de temps pour examiner la demande et mettre à jour les paramètres. Vous devrez peut-être actualiser cette page pour voir les modifications." }, "entity-field": { "created-time": "Date de création", @@ -3798,7 +4284,8 @@ "password-link-sent-message": "Le lien de réinitialisation a été envoyé", "email": "Email", "invalid-email-format": "Format d'email invalide.", - "login-with": "Connexion avec {{name}}", + "sign-in-with": "Se connecter avec {{name}}", + "sign-in-to-your-account": "Se connecter à votre compte", "or": "ou", "error": "Erreur de connexion", "verify-your-identity": "Vérifiez votre identité", @@ -3817,7 +4304,51 @@ "activation-link-expired": "Le lien d'activation a expiré", "activation-link-expired-message": "Le lien d’activation de votre profil a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email.", "reset-password-link-expired": "Le lien de réinitialisation du mot de passe a expiré", - "reset-password-link-expired-message": "Le lien de réinitialisation du mot de passe a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email." + "reset-password-link-expired-message": "Le lien de réinitialisation du mot de passe a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email.", + "two-fa": "Authentification à deux facteurs", + "two-fa-required": "L’authentification à deux facteurs est requise", + "set-up-verification-method": "Configurer une méthode de vérification pour continuer", + "set-up-verification-method-login": "Configurer une méthode de vérification ou se connecter", + "enable-authenticator-app": "Activer l’application d’authentification", + "enable-authenticator-app-description": "Veuillez saisir le code de sécurité de votre application d’authentification", + "enable-authenticator-sms": "Activer l’authentificateur SMS", + "enable-authenticator-sms-description": "Saisissez le code à 6 chiffres que nous venons d’envoyer à ", + "enable-authenticator-email": "Activer l’authentificateur Email", + "enable-authenticator-email-description": "Un code de sécurité a été envoyé à votre adresse email à ", + "enter-key-manually": "ou saisissez manuellement cette clé à 32 chiffres :", + "continue": "Continuer", + "confirm": "Confirmer", + "authenticator-app-success": "Application d’authentification activée avec succès", + "authenticator-app-success-description": "La prochaine fois que vous vous connecterez, vous devrez fournir un code d’authentification à deux facteurs", + "authenticator-sms-success": "Authentificateur SMS activé avec succès", + "authenticator-sms-success-description": "La prochaine fois que vous vous connecterez, vous serez invité à saisir le code de sécurité qui sera envoyé au numéro de téléphone", + "authenticator-email-success": "Authentificateur Email activé avec succès", + "authenticator-email-success-description": "La prochaine fois que vous vous connecterez, vous serez invité à saisir le code de sécurité qui sera envoyé à votre adresse email", + "authenticator-backup-code-success": "Code de secours activé avec succès", + "authenticator-backup-code-success-description": "La prochaine fois que vous vous connecterez, vous serez invité à saisir le code de sécurité ou à utiliser l’un des codes de secours.", + "add-verification-method": "Ajouter une méthode de vérification", + "get-backup-code": "Obtenir un code de secours", + "copy-key": "Copier la clé", + "send-code": "Envoyer le code", + "email-label": "Email", + "email-description": "Saisissez un email à utiliser comme authentificateur.", + "sms-description": "Saisissez un numéro de téléphone à utiliser comme authentificateur.", + "backup-code-description": "Imprimez les codes afin de les avoir à portée de main lorsque vous en aurez besoin pour vous connecter à votre compte. Vous pouvez utiliser chaque code de secours une seule fois.", + "backup-code-warn": "Une fois que vous quittez cette page, ces codes ne peuvent plus être affichés. Stockez-les en toute sécurité à l’aide des options ci-dessous.", + "download-txt": "Télécharger (txt)", + "print": "Imprimer", + "verification-code": "Code à 6 chiffres", + "verification-code-invalid": "Format du code de vérification non valide", + "verification-code-incorrect": "Le code de vérification est incorrect", + "verification-code-many-request": "Trop de demandes pour vérifier le code de vérification", + "scan-qr-code": "Scannez ce code QR avec votre application de vérification", + "phone-input": { + "phone-input-label": "Numéro de téléphone", + "phone-input-required": "Le numéro de téléphone est requis", + "phone-input-validation": "Le numéro de téléphone n’est pas valide ou n’est pas possible", + "phone-input-pattern": "Numéro de téléphone non valide. Doit être au format E.164, ex. {{phoneNumber}}", + "phone-input-hint": "Numéro de téléphone au format E.164, ex. {{phoneNumber}}" + } }, "mobile": { "add-application": "Ajouter une application", @@ -4185,6 +4716,7 @@ "api-usage-limit": "Limite d'utilisation de l'API", "device-activity": "Activité de l'appareil", "entities-limit": "Limite d'entités", + "entities-limit-increase-request": "Demande d’augmentation de la limite d’entités", "entity-action": "Action sur l'entité", "general": "Général", "rule-engine-lifecycle-event": "Événement du cycle de vie du moteur de règles", @@ -4402,6 +4934,12 @@ "at-least": "Au moins :", "character": "{ count, plural, =1 {1 caractère} other {# caractères} }", "digit": "{ count, plural, =1 {1 chiffre} other {# chiffres} }", + "password-tooltip-min-length": "Au moins {{minimumLength}} caractères", + "password-tooltip-max-length": "Au plus {{maximumLength}} caractères", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} caractère(s) en majuscule", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} caractère(s) en minuscule", + "password-tooltip-digit": "{{minimumDigits}} chiffre(s)", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} caractère(s) spécial(s)", "incorrect-password-try-again": "Mot de passe incorrect. Veuillez réessayer", "lowercase-letter": "{ count, plural, =1 {1 lettre minuscule} other {# lettres minuscules} }", "new-passwords-not-match": "Les nouveaux mots de passe ne correspondent pas", @@ -4460,7 +4998,8 @@ "additional-info": "Informations supplémentaires (JSON)", "invalid-additional-info": "Impossible d’analyser le JSON des informations supplémentaires.", "no-relations-text": "Aucune relation trouvée", - "not": "Non" + "not": "Non", + "copy-type": "Copier le type" }, "resource": { "add": "Ajouter une ressource", @@ -5411,7 +5950,7 @@ "time-series": "Séries temporelles", "latest": "Dernières valeurs", "web-sockets": "WebSockets", - "calculated-fields": "Champs calculés" + "calculated-fields-and-alarm-rules": "Champs calculés et règles d'alarme" }, "save-attribute": { "processing-settings": "Paramètres de traitement", @@ -5606,7 +6145,8 @@ "bad-request-params": "Paramètres de requête incorrects", "item-not-found": "Élément introuvable", "too-many-requests": "Trop de requêtes", - "too-many-updates": "Trop de mises à jour" + "too-many-updates": "Trop de mises à jour", + "entities-limit-exceeded": "Limite d’entités dépassée" }, "tenant": { "tenant": "Locataire", @@ -5744,6 +6284,27 @@ "max-arguments-per-cf": "Nombre maximal d’arguments par champ calculé", "max-arguments-per-cf-range": "Le nombre maximal d’arguments ne peut pas être négatif", "max-arguments-per-cf-required": "Le nombre maximal d’arguments est requis", + "max-related-level-per-argument": "Niveau de relation maximal par argument « Entités associées »", + "max-related-level-per-argument-range": "Le nombre maximal du niveau de relation par argument « Entités associées » ne peut pas être inférieur à « 1 »", + "max-related-level-per-argument-required": "Le nombre maximal du niveau de relation par argument « Entités associées » est requis", + "min-allowed-scheduled-update-interval": "Intervalle minimal de mise à jour autorisé pour les arguments « Entités associées » (secondes)", + "min-allowed-scheduled-update-interval-range": "La valeur minimale de l’intervalle de mise à jour autorisé ne peut pas être négative", + "min-allowed-deduplication-interval": "Intervalle minimal de déduplication autorisé (secondes)", + "min-allowed-deduplication-interval-range": "La valeur minimale de l’intervalle de déduplication autorisé ne peut pas être négative", + "min-allowed-deduplication-interval-required": "L’intervalle minimal de déduplication autorisé est requis", + "intermediate-aggregation-interval": "Intervalle d’agrégation intermédiaire (secondes)", + "intermediate-aggregation-interval-range": "La valeur de l’intervalle d’agrégation intermédiaire ne peut pas être inférieure à « 1 »", + "intermediate-aggregation-interval-required": "L’intervalle d’agrégation intermédiaire est requis", + "reevaluation-check-interval": "Intervalle de vérification de la réévaluation (secondes)", + "reevaluation-check-interval-range": "La valeur de l’intervalle de vérification de la réévaluation ne peut pas être inférieure à « 1 »", + "reevaluation-check-interval-required": "L’intervalle de vérification de la réévaluation est requis", + "alarms-reevaluation-interval": "Intervalle de réévaluation des alarmes (secondes)", + "alarms-reevaluation-interval-range": "La valeur de l’intervalle de réévaluation des alarmes ne peut pas être inférieure à « 1 »", + "alarms-reevaluation-interval-required": "L’intervalle de réévaluation des alarmes est requis", + "min-allowed-aggregation-interval": "Intervalle minimal d’agrégation autorisé (secondes)", + "min-allowed-aggregation-interval-range": "La valeur minimale de l’intervalle d’agrégation autorisé ne peut pas être négative", + "min-allowed-aggregation-interval-required": "L’intervalle minimal d’agrégation autorisé est requis", + "min-allowed-scheduled-update-interval-required": "La valeur minimale de l’intervalle de mise à jour autorisé est requise", "max-state-size": "Taille maximale de l'état (en Ko)", "max-state-size-range": "La taille maximale ne peut pas être négative", "max-state-size-required": "La taille maximale de l'état est requise", @@ -5819,6 +6380,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Nombre maximal d’abonnements par utilisateur régulier", "ws-limit-max-subscriptions-per-public-user": "Nombre maximal d’abonnements par utilisateur public", "ws-limit-updates-per-session": "Mises à jour WS par session", + "relation-search-entity-limit": "Limite de recherche d’entités par relation", + "relation-search-entity-limit-hint": "Limite le nombre d’entités résolues au dernier niveau du chemin de relation. S’applique aux arguments « Entités associées » et aux champs de propagation.", + "relation-search-entity-limit-required": "La limite de recherche d’entités par relation est requise.", + "relation-search-entity-limit-range": "La limite de recherche d’entités par relation ne peut pas être inférieure à « 1 »", "rate-limits": { "add-limit": "Ajouter une limite", "and-also-less-than": "et aussi inférieur à", @@ -6004,7 +6569,9 @@ "default-agg-interval": "Intervalle de regroupement par défaut", "edit-intervals-list-hint": "Il est possible de spécifier la liste des options d’intervalle disponibles.", "edit-grouping-intervals-list-hint": "Il est possible de configurer la liste des intervalles de regroupement et l’intervalle par défaut.", - "all": "Tous" + "all": "Tous", + "save-current-settings-as-default": "Enregistrer les paramètres actuels comme fenêtre temporelle par défaut", + "hide-option-from-end-users": "Masquer l’option aux utilisateurs finaux" }, "tooltip": { "trigger": "Déclencheur", @@ -6658,7 +7225,8 @@ "export-relations": "Exporter les relations", "export-attributes": "Exporter les attributs", "export-credentials": "Exporter les identifiants", - "export-calculated-fields": "Exporter les champs calculés", + "export-calculated-fields": "Exporter les champs calculés \net les règles d’alarme", + "export-alarm-rules": "Exporter les règles d’alarme", "entity-versions": "Versions des entités", "versions": "Versions", "created-time": "Date de création", @@ -6676,6 +7244,7 @@ "load-attributes": "Charger les attributs", "load-credentials": "Charger les identifiants", "load-calculated-fields": "Charger les champs calculés", + "load-alarm-rules": "Charger les règles d’alarme", "compare-with-current": "Comparer avec l'actuel", "diff-entity-with-version": "Comparer avec la version d'entité '{{versionName}}'", "previous-difference": "Différence précédente", @@ -6885,7 +7454,23 @@ "scan-qr-code": "Scanner le code QR", "make-phone-call": "Passer un appel téléphonique", "get-location": "Obtenir la localisation du téléphone", - "take-screenshot": "Faire une capture d'écran" + "take-screenshot": "Faire une capture d'écran", + "handle-provision-success-function": "Gérer la fonction de succès du provisionnement", + "get-location-function": "Obtenir la fonction de localisation", + "process-launch-result-function": "Traiter la fonction de résultat de lancement", + "get-phone-number-function": "Obtenir la fonction de numéro de téléphone", + "process-image-function": "Traiter la fonction d’image", + "process-qr-code-function": "Traiter la fonction de code QR", + "process-location-function": "Traiter la fonction de localisation", + "handle-empty-result-function": "Gérer la fonction de résultat vide", + "handle-error-function": "Gérer la fonction d’erreur", + "handle-non-mobile-fallback-function": "Gérer la fonction de repli non mobile", + "save-to-gallery": "Enregistrer dans la galerie", + "provision-type": "Type de provisionnement", + "auto": "Auto", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Fonction d'action personnalisée", "custom-pretty-function": "Fonction d'action personnalisée (avec modèle HTML)", @@ -6894,7 +7479,8 @@ "marker": "Marqueur", "polygon": "Polygone", "rectangle": "Rectangle", - "circle": "Cercle" + "circle": "Cercle", + "polyline": "Polyligne" }, "place-map-item": "Placer un élément cartographique", "map-item-tooltip": { @@ -6906,7 +7492,9 @@ "continue-draw-polygon": "Continuer le dessin du polygone", "finish-draw-polygon": "Terminer le dessin du polygone", "start-draw-circle": "Commencer à dessiner un cercle", - "finish-draw-circle": "Terminer le dessin du cercle" + "finish-draw-circle": "Terminer le dessin du cercle", + "start-draw-polyline": "Commencer à dessiner une polyligne", + "finish-draw-polyline": "Terminer le dessin de la polyligne" } }, "widgets-bundle": { @@ -7472,6 +8060,14 @@ "update-animation-delay": "Délai de l’animation de mise à jour" }, "chart-axis": { + "limit": "Limite", + "source": "Source", + "key-value": "Clé / valeur", + "value-required": "La valeur est requise.", + "entity-key-required": "La clé d’entité est requise.", + "key-required": "La clé est requise.", + "scale-limits": "Limites d’échelle", + "scale-appearance": "Apparence de l’échelle", "scale": "Échelle", "scale-min": "min", "scale-max": "max", @@ -8016,7 +8612,10 @@ "add-radio-option": "Ajouter une option radio", "radio-label-position": "Position de l’étiquette", "radio-label-position-before": "Avant", - "radio-label-position-after": "Après" + "radio-label-position-after": "Après", + "save-image": "Enregistrer l’image", + "save-to-gallery": "Enregistrer automatiquement les images capturées dans la galerie d’images", + "public-image": "Rendre l’image disponible pour tout utilisateur non autorisé" }, "invalid-qr-code-text": "Texte saisi invalide pour le code QR. L'entrée doit être une chaîne de caractères", "qr-code": { @@ -8311,7 +8910,8 @@ "trips": "Trajets", "markers": "Marqueurs", "polygons": "Polygones", - "circles": "Cercles" + "circles": "Cercles", + "polylines": "Polylignes" }, "data-layer": { "source": "Source", @@ -8508,11 +9108,30 @@ "finish-circle-hint-with-entity": "Cercle pour '{{entityName}}' : cliquez pour terminer et enregistrer le cercle", "finish-circle-hint": "Cercle : cliquez pour terminer le dessin" }, + "polyline": { + "polyline-key": "Clé de polyligne", + "polyline-key-required": "La clé de polyligne est requise", + "no-polylines": "Aucune polyligne configurée", + "add-polylines": "Ajouter une polyligne", + "polyline-configuration": "Configuration de la polyligne", + "remove-polyline": "Supprimer la polyligne", + "edit": "Modifier la polyligne", + "cut": "Couper la zone de polyligne", + "rotate": "Faire pivoter la polyligne", + "remove-polyline-for": "Supprimer la polyligne pour « {{entityName}} »", + "draw-polyline": "Dessiner une polyligne", + "polyline-place-first-point-hint-with-entity": "Polyligne pour « {{entityName}} » : cliquer pour placer le premier point", + "polyline-place-first-point-hint": "Polyligne : cliquer pour placer le premier point", + "finish-polyline-hint-with-entity": "Polyligne pour « {{entityName}} » : cliquer pour terminer le dessin", + "finish-polyline-hint": "Polyligne : cliquer pour terminer le dessin", + "polyline-place-first-point-cut-hint": "Cliquer pour placer le premier point", + "finish-polyline-cut-hint": "Cliquer sur le premier marqueur pour terminer et enregistrer" + }, "select-entity": "Sélectionner une entité", - "select-entity-hint": "Astuce : après sélection, cliquez sur la carte pour définir la position" + "select-entity-hint": "Astuce : après la sélection, cliquer sur la carte pour définir la position" }, "select-entity": "Sélectionner une entité", - "select-entity-hint": "Astuce : après la sélection, cliquez sur la carte pour définir la position", + "select-entity-hint": "Astuce : après la sélection, cliquer sur la carte pour définir la position", "tooltips": { "placeMarker": "Cliquez pour placer l’entité '{{entityName}}'", "firstVertex": "Polygone pour '{{entityName}}' : cliquez pour placer le premier point", @@ -8949,6 +9568,7 @@ "show-empty-space-hidden-action": "Afficher un espace vide à la place du bouton d'action masqué", "dont-reserve-space-hidden-action": "Ne pas réserver d'espace pour les boutons d'action masqués", "display-timestamp": "Horodatage", + "timestamp-column-name": "Horodatage", "display-pagination": "Afficher la pagination", "default-page-size": "Taille de page par défaut", "page-step-settings": "Paramètres de pas de page", @@ -9010,7 +9630,9 @@ "alarm-column-error": "Au moins une colonne d'alarme doit être spécifiée", "table-tabs": "Onglets du tableau", "show-cell-actions-menu-mobile": "Afficher le menu déroulant d'actions des cellules en mode mobile", - "disable-sorting": "Désactiver le tri" + "disable-sorting": "Désactiver le tri", + "sort-by": "Trier les onglets par", + "sort-timestamp-option": "Date de création" }, "latest-chart": { "total": "Total", @@ -9502,11 +10124,28 @@ "content": "

    En créant des tableaux de bord pour les utilisateurs finaux, un utilisateur client ne peut voir que ses propres appareils. Les données d'autres clients lui seront invisibles.

    Suivez la documentation pour savoir comment procéder :

    " } } + }, + "api-usage": { + "api-usage": "Utilisation de l’API", + "label": "Libellé", + "state-name": "Nom de l’état", + "status": "Statut", + "status-required": "Le statut est requis.", + "limit": "Limite max", + "limit-required": "La limite max est requise.", + "current-number": "Nombre actuel", + "current-number-required": "Le nombre actuel est requis.", + "add-key": "Ajouter une clé", + "no-key": "Aucune clé", + "delete-key": "Supprimer la clé", + "target-dashboard-state": "État cible du tableau de bord", + "go-to-main-state": "Aller à la vue par défaut" } }, "icon": { "icon": "Icône", "icons": "Icônes", + "custom": "Personnalisé", "select-icon": "Sélectionner une icône", "material-icons": "Icônes Material", "show-all": "Afficher toutes les icônes", @@ -9547,6 +10186,7 @@ "items-per-page-separator": "sur" }, "language": { + "auto": "Auto", "language": "Langue" } } \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-hi_IN.json b/ui-ngx/src/assets/locale/locale.constant-hi_IN.json new file mode 100644 index 0000000000..864a229e5d --- /dev/null +++ b/ui-ngx/src/assets/locale/locale.constant-hi_IN.json @@ -0,0 +1,9547 @@ +{ + "access": { + "unauthorized": "अनधिकृत", + "unauthorized-access": "अनधिकृत अभिगम", + "unauthorized-access-text": "इस संसाधन तक पहुँच के लिए आपको साइन इन करना आवश्यक है!", + "access-forbidden": "अभिगम निषिद्ध", + "access-forbidden-text": "आपके पास इस स्थान तक पहुँच अधिकार नहीं हैं!
    यदि आप अभी भी इस स्थान तक पहुँच प्राप्त करना चाहते हैं, तो किसी दूसरे उपयोगकर्ता से साइन इन करने का प्रयास करें।", + "refresh-token-expired": "सत्र समाप्त हो गया है", + "refresh-token-failed": "सत्र को रीफ़्रेश नहीं किया जा सका", + "permission-denied": "अनुमति अस्वीकृत", + "permission-denied-text": "आपके पास यह कार्य करने की अनुमति नहीं है!" + }, + "account": { + "account": "खाता", + "notification-settings": "सूचना सेटिंग्स" + }, + "action": { + "activate": "सक्रिय करें", + "suspend": "निलंबित करें", + "save": "सहेजें", + "saveAs": "इस रूप में सहेजें", + "move": "स्थानांतरित करें", + "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": "परिवर्तनों को अस्वीकार करें", + "decline": "अस्वीकार करें", + "close": "बंद करें", + "back": "वापस", + "run": "चलाएँ", + "sign-in": "साइन इन करें!", + "edit": "संपादित करें", + "view": "देखें", + "create": "बनाएँ", + "drag": "खींचें", + "refresh": "ताज़ा करें", + "undo": "पूर्ववत करें", + "copy": "कॉपी करें", + "paste": "पेस्ट करें", + "copy-reference": "संदर्भ कॉपी करें", + "paste-reference": "संदर्भ पेस्ट करें", + "import": "आयात करें", + "export": "निर्यात करें", + "share-via": "{{provider}} के माध्यम से साझा करें", + "select": "चयन करें", + "continue": "जारी रखें", + "discard-changes": "परिवर्तनों को त्यागें", + "download": "डाउनलोड करें", + "next": "अगला", + "next-with-label": "अगला: {{label}}", + "read-more": "और पढ़ें", + "hide": "छुपाएँ", + "test": "परीक्षण करें", + "done": "हो गया", + "print": "प्रिंट करें", + "restore": "पुनर्स्थापित करें", + "confirm": "पुष्टि करें", + "more": "अधिक", + "less": "कम", + "skip": "छोड़ें", + "send": "भेजें", + "reset": "रीसेट करें", + "show-more": "और दिखाएँ", + "dont-show-again": "दोबारा न दिखाएँ", + "see-documentation": "दस्तावेज़ देखें", + "clear": "साफ़ करें", + "upload": "अपलोड करें", + "delete-anyway": "फिर भी हटाएँ", + "delete-selected": "चयनित हटाएँ", + "set": "सेट करें" + }, + "aggregation": { + "aggregation": "संकलन", + "function": "डेटा संकलन फ़ंक्शन", + "limit": "अधिकतम मान", + "group-interval": "समूहीकरण अंतराल", + "min": "न्यूनतम", + "max": "अधिकतम", + "avg": "औसत", + "sum": "योग", + "count": "गणना", + "none": "कोई नहीं" + }, + "admin": { + "settings": "सेटिंग्स", + "general": "सामान्य", + "general-settings": "सामान्य सेटिंग्स", + "home-settings": "होम सेटिंग्स", + "home": "होम", + "outgoing-mail": "मेल सर्वर", + "outgoing-mail-settings": "आउटगोइंग मेल सर्वर सेटिंग्स", + "system-settings": "सिस्टम सेटिंग्स", + "test-mail-sent": "परीक्षण मेल सफलतापूर्वक भेजा गया!", + "base-url": "बेस URL", + "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 प्रोटोकॉल", + "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 संस्करण", + "enable-proxy": "प्रॉक्सी सक्षम करें", + "proxy-host": "प्रॉक्सी होस्ट", + "proxy-host-required": "प्रॉक्सी होस्ट आवश्यक है।", + "proxy-port": "प्रॉक्सी पोर्ट", + "proxy-port-required": "प्रॉक्सी पोर्ट आवश्यक है।", + "proxy-port-range": "प्रॉक्सी पोर्ट 1 से 65535 की सीमा में होना चाहिए।", + "proxy-user": "प्रॉक्सी उपयोगकर्ता", + "proxy-password": "प्रॉक्सी पासवर्ड", + "change-password": "पासवर्ड बदलें", + "send-test-mail": "टेस्ट मेल भेजें", + "sms-provider": "SMS प्रदाता", + "sms-provider-settings": "SMS प्रदाता सेटिंग्स", + "sms-provider-type": "SMS प्रदाता प्रकार", + "sms-provider-type-required": "SMS प्रदाता प्रकार आवश्यक है।", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID आवश्यक है", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key आवश्यक है", + "aws-region": "AWS Region", + "aws-region-required": "AWS Region आवश्यक है", + "number-from": "स्रोत फोन नंबर", + "number-from-required": "स्रोत फोन नंबर आवश्यक है।", + "number-to": "गंतव्य फोन नंबर", + "number-to-required": "गंतव्य फोन नंबर आवश्यक है।", + "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 संदेश", + "sms-message-required": "SMS संदेश आवश्यक है।", + "sms-message-max-length": "SMS संदेश 1600 अक्षरों से अधिक लंबा नहीं हो सकता", + "twilio-account-sid": "Twilio Account SID", + "twilio-account-sid-required": "Twilio Account SID आवश्यक है", + "twilio-account-token": "Twilio Account Token", + "twilio-account-token-required": "Twilio Account Token आवश्यक है", + "send-test-sms": "परीक्षण SMS भेजें", + "test-sms-sent": "परीक्षण SMS सफलतापूर्वक भेजा गया!", + "security-settings": "सुरक्षा सेटिंग्स", + "password-policy": "पासवर्ड नीति", + "minimum-password-length": "न्यूनतम पासवर्ड लंबाई", + "minimum-password-length-required": "न्यूनतम पासवर्ड लंबाई आवश्यक है", + "minimum-password-length-range": "न्यूनतम पासवर्ड लंबाई 6 से 50 की सीमा में होनी चाहिए", + "maximum-password-length": "अधिकतम पासवर्ड लंबाई", + "maximum-password-length-min": "अधिकतम पासवर्ड लंबाई कम-से-कम 6 होनी चाहिए", + "maximum-password-length-less-min": "अधिकतम पासवर्ड लंबाई न्यूनतम लंबाई से अधिक होनी चाहिए", + "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": "पासवर्ड पुन: उपयोग आवृत्ति (दिनों में) नकारात्मक नहीं हो सकती", + "allow-whitespace": "व्हाइटस्पेस की अनुमति दें", + "force-reset-password-if-no-valid": "अमान्य होने पर पासवर्ड रीसेट करने के लिए बाध्य करें", + "force-reset-password-if-no-valid-hint": "कृपया इस फ़ीचर को सक्षम करते समय सावधान रहें: यह अमान्य पासवर्ड वाले उपयोगकर्ताओं को ईमेल के माध्यम से अपना पासवर्ड रीसेट करने की आवश्यकता होगी।", + "general-policy": "सामान्य नीति", + "max-failed-login-attempts": "खाता लॉक होने से पहले असफल लॉगिन प्रयासों की अधिकतम संख्या", + "minimum-max-failed-login-attempts-range": "असफल लॉगिन प्रयासों की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "user-lockout-notification-email": "उपयोगकर्ता खाता लॉक होने की स्थिति में, ईमेल पर सूचना भेजें", + "user-activation-token-ttl": "उपयोगकर्ता सक्रियण लिंक TTL (घंटों में)", + "user-activation-token-ttl-range": "उपयोगकर्ता सक्रियण लिंक TTL 1 से 24 घंटे की सीमा में होना चाहिए", + "password-reset-token-ttl": "पासवर्ड रीसेट लिंक TTL (घंटों में)", + "password-reset-token-ttl-range": "पासवर्ड रीसेट लिंक TTL 1 से 24 घंटे की सीमा में होना चाहिए", + "mobile-secret-key-length": "मोबाइल सीक्रेट कुंजी की लंबाई", + "mobile-secret-key-length-range": "मोबाइल सीक्रेट कुंजी की लंबाई धनात्मक होनी चाहिए", + "domain-name": "डोमेन नाम", + "domain-name-unique": "डोमेन नाम और प्रोटोकॉल अद्वितीय होने चाहिए।", + "domain-name-max-length": "डोमेन नाम 256 अक्षरों से कम होना चाहिए", + "error-verification-url": "डोमेन नाम में '/' और ':' चिन्ह नहीं होने चाहिए। उदाहरण: thingsboard.io", + "connection-settings": "कनेक्शन सेटिंग्स", + "oauth2": { + "access-token-uri": "ऐक्सेस टोकन URI", + "access-token-uri-required": "ऐक्सेस टोकन URI आवश्यक है।", + "activate-user": "उपयोगकर्ता को सक्रिय करें", + "add-domain": "डोमेन जोड़ें", + "delete-domain": "डोमेन हटाएँ", + "add-provider": "प्रोवाइडर जोड़ें", + "delete-provider": "प्रोवाइडर हटाएँ", + "allow-user-creation": "उपयोगकर्ता बनाने की अनुमति दें", + "always-fullscreen": "हमेशा फुलस्क्रीन", + "authorization-uri": "ऑथराइजेशन URI", + "authorization-uri-required": "ऑथराइजेशन URI आवश्यक है।", + "add-client": "OAuth 2.0 क्लाइंट जोड़ें", + "client-details": "OAuth 2.0 क्लाइंट विवरण", + "client": "OAuth 2.0 क्लाइंट", + "clients": "OAuth 2.0 क्लाइंट्स", + "no-oauth2-clients": "कोई OAuth 2.0 क्लाइंट नहीं मिला", + "search-oauth2-clients": "OAuth 2.0 क्लाइंट्स खोजें", + "delete-client-title": "क्या आप वाकई OAuth 2.0 क्लाइंट '{{clientName}}' हटाना चाहते हैं?", + "delete-client-text": "सावधान रहें, पुष्टि के बाद क्लाइंट और उससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "delete-mobile-app-title": "क्या आप वाकई मोबाइल एप्लिकेशन '{{applicationName}}' हटाना चाहते हैं?", + "delete-mobile-app-text": "सावधान रहें, पुष्टि के बाद मोबाइल एप्लिकेशन और उससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "title": "शीर्षक", + "client-title-required": "शीर्षक आवश्यक है", + "client-title-max-length": "शीर्षक 100 अक्षरों से कम होना चाहिए", + "advanced-settings": "उन्नत सेटिंग्स", + "domain-details": "डोमेन विवरण", + "no-domains": "कोई डोमेन नहीं मिला", + "search-domains": "डोमेन खोजें", + "mobile-app-details": "मोबाइल एप्लिकेशन विवरण", + "add-mobile-app": "मोबाइल एप्लिकेशन जोड़ें", + "no-mobile-apps": "कोई मोबाइल एप्लिकेशन नहीं मिला", + "search-mobile-apps": "मोबाइल एप्लिकेशन खोजें", + "send-token": "टोकन भेजें", + "create-new": "नया बनाएँ", + "client-authentication-method": "क्लाइंट प्रमाणीकरण विधि", + "client-id": "क्लाइंट ID", + "client-id-required": "क्लाइंट ID आवश्यक है।", + "client-id-max-length": "क्लाइंट ID 256 अक्षरों से कम होना चाहिए", + "client-secret": "क्लाइंट सीक्रेट", + "client-secret-required": "क्लाइंट सीक्रेट आवश्यक है।", + "client-secret-max-length": "क्लाइंट सीक्रेट 2049 अक्षरों से कम होना चाहिए", + "custom-setting": "कस्टम सेटिंग्स", + "customer-name-pattern": "ग्राहक नाम पैटर्न", + "customer-name-pattern-max-length": "ग्राहक नाम पैटर्न 256 अक्षरों से कम होना चाहिए", + "default-dashboard-name": "डिफ़ॉल्ट डैशबोर्ड नाम", + "default-dashboard-name-max-length": "डिफ़ॉल्ट डैशबोर्ड नाम 256 अक्षरों से कम होना चाहिए", + "delete-domain-text": "सावधान रहें, पुष्टि के बाद डोमेन और सभी प्रदाता डेटा उपलब्ध नहीं रहेगा।", + "delete-domain-title": "क्या आप वाकई डोमेन '{{domainName}}' हटाना चाहते हैं?", + "delete-registration-text": "सावधान रहें, पुष्टि के बाद प्रदाता का डेटा उपलब्ध नहीं रहेगा।", + "delete-registration-title": "क्या आप वाकई प्रदाता '{{name}}' हटाना चाहते हैं?", + "email-attribute-key": "ईमेल विशेषता कुंजी", + "email-attribute-key-required": "ईमेल विशेषता कुंजी आवश्यक है।", + "email-attribute-key-max-length": "ईमेल विशेषता कुंजी 32 अक्षरों से कम होना चाहिए", + "first-name-attribute-key": "पहले नाम की विशेषता कुंजी", + "first-name-attribute-key-max-length": "पहले नाम की विशेषता कुंजी 32 अक्षरों से कम होना चाहिए", + "general": "सामान्य", + "jwk-set-uri": "JSON वेब की (JWK) URI", + "last-name-attribute-key": "अंतिम नाम की विशेषता कुंजी", + "last-name-attribute-key-max-length": "अंतिम नाम की विशेषता कुंजी 32 अक्षरों से कम होना चाहिए", + "login-button-icon": "लॉगिन बटन आइकन", + "login-button-label": "प्रदाता लेबल", + "login-button-label-placeholder": " $(Provider label) से लॉगिन करें", + "login-button-label-required": "लेबल आवश्यक है।", + "login-provider": "लॉगिन प्रदाता", + "mapper": "मैपर", + "new-domain": "नया डोमेन", + "oauth2": "OAuth 2.0", + "password-max-length": "पासवर्ड 256 अक्षरों से कम होना चाहिए", + "redirect-uri-template": "रीडायरेक्ट URI टेम्पलेट", + "copy-redirect-uri": "रीडायरेक्ट URI कॉपी करें", + "registration-id": "पंजीकरण ID", + "registration-id-required": "पंजीकरण ID आवश्यक है।", + "registration-id-unique": "पंजीकरण ID सिस्टम के लिए अद्वितीय होना चाहिए।", + "scope": "स्कोप", + "scope-required": "स्कोप आवश्यक है।", + "tenant-name-pattern": "टेनेंट नाम पैटर्न", + "tenant-name-pattern-required": "टेनेंट नाम पैटर्न आवश्यक है।", + "tenant-name-pattern-max-length": "टेनेंट नाम पैटर्न 256 अक्षरों से कम होना चाहिए", + "tenant-name-strategy": "टेनेंट नाम रणनीति", + "type": "मैपर प्रकार", + "uri-pattern-error": "अमान्य URI फ़ॉर्मेट।", + "url": "URL", + "url-pattern": "अमान्य URL फ़ॉर्मेट।", + "url-required": "URL आवश्यक है।", + "url-max-length": "URL 256 अक्षरों से कम होना चाहिए", + "user-info-uri": "उपयोगकर्ता जानकारी URI", + "user-info-uri-required": "उपयोगकर्ता जानकारी URI आवश्यक है।", + "username-max-length": "उपयोगकर्ता नाम 256 अक्षरों से कम होना चाहिए", + "user-name-attribute-name": "उपयोगकर्ता नाम विशेषता कुंजी", + "user-name-attribute-name-required": "उपयोगकर्ता नाम विशेषता कुंजी आवश्यक है", + "protocol": "प्रोटोकॉल", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "OAuth 2.0 सेटिंग्स सक्षम करें", + "disable": "OAuth 2.0 सेटिंग्स अक्षम करें", + "edge": "Edge पर प्रसार करें", + "edge-enable": "Edge पर प्रसार सक्षम करें", + "edge-disable": "Edge पर प्रसार अक्षम करें", + "domains": "डोमेन", + "mobile-apps": "मोबाइल एप्लिकेशन", + "mobile-package": "एप्लिकेशन पैकेज", + "mobile-package-placeholder": "उदा.: my.example.app", + "mobile-package-hint": "Android के लिए: आपका अपना अद्वितीय Application ID. iOS के लिए: Product bundle identifier.", + "mobile-package-unique": "एप्लिकेशन पैकेज अद्वितीय होना चाहिए।", + "mobile-package-required": "एप्लिकेशन पैकेज आवश्यक है।", + "mobile-package-max-length": "एप्लिकेशन पैकेज 256 अक्षरों से कम होना चाहिए", + "mobile-package-spaces": "एप्लिकेशन पैकेज में रिक्त स्थान नहीं होने चाहिए", + "mobile-app-secret": "एप्लिकेशन सीक्रेट", + "mobile-app-secret-hint": "Base64 एन्कोडेड स्ट्रिंग जो कम से कम 512 बिट डेटा का प्रतिनिधित्व करती हो।", + "mobile-app-secret-required": "एप्लिकेशन सीक्रेट आवश्यक है।", + "mobile-app-secret-min-length": "एप्लिकेशन सीक्रेट में कम से कम 512 बिट डेटा होना आवश्यक है।", + "mobile-app-secret-base64": "एप्लिकेशन सीक्रेट base64 फ़ॉर्मेट में होना चाहिए।", + "invalid-mobile-app-secret": "एप्लिकेशन सीक्रेट में केवल अल्फ़ान्यूमेरिक वर्ण होने चाहिए और इसकी लंबाई 16 से 2048 अक्षरों के बीच होनी चाहिए।", + "copy-mobile-app-secret": "एप्लिकेशन सीक्रेट कॉपी करें", + "delete-mobile-app": "एप्लिकेशन जानकारी हटाएँ", + "providers": "प्रदाता", + "platform-web": "वेब", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "सभी प्लेटफ़ॉर्म", + "smtp-provider": "SMTP प्रदाता", + "allowed-platforms": "अनुमत प्लेटफ़ॉर्म", + "authentication": "प्रमाणीकरण", + "basic": "बेसिक", + "provider": "प्रदाता", + "redirect-url": "रीडायरेक्ट URI", + "domain-name": "डोमेन नाम", + "domain-name-required": "डोमेन नाम आवश्यक है", + "redirect-url-template": "रीडायरेक्ट URI टेम्पलेट", + "microsoft-tenant-id": "डायरेक्टरी (टेनेंट) ID", + "microsoft-tenant-id-required": "डायरेक्टरी (टेनेंट) ID आवश्यक है", + "token-uri": "टोकन URI", + "token-uri-required": "टोकन URI आवश्यक है", + "redirect-uri": "रीडायरेक्ट URI", + "google-provider": "Google", + "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-port": "SMPP पोर्ट", + "smpp-port-required": "SMPP पोर्ट आवश्यक है", + "system-id": "सिस्टम ID", + "system-id-required": "सिस्टम ID आवश्यक है", + "password": "पासवर्ड", + "password-required": "पासवर्ड आवश्यक है", + "type-settings": "टाइप सेटिंग्स", + "source-settings": "सोर्स सेटिंग्स", + "destination-settings": "डेस्टिनेशन सेटिंग्स", + "additional-settings": "अतिरिक्त सेटिंग्स", + "system-type": "सिस्टम प्रकार", + "bind-type": "बाइंड प्रकार", + "service-type": "सर्विस प्रकार", + "source-address": "सोर्स पता", + "source-ton": "सोर्स TON", + "source-npi": "सोर्स NPI", + "destination-ton": "डेस्टिनेशन TON (Type of Number)", + "destination-npi": "डेस्टिनेशन NPI (Numbering Plan Identification)", + "address-range": "पते की रेंज", + "coding-scheme": "कोडिंग स्कीम", + "bind-type-tx": "ट्रांसमीटर", + "bind-type-rx": "रिसीवर", + "bind-type-trx": "ट्रांससीवर", + "ton-unknown": "अज्ञात", + "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 क्लाइंट Id (WAP Forum द्वारा परिभाषित किया जाना है)", + "scheme-smsc": "0 - SMSC डिफ़ॉल्ट अल्फ़ाबेट (short और long code के लिए ASCII और toll-free के लिए GSM)", + "scheme-ia5": "1 - IA5 (short और long code के लिए ASCII, toll-free के लिए Latin 9 (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octet Unspecified (8-bit binary)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octet Unspecified (8-bit binary)", + "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 - पिक्टोग्राम एन्कोडिंग", + "scheme-music-codes": "10 - म्यूजिक कोड्स (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - एक्सटेंडेड Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - कोरियन ग्राफ़िक कैरेक्टर सेट (KS C 5601/KS X 1001)" + }, + "queue-select-name": "क्यू नाम चुनें", + "queue-name": "नाम", + "queue-name-required": "क्यू नाम आवश्यक है!", + "queues": "क्यूज़", + "queue-partitions": "पार्टिशन", + "queue-submit-strategy": "सबमिट स्ट्रेटेजी", + "queue-processing-strategy": "प्रोसेसिंग स्ट्रेटेजी", + "queue-configuration": "क्यू कॉन्फ़िगरेशन", + "repository-settings": "रिपॉज़िटरी सेटिंग्स", + "repository": "रिपॉज़िटरी", + "repository-url": "रिपॉज़िटरी URL", + "repository-url-required": "रिपॉज़िटरी URL आवश्यक है।", + "default-branch": "डिफ़ॉल्ट ब्रांच नाम", + "repository-read-only": "केवल पढ़ने के लिए", + "show-merge-commits": "मर्ज कमिट दिखाएँ", + "authentication-settings": "प्रमाणीकरण सेटिंग्स", + "auth-method": "प्रमाणीकरण विधि", + "auth-method-username-password": "पासवर्ड / एक्सेस टोकन", + "auth-method-username-password-hint": "GitHub उपयोगकर्ताओं को रिपॉज़िटरी पर लिखने की अनुमति वाले एक्सेस tokens का उपयोग करना आवश्यक है।", + "auth-method-private-key": "प्राइवेट की", + "password-access-token": "पासवर्ड / एक्सेस टोकन", + "change-password-access-token": "पासवर्ड / एक्सेस टोकन बदलें", + "private-key": "प्राइवेट की", + "drop-private-key-file-or": "प्राइवेट की फ़ाइल को ड्रैग और ड्रॉप करें या", + "passphrase": "पासफ़्रेज़", + "enter-passphrase": "पासफ़्रेज़ दर्ज करें", + "change-passphrase": "पासफ़्रेज़ बदलें", + "check-access": "एक्सेस जाँचें", + "check-repository-access-success": "रिपॉज़िटरी एक्सेस सफलतापूर्वक सत्यापित हो गया!", + "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": "सावधान रहें, पुष्टि के बाद ऑटो-कमिट सेटिंग्स हटा दी जाएँगी और सभी एंटिटीज़ के लिए ऑटो-कमिट अक्षम हो जाएगा।", + "mobile-app": { + "mobile-app": "मोबाइल ऐप", + "mobile-app-qr-code-widget-settings": "मोबाइल ऐप QR कोड विजेट सेटिंग्स", + "applications": "एप्लिकेशन", + "default": "डिफ़ॉल्ट", + "custom": "कस्टम", + "android": "Android", + "ios": "iOS", + "appearance": "दिखावट", + "appearance-on-home-page": "होम पेज पर दिखावट", + "enabled": "सक्रिय", + "disabled": "निष्क्रिय", + "badges": "बैज", + "label": "लेबल", + "label-required": "लेबल आवश्यक है", + "label-max-length": "लेबल 50 अक्षरों से कम या बराबर होना चाहिए", + "right": "दायाँ", + "left": "बायाँ", + "set": "सेट करें", + "preview": "पूर्वावलोकन", + "connect-mobile-app": "मोबाइल ऐप कनेक्ट करें", + "use-system-settings": "सिस्टम सेटिंग्स का उपयोग करें" + }, + "2fa": { + "2fa": "दो-कारक प्रमाणीकरण", + "available-providers": "उपलब्ध प्रदाता", + "issuer-name": "जारीकर्ता नाम", + "issuer-name-required": "जारीकर्ता नाम आवश्यक है।", + "max-verification-failures-before-user-lockout": "उपयोगकर्ता लॉक होने से पहले अधिकतम सत्यापन विफलताएँ", + "max-verification-failures-before-user-lockout-pattern": "अधिकतम सत्यापन विफलताएँ धनात्मक पूर्णांक होनी चाहिए।", + "number-of-checking-attempts": "जाँच प्रयासों की संख्या", + "number-of-checking-attempts-pattern": "जाँच प्रयासों की संख्या धनात्मक पूर्णांक होनी चाहिए।", + "number-of-checking-attempts-required": "जाँच प्रयासों की संख्या आवश्यक है।", + "number-of-codes": "कोड की संख्या", + "number-of-codes-pattern": "कोड की संख्या धनात्मक पूर्णांक होनी चाहिए।", + "number-of-codes-required": "कोड की संख्या आवश्यक है।", + "provider": "प्रदाता", + "retry-verification-code-period": "सत्यापन कोड पुनः प्रयास अवधि", + "retry-verification-code-period-pattern": "न्यूनतम अवधि 5 सेकंड है।", + "retry-verification-code-period-required": "सत्यापन कोड पुनः प्रयास अवधि आवश्यक है।", + "total-allowed-time-for-verification": "सत्यापन के लिए कुल अनुमत समय", + "total-allowed-time-for-verification-pattern": "न्यूनतम कुल अनुमत समय 60 सेकंड है।", + "total-allowed-time-for-verification-required": "कुल अनुमत समय आवश्यक है।", + "use-system-two-factor-auth-settings": "सिस्टम दो-कारक प्रमाणीकरण सेटिंग्स का उपयोग करें", + "verification-code-check-rate-limit": "सत्यापन कोड जाँच दर सीमा", + "verification-code-lifetime": "सत्यापन कोड जीवनकाल", + "verification-code-lifetime-pattern": "सत्यापन कोड जीवनकाल धनात्मक पूर्णांक होना चाहिए।", + "verification-code-lifetime-required": "सत्यापन कोड जीवनकाल आवश्यक है।", + "verification-message-template": "सत्यापन संदेश टेम्पलेट", + "verification-limitations": "सत्यापन सीमाएँ", + "verification-message-template-pattern": "सत्यापन संदेश में यह पैटर्न होना चाहिए: ${code}", + "verification-message-template-required": "सत्यापन संदेश टेम्पलेट आवश्यक है।", + "within-time": "समय के भीतर", + "within-time-pattern": "समय धनात्मक पूर्णांक होना चाहिए।", + "within-time-required": "समय आवश्यक है।" + }, + "jwt": { + "security-settings": "JWT सुरक्षा सेटिंग्स", + "issuer-name": "जारीकर्ता नाम", + "issuer-name-required": "जारीकर्ता नाम आवश्यक है।", + "signings-key": "साइनिंग की", + "signings-key-hint": "Base64 एन्कोडेड स्ट्रिंग जो कम से कम 512 बिट डेटा का प्रतिनिधित्व करती हो।", + "signings-key-required": "साइनिंग की आवश्यक है।", + "signings-key-min-length": "साइनिंग की में कम से कम 512 बिट डेटा होना आवश्यक है।", + "signings-key-base64": "साइनिंग की base64 फ़ॉर्मेट में होनी चाहिए।", + "expiration-time": "टोकन समाप्ति समय (सेकंड में)", + "expiration-time-required": "टोकन समाप्ति समय आवश्यक है।", + "expiration-time-max": "अधिकतम अनुमत समय 2147483647 सेकंड (68 वर्ष) है।", + "expiration-time-min": "न्यूनतम समय 60 सेकंड (1 मिनट) है।", + "refresh-expiration-time": "रिफ़्रेश टोकन समाप्ति समय (सेकंड में)", + "refresh-expiration-time-required": "रिफ़्रेश टोकन समाप्ति समय आवश्यक है।", + "refresh-expiration-time-max": "अधिकतम अनुमत समय 2147483647 सेकंड (68 वर्ष) है।", + "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 सेटिंग्स", + "mobile-settings": "मोबाइल सेटिंग्स", + "firebase-service-account-file": "Firebase सर्विस अकाउंट क्रेडेंशियल्स JSON फ़ाइल", + "select-firebase-service-account-file": "अपनी Firebase सर्विस अकाउंट क्रेडेंशियल्स फ़ाइल को ड्रैग और ड्रॉप करें या ", + "trendz": "Trendz", + "trendz-settings": "Trendz सेटिंग्स", + "trendz-url": "Trendz URL", + "trendz-url-required": "Trendz URL आवश्यक है", + "trendz-api-key": "Trendz API की", + "trendz-enable": "Trendz सक्षम करें" + }, + "alarm": { + "alarm": "अलार्म", + "alarms": "अलार्म", + "all-alarms": "सभी अलार्म", + "select-alarm": "अलार्म चुनें", + "no-alarms-matching": "'{{entity}}' से मेल खाता कोई अलार्म नहीं मिला।", + "alarm-required": "अलार्म आवश्यक है", + "alarm-filter": "अलार्म फ़िल्टर", + "filter": "फ़िल्टर", + "alarm-status": "अलार्म स्थिति", + "alarm-status-list": "अलार्म स्थिति सूची", + "any-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": "विवरण", + "originator-label": "स्रोत लेबल", + "assign": "असाइन करें", + "assignments": "असाइनमेंट", + "assignee": "असाइन प्राप्तकर्ता", + "assignee-id": "असाइन प्राप्तकर्ता ID", + "assignee-first-name": "असाइन प्राप्तकर्ता पहला नाम", + "assignee-last-name": "असाइन प्राप्तकर्ता अंतिम नाम", + "assignee-email": "असाइन प्राप्तकर्ता ईमेल", + "unassigned": "असाइन नहीं", + "user-deleted": "उपयोगकर्ता हटाया गया", + "assignee-not-set": "सभी", + "status": "स्थिति", + "alarm-details": "अलार्म विवरण", + "start-time": "आरंभ समय", + "assign-time": "असाइन समय", + "end-time": "समाप्ति समय", + "ack-time": "स्वीकार किए जाने का समय", + "clear-time": "क्लियर किए जाने का समय", + "duration": "अवधि", + "alarm-severity": "अलार्म गंभीरता", + "alarm-severity-list": "अलार्म गंभीरता सूची", + "any-severity": "कोई भी गंभीरता", + "severity-critical": "क्रिटिकल", + "severity-major": "मेजर", + "severity-minor": "माइनर", + "severity-warning": "चेतावनी", + "severity-indeterminate": "अनिर्धारित", + "acknowledge": "स्वीकार करें", + "clear": "क्लियर करें", + "delete": "हटाएँ", + "search": "अलार्म खोजें", + "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 {1 अलार्म} other {# अलार्म} } स्वीकार करें", + "aknowledge-alarms-text": "क्या आप वाकई { count, plural, =1 {1 अलार्म} other {# अलार्म} } स्वीकार करना चाहते हैं?", + "aknowledge-alarm-title": "अलार्म स्वीकार करें", + "aknowledge-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-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-error-min": "न्यूनतम मान 10 है।", + "alarm-types": "अलार्म प्रकार", + "alarm-type-list": "अलार्म प्रकार सूची", + "any-type": "कोई भी प्रकार", + "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": "उपनाम जोड़ें", + "edit": "उपनाम संपादित करें", + "name": "उपनाम नाम", + "name-required": "उपनाम नाम आवश्यक है", + "duplicate-alias": "इसी नाम वाला उपनाम पहले से मौजूद है।", + "filter-type-single-entity": "एकल एंटिटी", + "filter-type-entity-list": "एंटिटी सूची", + "filter-type-entity-name": "एंटिटी नाम", + "filter-type-entity-type": "एंटिटी प्रकार", + "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-edge-type": "Edge प्रकार", + "filter-type-edge-type-description": "प्रकार '{{edgeTypes}}' के Edge", + "filter-type-edge-type-and-name-description": "प्रकार '{{edgeTypes}}' के Edge जिनका नाम '{{prefix}}' से शुरू होता है", + "filter-type-relations-query": "रिलेशन क्वेरी", + "filter-type-relations-query-description": "{{entities}} जिनके पास {{relationType}} रिलेशन {{direction}} {{rootEntity}} है", + "filter-type-edge-search-query": "Edge सर्च क्वेरी", + "filter-type-edge-search-query-description": "टाइप {{edgeTypes}} वाले Edge जिनके पास {{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}} है", + "filter-type-apiUsageState": "API उपयोग स्थिति", + "entity-filter": "एंटिटी फ़िल्टर", + "resolve-multiple": "कई एंटिटीज़ के रूप में रिज़ॉल्व करें", + "resolve-multiple-hint": "सभी फ़िल्टर की गई एंटिटीज़ से डेटा को एक साथ दिखाने के लिए सक्षम करें। \nअक्षम होने पर, विजेट केवल चयनित एंटिटी से डेटा दिखाता है।", + "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": "डिफ़ॉल्ट रूप से", + "query-options": "क्वेरी विकल्प", + "max-relation-level": "अधिकतम रिलेशन स्तर", + "unlimited-level": "असीमित स्तर", + "state-entity": "डैशबोर्ड स्टेट एंटिटी", + "all-entities": "सभी एंटिटीज़", + "any-relation": "कोई भी" + }, + "asset": { + "asset": "एसेट", + "assets": "एसेट", + "management": "एसेट प्रबंधन", + "view-assets": "एसेट देखें", + "add": "एसेट जोड़ें", + "asset-type-max-length": "एसेट प्रकार 256 अक्षरों से कम होना चाहिए", + "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": "नाम आवश्यक है।", + "name-max-length": "नाम 256 अक्षरों से कम होना चाहिए", + "label-max-length": "लेबल 256 अक्षरों से कम होना चाहिए", + "description": "विवरण", + "type": "प्रकार", + "type-required": "प्रकार आवश्यक है।", + "details": "विवरण", + "events": "घटनाएँ", + "add-asset-text": "नया एसेट जोड़ें", + "asset-details": "एसेट विवरण", + "assign-assets": "एसेट असाइन करें", + "assign-assets-text": "कस्टमर को { count, plural, =1 {1 एसेट} other {# एसेट} } असाइन करें", + "assign-asset-to-edge-title": "एसेट को Edge पर असाइन करें", + "assign-asset-to-edge-text": "कृपया Edge को असाइन करने के लिए एसेट चुनें", + "delete-assets": "एसेट हटाएँ", + "unassign-assets": "एसेट अनअसाइन करें", + "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 {1 एसेट} other {# एसेट} } हटाना चाहते हैं?", + "delete-assets-action-title": "{ count, plural, =1 {1 एसेट} 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 एसेट} other {# एसेट} } को अनअसाइन करना चाहते हैं?", + "unassign-assets-text": "पुष्टि के बाद सभी चयनित एसेट अनअसाइन हो जाएँगे और कस्टमर द्वारा एक्सेस नहीं किए जा सकेंगे।", + "copyId": "एसेट ID कॉपी करें", + "idCopiedMessage": "एसेट ID क्लिपबोर्ड पर कॉपी कर दी गई है", + "select-asset": "एसेट चुनें", + "no-assets-matching": "'{{entity}}' से मेल खाता कोई एसेट नहीं मिला।", + "asset-required": "एसेट आवश्यक है।", + "name-starts-with": "एसेट नाम अभिव्यक्ति", + "help-text": "ज़रूरत के अनुसार '%' का उपयोग करें: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "एसेट खोजें", + "import": "एसेट आयात करें", + "asset-file": "एसेट फ़ाइल", + "label": "लेबल", + "assign-asset-to-edge": "एसेट को Edge पर असाइन करें", + "unassign-asset-from-edge": "एसेट अनअसाइन करें", + "unassign-asset-from-edge-title": "क्या आप वाकई एसेट '{{assetName}}' को अनअसाइन करना चाहते हैं?", + "unassign-asset-from-edge-text": "पुष्टि के बाद एसेट अनअसाइन हो जाएगा और Edge द्वारा एक्सेस नहीं किया जा सकेगा।", + "unassign-assets-from-edge-title": "क्या आप वाकई { count, plural, =1 {1 एसेट} other {# एसेट} } को अनअसाइन करना चाहते हैं?", + "unassign-assets-from-edge-text": "पुष्टि के बाद सभी चयनित एसेट अनअसाइन हो जाएँगे और Edge द्वारा एक्सेस नहीं किए जा सकेंगे।", + "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": "साझा विशेषताएँ", + "scope-client-short": "क्लाइंट", + "scope-server-short": "सर्वर", + "scope-shared-short": "साझा", + "scope-latest-short": "नवीनतम", + "scope-any": "कोई भी", + "add": "विशेषता जोड़ें", + "key": "कुंजी", + "key-max-length": "कुंजी 256 अक्षरों से कम होनी चाहिए", + "last-update-time": "अंतिम अपडेट समय", + "key-required": "विशेषता कुंजी आवश्यक है।", + "value": "मान", + "value-required": "विशेषता मान आवश्यक है।", + "telemetry-key-required": "टेलीमेट्री कुंजी आवश्यक है", + "telemetry-value-required": "टेलीमेट्री मान आवश्यक है", + "delete-attributes-title": "क्या आप वाकई { count, plural, =1 {1 विशेषता} 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 {1 विशेषता} other {# विशेषताएँ} } चयनित", + "selected-telemetry": "{ count, plural, =1 {1 टेलीमेट्री यूनिट} other {# टेलीमेट्री यूनिट} } चयनित", + "no-attributes-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-features": "API सुविधाएँ", + "api-usage": "API उपयोग", + "alarm": "अलार्म", + "alarms-created": "बनाए गए अलार्म", + "queue-stats": "क्यू आँकड़े", + "processing-failures-and-timeouts": "प्रोसेसिंग विफलताएँ और टाइमआउट", + "exceptions": "अपवाद", + "alarms-created-daily-activity": "बनाए गए अलार्म की दैनिक गतिविधि", + "alarms-created-hourly-activity": "बनाए गए अलार्म की प्रति घंटा गतिविधि", + "alarms-created-monthly-activity": "बनाए गए अलार्म की मासिक गतिविधि", + "data-points": "डेटा पॉइंट", + "data-points-storage-days": "डेटा पॉइंट भंडारण (दिनों में)", + "device-api": "डिवाइस API", + "email": "ईमेल", + "email-messages": "ईमेल संदेश", + "email-messages-daily-activity": "ईमेल संदेशों की दैनिक गतिविधि", + "email-messages-monthly-activity": "ईमेल संदेशों की मासिक गतिविधि", + "executions": "निष्पादन", + "scripts": "स्क्रिप्ट", + "scripts-hourly-activity": "स्क्रिप्ट की प्रति घंटा गतिविधि", + "scripts-daily-activity": "स्क्रिप्ट की दैनिक गतिविधि", + "scripts-monthly-activity": "स्क्रिप्ट की मासिक गतिविधि", + "javascript": "JavaScript", + "javascript-executions": "JavaScript निष्पादन", + "tbel": "TBEL", + "tbel-executions": "TBEL निष्पादन", + "latest-error": "नवीनतम त्रुटि", + "messages": "संदेश", + "notifications": "सूचनाएँ", + "notifications-email-sms": "सूचनाएँ (Email/SMS)", + "notifications-hourly-activity": "सूचनाओं की प्रति घंटा गतिविधि", + "permanent-failures": "${entityName} स्थायी विफलताएँ", + "permanent-timeouts": "${entityName} स्थायी टाइमआउट", + "processing-failures": "${entityName} प्रोसेसिंग विफलताएँ", + "processing-timeouts": "${entityName} प्रोसेसिंग टाइमआउट", + "rule-chain": "रूल चेन", + "rule-engine": "रूल इंजन", + "rule-engine-daily-activity": "रूल इंजन की दैनिक सक्रियता", + "rule-engine-executions": "रूल इंजन निष्पादन", + "rule-engine-hourly-activity": "रूल इंजन की प्रति घंटा सक्रियता", + "rule-engine-monthly-activity": "रूल इंजन की मासिक सक्रियता", + "rule-engine-statistics": "रूल इंजन सांख्यिकी", + "rule-node": "रूल नोड", + "sms": "एसएमएस", + "sms-messages": "एसएमएस संदेश", + "sms-messages-daily-activity": "एसएमएस संदेशों की दैनिक सक्रियता", + "sms-messages-monthly-activity": "एसएमएस संदेशों की मासिक सक्रियता", + "successful": "${entityName} सफल", + "telemetry": "टेलीमेट्री", + "telemetry-persistence": "टेलीमेट्री परसिस्टेंस", + "telemetry-persistence-daily-activity": "टेलीमेट्री परसिस्टेंस दैनिक गतिविधि", + "telemetry-persistence-hourly-activity": "टेलीमेट्री परसिस्टेंस प्रति घंटा गतिविधि", + "telemetry-persistence-monthly-activity": "टेलीमेट्री परसिस्टेंस मासिक गतिविधि", + "transport": "ट्रांसपोर्ट", + "transport-daily-activity": "ट्रांसपोर्ट की दैनिक सक्रियता", + "transport-data-points": "ट्रांसपोर्ट डेटा पॉइंट्स", + "transport-messages": "ट्रांसपोर्ट संदेश", + "view-details": "विवरण देखें", + "view-statistics": "सांख्यिकी देखें" + }, + "api-limit": { + "cassandra-write-queries-core": "Rest API Cassandra लिखने की क्वेरीज़", + "cassandra-read-queries-core": "Rest API और WS टेलीमेट्री Cassandra पढ़ने की क्वेरीज़", + "cassandra-write-queries-rule-engine": "Rule Engine टेलीमेट्री Cassandra लिखने की क्वेरीज़", + "cassandra-read-queries-rule-engine": "Rule Engine टेलीमेट्री Cassandra पढ़ने की क्वेरीज़", + "cassandra-write-queries-monolith": "Monolith टेलीमेट्री Cassandra लिखने की क्वेरीज़", + "cassandra-read-queries-monolith": "Monolith टेलीमेट्री Cassandra पढ़ने की क्वेरीज़", + "entity-version-creation": "एंटिटी वर्ज़न निर्माण", + "entity-version-load": "एंटिटी वर्ज़न लोड", + "notification-requests": "सूचना अनुरोध", + "notification-requests-per-rule": "प्रति Rule के सूचना अनुरोध", + "rest-api-requests": "REST API अनुरोध", + "rest-api-requests-per-customer": "प्रति कस्टमर REST API अनुरोध", + "transport-messages": "ट्रांसपोर्ट संदेश", + "transport-messages-per-device": "प्रति डिवाइस ट्रांसपोर्ट संदेश", + "transport-messages-per-gateway": "प्रति Gateway ट्रांसपोर्ट संदेश", + "transport-messages-per-gateway-device": "प्रति Gateway डिवाइस ट्रांसपोर्ट संदेश", + "ws-updates-per-session": "प्रति सत्र WS अपडेट", + "edge-events": "Edge ईवेंट", + "edge-events-per-edge": "प्रति Edge ईवेंट", + "edge-uplink-messages": "Edge uplink संदेश", + "edge-uplink-messages-per-edge": "प्रति Edge uplink संदेश" + }, + "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-assigned-to-edge": "Edge को असाइन किया गया", + "type-unassigned-from-edge": "Edge से अनअसाइन किया गया", + "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-alarm-delete": "अलार्म हटाया गया", + "type-alarm-assign": "अलार्म असाइन किया गया", + "type-alarm-unassign": "अलार्म अनअसाइन किया गया", + "type-added-comment": "टिप्पणी जोड़ी गई", + "type-updated-comment": "टिप्पणी अपडेट की गई", + "type-deleted-comment": "टिप्पणी हटाई गई", + "type-login": "लॉगिन", + "type-logout": "लॉगआउट", + "type-lockout": "लॉकआउट", + "status-success": "सफलता", + "status-failure": "विफलता", + "audit-log-details": "ऑडिट लॉग विवरण", + "no-audit-logs-prompt": "कोई लॉग नहीं मिला", + "action-data": "कार्रवाई डेटा", + "failure-details": "विफलता विवरण", + "search": "ऑडिट लॉग खोजें", + "clear-search": "खोज साफ़ करें", + "type-assigned-from-tenant": "टेनेंट से असाइन किया गया", + "type-assigned-to-tenant": "टेनेंट को असाइन किया गया", + "type-provision-success": "डिवाइस प्रोविज़न किया गया", + "type-provision-failure": "डिवाइस प्रोविज़निंग विफल रही", + "type-timeseries-updated": "टेलीमेट्री अपडेट की गई", + "type-timeseries-deleted": "टेलीमेट्री हटाई गई", + "type-sms-sent": "SMS भेजा गया" + }, + "debug-settings": { + "label": "डिबग कॉन्फ़िगरेशन", + "on-failure": "केवल विफलताएँ (24/7)", + "all-messages": "सभी संदेश ({{time}})", + "failures": "विफलताएँ", + "entity": "एंटिटी", + "hint": { + "main-limited": "प्रति {{time}} अधिकतम {{msg}} {{entity}} डिबग संदेश रिकॉर्ड किए जाएँगे।", + "on-failure": "केवल त्रुटि संदेश लॉग करें।", + "all-messages": "सभी डिबग संदेश लॉग करें।" + } + }, + "calculated-fields": { + "expression": "व्यंजक", + "no-found": "कोई कैलक्युलेटेड फ़ील्ड नहीं मिला", + "list": "{ count, plural, =1 {एक कैलक्युलेटेड फ़ील्ड} other {# कैलक्युलेटेड फ़ील्ड्स की सूची} }", + "selected-fields": "{ count, plural, =1 {1 कैलक्युलेटेड फ़ील्ड} other {# कैलक्युलेटेड फ़ील्ड्स} } चुना गया", + "type": { + "simple": "सिंपल", + "script": "स्क्रिप्ट" + }, + "arguments": "आर्गुमेंट्स", + "decimals-by-default": "डिफ़ॉल्ट दशमलव", + "debugging": "कैलक्युलेटेड फ़ील्ड डिबगिंग", + "argument-name": "आर्गुमेंट नाम", + "datasource": "डेटा स्रोत", + "add-argument": "आर्गुमेंट जोड़ें", + "test-script-function": "स्क्रिप्ट फ़ंक्शन टेस्ट करें", + "no-arguments": "कोई आर्गुमेंट कॉन्फ़िगर नहीं है", + "argument-settings": "आर्गुमेंट सेटिंग्स", + "argument-current": "करेंट एंटिटी", + "argument-current-tenant": "करेंट टेनेंट", + "argument-device": "डिवाइस", + "argument-asset": "एसेट", + "argument-customer": "कस्टमर", + "argument-tenant": "करेंट टेनेंट", + "argument-owner": "करेंट ओनर", + "argument-type": "आर्गुमेंट प्रकार", + "attribute": "विशेषता", + "copy-argument-name": "आर्गुमेंट नाम कॉपी करें", + "timeseries-key": "टाइम सीरीज़ कुंजी", + "device-name": "डिवाइस नाम", + "latest-telemetry": "नवीनतम टेलीमेट्री", + "rolling": "टाइम सीरीज़ रोलिंग", + "attribute-scope": "विशेषता स्कोप", + "server-attributes": "सर्वर विशेषताएँ", + "client-attributes": "क्लाइंट विशेषताएँ", + "shared-attributes": "शेयर्ड विशेषताएँ", + "attribute-key": "विशेषता कुंजी", + "default-value": "डिफ़ॉल्ट मान", + "limit": "अधिकतम मान", + "time-window": "टाइम विंडो", + "customer-name": "कस्टमर नाम", + "asset-name": "एसेट नाम", + "timeseries": "टाइम सीरीज़", + "output": "आउटपुट", + "create": "नई कैलक्युलेटेड फ़ील्ड बनाएँ", + "file": "कैलक्युलेटेड फ़ील्ड फ़ाइल", + "invalid-file-error": "अमान्य फ़ाइल फ़ॉर्मेट। कृपया सुनिश्चित करें कि फ़ाइल एक मान्य JSON फ़ाइल है।", + "import": "कैलक्युलेटेड फ़ील्ड इम्पोर्ट करें", + "export": "कैलक्युलेटेड फ़ील्ड एक्सपोर्ट करें", + "export-failed-error": "कैलक्युलेटेड फ़ील्ड एक्सपोर्ट नहीं हो सका: {{error}}", + "output-type": "आउटपुट प्रकार", + "delete-title": "क्या आप वाकई कैलक्युलेटेड फ़ील्ड '{{title}}' को हटाना चाहते हैं?", + "delete-text": "सावधान रहें, पुष्टि के बाद कैलक्युलेटेड फ़ील्ड और उससे संबंधित सभी डेटा हमेशा के लिए मिटा दिए जाएँगे।", + "delete-multiple-title": "क्या आप वाकई { count, plural, =1 {1 कैलक्युलेटेड फ़ील्ड} other {# कैलक्युलेटेड फ़ील्ड्स} } को हटाना चाहते हैं?", + "delete-multiple-text": "सावधान रहें, पुष्टि के बाद सभी चुनी हुई कैलक्युलेटेड फ़ील्ड्स हटा दी जाएँगी और उनसे संबंधित सारा डेटा हमेशा के लिए मिटा दिया जाएगा।", + "test-with-this-message": "इस संदेश के साथ परीक्षण करें", + "use-latest-timestamp": "नवीनतम टाइमस्टैम्प का उपयोग करें", + "hint": { + "arguments-simple-with-rolling": "सिंपल टाइप की कैलक्युलेटेड फ़ील्ड में 'टाइम सीरीज़ रोलिंग' टाइप की कीज़ नहीं होनी चाहिए।", + "arguments-empty": "आर्गुमेंट्स खाली नहीं होने चाहिए।", + "expression-required": "एक्सप्रेशन आवश्यक है।", + "expression-invalid": "एक्सप्रेशन अमान्य है।", + "expression-max-length": "एक्सप्रेशन की लंबाई 255 अक्षरों से कम होनी चाहिए।", + "argument-name-required": "आर्गुमेंट नाम आवश्यक है।", + "argument-name-pattern": "आर्गुमेंट नाम अमान्य है।", + "argument-name-duplicate": "ऐसे नाम वाला आर्गुमेंट पहले से मौजूद है।", + "argument-name-max-length": "आर्गुमेंट नाम 256 अक्षरों से कम होना चाहिए।", + "argument-name-forbidden": "यह आर्गुमेंट नाम आरक्षित है और इसका उपयोग नहीं किया जा सकता।", + "argument-type-required": "आर्गुमेंट टाइप आवश्यक है।", + "max-args": "आर्गुमेंट्स की अधिकतम संख्या पूरी हो चुकी है।", + "decimals-range": "डिफ़ॉल्ट दशमलव 0 से 15 के बीच की संख्या होनी चाहिए।", + "expression": "डिफ़ॉल्ट एक्सप्रेशन दिखाता है कि तापमान को फ़ारेनहाइट से सेल्सियस में कैसे बदला जाए।", + "arguments-entity-not-found": "आर्गुमेंट की लक्षित एंटिटी नहीं मिली।", + "use-latest-timestamp": "अगर सक्षम किया गया, तो कैलक्युलेटेड मान को सर्वर समय की बजाय आर्गुमेंट्स की टेलीमेट्री के नवीनतम टाइमस्टैम्प के साथ सेव किया जाएगा।" + } + }, + "ai-models": { + "ai-models": "AI मॉडल्स", + "ai-model": "AI मॉडल", + "model": "मॉडल", + "name": "नाम", + "ai-provider": "AI प्रदाता", + "no-found": "कोई AI मॉडल नहीं मिला", + "list": "{ count, plural, =1 {एक मॉडल} other {# मॉडलों की सूची} }", + "selected-fields": "{ count, plural, =1 {1 मॉडल} other {# मॉडल्स} } चयनित", + "add": "मॉडल जोड़ें", + "delete-model-title": "क्या आप वाकई मॉडल '{{modelName}}' हटाना चाहते हैं?", + "delete-model-text": "सावधान रहें, पुष्टि के बाद मॉडल और उससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "delete-models-title": "क्या आप वाकई { count, plural, =1 {1 मॉडल} other {# मॉडल्स} } हटाना चाहते हैं?", + "delete-models-text": "सावधान रहें, पुष्टि के बाद सभी चयनित मॉडल्स हटा दिए जाएँगे और उनसे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "ai-providers": { + "openai": "OpenAI", + "azure-openai": "Azure OpenAI", + "google-ai-gemini": "Google AI Gemini", + "google-vertex-ai-gemini": "Google Vertex AI Gemini", + "mistral-ai": "Mistral AI", + "anthropic": "Anthropic", + "amazon-bedrock": "Amazon Bedrock", + "github-models": "GitHub Models", + "ollama": "Ollama" + }, + "name-required": "नाम आवश्यक है।", + "name-max-length": "नाम 255 अक्षरों या उससे कम का होना चाहिए।", + "provider": "प्रदाता", + "api-key": "API कुंजी", + "api-key-required": "API कुंजी आवश्यक है।", + "api-key-open-ai-required": "आधिकारिक OpenAI API का उपयोग करते समय API कुंजी आवश्यक है।", + "project-id": "प्रोजेक्ट ID", + "project-id-required": "प्रोजेक्ट ID आवश्यक है।", + "location": "स्थान", + "location-required": "स्थान आवश्यक है।", + "service-account-key-file": "सर्विस अकाउंट की फ़ाइल", + "service-account-key-file-required": "सर्विस अकाउंट की फ़ाइल आवश्यक है।", + "no-file": "कोई फ़ाइल चयनित नहीं है।", + "drop-file": "फ़ाइल यहाँ छोड़ें या फ़ाइल चुनने के लिए क्लिक करें।", + "personal-access-token": "पर्सनल ऐक्सेस टोकन", + "personal-access-token-required": "पर्सनल ऐکسेस टोकन आवश्यक है।", + "configuration": "कॉन्फ़िगरेशन", + "model-id": "मॉडल ID", + "model-id-required": "मॉडल ID आवश्यक है।", + "deployment-name": "डिप्लॉयमेंट नाम", + "deployment-name-required": "डिप्लॉयमेंट नाम आवश्यक है।", + "set": "सेट करें", + "region": "रीजन", + "region-required": "रीजन आवश्यक है।", + "access-key-id": "ऐक्सेस की ID", + "access-key-id-required": "ऐक्सेस की ID आवश्यक है।", + "secret-access-key": "सीक्रेट ऐक्सेस की", + "secret-access-key-required": "सीक्रेट ऐक्सेस की आवश्यक है।", + "temperature": "टेम्परेचर", + "temperature-hint": "मॉडल के आउटपुट में रैंडमनेस के स्तर को समायोजित करता है। अधिक मान रैंडमनेस बढ़ाते हैं, जबकि कम मान उसे घटाते हैं।", + "temperature-min": "0 या उससे अधिक होना चाहिए।", + "top-p": "टॉप P", + "top-p-hint": "मॉडल के चुनने के लिए सबसे संभावित टोकन्स का एक पूल बनाता है। अधिक मान बड़ा और अधिक विविध पूल बनाते हैं, जबकि कम मान छोटा पूल बनाते हैं।", + "top-p-min-max": "0 से बड़ा और अधिकतम 1 होना चाहिए।", + "top-k": "टॉप K", + "top-k-hint": "मॉडल के विकल्पों को \"K\" सबसे संभावित टोकन्स के एक निश्चित सेट तक सीमित करता है।", + "top-k-min": "0 या उससे अधिक होना चाहिए।", + "presence-penalty": "प्रेज़ेन्स पेनल्टी", + "presence-penalty-hint": "यदि कोई टोकन पहले से टेक्स्ट में आ चुका है, तो उसकी संभावना पर एक स्थिर पेनल्टी लागू करता है।", + "frequency-penalty": "फ़्रीक्वेंसी पेनल्टी", + "frequency-penalty-hint": "टोकन की संभावना पर ऐसी पेनल्टी लागू करता है जो टेक्स्ट में उसकी आवृत्ति के आधार पर बढ़ती है।", + "max-output-tokens": "अधिकतम आउटपुट टोकन्स", + "max-output-tokens-hint": "एक ही रिस्पॉन्स में मॉडल कितने अधिकतम टोकन्स जेनरेट कर सकता है,\nइसे सेट करता है।", + "context-length": "कॉन्टेक्स्ट लंबाई", + "context-length-hint": "कॉन्टेक्स्ट विंडो का आकार टोकन्स में परिभाषित करता है। यह मान मॉडल के लिए कुल मेमोरी लिमिट सेट करता है, जिसमें उपयोगकर्ता का इनपुट और जेनरेट किया गया रिस्पॉन्स दोनों शामिल होते हैं।", + "endpoint": "एंडपॉइंट", + "endpoint-required": "एंडपॉइंट आवश्यक है।", + "baseurl": "Base URL", + "baseurl-required": "Base URL आवश्यक है।", + "service-version": "सर्विस संस्करण", + "check-connectivity": "कनेक्टिविटी जाँचें", + "check-connectivity-success": "टेस्ट अनुरोध सफल रहा", + "check-connectivity-failed": "टेस्ट अनुरोध असफल रहा", + "no-model-matching": "'{{entity}}' से मेल खाते कोई मॉडल नहीं मिले।", + "model-required": "मॉडल आवश्यक है।", + "no-model-text": "कोई मॉडल नहीं मिला।", + "authentication": "प्रमाणीकरण", + "authentication-basic-hint": "मानक HTTP बेसिक प्रमाणीकरण का उपयोग करता है। उपयोगकर्ता नाम और पासवर्ड को जोड़ा जाएगा, Base64-एन्कोड किया जाएगा और प्रत्येक अनुरोध के साथ Ollama सर्वर को भेजी जाने वाली \"प्राधिकरण\" हेडर में शामिल किया जाएगा।", + "authentication-token-hint": "Bearer टोकन प्रमाणीकरण का उपयोग करता है। दिया गया टोकन सीधे \"प्राधिकरण\" हेडर में Ollama सर्वर को भेजे जाने वाले प्रत्येक अनुरोध के साथ भेजा जाएगा।", + "authentication-type": { + "none": "कोई नहीं", + "basic": "बेसिक", + "token": "टोकन" + }, + "username": "उपयोगकर्ता नाम", + "username-required": "उपयोगकर्ता नाम आवश्यक है।", + "password": "पासवर्ड", + "password-required": "पासवर्ड आवश्यक है।", + "token": "टोकन", + "token-required": "टोकन आवश्यक है।" + }, + "confirm-on-exit": { + "message": "आपके पास बिना सहेजे गए परिवर्तन हैं। क्या आप वाकई इस पेज से बाहर जाना चाहते हैं?", + "html-message": "आपके पास बिना सहेजे गए परिवर्तन हैं.
    क्या आप वाकई इस पेज से बाहर जाना चाहते हैं?", + "title": "बिना सहेजे गए परिवर्तन" + }, + "contact": { + "country": "देश", + "country-required": "देश आवश्यक है।", + "city": "शहर", + "state": "राज्य / प्रांत", + "postal-code": "ज़िप / डाक कोड", + "postal-code-invalid": "अमान्य ज़िप / डाक कोड फ़ॉर्मेट।", + "address": "पता", + "address2": "पता 2", + "phone": "फ़ोन", + "email": "ईमेल", + "no-address": "कोई पता नहीं", + "no-country-found": "कोई देश नहीं मिला।", + "no-country-matching": "'{{country}}' से मेल खाता कोई देश नहीं मिला।", + "state-max-length": "राज्य की लंबाई 256 से कम होनी चाहिए", + "phone-max-length": "फ़ोन नंबर 256 अक्षरों से कम होना चाहिए", + "city-max-length": "निर्दिष्ट शहर 256 अक्षरों से कम होना चाहिए" + }, + "common": { + "name": "नाम", + "type": "प्रकार", + "general": "सामान्य", + "username": "उपयोगकर्ता नाम", + "password": "पासवर्ड", + "data": "डेटा", + "timestamp": "टाइमस्टैम्प", + "enter-username": "उपयोगकर्ता नाम दर्ज करें", + "enter-password": "पासवर्ड दर्ज करें", + "enter-search": "खोज लिखें", + "created-time": "बनाए जाने का समय", + "disabled": "निष्क्रिय", + "loading": "लोड हो रहा है...", + "proceed": "आगे बढ़ें", + "open-details-page": "विवरण पृष्ठ खोलें", + "not-found": "नहीं मिला", + "value": "मान", + "documentation": "प्रलेखन", + "time-left": "{{time}} बाकी", + "output": "आउटपुट", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "नाम आवश्यक है।", + "name-pattern": "नाम अमान्य है।", + "name-max-length": "नाम 256 अक्षरों से कम होना चाहिए।", + "title-required": "शीर्षक आवश्यक है।", + "title-pattern": "शीर्षक अमान्य है।", + "title-max-length": "शीर्षक 256 अक्षरों से कम होना चाहिए।", + "key-required": "कुंजी आवश्यक है।", + "key-pattern": "कुंजी अमान्य है।", + "key-max-length": "कुंजी 256 अक्षरों से कम होनी चाहिए।" + }, + "required-fields": "आवश्यक फ़ील्ड्स भरी नहीं गई हैं" + }, + "content-type": { + "json": "JSON", + "text": "टेक्स्ट", + "binary": "बाइनरी (Base64)" + }, + "color": { + "color": "रंग" + }, + "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-customer-edges": "कस्टमर Edge प्रबंधित करें", + "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 कस्टमर} other {# कस्टमर} } हटाना चाहते हैं?", + "delete-customers-action-title": "{ count, plural, =1 {1 कस्टमर} other {# कस्टमर} } हटाएँ", + "delete-customers-text": "सावधान रहें, पुष्टि के बाद सभी चयनित कस्टमर हटा दिए जाएँगे और उनसे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "manage-users": "उपयोगकर्ता प्रबंधित करें", + "manage-assets": "एसेट प्रबंधित करें", + "manage-devices": "डिवाइस प्रबंधित करें", + "manage-dashboards": "डैशबोर्ड्स प्रबंधित करें", + "title": "शीर्षक", + "title-required": "शीर्षक आवश्यक है।", + "title-max-length": "शीर्षक 256 अक्षरों से कम होना चाहिए", + "description": "विवरण", + "details": "विवरण", + "events": "इवेंट्स", + "copyId": "कस्टमर Id कॉपी करें", + "idCopiedMessage": "कस्टमर Id क्लिपबोर्ड में कॉपी कर दी गई है", + "select-customer": "कस्टमर चुनें", + "no-customers-matching": "'{{entity}}' से मेल खाते कोई कस्टमर नहीं मिले।", + "customer-required": "कस्टमर आवश्यक है।", + "select-default-customer": "डिफ़ॉल्ट कस्टमर चुनें", + "default-customer": "डिफ़ॉल्ट कस्टमर", + "default-customer-required": "टेनेंट स्तर पर डैशबोर्ड डिबग करने के लिए डिफ़ॉल्ट कस्टमर आवश्यक है", + "search": "कस्टमर खोजें", + "selected-customers": "{ count, plural, =1 {1 कस्टमर} other {# कस्टमर} } चयनित", + "edges": "कस्टमर Edge इंस्टेंस", + "manage-edges": "Edge इंस्टेंस प्रबंधित करें" + }, + "css-size": { + "size-value-required": "आकार मान आवश्यक है", + "invalid-size-value": "आकार मान अमान्य है" + }, + "date": { + "last-update-n-ago": "अंतिम अपडेट N पहले", + "last-update-n-ago-text": "अंतिम अपडेट {{ agoText }}", + "custom-date": "कस्टम तिथि", + "format": "प्रारूप", + "preview": "पूर्वावलोकन", + "auto": "ऑटो", + "time-granularity-formats": "समय ग्रैन्युलैरिटी प्रारूप", + "unit-year": "वर्ष", + "unit-month": "महीने", + "unit-day": "दिन", + "unit-hour": "घंटे", + "unit-minute": "मिनट", + "unit-second": "सेकंड", + "unit-millisecond": "मिलीसेकंड" + }, + "datetime": { + "date-from": "तिथि से", + "time-from": "समय से", + "date-to": "तिथि तक", + "time-to": "समय तक", + "from": "से", + "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": "नया विजेट जोड़ें", + "add-widget-button-text": "विजेट जोड़ें", + "title": "शीर्षक", + "image": "डैशबोर्ड छवि", + "mobile-app-settings": "मोबाइल एप्लिकेशन सेटिंग्स", + "mobile-order": "मोबाइल एप्लिकेशन में डैशबोर्ड क्रम", + "mobile-hide": "मोबाइल एप्लिकेशन में डैशबोर्ड छिपाएँ", + "update-image": "डैशबोर्ड छवि अपडेट करें", + "take-screenshot": "स्क्रीनशॉट लें", + "select-widget-title": "विजेट चुनें", + "select-widget-value": "{{title}}: विजेट चुनें", + "select-widget-subtitle": "उपलब्ध विजेट प्रकारों की सूची", + "delete": "डैशबोर्ड हटाएँ", + "title-required": "शीर्षक आवश्यक है।", + "title-max-length": "शीर्षक 256 अक्षरों से कम होना चाहिए", + "description": "विवरण", + "details": "विवरण", + "dashboard-details": "डैशबोर्ड विवरण", + "add-dashboard-text": "नया डैशबोर्ड जोड़ें", + "assign-dashboards": "डैशबोर्ड्स असाइन करें", + "assign-new-dashboard": "नया डैशबोर्ड असाइन करें", + "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 {1 डैशबोर्ड} other {# डैशबोर्ड्स} } अनअसाइन करें", + "delete-dashboard-title": "क्या आप वाकई डैशबोर्ड '{{dashboardTitle}}' हटाना चाहते हैं?", + "delete-dashboard-text": "सावधान रहें, पुष्टि के बाद डैशबोर्ड और उससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "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 {1 डैशबोर्ड} 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": "कोई छवि चयनित नहीं है", + "empty-image": "कोई छवि नहीं", + "drop-image": "कोई छवि यहाँ छोड़ें या अपलोड करने के लिए फ़ाइल चुनने हेतु क्लिक करें।", + "maximum-upload-file-size": "अधिकतम अपलोड फ़ाइल आकार: {{ size }}", + "cannot-upload-file": "फ़ाइल अपलोड नहीं कर सकते", + "settings": "सेटिंग्स", + "move-all-widgets": "सभी विजेट्स को स्थानांतरित करें", + "move-by": "इतना स्थानांतरित करें", + "cols": "कॉलम", + "rows": "पंक्तियाँ", + "layout": "लेआउट", + "layout-type-default": "डिफ़ॉल्ट", + "layout-type-scada": "SCADA", + "layout-type-divider": "डिवाइडर", + "layout-settings-type": "लेआउट सेटिंग्स: {{ type }} ब्रेकपॉइंट", + "columns-count": "कॉलम की संख्या", + "columns-count-required": "कॉलम की संख्या आवश्यक है।", + "min-columns-count-message": "न्यूनतम कॉलम संख्या केवल 10 हो सकती है।", + "max-columns-count-message": "अधिकतम कॉलम संख्या केवल 1000 हो सकती है।", + "min-layout-width": "न्यूनतम लेआउट चौड़ाई", + "columns-suffix": "कॉलम", + "widgets-margins": "विजेट्स के बीच की मार्जिन", + "margin-required": "मार्जिन मान आवश्यक है।", + "min-margin-message": "न्यूनतम मार्जिन मान केवल 0 हो सकता है।", + "max-margin-message": "अधिकतम मार्जिन मान केवल 50 हो सकता है।", + "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 हो सकता है।", + "apply-outer-margin": "लेआउट के किनारों पर मार्जिन लागू करें", + "autofill-height": "लेआउट की ऊँचाई स्वतः भरें", + "mobile-layout": "मोबाइल लेआउट सेटिंग्स", + "mobile-row-height": "मोबाइल रो ऊँचाई", + "mobile-row-height-required": "मोबाइल रो ऊँचाई का मान आवश्यक है।", + "min-mobile-row-height-message": "न्यूनतम मोबाइल रो ऊँचाई मान केवल 5 पिक्सेल हो सकता है।", + "max-mobile-row-height-message": "अधिकतम मोबाइल रो ऊँचाई मान केवल 200 पिक्सेल हो सकता है।", + "row-height": "रो ऊँचाई", + "row-height-required": "रो ऊँचाई का मान आवश्यक है।", + "min-row-height-message": "न्यूनतम रो ऊँचाई मान केवल 5 पिक्सेल हो सकता है।", + "max-row-height-message": "अधिकतम रो ऊँचाई मान केवल 200 पिक्सेल हो सकता है।", + "display-first-in-mobile-view": "मोबाइल व्यू में सबसे पहले दिखाएँ", + "title-settings": "शीर्षक सेटिंग्स", + "display-title": "डैशबोर्ड शीर्षक दिखाएँ", + "title-color": "शीर्षक रंग", + "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": "डैशबोर्ड इमेज अपडेट विकल्प दिखाएँ", + "dashboard-logo-settings": "डैशबोर्ड लोगो सेटिंग्स", + "display-dashboard-logo": "डैशबोर्ड फुलस्क्रीन मोड में लोगो दिखाएँ", + "dashboard-logo-image": "डैशबोर्ड लोगो इमेज", + "advanced-settings": "एडवांस्ड सेटिंग्स", + "dashboard-css": "डैशबोर्ड CSS", + "import": "डैशबोर्ड इम्पोर्ट करें", + "export": "डैशबोर्ड एक्सपोर्ट करें", + "export-failed-error": "डैशबोर्ड एक्सपोर्ट नहीं किया जा सका: {{error}}", + "export-prompt": "डैशबोर्ड इमेज और संसाधन एम्बेड करें", + "create-new-dashboard": "नया डैशबोर्ड बनाएँ", + "dashboard-file": "डैशबोर्ड फ़ाइल", + "invalid-dashboard-file-error": "डैशबोर्ड इम्पोर्ट नहीं किया जा सका: डैशबोर्ड डेटा संरचना अमान्य है।", + "dashboard-import-missing-aliases-title": "इम्पोर्ट किए गए डैशबोर्ड द्वारा उपयोग किए गए उपनाम (aliases) कॉन्फ़िगर करें", + "create-new-widget": "नया विजेट बनाएँ", + "import-widget": "विजेट इम्पोर्ट करें", + "widget-file": "विजेट फ़ाइल", + "invalid-widget-file-error": "विजेट इम्पोर्ट नहीं किया जा सका: विजेट डेटा संरचना अमान्य है।", + "widget-import-missing-aliases-title": "इम्पोर्ट किए गए विजेट द्वारा उपयोग किए गए उपनाम (aliases) कॉन्फ़िगर करें", + "open-toolbar": "डैशबोर्ड टूलबार खोलें", + "close-toolbar": "टूलबार बंद करें", + "configuration-error": "कॉन्फ़िगरेशन त्रुटि", + "alias-resolution-error-title": "डैशबोर्ड उपनाम कॉन्फ़िगरेशन त्रुटि", + "invalid-aliases-config": "कुछ उपनाम फ़िल्टर के लिए मेल खाते डिवाइस नहीं मिल सके।
    कृपया इस समस्या के समाधान के लिए अपने व्यवस्थापक से संपर्क करें।", + "select-devices": "डिवाइस चुनें", + "assignedToCustomer": "कस्टमर को असाइन किया गया", + "assignedToCustomers": "कस्टमर्स को असाइन किया गया", + "public": "सार्वजनिक", + "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 {1 डैशबोर्ड स्टेट} other {# डैशबोर्ड स्टेट्स} } चयनित", + "edit-state": "डैशबोर्ड स्टेट संपादित करें", + "delete-state": "डैशबोर्ड स्टेट हटाएँ", + "add-state": "डैशबोर्ड स्टेट जोड़ें", + "no-states-text": "कोई स्टेट नहीं मिला", + "state": "डैशबोर्ड स्टेट", + "state-name": "नाम", + "state-name-required": "डैशबोर्ड स्टेट नाम आवश्यक है।", + "state-id": "स्टेट Id", + "state-id-required": "डैशबोर्ड स्टेट Id आवश्यक है।", + "state-id-exists": "एक ही Id वाला डैशबोर्ड स्टेट पहले से मौजूद है।", + "is-root-state": "रूट स्टेट", + "delete-state-title": "डैशबोर्ड स्टेट हटाएँ", + "delete-state-text": "क्या आप वाकई '{{stateName}}' नाम वाले डैशबोर्ड स्टेट को हटाना चाहते हैं?", + "show-details": "विवरण दिखाएँ", + "hide-details": "विवरण छिपाएँ", + "select-state": "लक्षित स्टेट चुनें", + "state-controller": "स्टेट कंट्रोलर", + "state-controller-default": "स्टैटिक (अप्रचलित)", + "search": "डैशबोर्ड्स खोजें", + "selected-dashboards": "{ count, plural, =1 {1 डैशबोर्ड} other {# डैशबोर्ड्स} } चयनित", + "home-dashboard": "होम डैशबोर्ड", + "home-dashboard-hide-toolbar": "होम डैशबोर्ड टूलबार छिपाएँ", + "unassign-dashboard-from-edge-text": "पुष्टि के बाद डैशबोर्ड अनअसाइन कर दिया जाएगा और Edge द्वारा एक्सेस नहीं किया जा सकेगा।", + "unassign-dashboards-from-edge-title": "क्या आप वाकई { count, plural, =1 {1 डैशबोर्ड} other {# डैशबोर्ड्स} } को अनअसाइन करना चाहते हैं?", + "unassign-dashboards-from-edge-text": "पुष्टि के बाद सभी चयनित डैशबोर्ड्स अनअसाइन कर दिए जाएँगे और Edge द्वारा एक्सेस नहीं किए जा सकेंगे।", + "assign-dashboard-to-edge": "डैशबोर्ड(स) को Edge पर असाइन करें", + "assign-dashboard-to-edge-text": "कृपया वे डैशबोर्ड चुनें जिन्हें Edge को असाइन करना है", + "non-existent-dashboard-state-error": "Id \"{{ stateId }}\" वाला डैशबोर्ड स्टेट नहीं मिला", + "edit-mode": "एडिट मोड", + "duplicate-state-action": "स्टेट डुप्लिकेट करें", + "breakpoint-value": "ब्रेकपॉइंट ({{ value }})", + "breakpoints-id": { + "default": "डिफ़ॉल्ट", + "xs": "मोबाइल (xs)", + "sm": "टैबलेट (sm)", + "md": "लैपटॉप (md)", + "lg": "डेस्कटॉप (lg)", + "xl": "डेस्कटॉप (xl)" + }, + "view-format-type-grid": "ग्रिड", + "view-format-type-list": "सूची", + "view-format": "व्यू फ़ॉर्मेट" + }, + "datakey": { + "settings": "सेटिंग्स", + "general": "सामान्य", + "advanced": "एडवांस्ड", + "key": "कुंजी", + "keys": "कुंजियाँ", + "label": "लेबल", + "color": "रंग", + "units": "मान के बगल में दिखाने के लिए विशेष चिह्न", + "decimals": "दशमलव के बाद अंकों की संख्या", + "data-generation-func": "डेटा जनरेशन फ़ंक्शन", + "use-data-post-processing-func": "डेटा पोस्ट-प्रोसेसिंग फ़ंक्शन का उपयोग करें", + "configuration": "डेटा कुंजी कॉन्फ़िगरेशन", + "timeseries": "टाइम सीरीज़", + "attributes": "विशेषताएँ", + "entity-field": "एंटिटी फ़ील्ड", + "alarm": "अलार्म फ़ील्ड्स", + "timeseries-required": "एंटिटी टाइम सीरीज़ आवश्यक हैं।", + "timeseries-or-attributes-required": "एंटिटी टाइम सीरीज़/विशेषताएँ आवश्यक हैं।", + "alarm-fields-timeseries-or-attributes-required": "अलार्म फ़ील्ड्स या एंटिटी टाइम सीरीज़/विशेषताएँ आवश्यक हैं।", + "maximum-timeseries-or-attributes": "अधिकतम { count, plural, =1 {1 टाइम सीरीज़/विशेषता की अनुमति है।} other {# टाइम सीरीज़/विशेषताओं की अनुमति है} }", + "alarm-fields-required": "अलार्म फ़ील्ड्स आवश्यक हैं।", + "function-types": "फ़ंक्शन प्रकार", + "function-type": "फ़ंक्शन प्रकार", + "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-key": "टाइम सीरीज़ डेटा कुंजी", + "timeseries-key-functions": "टाइम सीरीज़ कुंजी फ़ंक्शंस", + "timeseries-key-function": "टाइम सीरीज़ कुंजी फ़ंक्शन", + "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": "चयनित समय विंडो के भीतर डेटा पॉइंट्स की कुल संख्या।", + "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": "डेल्टा (प्रतिशत)", + "source": "स्रोत", + "latest": "नवीनतम", + "latest-value": "नवीनतम मान", + "delta": "डेल्टा", + "percent": "प्रतिशत", + "absolute": "पूर्ण" + }, + "datasource": { + "type": "डेटा स्रोत प्रकार", + "name": "नाम", + "label": "लेबल", + "add-datasource-prompt": "कृपया डेटा स्रोत जोड़ें" + }, + "details": { + "details": "विवरण", + "edit-mode": "एडिट मोड", + "edit-json": "JSON संपादित करें", + "toggle-edit-mode": "एडिट मोड बदलें" + }, + "device": { + "device": "डिवाइस", + "device-required": "डिवाइस आवश्यक है।", + "devices": "डिवाइस", + "management": "डिवाइस प्रबंधन", + "view-devices": "डिवाइस देखें", + "device-alias": "डिवाइस उपनाम", + "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": "नई कुंजी बनाएँ!", + "duplicate-alias-error": "डुप्लिकेट उपनाम '{{alias}}' मिला।
    डैशबोर्ड के भीतर डिवाइस उपनाम यूनिक होने चाहिए।", + "configure-alias": "'{{alias}}' उपनाम कॉन्फ़िगर करें", + "no-devices-matching": "'{{entity}}' से मेल खाते कोई डिवाइस नहीं मिले।", + "alias": "उपनाम", + "alias-required": "डिवाइस उपनाम आवश्यक है।", + "remove-alias": "डिवाइस उपनाम हटाएँ", + "add-alias": "डिवाइस उपनाम जोड़ें", + "name-starts-with": "डिवाइस नाम अभिव्यक्ति", + "help-text": "आवश्यकता अनुसार '%' का उपयोग करें: '%device_name_contains%', '%device_name_ends', 'device_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 डिवाइस} other {# डिवाइस} } असाइन करें", + "delete-devices": "डिवाइस हटाएँ", + "unassign-from-customer": "कस्टमर से अनअसाइन करें", + "unassign-devices": "डिवाइस अनअसाइन करें", + "unassign-devices-action-title": "कस्टमर से { count, plural, =1 {1 डिवाइस} other {# डिवाइस} } अनअसाइन करें", + "unassign-device-from-edge-title": "क्या आप वाकई डिवाइस '{{deviceName}}' को Edge से अनअसाइन करना चाहते हैं?", + "unassign-device-from-edge-text": "पुष्टि के बाद डिवाइस अनअसाइन कर दिया जाएगा और Edge द्वारा एक्सेस नहीं किया जा सकेगा।", + "unassign-devices-from-edge": "Edge से डिवाइस अनअसाइन करें", + "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 डिवाइस} 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 {1 डिवाइस} other {# डिवाइस} } को अनअसाइन करना चाहते हैं?", + "unassign-devices-text": "पुष्टि के बाद सभी चयनित डिवाइस अनअसाइन कर दिए जाएँगे और कस्टमर द्वारा एक्सेस नहीं किए जा सकेंगे।", + "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": "प्रमाणपत्र आवश्यक है।", + "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 पहचानकर्ता (identifier) एक मनमाना 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-public-key": "क्लाइंट सार्वजनिक कुंजी", + "client-public-key-hint": "यदि क्लाइंट सार्वजनिक कुंजी खाली है, तो विश्वसनीय प्रमाणपत्र का उपयोग किया जाएगा", + "client-public-key-tooltip": "X509 सार्वजनिक कुंजी DER-encoded X509v3 फॉर्मेट में होनी चाहिए, केवल EC algorithm को सपोर्ट करनी चाहिए और फिर Base64 फॉर्मेट में एनकोड की जानी चाहिए!", + "mode": "सुरक्षा कॉन्फ़िगरेशन मोड", + "client-tab": "क्लाइंट सुरक्षा कॉन्फ़िगरेशन", + "client-certificate": "क्लाइंट प्रमाणपत्र", + "bootstrap-tab": "Bootstrap क्लाइंट", + "bootstrap-server": "Bootstrap सर्वर", + "lwm2m-server": "LwM2M सर्वर", + "client-publicKey-or-id": "क्लाइंट सार्वजनिक कुंजी या आईडी", + "client-publicKey-or-id-required": "क्लाइंट सार्वजनिक कुंजी या आईडी आवश्यक है।", + "client-publicKey-or-id-tooltip-psk": "PSK पहचानकर्ता (identifier) एक मनमाना PSK पहचानकर्ता होता है जिसकी लंबाई अधिकतम 128 बाइट तक हो सकती है, जैसा कि मानक [RFC7925] में वर्णित है।\nPSK पहचानकर्ता को पहले एक कैरेक्टर स्ट्रिंग में बदला जाना चाहिए और फिर UTF-8 का उपयोग करके ऑक्टेट्स में एनकोड किया जाना चाहिए।", + "client-publicKey-or-id-tooltip-rpk": "RPK सार्वजनिक कुंजी या आईडी मानक [RFC7250] के अनुरूप होनी चाहिए और Base64 फॉर्मेट में एनकोड की जानी चाहिए!", + "client-publicKey-or-id-tooltip-x509": "X509 सार्वजनिक कुंजी DER-encoded X509v3 फॉर्मेट में होनी चाहिए, केवल EC algorithm को सपोर्ट करनी चाहिए और फिर Base64 फॉर्मेट में एनकोड की जानी चाहिए।", + "client-secret-key": "क्लाइंट सीक्रेट कुंजी", + "client-secret-key-required": "क्लाइंट सीक्रेट कुंजी आवश्यक है।", + "client-secret-key-tooltip-psk": "PSK कुंजी मानक [RFC4279] के अनुरूप होनी चाहिए और HexDec फॉर्मेट में हो: 32, 64, 128 अक्षर!", + "client-secret-key-tooltip-prk": "RPK सीक्रेट कुंजी PKCS_8 फॉर्मेट (DER एनकोडिंग, मानक [RFC5958]) में होनी चाहिए और फिर Base64 फॉर्मेट में एनकोड की जानी चाहिए!", + "client-secret-key-tooltip-x509": "X509 सीक्रेट कुंजी PKCS_8 फॉर्मेट (DER एनकोडिंग, मानक [RFC5958]) में होनी चाहिए और फिर Base64 फॉर्मेट में एनकोड की जानी चाहिए!" + }, + "client-id": "क्लाइंट ID", + "client-id-pattern": "अमान्य वर्ण शामिल है।", + "user-name": "यूज़र नाम", + "user-name-required": "यूज़र नाम आवश्यक है।", + "client-id-or-user-name-necessary": "क्लाइंट ID और/या यूज़र नाम आवश्यक हैं", + "password": "पासवर्ड", + "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-profile-type-list-empty": "कम से कम एक डिवाइस प्रोफ़ाइल चयनित होनी चाहिए।", + "device-types": "डिवाइस प्रकार", + "name": "नाम", + "name-required": "नाम आवश्यक है।", + "name-max-length": "नाम 256 अक्षरों से कम होना चाहिए", + "label-max-length": "लेबल 256 अक्षरों से कम होना चाहिए", + "description": "विवरण", + "label": "लेबल", + "events": "इवेंट्स", + "details": "विवरण", + "copyId": "डिवाइस Id कॉपी करें", + "copyAccessToken": "ऐक्सेस टोकन कॉपी करें", + "copy-mqtt-authentication": "MQTT क्रेडेंशियल्स कॉपी करें", + "idCopiedMessage": "डिवाइस Id क्लिपबोर्ड में कॉपी कर दी गई है", + "accessTokenCopiedMessage": "डिवाइस ऐक्सेस टोकन क्लिपबोर्ड में कॉपी कर दिया गया है", + "mqtt-authentication-copied-message": "डिवाइस MQTT ऑथेंटिकेशन क्लिपबोर्ड में कॉपी कर दिया गया है", + "assignedToCustomer": "कस्टमर को असाइन किया गया", + "unable-delete-device-alias-title": "डिवाइस उपनाम हटाने में असमर्थ", + "unable-delete-device-alias-text": "डिवाइस उपनाम '{{deviceAlias}}' हटाया नहीं जा सकता क्योंकि इसका उपयोग इन विजेट(s) द्वारा किया जा रहा है:
    {{widgetsList}}", + "is-gateway": "Gateway है", + "overwrite-activity-time": "कनेक्टेड डिवाइस के गतिविधि समय को ओवरराइट करें", + "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 {1 डिवाइस} other {# डिवाइस} } चयनित", + "device-configuration": "डिवाइस कॉन्फ़िगरेशन", + "transport-configuration": "ट्रांसपोर्ट कॉन्फ़िगरेशन", + "wizard": { + "device-details": "डिवाइस विवरण" + }, + "unassign-devices-from-edge-title": "क्या आप वाकई { count, plural, =1 {1 डिवाइस} other {# डिवाइस} } को अनअसाइन करना चाहते हैं?", + "unassign-devices-from-edge-text": "पुष्टि के बाद सभी चयनित डिवाइस अनअसाइन कर दिए जाएँगे और Edge द्वारा एक्सेस नहीं किए जा सकेंगे।", + "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": "CoAP over DTLS के माध्यम से X509 ऑथराइजेशन के साथ डिवाइस कनेक्ट करने के लिए निम्न डॉक्यूमेंटेशन का उपयोग करें", + "snmp-command": "डिवाइस को SNMP के माध्यम से कनेक्ट करने के लिए निम्न डॉक्यूमेंटेशन का उपयोग करें।", + "sparkplug-command": "डिवाइस को MQTT Sparkplug के माध्यम से कनेक्ट करने के लिए निम्न डॉक्यूमेंटेशन का उपयोग करें।", + "lwm2m-command": "डिवाइस को LWM2M के माध्यम से कनेक्ट करने के लिए निम्न डॉक्यूमेंटेशन का उपयोग करें।" + } + }, + "dynamic-form": { + "property": { + "properties": "प्रॉपर्टीज़", + "property": "प्रॉपर्टी", + "id": "Id", + "name": "नाम", + "type": "प्रकार", + "type-text": "टेक्स्ट", + "type-password": "पासवर्ड", + "type-textarea": "टेक्स्ट एरिया", + "type-number": "नंबर", + "type-switch": "स्विच", + "type-select": "सेलेक्ट", + "type-radios": "रेडियो बटन", + "type-datetime": "तारीख/समय", + "type-image": "इमेज", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "रंग", + "type-color-settings": "रंग सेटिंग्स", + "type-font": "फ़ॉन्ट", + "type-units": "यूनिट्स", + "type-icon": "आइकन", + "type-fieldset": "फील्डसेट", + "type-array": "ऐरे", + "type-html-section": "HTML सेक्शन", + "group-title": "ग्रुप शीर्षक", + "no-properties": "कोई प्रॉपर्टीज़ कॉन्फ़िगर नहीं की गईं", + "add-property": "प्रॉपर्टी जोड़ें", + "property-settings": "प्रॉपर्टी सेटिंग्स", + "remove-property": "प्रॉपर्टी हटाएँ", + "default-value": "डिफ़ॉल्ट मान", + "value-required": "मान आवश्यक है।", + "number-settings": "नंबर सेटिंग्स", + "min": "न्यूनतम", + "max": "अधिकतम", + "step": "स्टेप", + "selected-options-limit": "चयनित विकल्पों की सीमा", + "advanced-ui-settings": "एडवांस्ड UI सेटिंग्स", + "disable-on-property": "प्रॉपर्टी के आधार पर डिसेबल करें", + "disable-on-property-none": "कोई नहीं (फ़ील्ड हमेशा सक्षम रहती है)", + "display-condition-function": "डिस्प्ले कंडीशन फ़ंक्शन", + "sub-label": "सब-लेबल", + "vertical-divider-after": "इसके बाद ऊर्ध्वाधर डिवाइडर", + "input-field-suffix": "इनपुट फ़ील्ड प्रत्यय", + "property-row-classes": "प्रॉपर्टी पंक्ति क्लासेज़", + "property-field-classes": "प्रॉपर्टी फ़ील्ड क्लासेज़", + "not-unique-property-ids-error": "प्रॉपर्टी Id यूनिक होने चाहिए!", + "enable-multiple-select": "मल्टीपल सेलेक्ट सक्षम करें", + "allow-empty-select-option": "खाली विकल्प की अनुमति दें", + "select-options": "सेलेक्ट विकल्प", + "not-unique-select-option-value-error": "सेलेक्ट विकल्प मान यूनिक होने चाहिए!", + "value": "मान", + "label": "लेबल", + "add-option": "विकल्प जोड़ें", + "no-options": "कोई विकल्प कॉन्फ़िगर नहीं किए गए हैं", + "remove-option": "विकल्प हटाएँ", + "textarea-rows": "टेक्स्टएरिया पंक्तियाँ", + "help-id": "हेल्प Id", + "buttons-direction": "बटन दिशा", + "direction-row": "पंक्ति", + "direction-column": "कॉलम", + "radio-button-options": "रेडियो बटन विकल्प", + "datetime-type": "तारीख/समय फ़ील्ड प्रकार", + "datetime-type-date": "तारीख", + "datetime-type-time": "समय", + "datetime-type-datetime": "तारीख/समय", + "enable-clear-button": "क्लियर बटन सक्षम करें", + "html-section-settings": "HTML सेक्शन सेटिंग्स", + "html-section-classes": "HTML सेक्शन क्लासेज़", + "html-section-content": "HTML सेक्शन सामग्री", + "array-item": "ऐरे आइटम", + "item-type": "आइटम प्रकार", + "item-name": "आइटम नाम", + "no-items": "कोई आइटम नहीं", + "support-unit-conversion": "यूनिट रूपांतरण का समर्थन करें" + }, + "clear-form": "फॉर्म साफ़ करें", + "clear-form-prompt": "क्या आप वाकई सभी फॉर्म प्रॉपर्टीज़ हटाना चाहते हैं?", + "import-form": "JSON से फॉर्म आयात करें", + "export-form": "फॉर्म को JSON में निर्यात करें", + "json-file": "JSON फ़ाइल", + "json-content": "JSON सामग्री", + "invalid-form-json-file-error": "JSON से फॉर्म आयात करने में असमर्थ: फॉर्म JSON डेटा संरचना अमान्य है।" + }, + "asset-profile": { + "asset-profile": "एसेट प्रोफ़ाइल", + "asset-profiles": "एसेट प्रोफ़ाइलें", + "all-asset-profiles": "सभी", + "add": "एसेट प्रोफ़ाइल जोड़ें", + "edit": "एसेट प्रोफ़ाइल संपादित करें", + "asset-profile-details": "एसेट प्रोफ़ाइल विवरण", + "no-asset-profiles-text": "कोई एसेट प्रोफ़ाइल नहीं मिली", + "search": "एसेट प्रोफ़ाइल खोजें", + "selected-asset-profiles": "{ count, plural, =1 {1 एसेट प्रोफ़ाइल} other {# एसेट प्रोफ़ाइलें} } चयनित", + "no-asset-profiles-matching": "कोई एसेट प्रोफ़ाइल '{{entity}}' से मेल नहीं खाती।", + "asset-profile-required": "एसेट प्रोफ़ाइल आवश्यक है", + "idCopiedMessage": "एसेट प्रोफ़ाइल Id क्लिपबोर्ड में कॉपी कर दी गई है", + "set-default": "एसेट प्रोफ़ाइल को डिफ़ॉल्ट बनाएं", + "delete": "एसेट प्रोफ़ाइल हटाएँ", + "copyId": "एसेट प्रोफ़ाइल Id कॉपी करें", + "name-max-length": "नाम 256 अक्षरों से कम होना चाहिए", + "new-device-profile-name": "एसेट प्रोफ़ाइल नाम", + "new-device-profile-name-required": "एसेट प्रोफ़ाइल नाम आवश्यक है।", + "name": "नाम", + "name-required": "नाम आवश्यक है।", + "image": "एसेट प्रोफ़ाइल इमेज", + "description": "विवरण", + "default": "डिफ़ॉल्ट", + "default-rule-chain": "डिफ़ॉल्ट नियम शृंखला", + "default-edge-rule-chain": "डिफ़ॉल्ट Edge नियम शृंखला", + "default-edge-rule-chain-hint": "Edge पर इस एसेट प्रोफ़ाइल के एसेट्स से आने वाले डेटा को प्रोसेस करने के लिए नियम शृंखला के रूप में उपयोग की जाती है", + "mobile-dashboard": "मोबाइल डैशबोर्ड", + "mobile-dashboard-hint": "मोबाइल एप्लिकेशन द्वारा एसेट विवरण डैशबोर्ड के रूप में उपयोग किया जाता है", + "select-queue-hint": "ड्रॉपडाउन सूची से चुनें।", + "delete-asset-profile-title": "क्या आप वाकई एसेट प्रोफ़ाइल '{{assetProfileName}}' को हटाना चाहते हैं?", + "delete-asset-profile-text": "सावधान रहें, पुष्टि के बाद एसेट प्रोफ़ाइल और इससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "delete-asset-profiles-title": "क्या आप वाकई { count, plural, =1 {1 एसेट प्रोफ़ाइल} other {# एसेट प्रोफ़ाइलें} } हटाना चाहते हैं?", + "delete-asset-profiles-text": "सावधान रहें, पुष्टि के बाद सभी चयनित एसेट प्रोफ़ाइलें हटा दी जाएँगी और उनसे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "set-default-asset-profile-title": "क्या आप वाकई एसेट प्रोफ़ाइल '{{assetProfileName}}' को डिफ़ॉल्ट बनाना चाहते हैं?", + "set-default-asset-profile-text": "पुष्टि के बाद यह एसेट प्रोफ़ाइल डिफ़ॉल्ट के रूप में चिन्हित हो जाएगी और नई एसेट्स के लिए उपयोग होगी, जिनके लिए कोई प्रोफ़ाइल निर्दिष्ट नहीं है।", + "no-asset-profiles-found": "कोई एसेट प्रोफ़ाइल नहीं मिली।", + "create-new-asset-profile": "नया बनाएँ!", + "create-asset-profile": "नई एसेट प्रोफ़ाइल बनाएँ", + "import": "एसेट प्रोफ़ाइल इम्पोर्ट करें", + "export": "एसेट प्रोफ़ाइल एक्सपोर्ट करें", + "export-failed-error": "एसेट प्रोफ़ाइल एक्सपोर्ट करने में असमर्थ: {{error}}", + "asset-profile-file": "एसेट प्रोफ़ाइल फ़ाइल", + "invalid-asset-profile-file-error": "एसेट प्रोफ़ाइल इम्पोर्ट करने में असमर्थ: एसेट प्रोफ़ाइल डेटा संरचना अमान्य है।" + }, + "device-profile": { + "device-profile": "डिवाइस प्रोफ़ाइल", + "device-profiles": "डिवाइस प्रोफ़ाइलें", + "all-device-profiles": "सभी", + "add": "डिवाइस प्रोफ़ाइल जोड़ें", + "edit": "डिवाइस प्रोफ़ाइल संपादित करें", + "device-profile-details": "डिवाइस प्रोफ़ाइल विवरण", + "no-device-profiles-text": "कोई डिवाइस प्रोफ़ाइल नहीं मिली", + "search": "डिवाइस प्रोफ़ाइल खोजें", + "selected-device-profiles": "{ count, plural, =1 {1 डिवाइस प्रोफ़ाइल} other {# डिवाइस प्रोफ़ाइलें} } चयनित", + "no-device-profiles-matching": "कोई डिवाइस प्रोफ़ाइल '{{entity}}' से मेल नहीं खाती।", + "device-profile-required": "डिवाइस प्रोफ़ाइल आवश्यक है", + "idCopiedMessage": "डिवाइस प्रोफ़ाइल Id क्लिपबोर्ड में कॉपी कर दी गई है", + "set-default": "डिवाइस प्रोफ़ाइल को डिफ़ॉल्ट बनाएं", + "delete": "डिवाइस प्रोफ़ाइल हटाएँ", + "copyId": "डिवाइस प्रोफ़ाइल Id कॉपी करें", + "name-max-length": "नाम 256 अक्षरों से कम होना चाहिए", + "name": "नाम", + "name-required": "नाम आवश्यक है।", + "type": "प्रोफ़ाइल प्रकार", + "type-required": "प्रोफ़ाइल प्रकार आवश्यक है।", + "type-default": "डिफ़ॉल्ट", + "image": "डिवाइस प्रोफ़ाइल इमेज", + "transport-type": "ट्रांसपोर्ट प्रकार", + "transport-type-required": "ट्रांसपोर्ट प्रकार आवश्यक है।", + "transport-type-default": "डिफ़ॉल्ट", + "transport-type-default-hint": "बेसिक MQTT, HTTP और CoAP ट्रांसपोर्ट को सपोर्ट करता है", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "एडवांस्ड MQTT ट्रांसपोर्ट सेटिंग्स सक्षम करता है", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "एडवांस्ड CoAP ट्रांसपोर्ट सेटिंग्स सक्षम करता है", + "transport-type-lwm2m": "LWM2M", + "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": "डिफ़ॉल्ट Edge रूल चेन", + "default-edge-rule-chain-hint": "Edge पर इस डिवाइस प्रोफ़ाइल के डिवाइसों से आने वाले डेटा को प्रोसेस करने के लिए रूल चेन के रूप में उपयोग की जाती है", + "mobile-dashboard": "मोबाइल डैशबोर्ड", + "mobile-dashboard-hint": "मोबाइल एप्लिकेशन द्वारा डिवाइस विवरण डैशबोर्ड के रूप में उपयोग किया जाता है", + "select-queue-hint": "ड्रॉपडाउन सूची से चुनें।", + "delete-device-profile-title": "क्या आप वाकई डिवाइस प्रोफ़ाइल '{{deviceProfileName}}' को हटाना चाहते हैं?", + "delete-device-profile-text": "सावधान रहें, पुष्टि के बाद डिवाइस प्रोफ़ाइल और इससे संबंधित सभी डेटा, जिनमें संबंधित OTA अपडेट भी शामिल हैं, पुनर्प्राप्त नहीं किए जा सकेंगे।", + "delete-device-profiles-title": "क्या आप वाकई { count, plural, =1 {1 डिवाइस प्रोफ़ाइल} other {# डिवाइस प्रोफ़ाइलें} } हटाना चाहते हैं?", + "delete-device-profiles-text": "सावधान रहें, पुष्टि के बाद सभी चयनित डिवाइस प्रोफ़ाइलें और उनसे संबंधित सभी डेटा, जिनमें संबंधित OTA अपडेट भी शामिल हैं, पुनर्प्राप्त नहीं किए जा सकेंगे।", + "set-default-device-profile-title": "क्या आप वाकई डिवाइस प्रोफ़ाइल '{{deviceProfileName}}' को डिफ़ॉल्ट बनाना चाहते हैं?", + "set-default-device-profile-text": "पुष्टि के बाद यह डिवाइस प्रोफ़ाइल डिफ़ॉल्ट के रूप में चिन्हित हो जाएगी और उन नए डिवाइसों के लिए उपयोग होगी जिनके लिए कोई प्रोफ़ाइल निर्दिष्ट नहीं है।", + "no-device-profiles-found": "कोई डिवाइस प्रोफ़ाइल नहीं मिली।", + "create-new-device-profile": "नया बनाएँ!", + "mqtt-device-topic-filters": "MQTT डिवाइस टॉपिक फ़िल्टर", + "mqtt-device-topic-filters-unique": "MQTT डिवाइस टॉपिक फ़िल्टर यूनिक होने चाहिए।", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge of Network (EoN) नोड।", + "mqtt-device-topic-filters-spark-plug-hint": "Sparkplug B पेलोड और टॉपिक फ़ॉर्मेट के साथ 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 डिवाइस पेलोड", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "अन्य पेलोड फ़ॉर्मेट के साथ कम्पैटिबिलिटी सक्षम करें।", + "mqtt-enable-compatibility-with-json-payload-format-hint": "जब सक्षम होता है, प्लेटफ़ॉर्म डिफ़ॉल्ट रूप से Protobuf पेलोड फ़ॉर्मेट का उपयोग करेगा। यदि पार्सिंग विफल हो जाती है, तो प्लेटफ़ॉर्म JSON पेलोड फ़ॉर्मेट का उपयोग करने का प्रयास करेगा। यह फ़र्मवेयर अपडेट के दौरान बैकवर्ड कम्पैटिबिलिटी के लिए उपयोगी है। उदाहरण के लिए, फ़र्मवेयर का शुरुआती रिलीज Json का उपयोग करता है, जबकि नया रिलीज Protobuf का उपयोग करता है। डिवाइसों के पूरे बेड़े के लिए फ़र्मवेयर अपडेट की प्रक्रिया के दौरान Protobuf और JSON दोनों को एक साथ सपोर्ट करना आवश्यक होता है। कम्पैटिबिलिटी मोड थोड़ा प्रदर्शन कम कर देता है, इसलिए अनुशंसा की जाती है कि सभी डिवाइस अपडेट हो जाने के बाद इस मोड को निष्क्रिय कर दिया जाए।", + "mqtt-use-json-format-for-default-downlink-topics": "डिफ़ॉल्ट डाउनलिंक टॉपिक के लिए Json फ़ॉर्मेट उपयोग करें", + "mqtt-use-json-format-for-default-downlink-topics-hint": "जब सक्षम होता है, प्लेटफ़ॉर्म निम्न टॉपिक्स के माध्यम से विशेषताएँ और RPC पुश करने के लिए Json पेलोड फ़ॉर्मेट का उपयोग करेगा: 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। यह सेटिंग नई (v2) टॉपिक्स के माध्यम से भेजी गई विशेषता और RPC सदस्यताओं को प्रभावित नहीं करती: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id। जहाँ $request_id पूर्णांक रिक्वेस्ट पहचानकर्ता है।", + "mqtt-send-ack-on-validation-exception": "PUBLISH मैसेज वैलिडेशन विफल होने पर PUBACK भेजें", + "mqtt-send-ack-on-validation-exception-hint": "डिफ़ॉल्ट रूप से, मैसेज वैलिडेशन विफल होने पर प्लेटफ़ॉर्म MQTT सत्र को बंद कर देगा। जब सक्षम होता है, प्लेटफ़ॉर्म सत्र बंद करने के बजाय पब्लिश पुष्टिकरण भेजेगा।", + "mqtt-protocol-version": "प्रोटोकॉल संस्करण", + "snmp-add-mapping": "SNMP मैपिंग जोड़ें", + "snmp-mapping-not-configured": "OID से टाइम सीरीज़/टेलीमेट्री के लिए कोई मैपिंग कॉन्फ़िगर नहीं की गई", + "snmp-timseries-or-attribute-name": "मैपिंग के लिए टाइम सीरीज़/विशेषता नाम", + "snmp-timseries-or-attribute-type": "मैपिंग के लिए टाइम सीरीज़/विशेषता प्रकार", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "पेलोड प्रकार आवश्यक है।", + "coap-device-type": "CoAP डिवाइस प्रकार", + "coap-device-payload-type": "CoAP डिवाइस पेलोड", + "coap-device-type-required": "CoAP डिवाइस प्रकार आवश्यक है।", + "coap-device-type-default": "डिफ़ॉल्ट", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "सिंगल [+] और मल्टी-लेवल [#] वाइल्डकार्ड समर्थित हैं।", + "telemetry-topic-filter": "टेलीमेट्री टॉपिक फ़िल्टर", + "telemetry-topic-filter-required": "टेलीमेट्री टॉपिक फ़िल्टर आवश्यक है।", + "attributes-topic-filter": "विशेषताएँ पब्लिश टॉपिक फ़िल्टर", + "attributes-subscribe-topic-filter": "विशेषताएँ सब्सक्राइब टॉपिक फ़िल्टर", + "attributes-topic-filter-required": "विशेषताएँ पब्लिश टॉपिक फ़िल्टर आवश्यक है।", + "attributes-subscribe-topic-filter-required": "विशेषताएँ सब्सक्राइब टॉपिक फ़िल्टर आवश्यक है।", + "telemetry-proto-schema": "टेलीमेट्री प्रोटो स्कीमा", + "telemetry-proto-schema-required": "टेलीमेट्री प्रोटो स्कीमा आवश्यक है।", + "attributes-proto-schema": "विशेषताएँ प्रोटो स्कीमा", + "attributes-proto-schema-required": "विशेषताएँ प्रोटो स्कीमा आवश्यक है।", + "rpc-response-proto-schema": "RPC रिस्पॉन्स प्रोटो स्कीमा", + "rpc-response-proto-schema-required": "RPC रिस्पॉन्स प्रोटो स्कीमा आवश्यक है।", + "rpc-response-topic-filter": "RPC रिस्पॉन्स टॉपिक फ़िल्टर", + "rpc-response-topic-filter-required": "RPC रिस्पॉन्स टॉपिक फ़िल्टर आवश्यक है।", + "rpc-request-proto-schema": "RPC रिक्वेस्ट प्रोटो स्कीमा", + "rpc-request-proto-schema-required": "RPC रिक्वेस्ट प्रोटो स्कीमा आवश्यक है।", + "rpc-request-proto-schema-hint": "RPC रिक्वेस्ट मैसेज में हमेशा ये फ़ील्ड होने चाहिए: string method = 1; int32 requestId = 2; और params = 3 (किसी भी डेटा टाइप के)।", + "not-valid-pattern-topic-filter": "अमान्य पैटर्न टॉपिक फ़िल्टर", + "not-valid-single-character": "सिंगल-लेवल वाइल्डकार्ड कैरेक्टर का अमान्य उपयोग", + "not-valid-multi-character": "मल्टी-लेवल वाइल्डकार्ड कैरेक्टर का अमान्य उपयोग", + "single-level-wildcards-hint": "[+] किसी भी टॉपिक फ़िल्टर लेवल के लिए उपयुक्त है। उदाहरण: v1/devices/+/telemetry या +/devices/+/attributes।", + "multi-level-wildcards-hint": "[#] स्वयं टॉपिक फ़िल्टर को रिप्लेस कर सकता है और टॉपिक का अंतिम चिन्ह होना चाहिए। उदाहरण: # या v1/devices/me/#।", + "alarm-rules": "अलार्म नियम", + "alarm-rules-with-count": "अलार्म नियम ({{count}})", + "no-alarm-rules": "कोई अलार्म नियम कॉन्फ़िगर नहीं किए गए हैं", + "add-alarm-rule": "अलार्म नियम जोड़ें", + "edit-alarm-rule": "अलार्म नियम संपादित करें", + "alarm-type": "अलार्म प्रकार", + "alarm-type-required": "अलार्म प्रकार आवश्यक है।", + "alarm-type-unique": "अलार्म प्रकार डिवाइस प्रोफ़ाइल के अलार्म नियमों के भीतर यूनिक होना चाहिए।", + "alarm-type-max-length": "अलार्म प्रकार 256 अक्षरों से कम होना चाहिए।", + "create-alarm-pattern": "{{alarmType}} अलार्म बनाएँ", + "create-alarm-rules": "अलार्म नियम बनाएँ", + "no-create-alarm-rules": "कोई क्रिएशन कंडीशन कॉन्फ़िगर नहीं की गई है", + "add-create-alarm-rule-prompt": "कृपया क्रिएशन अलार्म नियम जोड़ें", + "clear-alarm-rule": "क्लियर अलार्म नियम", + "no-clear-alarm-rule": "कोई क्लियरिंग कंडीशन कॉन्फ़िगर नहीं की गई है", + "add-create-alarm-rule": "क्रिएशन कंडीशन जोड़ें", + "add-clear-alarm-rule": "क्लियरिंग कंडीशन जोड़ें", + "select-alarm-severity": "अलार्म गंभीरता चुनें", + "alarm-severity-required": "अलार्म गंभीरता आवश्यक है।", + "condition-duration": "कंडीशन अवधि", + "condition-duration-value": "अवधि मान", + "condition-duration-time-unit": "समय इकाई", + "condition-duration-value-range": "अवधि मान 1 से 2147483647 के बीच होना चाहिए।", + "condition-duration-value-pattern": "अवधि मान पूर्णांक होना चाहिए।", + "condition-duration-value-required": "अवधि मान आवश्यक है।", + "condition-duration-time-unit-required": "समय इकाई आवश्यक है।", + "advanced-settings": "उन्नत सेटिंग्स", + "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-rule-condition": "अलार्म नियम कंडीशन", + "enter-alarm-rule-condition-prompt": "कृपया अलार्म नियम कंडीशन जोड़ें", + "edit-alarm-rule-condition": "अलार्म नियम कंडीशन संपादित करें", + "device-provisioning": "डिवाइस प्रोविजनिंग", + "provision-strategy": "प्रोविजन रणनीति", + "provision-strategy-required": "प्रोविजन रणनीति आवश्यक है।", + "provision-strategy-disabled": "निष्क्रिय", + "provision-strategy-created-new": "नए डिवाइस बनाने की अनुमति दें", + "provision-strategy-check-pre-provisioned": "प्री-प्रोविज़न किए गए डिवाइसों की जाँच करें", + "provision-device-key": "डिवाइस प्रोविजन कुंजी", + "provision-device-key-required": "डिवाइस प्रोविजन कुंजी आवश्यक है।", + "copy-provision-key": "प्रोविजन कुंजी कॉपी करें", + "provision-key-copied-message": "प्रोविजन कुंजी क्लिपबोर्ड में कॉपी कर दी गई है", + "provision-device-secret": "डिवाइस प्रोविजन सीक्रेट", + "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-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 {1 बार} other {# बार} } दोहराता है", + "condition-repeat-times-dynamic": "\"{ attribute }\" ({ count, plural, =1 {1 बार} other {# बार} }) दोहराता है", + "schedule-type": "शेड्यूलर प्रकार", + "schedule-type-required": "शेड्यूलर प्रकार आवश्यक है।", + "schedule": "शेड्यूल", + "edit-schedule": "अलार्म शेड्यूल संपादित करें", + "schedule-any-time": "हर समय सक्रिय", + "schedule-specific-time": "किसी विशिष्ट समय पर सक्रिय", + "schedule-custom": "कस्टम", + "schedule-day": { + "monday": "सोमवार", + "tuesday": "मंगलवार", + "wednesday": "बुधवार", + "thursday": "गुरुवार", + "friday": "शुक्रवार", + "saturday": "शनिवार", + "sunday": "रविवार" + }, + "schedule-days": "दिन", + "schedule-time": "समय", + "schedule-time-from": "से", + "schedule-time-to": "तक", + "schedule-days-of-week-required": "सप्ताह के कम-से-कम एक दिन का चयन करना आवश्यक है।", + "create-device-profile": "नया डिवाइस प्रोफ़ाइल बनाएं", + "import": "डिवाइस प्रोफ़ाइल इम्पोर्ट करें", + "export": "डिवाइस प्रोफ़ाइल एक्सपोर्ट करें", + "export-failed-error": "डिवाइस प्रोफ़ाइल एक्सपोर्ट नहीं की जा सकी: {{error}}", + "device-profile-file": "डिवाइस प्रोफ़ाइल फ़ाइल", + "invalid-device-profile-file-error": "डिवाइस प्रोफ़ाइल इम्पोर्ट नहीं की जा सकी: डिवाइस प्रोफ़ाइल डेटा संरचना अमान्य है।", + "power-saving-mode": "पावर सेविंग मोड", + "power-saving-mode-type": { + "default": "डिवाइस प्रोफ़ाइल पावर सेविंग मोड का उपयोग करें", + "psm": "पावर सेविंग मोड (PSM)", + "drx": "डिसकंटीन्यूस रिसेप्शन (DRX)", + "edrx": "एक्सटेंडेड डिसकंटीन्यूस रिसेप्शन (eDRX)" + }, + "edrx-cycle": "eDRX साइकल", + "edrx-cycle-required": "eDRX साइकल आवश्यक है।", + "edrx-cycle-pattern": "eDRX साइकल एक धनात्मक पूर्णांक होना चाहिए।", + "edrx-cycle-min": "eDRX साइकल की न्यूनतम संख्या {{ min }} सेकंड है।", + "paging-transmission-window": "पेजिंग ट्रांसमिशन विंडो", + "paging-transmission-window-required": "पेजिंग ट्रांसमिशन विंडो आवश्यक है।", + "paging-transmission-window-pattern": "पेजिंग ट्रांसमिशन विंडो एक धनात्मक पूर्णांक होना चाहिए।", + "paging-transmission-window-min": "पेजिंग ट्रांसमिशन विंडो की न्यूनतम संख्या {{ min }} सेकंड है।", + "psm-activity-timer": "PSM एक्टिविटी टाइमर", + "psm-activity-timer-required": "PSM एक्टिविटी टाइमर आवश्यक है।", + "psm-activity-timer-pattern": "PSM एक्टिविटी टाइमर एक धनात्मक पूर्णांक होना चाहिए।", + "psm-activity-timer-min": "PSM एक्टिविटी टाइमर का न्यूनतम मान {{ min }} सेकंड है।", + "lwm2m": { + "object-list": "ऑब्जेक्ट सूची", + "object-list-empty": "कोई ऑब्जेक्ट चयनित नहीं है।", + "no-objects-found": "कोई ऑब्जेक्ट नहीं मिला।", + "no-objects-matching": "कोई ऑब्जेक्ट '{{object}}' से मेल नहीं खा रहा है।", + "model-tab": "LWM2M मॉडल", + "add-new-instances": "नए इंस्टेंस जोड़ें", + "instances-list": "इंस्टेंस सूची", + "instances-list-required": "इंस्टेंस सूची आवश्यक है।", + "instance-id-pattern": "इंस्टेंस ID एक धनात्मक पूर्णांक होना चाहिए।", + "instance-id-max": "इंस्टेंस ID का अधिकतम मान {{max}} है।", + "instance": "इंस्टेंस", + "resource-label": "#ID रिसोर्स नाम", + "observe-label": "निगरानी", + "attribute-label": "विशेषता", + "telemetry-label": "टेलीमेट्री", + "edit-observe-select": "Observe को संपादित करने के लिए टेलीमेट्री या विशेषता चुनें।", + "edit-attributes-select": "विशेषताएँ संपादित करने के लिए टेलीमेट्री या विशेषता चुनें।", + "no-attributes-set": "कोई विशेषताएँ सेट नहीं हैं।", + "key-name": "कुंजी नाम", + "key-name-required": "कुंजी नाम आवश्यक है।", + "attribute-name": "नाम विशेषता", + "attribute-name-required": "नाम विशेषता आवश्यक है।", + "attribute-value": "विशेषता मान", + "attribute-value-required": "विशेषता मान आवश्यक है।", + "attribute-value-pattern": "विशेषता मान एक धनात्मक पूर्णांक होना चाहिए।", + "edit-attributes": "विशेषताएँ संपादित करें: {{ name }}", + "view-attributes": "विशेषताएँ देखें: {{ name }}", + "add-attribute": "विशेषता जोड़ें", + "edit-attribute": "विशेषता संपादित करें", + "view-attribute": "विशेषता देखें", + "remove-attribute": "विशेषता हटाएँ", + "delete-server-text": "सावधान रहें, पुष्टि के बाद सर्वर कॉन्फ़िगरेशन को वापस नहीं लाया जा सकेगा।", + "delete-server-title": "क्या आप वाकई सर्वर को हटाना चाहते हैं?", + "mode": "सुरक्षा कॉन्फ़िग मोड", + "bootstrap-tab": "बूटस्ट्रैप", + "bootstrap-server-legend": "बूटस्ट्रैप सर्वर (ShortId...)", + "lwm2m-server-legend": "LwM2M सर्वर (ShortId...)", + "server": "सर्वर", + "short-id": "शॉर्ट सर्वर Id", + "short-id-tooltip": "सर्वर का शॉर्ट Id। सर्वर ऑब्जेक्ट इंस्टेंस को जोड़ने के लिए लिंक की तरह उपयोग किया जाता है।\nयह आइडेंटिफ़ायर LwM2M क्लाइंट के लिए कॉन्फ़िगर किए गए प्रत्येक LwM2M सर्वर को यूनिक रूप से पहचानता है।\nजब Bootstrap-Server रिसोर्स का मान 'false' हो, तब इस रिसोर्स को अवश्य सेट किया जाना चाहिए।\nLwM2M सर्वर की पहचान के लिए ID:0 और ID:65535 मानों का उपयोग नहीं किया जाना चाहिए।", + "short-id-tooltip-bootstrap": "सर्वर का शॉर्ट Id। सर्वर ऑब्जेक्ट इंस्टेंस को जोड़ने के लिए लिंक की तरह उपयोग किया जाता है।\nयह आइडेंटिफ़ायर LwM2M क्लाइंट के लिए कॉन्फ़िगर किए गए प्रत्येक LwM2M सर्वर को यूनिक रूप से पहचानता है।\nजब Bootstrap-Server रिसोर्स का मान 'false' हो, तब इस रिसोर्स को अवश्य सेट किया जाना चाहिए।", + "short-id-required": "शॉर्ट सर्वर Id आवश्यक है।", + "short-id-range": "शॉर्ट सर्वर Id {{ min }} से {{ max }} की रेंज में होना चाहिए।", + "short-id-pattern": "शॉर्ट सर्वर Id एक धनात्मक पूर्णांक होना चाहिए।", + "lifetime": "क्लाइंट रजिस्ट्रेशन लाइफ़टाइम", + "lifetime-required": "क्लाइंट रजिस्ट्रेशन लाइफ़टाइम आवश्यक है।", + "lifetime-pattern": "क्लाइंट रजिस्ट्रेशन लाइफ़टाइम एक धनात्मक पूर्णांक होना चाहिए।", + "default-min-period": "दो नोटिफ़िकेशन के बीच न्यूनतम अवधि (सेकंड)", + "default-min-period-tooltip": "जब ऑब्ज़र्वेशन में यह पैरामीटर शामिल नहीं होता, तो LwM2M क्लाइंट को ऑब्ज़र्वेशन की न्यूनतम अवधि के लिए जो डिफ़ॉल्ट मान उपयोग करना चाहिए।", + "default-min-period-required": "न्यूनतम अवधि आवश्यक है।", + "default-min-period-pattern": "न्यूनतम अवधि एक धनात्मक पूर्णांक होनी चाहिए।", + "notification-storing": "डिसेबल या ऑफ़लाइन होने पर नोटिफ़िकेशन स्टोर करना", + "binding": "बाइंडिंग", + "binding-type": { + "u": "U: क्लाइंट किसी भी समय UDP बाइंडिंग के माध्यम से पहुँचा जा सकता है।", + "m": "M: क्लाइंट किसी भी समय MQTT बाइंडिंग के माध्यम से पहुँचा जा सकता है।", + "h": "H: क्लाइंट किसी भी समय HTTP बाइंडिंग के माध्यम से पहुँचा जा सकता है।", + "t": "T: क्लाइंट किसी भी समय TCP बाइंडिंग के माध्यम से पहुँचा जा सकता है।", + "s": "S: क्लाइंट किसी भी समय SMS बाइंडिंग के माध्यम से पहुँचा जा सकता है।", + "n": "N: क्लाइंट को ऐसे अनुरोध के उत्तर Non-IP बाइंडिंग के माध्यम से भेजने होंगे (LWM2M 1.1 से समर्थित)।", + "uq": "UQ: UDP कनेक्शन क्यू मोड में (LWM2M 1.1 से समर्थित नहीं है)", + "uqs": "UQS: UDP और SMS दोनों कनेक्शन सक्रिय; UDP क्यू मोड में, SMS स्टैंडर्ड मोड में (LWM2M 1.1 से समर्थित नहीं है)", + "tq": "TQ: TCP कनेक्शन क्यू मोड में (LWM2M 1.1 से समर्थित नहीं है)", + "tqs": "TQS: TCP और SMS दोनों कनेक्शन सक्रिय; TCP क्यू मोड में, SMS स्टैंडर्ड मोड में (LWM2M 1.1 से समर्थित नहीं है)", + "sq": "SQ: SMS कनेक्शन क्यू मोड में (LWM2M 1.1 से समर्थित नहीं है)" + }, + "binding-tooltip": "यह LwM2M सर्वर ऑब्जेक्ट - /1/x/7 के \"बाइंडिंग\" रिसोर्स में मौजूद सूची है.\nयह LwM2M क्लाइंट में समर्थित बाइंडिंग मोड्स को दर्शाता है.\nयह मान डिवाइस ऑब्जेक्ट (/3/0/16) में “Supported Binding and Modes” रिसोर्स में दिए गए मान के समान होना चाहिए.\nहालाँकि कई ट्रांसपोर्ट समर्थित हैं, लेकिन पूरे ट्रांसपोर्ट सेशन के दौरान केवल एक ही ट्रांसपोर्ट बाइंडिंग का उपयोग किया जा सकता है.\nउदाहरण के लिए, जब UDP और SMS दोनों समर्थित हों, तो पूरे ट्रांसपोर्ट सेशन के दौरान LwM2M क्लाइंट और LwM2M सर्वर UDP या SMS में से किसी एक के माध्यम से संचार करने का चयन कर सकते हैं.", + "bootstrap-server": "बूटस्ट्रैप सर्वर", + "lwm2m-server": "LwM2M सर्वर", + "include-bootstrap-server": "बूटस्ट्रैप सर्वर अपडेट शामिल करें", + "bootstrap-update-title": "आपने पहले से ही बूटस्ट्रैप सर्वर कॉन्फ़िगर कर लिया है। क्या आप वाकई अपडेट को बाहर करना चाहते हैं?", + "bootstrap-update-text": "सावधान रहें, पुष्टि के बाद बूटस्ट्रैप सर्वर कॉन्फ़िगरेशन डेटा पुनर्प्राप्त नहीं किया जा सकेगा.", + "server-host": "होस्ट", + "server-host-required": "होस्ट आवश्यक है।", + "server-port": "पोर्ट", + "server-port-required": "पोर्ट आवश्यक है।", + "server-port-pattern": "पोर्ट एक धनात्मक पूर्णांक होना चाहिए।", + "server-port-range": "पोर्ट 1 से 65535 की सीमा में होना चाहिए।", + "server-public-key": "सर्वर सार्वजनिक कुंजी", + "server-public-key-required": "सर्वर सार्वजनिक कुंजी आवश्यक है।", + "client-hold-off-time": "होल्ड-ऑफ़ समय", + "client-hold-off-time-required": "होल्ड-ऑफ़ समय आवश्यक है।", + "client-hold-off-time-pattern": "होल्ड-ऑफ़ समय एक धनात्मक पूर्णांक होना चाहिए।", + "client-hold-off-time-tooltip": "केवल बूटस्ट्रैप सर्वर के साथ उपयोग के लिए क्लाइंट होल्ड-ऑफ़ समय।", + "account-after-timeout": "टाइमआउट के बाद खाता", + "account-after-timeout-required": "टाइमआउट के बाद खाता का मान आवश्यक है।", + "account-after-timeout-pattern": "टाइमआउट के बाद खाता का मान एक धनात्मक पूर्णांक होना चाहिए।", + "account-after-timeout-tooltip": "यह संसाधन बूटस्ट्रैप सर्वर के \"टाइमआउट के बाद खाता\" मान को निर्धारित करता है।", + "server-type": "सर्वर प्रकार", + "add-new-server-title": "नया सर्वर कॉन्फ़िग जोड़ें", + "add-server-config": "सर्वर कॉन्फ़िग जोड़ें", + "add-lwm2m-server-config": "LwM2M सर्वर जोड़ें", + "no-config-servers": "कोई सर्वर कॉन्फ़िगर नहीं है", + "others-tab": "अन्य सेटिंग्स", + "ota-update": "OTA अपडेट", + "use-object-19-for-ota-update": "OTA फ़ाइल मेटाडेटा (चेकसम, आकार, संस्करण, नाम) के लिए Object 19 का उपयोग करें", + "use-object-19-for-ota-update-hint": "OTA अपडेट के लिए Resource ObjectId = 19 का उपयोग करें: फ़र्मवेयर → InstanceId = 65534, सॉफ़्टवेयर → InstanceId = 65535। डेटा प्रारूप Base64 में लिपटा हुआ JSON है। यह JSON OTA फ़ाइल मेटाडेटा (फ़ाइल जानकारी) रखता है: \"Checksum\" (SHA256)। अतिरिक्त फ़ील्ड: \"Title\" (OTA नाम), \"Version\" (OTA संस्करण), \"File Name\" (क्लाइंट पर OTA संग्रहीत करने के लिए फ़ाइल नाम), \"File Size\" (बाइट्स में OTA आकार)।", + "client-strategy": "कनेक्शन के समय क्लाइंट की रणनीति", + "client-strategy-label": "रणनीति", + "client-strategy-only-observe": "प्रारंभिक कनेक्शन के बाद केवल Observe अनुरोध क्लाइंट को भेजें", + "client-strategy-read-all": "पंजीकरण के बाद सभी रिसोर्स पढ़ें और Observe अनुरोध क्लाइंट को भेजें", + "fw-update": "फ़र्मवेयर अपडेट", + "fw-update-strategy": "फ़र्मवेयर अपडेट रणनीति", + "fw-update-strategy-data": "Object 19 और Resource 0 (Data) का उपयोग करके फ़र्मवेयर अपडेट को बाइनरी फ़ाइल के रूप में पुश करें", + "fw-update-strategy-package": "Object 5 और Resource 0 (Package) का उपयोग करके फ़र्मवेयर अपडेट को बाइनरी फ़ाइल के रूप में पुश करें", + "fw-update-strategy-package-uri": "पैकेज डाउनलोड करने के लिए यूनिक CoAP URL स्वतः जेनरेट करें और फ़र्मवेयर अपडेट को Object 5 और Resource 1 (Package URI) के रूप में पुश करें", + "sw-update": "सॉफ़्टवेयर अपडेट", + "sw-update-strategy": "सॉफ़्टवेयर अपडेट रणनीति", + "sw-update-strategy-package": "Object 9 और Resource 2 (Package) का उपयोग करके बाइनरी फ़ाइल पुश करें", + "sw-update-strategy-package-uri": "पैकेज डाउनलोड करने के लिए यूनिक CoAP URL स्वतः जेनरेट करें और Object 9 और Resource 3 (Package URI) का उपयोग करके सॉफ़्टवेयर अपडेट पुश करें", + "fw-update-resource": "फ़र्मवेयर अपडेट CoAP रिसोर्स", + "fw-update-resource-required": "फ़र्मवेयर अपडेट CoAP रिसोर्स आवश्यक है।", + "sw-update-resource": "सॉफ़्टवेयर अपडेट CoAP रिसोर्स", + "sw-update-resource-required": "सॉफ़्टवेयर अपडेट CoAP रिसोर्स आवश्यक है।", + "config-json-tab": "डिवाइस प्रोफ़ाइल JSON कॉन्फ़िग", + "attributes-name": { + "min-period": "न्यूनतम अवधि", + "max-period": "अधिकतम अवधि", + "greater-than": "से बड़ा", + "less-than": "से छोटा", + "step": "स्टेप", + "min-evaluation-period": "न्यूनतम मूल्यांकन अवधि", + "max-evaluation-period": "अधिकतम मूल्यांकन अवधि" + }, + "default-object-id": "डिफ़ॉल्ट ऑब्जेक्ट वर्ज़न (विशेषता)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1", + "v1-2": "1.2" + }, + "observe-strategy": { + "observe-strategy": "Observe रणनीति", + "single": "सिंगल", + "single-description": "प्रत्येक रिसोर्स के लिए एक Observe अनुरोध (ज़्यादा सटीकता, अधिक नेटवर्क ट्रैफिक)", + "composite-all": "कंपोज़िट (सभी)", + "composite-all-description": "सभी रिसोर्स एक ही कंपोज़िट Observe अनुरोध से ऑब्ज़र्व किए जाते हैं (ज़्यादा प्रभावी, कम लचीलापन)", + "composite-by-object": "ऑब्जेक्ट के अनुसार कंपोज़िट", + "composite-by-object-description": "रिसोर्स को ऑब्जेक्ट प्रकार के अनुसार समूहित करके अलग-अलग कंपोज़िट Observe अनुरोधों से ऑब्ज़र्व किया जाता है (संतुलित तरीका)" + } + }, + "snmp": { + "add-communication-config": "कम्युनिकेशन कॉन्फ़िगरेशन जोड़ें", + "add-mapping": "मैपिंग जोड़ें", + "authentication-passphrase": "ऑथेंटिकेशन पासफ़्रेज़", + "authentication-passphrase-required": "ऑथेंटिकेशन पासफ़्रेज़ आवश्यक है।", + "authentication-protocol": "ऑथेंटिकेशन प्रोटोकॉल", + "authentication-protocol-required": "ऑथेंटिकेशन प्रोटोकॉल आवश्यक है।", + "communication-configs": "कम्युनिकेशन कॉन्फ़िगरेशन", + "community": "कम्युनिटी स्ट्रिंग", + "community-required": "कम्युनिटी स्ट्रिंग आवश्यक है।", + "context-name": "कॉन्टेक्स्ट नाम", + "data-key": "डेटा की", + "data-key-required": "डेटा की आवश्यक है।", + "data-type": "डेटा प्रकार", + "data-type-required": "डेटा प्रकार आवश्यक है।", + "engine-id": "इंजन ID", + "host": "होस्ट", + "host-required": "होस्ट आवश्यक है।", + "oid": "OID", + "oid-pattern": "अमान्य OID फ़ॉर्मेट।", + "oid-required": "OID आवश्यक है।", + "please-add-communication-config": "कृपया कम्युनिकेशन कॉन्फ़िगरेशन जोड़ें", + "please-add-mapping-config": "कृपया मैपिंग कॉन्फ़िगरेशन जोड़ें", + "port": "पोर्ट", + "port-format": "अमान्य पोर्ट फ़ॉर्मेट।", + "port-required": "पोर्ट आवश्यक है।", + "privacy-passphrase": "प्राइवेसी पासफ़्रेज़", + "privacy-passphrase-required": "प्राइवेसी पासफ़्रेज़ आवश्यक है।", + "privacy-protocol": "प्राइवेसी प्रोटोकॉल", + "privacy-protocol-required": "प्राइवेसी प्रोटोकॉल आवश्यक है।", + "protocol-version": "प्रोटोकॉल वर्ज़न", + "protocol-version-required": "प्रोटोकॉल वर्ज़न आवश्यक है।", + "querying-frequency": "क्वेरी फ़्रीक्वेंसी, ms", + "querying-frequency-invalid-format": "क्वेरी फ़्रीक्वेंसी एक धनात्मक पूर्णांक होना चाहिए।", + "querying-frequency-required": "क्वेरी फ़्रीक्वेंसी आवश्यक है।", + "retries": "रीट्राई", + "retries-invalid-format": "रीट्राई एक धनात्मक पूर्णांक होना चाहिए।", + "retries-required": "रीट्राई आवश्यक है।", + "scope": "स्कोप", + "scope-required": "स्कोप आवश्यक है।", + "security-name": "सिक्योरिटी नाम", + "security-name-required": "सिक्योरिटी नाम आवश्यक है।", + "timeout-ms": "टाइमआउट, ms", + "timeout-ms-invalid-format": "टाइमआउट एक धनात्मक पूर्णांक होना चाहिए।", + "timeout-ms-required": "टाइमआउट आवश्यक है।", + "user-name": "यूज़र नेम", + "user-name-required": "यूज़र नेम आवश्यक है।" + } + }, + "dialog": { + "close": "डायलॉग बंद करें", + "error-message-title": "त्रुटि संदेश:", + "error-details-title": "त्रुटि विवरण" + }, + "direction": { + "column": "स्तंभ", + "row": "पंक्ति" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Edge इंस्टैंसेज़", + "instances": "इंस्टैंसेज़", + "edge-file": "Edge फ़ाइल", + "name-max-length": "नाम 256 से कम होना चाहिए", + "label-max-length": "लेबल 256 से कम होना चाहिए", + "type-max-length": "प्रकार 256 से कम होना चाहिए", + "management": "Edge प्रबंधन", + "no-edges-matching": "'{{entity}}' से मेल खाते कोई Edge नहीं मिले।", + "add": "Edge जोड़ें", + "no-edges-text": "कोई Edge नहीं मिले", + "edge-details": "Edge विवरण", + "add-edge-text": "नया Edge जोड़ें", + "delete": "Edge हटाएं", + "delete-edge-title": "क्या आप वाकई Edge '{{edgeName}}' हटाना चाहते हैं?", + "delete-edge-text": "सावधान, पुष्टि के बाद Edge और उससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किए जा सकेंगे।", + "delete-edges-title": "क्या आप वाकई { count, plural, =1 {1 Edge} other {# Edge} } हटाना चाहते हैं?", + "delete-edges-text": "सावधान, पुष्टि के बाद सभी चयनित Edge हटा दिए जाएंगे और उनसे संबंधित सभी डेटा पुनर्प्राप्त नहीं किए जा सकेंगे।", + "name": "नाम", + "name-starts-with": "Edge नाम इससे शुरू होता है", + "name-required": "नाम आवश्यक है।", + "description": "विवरण", + "details": "विवरण", + "events": "इवेंट्स", + "copy-id": "Edge ID कॉपी करें", + "id-copied-message": "एज Id क्लिपबोर्ड में कॉपी कर दी गई है", + "sync": "Edge सिंक करें", + "edge-required": "Edge आवश्यक है", + "edge-type": "Edge प्रकार", + "edge-type-required": "Edge प्रकार आवश्यक है।", + "event-action": "इवेंट एक्शन", + "entity-id": "एंटिटी ID", + "select-edge-type": "Edge प्रकार चुनें", + "assign-to-customer": "कस्टमर को असाइन करें", + "assign-to-customer-text": "कृपया Edge असाइन करने के लिए कस्टमर चुनें", + "assign-edge-to-customer": "कस्टमर को Edge असाइन करें", + "assign-edge-to-customer-text": "कृपया कस्टमर को असाइन करने के लिए Edge चुनें", + "assignedToCustomer": "कस्टमर को असाइन किया गया", + "edge-public": "Edge सार्वजनिक है", + "assigned-to-customer": "असाइन किया गया: {{customerTitle}}", + "unassign-from-customer": "कस्टमर से अनअसाइन करें", + "unassign-edge-title": "क्या आप वाकई Edge '{{edgeName}}' को अनअसाइन करना चाहते हैं?", + "unassign-edge-text": "पुष्टि के बाद Edge अनअसाइन कर दिया जाएगा और कस्टमर के लिए उपलब्ध नहीं रहेगा।", + "unassign-edges-title": "क्या आप वाकई { count, plural, =1 {1 Edge} other {# Edge} } को अनअसाइन करना चाहते हैं?", + "unassign-edges-text": "पुष्टि के बाद सभी चयनित Edge अनअसाइन कर दिए जाएंगे और कस्टमर के लिए उपलब्ध नहीं रहेंगे।", + "make-public": "Edge को सार्वजनिक करें", + "make-public-edge-title": "क्या आप वाकई Edge '{{edgeName}}' को सार्वजनिक करना चाहते हैं?", + "make-public-edge-text": "पुष्टि के बाद Edge और उसका सारा डेटा सार्वजनिक कर दिया जाएगा और दूसरों के लिए उपलब्ध होगा।", + "make-private": "Edge को निजी करें", + "public": "सार्वजनिक", + "make-private-edge-title": "क्या आप वाकई Edge '{{edgeName}}' को निजी करना चाहते हैं?", + "make-private-edge-text": "पुष्टि के बाद Edge और उसका सारा डेटा निजी कर दिया जाएगा और दूसरों के लिए उपलब्ध नहीं होगा।", + "import": "Edge आयात करें", + "install-connect-instructions": "इंस्टॉल और कनेक्ट निर्देश", + "install-connect-instructions-edge-created": "Edge बनाया गया! इंस्टॉल और कनेक्ट निर्देश देखें", + "loading-edge-instructions": "Edge निर्देश लोड हो रहे हैं...", + "label": "लेबल", + "load-entity-error": "डेटा लोड नहीं हो सका। एंटिटी हटाई जा चुकी है।", + "assign-new-edge": "नया Edge असाइन करें", + "unassign-from-edge": "Edge से अनअसाइन करें", + "edge-key": "Edge कुंजी", + "copy-edge-key": "Edge कुंजी कॉपी करें", + "edge-key-copied-message": "Edge कुंजी क्लिपबोर्ड पर कॉपी कर दी गई है", + "edge-secret": "Edge सीक्रेट", + "copy-edge-secret": "Edge सीक्रेट कॉपी करें", + "edge-secret-copied-message": "Edge सीक्रेट क्लिपबोर्ड पर कॉपी कर दिया गया है", + "manage-assets": "एसेट प्रबंधित करें", + "manage-devices": "डिवाइस प्रबंधित करें", + "manage-entity-views": "एंटिटी व्यू प्रबंधित करें", + "manage-dashboards": "डैशबोर्ड प्रबंधित करें", + "manage-rulechains": "रूल चेन प्रबंधित करें", + "assets": "Edge एसेट", + "devices": "Edge डिवाइस", + "entity-views": "Edge एंटिटी व्यूज़", + "dashboard": "Edge डैशबोर्ड", + "dashboards": "Edge डैशबोर्ड्स", + "rulechain-templates": "रूल चेन टेम्पलेट्स", + "edge-rulechain-templates": "Edge रूल चेन टेम्पलेट्स", + "rulechains": "Edge रूल चेन्स", + "search": "Edge खोजें", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edges} } चयनित", + "any-edge": "कोई भी Edge", + "no-edge-types-matching": "'{{entitySubtype}}' से मेल खाते कोई Edge प्रकार नहीं मिले।", + "edge-type-list-empty": "कोई Edge प्रकार चयनित नहीं है।", + "edge-types": "Edge प्रकार", + "enter-edge-type": "Edge प्रकार दर्ज करें", + "deployed": "डिप्लॉय्ड", + "pending": "लंबित", + "downlinks": "डाउनलिंक्स", + "no-downlinks-prompt": "कोई डाउनलिंक्स नहीं मिले", + "sync-process-started-successfully": "सिंक प्रक्रिया सफलतापूर्वक शुरू हो गई!", + "missing-related-rule-chains-title": "Edge में संबंधित रूल चेन(स) गायब हैं", + "missing-related-rule-chains-text": "Edge को असाइन की गई रूल चेन(स) ऐसे रूल नोड्स का उपयोग करती हैं जो संदेश(ओं) को उन रूल चेन(स) की ओर फ़ॉरवर्ड करती हैं जो इस Edge को असाइन नहीं हैं।

    गायब रूल चेन(स) की सूची:
    {{missingRuleChains}}", + "widget-datasource-error": "यह विजेट केवल EDGE एंटिटी डेटासोर्स को सपोर्ट करता है", + "upgrade-instructions": "अपग्रेड निर्देश", + "connected": "कनेक्टेड", + "disconnected": "डिस्कनेक्टेड" + }, + "edge-event": { + "type-dashboard": "डैशबोर्ड", + "type-asset": "एसेट", + "type-device": "डिवाइस", + "type-device-profile": "डिवाइस प्रोफ़ाइल", + "type-asset-profile": "एसेट प्रोफ़ाइल", + "type-entity-view": "एंटिटी व्यू", + "type-alarm": "अलार्म", + "type-rule-chain": "रूल चेन", + "type-rule-chain-metadata": "रूल चेन मेटाडेटा", + "type-edge": "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": "अपडेट किया गया", + "action-type-post-attributes": "विशेषताएँ पोस्ट की गईं", + "action-type-attributes-updated": "विशेषताएँ अपडेट की गईं", + "action-type-attributes-deleted": "विशेषताएँ हटाई गईं", + "action-type-timeseries-updated": "टाइम सीरीज़ अपडेट की गई", + "action-type-credentials-updated": "क्रेडेंशियल्स अपडेट किए गए", + "action-type-assigned-to-customer": "कस्टमर को असाइन किया गया", + "action-type-unassigned-from-customer": "कस्टमर से अनअसाइन किया गया", + "action-type-relation-add-or-update": "रिलेशन जोड़ा या अपडेट किया गया", + "action-type-relation-deleted": "रिलेशन हटाया गया", + "action-type-rpc-call": "RPC कॉल", + "action-type-alarm-ack": "अलार्म Ack", + "action-type-alarm-clear": "अलार्म क्लियर", + "action-type-alarm-assigned": "अलार्म असाइन किया गया", + "action-type-alarm-unassigned": "अलार्म अनअसाइन किया गया", + "action-type-assigned-to-edge": "Edge को असाइन किया गया", + "action-type-unassigned-from-edge": "Edge से अनअसाइन किया गया", + "action-type-credentials-request": "क्रेडेंशियल्स रिक्वेस्ट", + "action-type-entity-merge-request": "एंटिटी मर्ज रिक्वेस्ट" + }, + "error": { + "unable-to-connect": "सर्वर से कनेक्ट नहीं हो पा रहा है! कृपया अपना इंटरनेट कनेक्शन जाँचें.", + "unhandled-error-code": "अनहैंडल्ड एरर कोड: {{errorCode}}", + "unknown-error": "अज्ञात त्रुटि" + }, + "entity": { + "entity": "एंटिटी", + "entities": "एंटिटीज़", + "entities-count": "एंटिटीज़ की संख्या", + "alarms-count": "अलार्म की संख्या", + "aliases": "एंटिटी उपनाम", + "aliases-short": "उपनाम", + "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": "एंटिटी उपनाम जोड़ें", + "edit-alias": "एंटिटी उपनाम संपादित करें", + "entity-list": "एंटिटी सूची", + "entity-type": "एंटिटी प्रकार", + "entity-types": "एंटिटी प्रकार", + "entity-type-list": "एंटिटी प्रकार सूची", + "any-entity": "कोई भी एंटिटी", + "add-entity-type": "एंटिटी प्रकार जोड़ें", + "enter-entity-type": "एंटिटी प्रकार दर्ज करें", + "no-entities-matching": "'{{entity}}' से मेल खाने वाली कोई एंटिटी नहीं मिली.", + "no-entities-text": "कोई एंटिटी नहीं मिली.", + "no-entity-types-matching": "'{{entityType}}' से मेल खाने वाले कोई एंटिटी प्रकार नहीं मिले.", + "name-starts-with": "नाम अभिव्यक्ति", + "help-text": "ज़रूरत के अनुसार '%' का उपयोग करें: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "फ़िल्टर उपयोग करें", + "entity-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": "नया बनाएँ", + "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-device-profile": "डिवाइस प्रोफ़ाइल", + "type-device-profiles": "डिवाइस प्रोफ़ाइल्स", + "clear-selected-profiles": "चयनित प्रोफ़ाइल्स साफ़ करें", + "list-of-device-profiles": "{ count, plural, =1 {एक डिवाइस प्रोफ़ाइल} other {# डिवाइस प्रोफ़ाइल की सूची} }", + "device-profile-name-starts-with": "वे डिवाइस प्रोफ़ाइल्स जिनके नाम '{{prefix}}' से शुरू होते हैं", + "type-asset-profile": "एसेट प्रोफ़ाइल", + "type-asset-profiles": "एसेट प्रोफ़ाइल्स", + "list-of-asset-profiles": "{ count, plural, =1 {एक एसेट प्रोफ़ाइल} other {# एसेट प्रोफ़ाइल की सूची} }", + "asset-profile-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-tenant-profile": "टेनेंट प्रोफ़ाइल", + "type-tenant-profiles": "टेनेंट प्रोफ़ाइल्स", + "list-of-tenant-profiles": "{ count, plural, =1 {एक टेनेंट प्रोफ़ाइल} other {# टेनेंट प्रोफ़ाइल की सूची} }", + "tenant-profile-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": "वर्तमान टेनेंट", + "type-current-user": "वर्तमान उपयोगकर्ता", + "type-current-user-owner": "वर्तमान उपयोगकर्ता मालिक", + "type-calculated-field": "कैलक्युलेटेड फ़ील्ड", + "type-calculated-fields": "कैलक्युलेटेड फ़ील्ड्स", + "type-ai-model": "AI मॉडल", + "type-ai-models": "AI मॉडल्स", + "type-widgets-bundle": "विजेट बंडल", + "type-widgets-bundles": "विजेट बंडल्स", + "list-of-widgets-bundles": "{ count, plural, =1 {एक विजेट बंडल} other {# विजेट बंडल्स की सूची} }", + "type-widget": "विजेट", + "type-widgets": "विजेट", + "list-of-widgets": "{ count, plural, =1 {एक विजेट} other {# विजेट की सूची} }", + "search": "एंटिटीज़ खोजें", + "selected-entities": "{ count, plural, =1 {1 एंटिटी} other {# एंटिटीज़} } चयनित", + "entity-name": "एंटिटी नाम", + "entity-label": "एंटिटी लेबल", + "details": "एंटिटी विवरण", + "no-entities-prompt": "कोई एंटिटी नहीं मिली", + "no-data": "दिखाने के लिए कोई डेटा नहीं", + "columns-to-display": "दिखाने वाले कॉलम", + "type-api-usage-state": "API उपयोग स्थिति", + "type-edge": "Edge", + "type-edges": "Edge", + "list-of-edges": "{ count, plural, =1 {एक Edge} other {# Edge की सूची} }", + "edge-name-starts-with": "Edge जिनके नाम '{{prefix}}' से शुरू होते हैं", + "version-conflict": { + "message": "क्या आप मौजूदा संस्करण को ओवरराइट करना चाहते हैं या बदलावों को छोड़कर नवीनतम संस्करण लोड करना चाहते हैं?", + "link": "आप इसे उपयोग करके {{entityType}} का अपना संस्करण डाउनलोड कर सकते हैं", + "overwrite": "संस्करण ओवरराइट करें", + "discard": "बदलाव छोड़ें" + }, + "type-tb-resource": "रिसोर्स", + "type-tb-resources": "रिसोर्सेज़", + "list-of-tb-resources": "{ count, plural, =1 {एक रिसोर्स} other {# रिसोर्सेज़ की सूची} }", + "type-ota-package": "OTA पैकेज", + "type-ota-packages": "OTA पैकेजेज़", + "list-of-ota-packages": "{ count, plural, =1 {एक OTA पैकेज} other {# OTA पैकेजेज़ की सूची} }", + "type-rpc": "RPC", + "type-queue": "क्यू", + "type-queue-stats": "क्यू आँकड़े", + "type-queues-stats": "क्यूज़ आँकड़े", + "type-notification": "अधिसूचना", + "type-notification-rule": "अधिसूचना नियम", + "type-notification-rules": "अधिसूचना नियम", + "list-of-notification-rules": "{ count, plural, =1 {एक अधिसूचना नियम} other {# अधिसूचना नियमों की सूची} }", + "type-notification-target": "अधिसूचना प्राप्तकर्ता", + "type-notification-targets": "अधिसूचना प्राप्तकर्ता", + "list-of-notification-targets": "{ count, plural, =1 {एक अधिसूचना प्राप्तकर्ता} other {# अधिसूचना प्राप्तकर्ताओं की सूची} }", + "type-notification-request": "अधिसूचना अनुरोध", + "type-notification-template": "अधिसूचना टेम्पलेट", + "type-notification-templates": "अधिसूचना टेम्पलेट्स", + "list-of-notification-templates": "{ count, plural, =1 {एक अधिसूचना टेम्पलेट} other {# अधिसूचना टेम्पलेट्स की सूची} }", + "link": "लिंक", + "type-oauth2-client": "OAuth 2.0 क्लाइंट", + "type-oauth2-clients": "OAuth 2.0 क्लाइंट्स", + "list-of-oauth2-clients": "{ count, plural, =1 {एक OAuth 2.0 क्लाइंट} other {# OAuth 2.0 क्लाइंट्स की सूची} }", + "type-domain": "डोमेन", + "type-domains": "डोमेन्स", + "list-of-domains": "{ count, plural, =1 {एक डोमेन} other {# डोमेन्स की सूची} }", + "type-mobile-app": "मोबाइल एप्लिकेशन", + "type-mobile-apps": "मोबाइल एप्लिकेशन्स", + "list-of-mobile-apps": "{ count, plural, =1 {एक मोबाइल एप्लिकेशन} other {# मोबाइल एप्लिकेशन्स की सूची} }", + "type-mobile-app-bundle": "मोबाइल बंडल", + "type-mobile-app-bundles": "मोबाइल बंडल्स", + "list-of-mobile-app-bundles": "{ count, plural, =1 {एक मोबाइल बंडल} other {# मोबाइल बंडल्स की सूची} }" + }, + "entity-field": { + "created-time": "बनाने का समय", + "name": "नाम", + "type": "प्रकार", + "first-name": "पहला नाम", + "last-name": "अंतिम नाम", + "email": "ईमेल", + "title": "शीर्षक", + "country": "देश", + "state": "राज्य", + "city": "शहर", + "address": "पता", + "address2": "पता 2", + "zip": "ज़िप", + "phone": "फ़ोन", + "label": "लेबल", + "queue-name": "क्यू नाम", + "service-id": "सर्विस Id", + "owner-name": "ओनर नाम", + "owner-type": "ओनर प्रकार" + }, + "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}}' से मेल खाने वाले कोई एंटिटी व्यूज़ नहीं मिले.", + "public": "पब्लिक", + "alias": "उपनाम", + "alias-required": "एंटिटी व्यू उपनाम आवश्यक है.", + "remove-alias": "एंटिटी व्यू उपनाम हटाएँ", + "add-alias": "एंटिटी व्यू उपनाम जोड़ें", + "name-starts-with": "एंटिटी व्यू नाम अभिव्यक्ति", + "help-text": "ज़रूरत के अनुसार '%' का उपयोग करें: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_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": "एंटिटी व्यू जोड़ें", + "entity-view-public": "एंटिटी व्यू पब्लिक है", + "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 एंटिटी व्यू} other {# एंटिटी व्यूज़} } असाइन करें", + "delete-entity-views": "एंटिटी व्यूज़ हटाएँ", + "make-public": "एंटिटी व्यू को पब्लिक करें", + "make-private": "एंटिटी व्यू को प्राइवेट करें", + "unassign-from-customer": "कस्टमर से अनअसाइन करें", + "unassign-entity-views": "एंटिटी व्यूज़ अनअसाइन करें", + "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 {1 एंटिटी व्यू} other {# एंटिटी व्यूज़} } को हटाना चाहते हैं?", + "delete-entity-views-action-title": "हटाएँ { count, plural, =1 {1 एंटिटी व्यू} other {# एंटिटी व्यूज़} }", + "delete-entity-views-text": "सावधान रहें, पुष्टि के बाद सभी चयनित एंटिटी व्यूज़ हटा दिए जाएँगे और उनसे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "make-public-entity-view-title": "क्या आप वाकई एंटिटी व्यू '{{entityViewName}}' को पब्लिक बनाना चाहते हैं?", + "make-public-entity-view-text": "पुष्टि के बाद एंटिटी व्यू और उसका सारा डेटा पब्लिक हो जाएगा और दूसरों के लिए सुलभ होगा.", + "make-private-entity-view-title": "क्या आप वाकई एंटिटी व्यू '{{entityViewName}}' को प्राइवेट बनाना चाहते हैं?", + "make-private-entity-view-text": "पुष्टि के बाद एंटिटी व्यू और उसका सारा डेटा प्राइवेट हो जाएगा और दूसरों के लिए सुलभ नहीं होगा.", + "unassign-entity-view-title": "क्या आप वाकई एंटिटी व्यू '{{entityViewName}}' को अनअसाइन करना चाहते हैं?", + "unassign-entity-view-text": "पुष्टि के बाद एंटिटी व्यू अनअसाइन हो जाएगा और कस्टमर के लिए सुलभ नहीं रहेगा.", + "unassign-entity-view": "एंटिटी व्यू अनअसाइन करें", + "unassign-entity-views-title": "क्या आप वाकई { count, plural, =1 {1 एंटिटी व्यू} 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": "एंटिटी व्यू प्रकार", + "created-time": "बनाने का समय", + "name": "नाम", + "name-required": "नाम आवश्यक है.", + "name-max-length": "नाम 256 वर्णों से कम होना चाहिए", + "type-max-length": "एंटिटी व्यू प्रकार 256 वर्णों से कम होना चाहिए", + "description": "वर्णन", + "events": "इवेंट्स", + "details": "विवरण", + "copyId": "एंटिटी व्यू Id कॉपी करें", + "idCopiedMessage": "एंटिटी व्यू Id क्लिपबोर्ड पर कॉपी कर दी गई है", + "assignedToCustomer": "कस्टमर को असाइन किया गया", + "unable-entity-view-device-alias-title": "एंटिटी व्यू उपनाम हटाने में असमर्थ", + "unable-entity-view-device-alias-text": "डिवाइस उपनाम '{{entityViewAlias}}' को हटाया नहीं जा सकता क्योंकि यह निम्न विजेट(स) द्वारा उपयोग किया जा रहा है:
    {{widgetsList}}", + "select-entity-view": "एंटिटी व्यू चुनें", + "start-ts": "प्रारंभ समय", + "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": "टार्गेट एंटिटी के टाइम सीरीज़ डेटा की-ज़ कॉन्फ़िगर करें जो एंटिटी व्यू के लिए उपलब्ध होंगी। यह टाइम सीरीज़ डेटा केवल पढ़ने योग्य है.", + "search": "एंटिटी व्यूज़ खोजें", + "selected-entity-views": "{ count, plural, =1 {1 एंटिटी व्यू} other {# एंटिटी व्यूज़} } चयनित", + "assign-entity-view-to-edge": "एंटिटी व्यूज़ को Edge को असाइन करें", + "assign-entity-view-to-edge-text": "Edge को असाइन करने के लिए कृपया एंटिटी व्यूज़ चुनें", + "unassign-entity-view-from-edge-title": "क्या आप वाकई एंटिटी व्यू '{{entityViewName}}' को अनअसाइन करना चाहते हैं?", + "unassign-entity-view-from-edge-text": "पुष्टि के बाद एंटिटी व्यू अनअसाइन हो जाएगा और Edge के लिए सुलभ नहीं रहेगा.", + "unassign-entity-views-from-edge-action-title": "Edge से { count, plural, =1 {1 एंटिटी व्यू} other {# एंटिटी व्यूज़} } अनअसाइन करें", + "unassign-entity-view-from-edge": "एंटिटी व्यू अनअसाइन करें", + "unassign-entity-views-from-edge-title": "क्या आप वाकई { count, plural, =1 {1 एंटिटी व्यू} other {# एंटिटी व्यूज़} } को अनअसाइन करना चाहते हैं?", + "unassign-entity-views-from-edge-text": "पुष्टि के बाद सभी चयनित एंटिटी व्यूज़ अनअसाइन हो जाएँगे और Edge के लिए सुलभ नहीं रहेंगे." + }, + "event": { + "event-type": "इवेंट प्रकार", + "events-filter": "इवेंट्स फ़िल्टर", + "clean-events": "इवेंट्स साफ़ करें", + "type-error": "त्रुटि", + "type-lc-event": "लाइफसाइकल इवेंट", + "type-stats": "आँकड़े", + "type-debug-rule-node": "डिबग", + "type-debug-rule-chain": "डिबग", + "type-debug-calculated-field": "डिबग", + "arguments": "आर्गुमेंट्स", + "result": "परिणाम", + "no-events-prompt": "कोई इवेंट नहीं मिला", + "error": "त्रुटि", + "alarm": "अलार्म", + "event-time": "इवेंट समय", + "server": "सर्वर", + "body": "बॉडी", + "method": "मेथड", + "type": "प्रकार", + "metadata": "मेटाडेटा", + "message": "संदेश", + "message-id": "संदेश Id", + "copy-message-id": "संदेश Id कॉपी करें", + "message-type": "संदेश प्रकार", + "data-type": "डेटा प्रकार", + "relation-type": "रिलेशन प्रकार", + "data": "डेटा", + "event": "इवेंट", + "status": "स्थिति", + "success": "सफल", + "failed": "विफल", + "messages-processed": "प्रोसेस किए गए संदेश", + "max-messages-processed": "अधिकतम प्रोसेस किए गए संदेश", + "min-messages-processed": "न्यूनतम प्रोसेस किए गए संदेश", + "errors-occurred": "त्रुटियाँ हुईं", + "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-request-title": "सभी इवेंट्स साफ़ करें", + "clear-request-text": "क्या आप वाकई सभी इवेंट्स साफ़ करना चाहते हैं?", + "started": "शुरू हुआ", + "updated": "अपडेट हुआ", + "stopped": "रुका हुआ" + }, + "extension": { + "extensions": "एक्सटेंशन्स", + "selected-extensions": "{ count, plural, =1 {1 एक्सटेंशन} other {# एक्सटेंशन्स} } चयनित", + "type": "प्रकार", + "key": "कुंजी", + "value": "मान", + "id": "Id", + "extension-id": "एक्सटेंशन Id", + "extension-type": "एक्सटेंशन प्रकार", + "transformer-json": "JSON *", + "unique-id-required": "वर्तमान एक्सटेंशन Id पहले से मौजूद है.", + "delete": "एक्सटेंशन हटाएँ", + "add": "एक्सटेंशन जोड़ें", + "edit": "एक्सटेंशन संपादित करें", + "delete-extension-title": "क्या आप वाकई एक्सटेंशन '{{extensionId}}' को हटाना चाहते हैं?", + "delete-extension-text": "सावधान रहें, पुष्टि के बाद एक्सटेंशन और उससे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "delete-extensions-title": "क्या आप वाकई { count, plural, =1 {1 एक्सटेंशन} other {# एक्सटेंशन्स} } को हटाना चाहते हैं?", + "delete-extensions-text": "सावधान रहें, पुष्टि के बाद सभी चयनित एक्सटेंशन्स हटा दिए जाएँगे.", + "converters": "कन्वर्टर्स", + "converter-id": "कन्वर्टर Id", + "configuration": "कॉन्फ़िगरेशन", + "converter-configurations": "कन्वर्टर कॉन्फ़िगरेशन", + "token": "सुरक्षा टोकन", + "add-converter": "कन्वर्टर जोड़ें", + "add-config": "कन्वर्टर कॉन्फ़िगरेशन जोड़ें", + "device-name-expression": "डिवाइस नाम अभिव्यक्ति", + "device-type-expression": "डिवाइस प्रकार अभिव्यक्ति", + "custom": "कस्टम", + "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": "रिक्वेस्ट Id अभिव्यक्ति", + "request-id-json-expression": "रिक्वेस्ट Id JSON अभिव्यक्ति", + "request-id-topic-expression": "रिक्वेस्ट Id टॉपिक अभिव्यक्ति", + "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-device-name-pattern": "डिवाइस नाम पैटर्न", + "modbus-server": "सर्वर/स्लेव", + "modbus-add-server": "सर्वर/स्लेव जोड़ें", + "modbus-add-server-prompt": "कृपया सर्वर/स्लेव जोड़ें", + "modbus-transport": "ट्रांसपोर्ट", + "modbus-tcp-reconnect": "स्वतः पुनः कनेक्ट करें", + "modbus-rtu-over-tcp": "RTU over TCP", + "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": "यूनिट Id", + "modbus-unit-id-range": "यूनिट Id 1 से 247 की सीमा में होना चाहिए.", + "modbus-device-name": "डिवाइस नाम", + "modbus-poll-period": "पोल अवधि (ms)", + "modbus-attributes-poll-period": "विशेषताएँ पोल अवधि (ms)", + "modbus-timeseries-poll-period": "टाइम सीरीज़ पोल अवधि (ms)", + "modbus-poll-period-range": "पोल अवधि धनात्मक मान होनी चाहिए.", + "modbus-tag": "टैग", + "modbus-function": "फ़ंक्शन", + "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": "अमान्य एक्सटेंशन फ़ाइल" + }, + "feature": { + "advanced-features": "एडवांस्ड फीचर्स" + }, + "filter": { + "add": "फ़िल्टर जोड़ें", + "edit": "फ़िल्टर संपादित करें", + "name": "फ़िल्टर नाम", + "name-required": "फ़िल्टर नाम आवश्यक है.", + "duplicate-filter": "इसी नाम वाला फ़िल्टर पहले से मौजूद है.", + "filters": "फ़िल्टर्स", + "unable-delete-filter-title": "फ़िल्टर हटाने में असमर्थ", + "unable-delete-filter-text": "फ़िल्टर '{{filter}}' को हटाया नहीं जा सकता क्योंकि यह निम्न विजेट(स) द्वारा उपयोग किया जा रहा है:
    {{widgetsList}}", + "duplicate-filter-error": "डुप्लिकेट फ़िल्टर '{{filter}}' मिला।
    डैशबोर्ड के भीतर फ़िल्टर्स अद्वितीय होने चाहिए.", + "missing-key-filters-error": "फ़िल्टर '{{filter}}' के लिए की फ़िल्टर्स नहीं दिए गए हैं.", + "filter": "फ़िल्टर", + "editable": "संपादन योग्य", + "editable-hint": "डैशबोर्ड्स में उपयोगकर्ता को फ़िल्टर मान बदलने की अनुमति दें.", + "no-filters-found": "कोई फ़िल्टर नहीं मिला.", + "no-filter-text": "कोई फ़िल्टर निर्दिष्ट नहीं है", + "add-filter-prompt": "कृपया फ़िल्टर जोड़ें", + "no-filter-matching": "'{{filter}}' नहीं मिला.", + "create-new-filter": "नया बनाएँ!", + "create-new": "नया बनाएँ", + "filter-required": "फ़िल्टर आवश्यक है.", + "operation": { + "operation": "ऑपरेशन", + "equal": "बराबर", + "not-equal": "बराबर नहीं", + "starts-with": "से शुरू होता है", + "ends-with": "पर समाप्त होता है", + "contains": "शामिल है", + "not-contains": "शामिल नहीं है", + "greater": "से बड़ा", + "less": "से छोटा", + "greater-or-equal": "बड़ा या बराबर", + "less-or-equal": "छोटा या बराबर", + "and": "और", + "or": "या", + "in": "में", + "not-in": "में नहीं" + }, + "ignore-case": "केस अनदेखा करें", + "value": "मान", + "remove-filter": "फ़िल्टर हटाएँ", + "duplicate-filter-action": "फ़िल्टर डुप्लिकेट करें", + "preview": "फ़िल्टर प्रीव्यू", + "no-filters": "कोई फ़िल्टर कॉन्फ़िगर नहीं है", + "add-filter": "फ़िल्टर जोड़ें", + "add-complex-filter": "कॉम्प्लेक्स फ़िल्टर जोड़ें", + "add-complex": "कॉम्प्लेक्स जोड़ें", + "complex-filter": "कॉम्प्लेक्स फ़िल्टर", + "edit-complex-filter": "कॉम्प्लेक्स फ़िल्टर संपादित करें", + "edit-filter-user-params": "फ़िल्टर प्रेडिकेट यूज़र पैरामीटर्स संपादित करें", + "filter-user-params": "फ़िल्टर प्रेडिकेट यूज़र पैरामीटर्स", + "user-parameters": "यूज़र पैरामीटर्स", + "display-label": "दिखाने के लिए लेबल", + "custom-label": "कस्टम लेबल", + "custom-label-hint": "फ़िल्टर के लिए अपना लेबल सेट करने हेतु सक्षम करें। अक्षम होने पर लेबल स्वतः जेनरेट हो जाएगा.", + "order-priority": "डिस्प्ले क्रम", + "key-filter": "की फ़िल्टर", + "key-filters": "की फ़िल्टर्स", + "key-name": "की नाम", + "key-name-required": "की नाम आवश्यक है.", + "key-type": { + "key-type": "की प्रकार", + "attribute": "विशेषता", + "timeseries": "टाइम सीरीज़", + "entity-field": "एंटिटी फ़ील्ड", + "constant": "कॉनस्टेंट", + "client-attribute": "क्लाइंट विशेषता", + "server-attribute": "सर्वर विशेषता", + "shared-attribute": "साझा विशेषता" + }, + "value-type": { + "value-type": "मान प्रकार", + "string": "स्ट्रिंग", + "numeric": "न्यूमेरिक", + "boolean": "बूलियन", + "date-time": "दिनांक-समय" + }, + "value-type-required": "की मान प्रकार आवश्यक है.", + "key-value-type-change-title": "क्या आप वाकई की मान प्रकार बदलना चाहते हैं?", + "key-value-type-change-message": "यदि आप नए मान प्रकार की पुष्टि करते हैं, तो दर्ज किए गए सभी की फ़िल्टर्स हटाए जाएँगे.", + "no-key-filters": "कोई की फ़िल्टर्स कॉन्फ़िगर नहीं हैं", + "add-key-filter": "की फ़िल्टर जोड़ें", + "remove-key-filter": "की फ़िल्टर हटाएँ", + "edit-key-filter": "की फ़िल्टर संपादित करें", + "date": "तारीख", + "time": "समय", + "current-tenant": "वर्तमान टेनेंट", + "current-customer": "वर्तमान कस्टमर", + "current-user": "वर्तमान उपयोगकर्ता", + "current-device": "वर्तमान डिवाइस", + "default-value": "डिफ़ॉल्ट मान", + "default-comma-separated-values": "डिफ़ॉल्ट कॉमा द्वारा अलग किए गए मान", + "dynamic-source-type": "डायनेमिक स्रोत प्रकार", + "dynamic-value": "डायनेमिक मान", + "no-dynamic-value": "कोई डायनेमिक मान नहीं", + "source-attribute": "स्रोत विशेषता", + "switch-to-dynamic-value": "डायनेमिक मान पर स्विच करें", + "switch-to-default-value": "डिफ़ॉल्ट मान पर स्विच करें", + "inherit-owner": "ओनर से विरासत में लें", + "source-attribute-not-set": "यदि स्रोत विशेषता सेट नहीं है", + "unit": "इकाई" + }, + "fullscreen": { + "expand": "फुलस्क्रीन पर फैलाएँ", + "exit": "फुलस्क्रीन से बाहर निकलें", + "toggle": "फुलस्क्रीन मोड टॉगल करें", + "fullscreen": "फुलस्क्रीन" + }, + "function": { + "function": "फ़ंक्शन" + }, + "gateway": { + "gateway-name": "Gateway नाम", + "gateway-name-required": "Gateway नाम आवश्यक है.", + "gateways": "Gateways", + "create-new-gateway": "नया Gateway बनाएँ", + "create-new-gateway-text": "क्या आप वाकई '{{gatewayName}}' नाम का नया Gateway बनाना चाहते हैं?", + "launch-command": "लॉन्च कमांड", + "no-gateway-found": "कोई Gateway नहीं मिला.", + "no-gateway-matching": " '{{item}}' नहीं मिला." + }, + "grid": { + "delete-item-title": "क्या आप वाकई इस आइटम को हटाना चाहते हैं?", + "delete-item-text": "सावधान रहें, पुष्टि के बाद यह आइटम और इससे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "delete-items-title": "क्या आप वाकई { count, plural, =1 {1 आइटम} other {# आइटम} } को हटाना चाहते हैं?", + "delete-items-action-title": "हटाएँ { count, plural, =1 {1 आइटम} other {# आइटम} }", + "delete-items-text": "सावधान रहें, पुष्टि के बाद सभी चयनित आइटम हटा दिए जाएँगे और उनसे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "add-item-text": "नया आइटम जोड़ें", + "no-items-text": "कोई आइटम नहीं मिला", + "item-details": "आइटम विवरण", + "delete-item": "आइटम हटाएँ", + "delete-items": "आइटम्स हटाएँ", + "scroll-to-top": "ऊपर स्क्रॉल करें" + }, + "help": { + "goto-help-page": "हेल्प पेज पर जाएँ", + "show-help": "हेल्प दिखाएँ" + }, + "home": { + "home": "होम", + "profile": "प्रोफ़ाइल", + "logout": "लॉगआउट", + "menu": "मेन्यू", + "avatar": "अवतार", + "open-user-menu": "उपयोगकर्ता मेन्यू खोलें" + }, + "file-input": { + "browse-file": "फ़ाइल ब्राउज़ करें", + "browse-files": "फ़ाइलें ब्राउज़ करें" + }, + "image": { + "gallery": "इमेज गैलरी", + "search": "इमेज खोजें", + "selected-images": "{ count, plural, =1 {1 इमेज} other {# इमेज} } चयनित", + "created-time": "बनाने का समय", + "name": "नाम", + "name-required": "नाम आवश्यक है.", + "resolution": "रेज़ोल्यूशन", + "size": "आकार", + "system": "सिस्टम", + "download-image": "इमेज डाउनलोड करें", + "export-image": "इमेज को JSON में एक्सपोर्ट करें", + "import-image": "JSON से इमेज इम्पोर्ट करें", + "upload-image": "इमेज अपलोड करें", + "edit-image": "इमेज संपादित करें", + "image-details": "इमेज विवरण", + "no-images": "कोई इमेज नहीं मिली", + "delete-image": "इमेज हटाएँ", + "delete-image-title": "क्या आप वाकई इमेज '{{imageTitle}}' को हटाना चाहते हैं?", + "delete-image-text": "सावधान रहें, पुष्टि के बाद इमेज हमेशा के लिए खो जाएगी.", + "delete-images-title": "क्या आप वाकई { count, plural, =1 {1 इमेज} other {# इमेज} } को हटाना चाहते हैं?", + "delete-images-text": "सावधान रहें, पुष्टि के बाद सभी चयनित इमेज हटा दी जाएँगी और उनसे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "list-mode": "सूची दृश्य", + "grid-mode": "ग्रिड दृश्य", + "image-preview": "इमेज प्रीव्यू", + "update-image": "इमेज अपडेट करें", + "export-failed-error": "इमेज एक्सपोर्ट नहीं की जा सकी: {{error}}", + "image-json-file": "इमेज JSON फ़ाइल", + "invalid-image-json-file-error": "JSON से इमेज इम्पोर्ट नहीं कर सके: इमेज JSON डेटा संरचना अमान्य है.", + "image-is-in-use": "इमेज अन्य एंटिटीज़ द्वारा उपयोग की जा रही है", + "images-are-in-use": "इमेजें अन्य एंटिटीज़ द्वारा उपयोग की जा रही हैं", + "image-is-in-use-text": "इमेज '{{title}}' को नहीं हटाया गया क्योंकि इसे निम्न एंटिटीज़ द्वारा उपयोग किया जा रहा है:", + "images-are-in-use-text": "सभी इमेजें नहीं हटाई गईं क्योंकि वे अन्य एंटिटीज़ द्वारा उपयोग की जा रही हैं.
    आप संबंधित एंटिटीज़ को संबंधित इमेज की पंक्ति में संदर्भ बटन पर क्लिक करके देख सकते हैं.
    यदि आप फिर भी इन इमेजों को हटाना चाहते हैं, तो उन्हें नीचे दी गई तालिका में चुनें और चयनित हटाएँ बटन पर क्लिक करें.", + "delete-image-in-use-text": "यदि आप फिर भी इमेज हटाना चाहते हैं, तो फिर भी हटाएँ बटन पर क्लिक करें.", + "system-entities": "सिस्टम एंटिटीज़:", + "entities": "एंटिटीज़:", + "references": "संदर्भ", + "include-system-images": "सिस्टम इमेजें शामिल करें", + "clear-image": "इमेज साफ़ करें", + "no-image": "कोई इमेज नहीं", + "no-image-selected": "कोई इमेज चयनित नहीं है", + "browse-from-gallery": "गैलरी से ब्राउज़ करें", + "set-link": "लिंक सेट करें", + "image-link": "इमेज लिंक", + "link": "लिंक", + "copy-image-link": "इमेज लिंक कॉपी करें", + "embed-image": "इमेज एम्बेड करें", + "embed-to-html": "HTML में एम्बेड करें", + "embed-to-html-hint": "यह फीचर लिंक को किसी भी अनधिकृत उपयोगकर्ता के लिए उपलब्ध कर देगा.", + "embed-to-html-text": "निम्न कोड स्निपेट का उपयोग करके आप केवल HTML पर आधारित कॉम्पोनेंट्स में इमेज एम्बेड कर सकते हैं.
    ऐसे कॉम्पोनेंट्स में HTML कार्ड विजेट्स, सेल कंटेंट फ़ंक्शंस आदि शामिल हैं.", + "embed-to-angular-template": "Angular HTML टेम्पलेट में एम्बेड करें", + "embed-to-angular-template-text": "निम्न कोड स्निपेट का उपयोग करके आप इमेज को Angular HTML टेम्पलेट में एम्बेड कर सकते हैं जो कॉम्पोनेंट्स के लिए उपयोग किया जाएगा.
    ऐसे कॉम्पोनेंट्स में Markdown विजेट, विजेट एडिटर में HTML सेक्शन, कस्टम एक्शन्स आदि शामिल हैं." + }, + "image-input": { + "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 फ़ाइल को ड्रैग-एंड-ड्रॉप करें या", + "column-value": "मान", + "column-title": "शीर्षक", + "column-example": "उदाहरण मान डेटा", + "column-key": "विशेषता/टेलीमेट्री कुंजी", + "credentials": "क्रेडेंशियल्स", + "csv-delimiter": "CSV डिलिमिटर", + "csv-first-line-header": "पहली पंक्ति में कॉलम नाम होते हैं", + "csv-update-data": "विशेषताएँ/टेलीमेट्री अपडेट करें", + "details": "विवरण", + "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": "एंटिटी फ़ील्ड", + "access-token": "ऐक्सेस टोकन", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT क्लाइंट ID", + "user-name": "MQTT यूज़र नेम", + "password": "MQTT पासवर्ड" + }, + "lwm2m": { + "client-endpoint": "LwM2M एंडपॉइंट क्लाइंट नाम", + "security-config-mode": "LwM2M सिक्योरिटी कॉन्फ़िग मोड", + "client-identity": "LwM2M क्लाइंट आइडेंटिटी", + "client-key": "LwM2M क्लाइंट कुंजी", + "client-cert": "LwM2M क्लाइंट पब्लिक की", + "bootstrap-server-security-mode": "LwM2M बूटस्ट्रैप सर्वर सिक्योरिटी मोड", + "bootstrap-server-secret-key": "LwM2M बूटस्ट्रैप सर्वर सीक्रेट की", + "bootstrap-server-public-key-id": "LwM2M बूटस्ट्रैप सर्वर पब्लिक की या Id", + "lwm2m-server-security-mode": "LwM2M सर्वर सिक्योरिटी मोड", + "lwm2m-server-secret-key": "LwM2M सर्वर सीक्रेट की", + "lwm2m-server-public-key-id": "LwM2M सर्वर पब्लिक की या Id" + }, + "snmp": { + "host": "SNMP होस्ट", + "port": "SNMP पोर्ट", + "version": "SNMP वर्ज़न (v1, v2c या v3)", + "community-string": "SNMP कम्युनिटी स्ट्रिंग" + }, + "isgateway": "Gateway है", + "activity-time-from-gateway-device": "Gateway डिवाइस से एक्टिविटी समय", + "description": "वर्णन", + "routing-key": "Edge की", + "secret": "Edge सीक्रेट" + }, + "stepper-text": { + "select-file": "फ़ाइल चुनें", + "configuration": "इम्पोर्ट कॉन्फ़िगरेशन", + "column-type": "कॉलम प्रकार चुनें", + "creat-entities": "नई एंटिटीज़ बनाई जा रही हैं" + }, + "message": { + "create-entities": "{{count}} नई एंटिटीज़ सफलतापूर्वक बनाई गईं.", + "update-entities": "{{count}} एंटिटीज़ सफलतापूर्वक अपडेट की गईं.", + "error-entities": "{{count}} एंटिटीज़ बनाते समय त्रुटि हुई." + } + }, + "scada": { + "symbols": "SCADA सिंबल्स", + "search": "सिंबल खोजें", + "selected-symbols": "{ count, plural, =1 {1 सिंबल} other {# सिंबल} } चयनित", + "download-symbol": "SCADA सिंबल डाउनलोड करें", + "export-symbol": "SCADA सिंबल को JSON में एक्सपोर्ट करें", + "import-symbol": "JSON से SCADA सिंबल इम्पोर्ट करें", + "upload-symbol": "SCADA सिंबल अपलोड करें", + "update-symbol": "SCADA सिंबल अपडेट करें", + "edit-symbol": "SCADA सिंबल संपादित करें", + "symbol-details": "SCADA सिंबल विवरण", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "कोई सिंबल नहीं मिला", + "show-hidden-elements": "छिपे हुए एलिमेंट्स दिखाएँ", + "hide-hidden-elements": "छिपे हुए एलिमेंट्स छिपाएँ", + "delete-symbol": "SCADA सिंबल हटाएँ", + "delete-symbol-title": "क्या आप वाकई SCADA सिंबल '{{imageTitle}}' को हटाना चाहते हैं?", + "delete-symbol-text": "सावधान रहें, पुष्टि के बाद SCADA सिंबल हमेशा के लिए खो जाएगा.", + "delete-symbols-title": "क्या आप वाकई { count, plural, =1 {1 SCADA सिंबल} other {# SCADA सिंबल्स} } को हटाना चाहते हैं?", + "delete-symbols-text": "सावधान रहें, पुष्टि के बाद सभी चयनित SCADA सिंबल्स हटा दिए जाएँगे और उनसे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "include-system-symbols": "सिस्टम सिंबल्स शामिल करें", + "symbol-preview": "सिंबल प्रीव्यू", + "general": "सामान्य", + "tags": "टैग्स", + "properties": "प्रॉपर्टीज़", + "title": "शीर्षक", + "description": "वर्णन", + "search-tags": "टैग्स खोजें", + "widget-size": "विजेट आकार", + "cols": "कॉलम", + "rows": "पंक्तियाँ", + "state-render-function": "स्टेट रेंडर फ़ंक्शन", + "preview": "प्रीव्यू", + "preview-widget-action-text": "विजेट क्रिया '{{type}}' सफलतापूर्वक कॉल की गई!", + "no-symbol": "कोई SCADA सिंबल नहीं", + "no-symbol-selected": "कोई SCADA सिंबल चयनित नहीं है", + "clear-symbol": "SCADA सिंबल साफ़ करें", + "browse-symbol-from-gallery": "गैलरी से SCADA सिंबल ब्राउज़ करें", + "zoom-in": "ज़ूम इन", + "zoom-out": "ज़ूम आउट", + "create-widget": "विजेट बनाएँ", + "create-widget-from-symbol": "SCADA सिंबल से विजेट बनाएँ", + "hidden": "छिपा हुआ", + "tag": { + "tag": "टैग", + "on-click-action": "क्लिक पर क्रिया", + "no-tags": "कोई टैग कॉन्फ़िगर नहीं है", + "delete-tag-text": "क्या आप वाकई {{elementType}} एलिमेंट से टैग
    {{tag}} हटाना चाहते हैं?", + "update-tag": "टैग अपडेट करें", + "enter-tag": "टैग दर्ज करें", + "tag-settings": "टैग सेटिंग्स", + "remove-tag": "टैग हटाएँ", + "add-tag": "टैग जोड़ें" + }, + "behavior": { + "behavior": "व्यवहार", + "id": "Id", + "name": "नाम", + "type": "प्रकार", + "no-behaviors": "कोई व्यवहार कॉन्फ़िगर नहीं है", + "add-behavior": "व्यवहार जोड़ें", + "type-action": "क्रिया", + "type-value": "मान", + "type-widget-action": "विजेट क्रिया", + "behavior-settings": "व्यवहार सेटिंग्स", + "remove-behavior": "व्यवहार हटाएँ", + "hint": "संकेत", + "group-title": "समूह शीर्षक", + "value-type": "मान प्रकार", + "default-value": "डिफ़ॉल्ट मान", + "true-label": "सत्य लेबल", + "false-label": "असत्य लेबल", + "state-label": "स्थिति लेबल", + "default-payload": "डिफ़ॉल्ट पेलोड", + "not-unique-behavior-ids-error": "व्यवहार Ids अद्वितीय होने चाहिए!", + "default-settings": "डिफ़ॉल्ट सेटिंग्स" + }, + "symbol": { + "symbol": "SCADA सिंबल", + "fluid-presence": "द्रव की उपस्थिति", + "fluid-presence-hint": "संकेत करता है कि पाइप में द्रव मौजूद है या नहीं.", + "fluid-present": "द्रव मौजूद", + "present": "मौजूद", + "absent": "अनुपस्थित", + "flow-presence": "प्रवाह की उपस्थिति", + "flow-presence-hint": "संकेत करता है कि पाइप में द्रव प्रवाहित हो रहा है या नहीं.", + "flow-present": "प्रवाह मौजूद", + "flow-direction": "प्रवाह की दिशा", + "flow-direction-hint": "द्रव के प्रवाह की दिशा दर्शाता है.", + "forward": "आगे", + "reverse": "उल्टा", + "flow-animation-speed": "प्रवाह एनीमेशन गति", + "flow-animation-speed-hint": "डबल मान जो प्रवाह की एनीमेशन गति दर्शाता है. 1 - सामान्य गति, 0 - कोई एनीमेशन नहीं, < 1 - धीमी एनीमेशन, > 1 - तेज़ एनीमेशन.", + "leak": "रिसाव", + "leak-hint": "संकेत करता है कि पाइप में रिसाव मौजूद है या नहीं.", + "leak-present": "रिसाव मौजूद", + "fluid-color": "द्रव का रंग", + "pipe-color": "पाइप का रंग", + "horizontal-pipe": "क्षैतिज पाइप", + "vertical-pipe": "ऊर्ध्वाधर पाइप", + "horizontal-fluid-color": "क्षैतिज द्रव का रंग", + "vertical-fluid-color": "ऊर्ध्वाधर द्रव का रंग", + "left-pipe": "बायाँ पाइप", + "right-pipe": "दायाँ पाइप", + "top-pipe": "ऊपरी पाइप", + "bottom-pipe": "निचला पाइप", + "left-fluid-color": "बाएँ द्रव का रंग", + "right-fluid-color": "दाएँ द्रव का रंग", + "top-fluid-color": "ऊपरी द्रव का रंग", + "bottom-fluid-color": "निचले द्रव का रंग", + "display": "डिस्प्ले", + "display-format": "डिस्प्ले फ़ॉर्मेट", + "value": "मान", + "decimals": "दशमलव", + "units": "इकाइयाँ", + "flow-meter-value-hint": "फ़्लो मीटर डिस्प्ले पर दिखाया जाने वाला डबल मान", + "value-hint": "वर्तमान मान दर्शाने वाला डबल मान", + "running": "रनिंग", + "running-hint": "दर्शाता है कि कंपोनेंट रनिंग स्थिति में है या नहीं.", + "warning-state": "चेतावनी स्थिति", + "warning": "चेतावनी", + "warning-click": "चेतावनी क्लिक", + "warning-state-hint": "दर्शाता है कि कंपोनेंट चेतावनी स्थिति में है या नहीं.", + "critical-state": "क्रिटिकल स्थिति", + "critical": "क्रिटिकल", + "critical-click": "क्रिटिकल क्लिक", + "critical-state-hint": "दर्शाता है कि कंपोनेंट क्रिटिकल स्थिति में है या नहीं.", + "critical-state-animation": "क्रिटिकल स्थिति एनीमेशन", + "critical-state-animation-hint": "जब कंपोनेंट क्रिटिकल स्थिति में हो, तो क्या ब्लिंकिंग एनीमेशन सक्षम हो.", + "warning-critical-state-animation": "चेतावनी/क्रिटिकल स्थिति एनीमेशन", + "warning-critical-state-animation-hint": "जब कंपोनेंट चेतावनी या क्रिटिकल स्थिति में हो, तो क्या ब्लिंकिंग एनीमेशन सक्षम हो.", + "animation": "एनीमेशन", + "broken": "टूटा हुआ", + "broken-hint": "दर्शाता है कि कंपोनेंट टूटा हुआ है या नहीं.", + "on-display-click": "डिस्प्ले पर क्लिक पर", + "on-display-click-hint": "जब उपयोगकर्ता डिस्प्ले पर क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "pipe": "पाइप", + "default-border-color": "डिफ़ॉल्ट बॉर्डर रंग", + "active-border-color": "एक्टिव बॉर्डर रंग", + "warning-border-color": "चेतावनी बॉर्डर रंग", + "critical-border-color": "क्रिटिकल बॉर्डर रंग", + "background-color": "पृष्ठभूमि रंग", + "rotation-animation-speed": "रोटेशन एनीमेशन गति", + "rotation-animation-speed-hint": "डबल मान जो रोटेशन एनीमेशन की गति दर्शाता है. 1 - सामान्य गति, 0 - कोई एनीमेशन नहीं, < 1 - धीमी एनीमेशन, > 1 - तेज़ एनीमेशन.", + "on-click": "क्लिक पर", + "on-click-hint": "जब उपयोगकर्ता कंपोनेंट पर क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "connectors-positions": "कनेक्टर्स की पोज़िशन", + "right-connector": "दायाँ कनेक्टर", + "right-top-connector": "दायाँ ऊपर कनेक्टर", + "right-bottom-connector": "दायाँ नीचे कनेक्टर", + "left-connector": "बायाँ कनेक्टर", + "left-top-connector": "बायाँ ऊपर कनेक्टर", + "left-bottom-connector": "बायाँ नीचे कनेक्टर", + "top-left-connector": "ऊपरी बायाँ कनेक्टर", + "top-right-connector": "ऊपरी दायाँ कनेक्टर", + "top-connector": "ऊपरी कनेक्टर", + "bottom-connector": "निचला कनेक्टर", + "running-color": "रनिंग रंग", + "stopped-color": "रुका हुआ रंग", + "stopped": "रुका हुआ", + "warning-color": "चेतावनी रंग", + "critical-color": "क्रिटिकल रंग", + "opened": "खुला हुआ", + "opened-hint": "दर्शाता है कि कंपोनेंट ओपन स्थिति में है या नहीं.", + "open": "ओपन करें", + "open-hint": "जब उपयोगकर्ता कंपोनेंट को ओपन करने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "close": "क्लोज़", + "close-hint": "जब उपयोगकर्ता कंपोनेंट को क्लोज़ करने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "close-state-animation": "क्लोज़ स्थिति एनीमेशन", + "close-state-animation-hint": "जब कंपोनेंट क्लोज़ स्थिति में हो, तो क्या ब्लिंकिंग एनीमेशन सक्षम हो.", + "opened-color": "ओपन स्थिति रंग", + "closed-color": "क्लोज़ स्थिति रंग", + "opened-rotation-angle": "ओपन स्थिति रोटेशन एंगल", + "closed-rotation-angle": "क्लोज़ स्थिति रोटेशन एंगल", + "tank-capacity": "टैंक क्षमता", + "tank-capacity-hint": "टैंक की कुल क्षमता दर्शाने वाला डबल मान.", + "current-volume": "वर्तमान वॉल्यूम", + "current-volume-hint": "वर्तमान भरे हुए वॉल्यूम को दर्शाने वाला डबल मान.", + "tank-color": "टैंक रंग", + "value-box": "वैल्यू बॉक्स", + "value-text": "वैल्यू टेक्स्ट", + "scale": "स्केल", + "transparent-mode": "ट्रांसपेरेंट मोड", + "major-ticks": "मेजर टिक्स", + "intervals": "इंटरवल्स", + "major-ticks-color": "मेजर टिक्स रंग", + "normal": "नॉर्मल", + "minor-ticks": "माइनर टिक्स", + "minor-ticks-color": "माइनर टिक्स रंग", + "temperature": "तापमान", + "temperature-hint": "वर्तमान तापमान दर्शाने वाला डबल मान.", + "update-temperature": "तापमान अपडेट करें", + "update-temperature-hint": "जब उपयोगकर्ता वर्तमान तापमान बदलने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "run": "रन", + "run-hint": "जब उपयोगकर्ता कंपोनेंट को रन करने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "stop": "रोकें", + "stop-hint": "जब उपयोगकर्ता कंपोनेंट को रोकने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "temperature-step": "तापमान स्टेप इंक्रीमेंट", + "heat-pump-color": "हीट पंप रंग", + "power-button-background": "पावर बटन बैकग्राउंड", + "value-box-background": "वैल्यू बॉक्स बैकग्राउंड", + "value-units": "वैल्यू यूनिट्स", + "enable-units-scale": "स्केल पर यूनिट्स सक्षम करें", + "filtration-mode": "फ़िल्ट्रेशन मोड", + "filtration-mode-hint": "वर्तमान फ़िल्ट्रेशन मोड दर्शाने वाला इंटीजर मान.", + "filtration-mode-update": "फ़िल्ट्रेशन मोड अपडेट स्थिति", + "filtration-mode-update-hint": "जब उपयोगकर्ता वर्तमान फ़िल्ट्रेशन मोड बदलने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "filter-mode": "फ़िल्टर", + "waste-mode": "वेस्ट", + "backwash-mode": "बैकवॉश", + "recirculate-mode": "रीसर्क्युलेट", + "rinse-mode": "रिन्स", + "closed-mode": "क्लोज़्ड", + "sand-filter-color": "सैंड फ़िल्टर रंग", + "mode-box-background": "मोड बॉक्स बैकग्राउंड", + "border-color": "बॉर्डर रंग", + "label-color": "लेबल रंग", + "water-leak-hint": "संकेत करता है कि रिसाव है या नहीं.", + "default-color": "डिफ़ॉल्ट रंग", + "leak-color": "रिसाव रंग", + "full-value": "फुल वैल्यू", + "full-value-hint": "फुल वैल्यू दर्शाने वाला डबल मान.", + "label": "लेबल", + "icon": "आइकन", + "button-color": "बटन रंग", + "on-label": "'On' लेबल टेक्स्ट", + "off-label": "'Off' लेबल टेक्स्ट", + "arrow-presence": "ऐरो की उपस्थिति", + "arrow-presence-hint": "संकेत करता है कि कनेक्टर में ऐरो मौजूद है या नहीं.", + "arrow-present": "ऐरो मौजूद", + "arrow-direction": "प्रवाह की दिशा", + "arrow-direction-hint": "प्रवाह की दिशा दर्शाता है.", + "flow-animation": "प्रवाह की उपस्थिति", + "flow-animation-hint": "संकेत करता है कि कनेक्टर में द्रव प्रवाहित हो रहा है या नहीं.", + "flow": "प्रवाह", + "flow-line": "लाइन", + "flow-line-style": "लाइन शैली", + "flow-style-hint": "डैश और गैप मान इस तरह सेट करें कि उनका योग 100 से पूरी तरह विभाज्य हो ताकि एनीमेशन सिंक्रोनाइज़ेशन परफेक्ट हो.", + "flow-dash-cap": "डैश कैप", + "dash-cap-butt": "सीधा", + "dash-cap-round": "गोल", + "dash-cap-square": "चौकोर", + "dash": "डैश", + "gap": "गैप", + "main-line": "मुख्य लाइन", + "line": "लाइन", + "line-color": "लाइन रंग", + "arrow-color": "ऐरो रंग", + "target-value": "टार्गेट वैल्यू", + "target-value-hint": "स्केल पर टार्गेट बिंदु दर्शाता है.", + "min-max-value": "न्यूनतम और अधिकतम मान", + "min-value": "न्यूनतम", + "max-value": "अधिकतम", + "progress-bar": "प्रोग्रेस बार", + "progress-arrow": "प्रोग्रेस ऐरो", + "warning-scale-color": "चेतावनी स्केल रंग", + "critical-scale-color": "क्रिटिकल स्केल रंग", + "scale-color": "स्केल रंग", + "target": "टार्गेट", + "high-warning-state": "हाई चेतावनी स्थिति", + "show-high-warning-scale": "हाई चेतावनी स्केल दिखाएँ", + "high-warning-scale": "हाई चेतावनी स्केल", + "high-warning-state-hint": "डबल मान जो अधिकतम क्रिटिकल या अधिकतम मान तक हाई चेतावनी रेंज को दर्शाता है.", + "low-warning-state": "लो चेतावनी स्थिति", + "show-low-warning-scale": "लो चेतावनी स्केल दिखाएँ", + "low-warning-scale": "लो चेतावनी स्केल", + "low-warning-state-hint": "डबल मान जो न्यूनतम क्रिटिकल या न्यूनतम मान तक लो चेतावनी रेंज को दर्शाता है.", + "high-critical-state": "हाई क्रिटिकल स्थिति", + "show-high-critical-scale": "हाई क्रिटिकल स्केल दिखाएँ", + "high-critical-scale": "हाई क्रिटिकल स्केल", + "high-critical-state-hint": "डबल मान जो अधिकतम मान स्केल तक हाई क्रिटिकल रेंज को दर्शाता है.", + "low-critical-state": "लो क्रिटिकल स्थिति", + "show-low-critical-scale": "लो क्रिटिकल स्थिति दिखाएँ", + "low-critical-scale": "लो क्रिटिकल स्थिति", + "low-critical-state-hint": "डबल मान जो न्यूनतम मान स्केल तक लो क्रिटिकल रेंज को दर्शाता है.", + "filter-color": "फ़िल्टर रंग", + "colors": "रंग", + "indicator-colors": "इंडिकेटर रंग", + "enabled": "सक्षम", + "disabled": "अक्षम", + "on": "चालू", + "off": "बंद", + "on-off-state": "चालू/बंद स्थिति", + "on-off-state-hint": "दर्शाता है कि कंपोनेंट चालू या बंद स्थिति में है.", + "on-update-state": "चालू स्थिति अपडेट", + "on-update-state-hint": "जब उपयोगकर्ता स्थिति को चालू करने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "off-update-state": "बंद स्थिति अपडेट", + "off-update-state-hint": "जब उपयोगकर्ता स्थिति को बंद करने के लिए क्लिक करता है तब कॉल की जाने वाली क्रिया.", + "voltage": "वोल्टेज", + "input-voltage": "इनपुट वोल्टेज", + "input-voltage-hint": "डबल मान जो इनपुट वोल्टेज मान दर्शाता है.", + "output-voltage": "आउटपुट वोल्टेज", + "output-voltage-hint": "डबल मान जो आउटपुट वोल्टेज मान दर्शाता है.", + "first-phase-voltage": "पहले फेज का वोल्टेज", + "second-phase-voltage": "दूसरे फेज का वोल्टेज", + "third-phase-voltage": "तीसरे फेज का वोल्टेज", + "phase-voltage-hint": "डबल मान जो वर्तमान फेज के वोल्टेज मान को दर्शाता है.", + "voltage-hint": "डबल मान जो वर्तमान वोल्टेज दर्शाता है.", + "current-voltage-color": "वर्तमान वोल्टेज रंग", + "phase-indicator-color": "फेज इंडिकेटर रंग", + "measured": "मापा गया", + "measured-hint": "डबल मान किलोवाट-घंटों में ऊर्जा खपत दर्शाता है.", + "day-rate": "दैनिक दर", + "night-rate": "रात्रि दर", + "off-peak-rate": "ऑफ़-पीक दर", + "peak-rate": "पीक दर", + "export-rate": "निर्यात दर", + "operating-mode": "ऑपरेटिंग मोड", + "bypass-mode": "बायपास", + "operating-mode-hint": "इंटीजर मान वर्तमान ऑपरेटिंग मोड दर्शाता है (0 - बंद, 1 - चालू, 2 - बायपास).", + "connected": "कनेक्टेड", + "connected-hint": "संकेत करता है कि कंपोनेंट कनेक्टेड स्थिति में है या नहीं.", + "disconnected": "डिसकनेक्टेड", + "indicator": "इंडिकेटर", + "operation-mode": "ऑपरेशन मोड", + "operation-mode-hint": "संकेत करता है कि इन्वर्टर मेन्स या इन्वर्टर मोड में है.", + "operation-mode-indicators-color": "ऑपरेशन मोड इंडिकेटर्स रंग", + "mains-on-mode": "मेन्स चालू", + "inverter-on-mode": "इन्वर्टर चालू", + "charging-mode": "चार्जिंग मोड", + "charging-mode-hint": "इंटीजर मान वर्तमान चार्जिंग मोड दर्शाता है (1 - बल्क, 2 - ऐब्सॉर्प्शन, 3 - फ़्लोट).", + "charging-mode-indicators-color": "चार्जिंग मोड इंडिकेटर्स रंग", + "inverter-faults": "फॉल्ट्स", + "inverter-fault-indicators-color": "फॉल्ट इंडिकेटर्स रंग", + "overload-fault": "ओवरलोड", + "overload-fault-hint": "संकेत करता है कि इन्वर्टर ओवरलोड स्थिति में है या नहीं.", + "low-battery-fault": "लो बैटरी", + "low-battery-fault-hint": "संकेत करता है कि बैटरी अत्यधिक डिस्चार्ज हो चुकी है या नहीं.", + "temperature-fault": "तापमान", + "temperature-fault-hint": "संकेत करता है कि इन्वर्टर के भीतर उच्च तापमान है या नहीं.", + "triangle": "त्रिभुज", + "socket": "सॉकेट", + "left-button": "बायाँ बटन", + "right-button": "दायाँ बटन", + "alarm-colors": "अलार्म रंग", + "hook-color": "हुक रंग" + } + }, + "item": { + "selected": "चयनित" + }, + "js-func": { + "no-return-error": "फ़ंक्शन को मान लौटाना चाहिए!", + "return-type-mismatch": "फ़ंक्शन को '{{type}}' प्रकार का मान लौटाना चाहिए!", + "tidy": "फॉर्मैट करें", + "mini": "मिनी", + "modules": "मॉड्यूल्स", + "remove-module": "मॉड्यूल हटाएँ", + "no-modules": "कोई मॉड्यूल कॉन्फ़िगर नहीं है", + "add-module": "मॉड्यूल जोड़ें", + "module-alias": "उपनाम", + "invalid-module-alias-name": "अमान्य उपनाम नाम", + "module-resource": "JS मॉड्यूल संसाधन", + "not-unique-module-aliases-error": "मॉड्यूल उपनाम अद्वितीय होने चाहिए!", + "show-module-info": "मॉड्यूल जानकारी दिखाएँ", + "show-module-source-code": "मॉड्यूल सोर्स कोड दिखाएँ", + "module-members": "मॉड्यूल सदस्य", + "module-no-members": "मॉड्यूल के कोई निर्यातित सदस्य नहीं हैं", + "module-load-error": "मॉड्यूल लोड त्रुटि", + "source-code": "सोर्स कोड", + "source-code-load-error": "सोर्स कोड लोड त्रुटि", + "no-js-module-text": "कोई JS मॉड्यूल नहीं मिला", + "no-js-module-matching": "'{{module}}' से मेल खाते कोई JS मॉड्यूल नहीं मिले." + }, + "key-val": { + "key": "कुंजी", + "value": "मान", + "remove-entry": "प्रविष्टि हटाएँ", + "add-entry": "प्रविष्टि जोड़ें", + "no-data": "कोई प्रविष्टि नहीं" + }, + "layout": { + "layout": "लेआउट", + "layouts": "लेआउट्स", + "manage": "लेआउट्स प्रबंधित करें", + "settings": "लेआउट सेटिंग्स", + "color": "रंग", + "main": "मुख्य", + "right": "दायाँ", + "left": "बायाँ", + "select": "लक्ष्य लेआउट चुनें", + "percentage-width": "प्रतिशत चौड़ाई (%)", + "fixed-width": "निश्चित चौड़ाई (px)", + "left-width": "बाएँ कॉलम (%)", + "right-width": "दाएँ कॉलम (%)", + "pick-fixed-side": "निश्चित साइड चुनें: ", + "layout-fixed-width": "निश्चित चौड़ाई (px)", + "value-min-error": "मान {{min}}{{unit}} से अधिक होना चाहिए", + "value-max-error": "मान {{max}}{{unit}} से कम होना चाहिए", + "layout-fixed-width-required": "निश्चित चौड़ाई आवश्यक है", + "right-width-percentage-required": "दाएँ प्रतिशत आवश्यक है", + "left-width-percentage-required": "बाएँ प्रतिशत आवश्यक है", + "divider": "डिवाइडर", + "right-side": "दाएँ साइड लेआउट", + "left-side": "बाएँ साइड लेआउट", + "add-new-breakpoint": "नया ब्रेकपॉइंट जोड़ें", + "breakpoint": "ब्रेकपॉइंट", + "breakpoints": "ब्रेकपॉइंट्स", + "copy-from": "से कॉपी करें", + "size": "आकार", + "delete-breakpoint-title": "क्या आप वाकई ब्रेकपॉइंट '{{name}}' को हटाना चाहते हैं?", + "delete-breakpoint-text": "कृपया ध्यान दें, पुष्टि के बाद यह ब्रेकपॉइंट हमेशा के लिए खो जाएगा और सेटिंग्स डिफ़ॉल्ट ब्रेकपॉइंट पर वापस चली जाएँगी." + }, + "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": "नवीनतम मान दिखाएँ", + "settings": "लीजेंड सेटिंग्स", + "min": "न्यूनतम", + "max": "अधिकतम", + "avg": "औसत", + "total": "कुल", + "latest": "नवीनतम", + "Min": "न्यूनतम", + "Max": "अधिकतम", + "Avg": "औसत", + "Total": "कुल", + "Latest": "नवीनतम", + "comparison-time-ago": { + "previousInterval": "(पिछला अंतराल)", + "customInterval": "(कस्टम अंतराल)", + "days": "(दिन पहले)", + "weeks": "(सप्ताह पहले)", + "months": "(महीना पहले)", + "years": "(साल पहले)" + }, + "column-title": "कॉलम शीर्षक", + "label": "लेबल", + "value": "मान" + }, + "login": { + "login": "लॉगिन", + "request-password-reset": "पासवर्ड रीसेट का अनुरोध करें", + "reset-password": "पासवर्ड रीसेट करें", + "create-password": "पासवर्ड बनाएँ", + "two-factor-authentication": "दो-कारक प्रमाणीकरण", + "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": "ईमेल", + "invalid-email-format": "अमान्य ईमेल फ़ॉर्मेट.", + "or": "या", + "error": "लॉगिन त्रुटि", + "verify-your-identity": "अपनी पहचान सत्यापित करें", + "select-way-to-verify": "सत्यापित करने का तरीका चुनें", + "resend-code": "कोड फिर से भेजें", + "resend-code-wait": "{ time, plural, =1 {1 सेकंड} other {# सेकंड} } में कोड फिर से भेजें", + "try-another-way": "कोई अन्य तरीका आज़माएँ", + "totp-auth-description": "कृपया अपने ऑथेंटिकेटर ऐप से सिक्योरिटी कोड दर्ज करें.", + "totp-auth-placeholder": "कोड", + "sms-auth-description": "आपके फ़ोन {{contact}} पर सिक्योरिटी कोड भेजा गया है.", + "sms-auth-placeholder": "SMS कोड", + "email-auth-description": "आपके ईमेल पते {{contact}} पर सिक्योरिटी कोड भेजा गया है.", + "email-auth-placeholder": "ईमेल कोड", + "backup-code-auth-description": "कृपया अपने बैकअप कोड्स में से एक दर्ज करें.", + "backup-code-auth-placeholder": "बैकअप कोड", + "activation-link-expired": "सक्रियन लिंक की अवधि समाप्त हो गई है", + "activation-link-expired-message": "आपके प्रोफ़ाइल को सक्रिय करने वाला लिंक समाप्त हो गया है। आप नया ईमेल प्राप्त करने के लिए लॉगिन पेज पर वापस जा सकते हैं.", + "reset-password-link-expired": "पासवर्ड रीसेट लिंक की अवधि समाप्त हो गई है", + "reset-password-link-expired-message": "आपके पासवर्ड को रीसेट करने वाला लिंक समाप्त हो गया है। आप नया ईमेल प्राप्त करने के लिए लॉगिन पेज पर वापस जा सकते हैं." + }, + "mobile": { + "add-application": "एप्लिकेशन जोड़ें", + "app-id": "ऐप साइट एसोसिएशन ID", + "app-id-required": "ऐप साइट एसोसिएशन ID आवश्यक है", + "app-id-pattern": "अमान्य App Site Association ID फ़ॉर्मेट", + "app-store-link": "App Store लिंक", + "app-store-link-required": "App Store लिंक आवश्यक है", + "application-details": "एप्लिकेशन विवरण", + "application-package": "एप्लिकेशन पैकेज", + "application-secret": "एप्लिकेशन सीक्रेट", + "application-secret-required": "एप्लिकेशन सीक्रेट आवश्यक है", + "application": "एप्लिकेशन", + "applications": "एप्लिकेशन्स", + "copy-app-id": "App ID कॉपी करें", + "copy-app-store-link": "App Store लिंक कॉपी करें", + "copy-application-package": "एप्लिकेशन पैकेज कॉपी करें", + "copy-application-secret": "एप्लिकेशन सीक्रेट कॉपी करें", + "copy-google-play-link": "Google Play लिंक कॉपी करें", + "copy-sha256-certificate-fingerprints": "SHA256 सर्टिफिकेट फिंगरप्रिंट्स कॉपी करें", + "delete-application": "एप्लिकेशन हटाएँ", + "delete-application-button-text": "मैं परिणामों को समझता हूँ, एप्लिकेशन हटाएँ", + "delete-application-text": "यह क्रिया वापस नहीं ली जा सकती. यह आपकी एप्लिकेशन को स्थायी रूप से हटा देगी.
    यदि आप इसे स्थायी रूप से हटाना नहीं चाहते हैं तो आप एप्लिकेशन को अस्थायी रूप से निलंबित कर सकते हैं.
    एप्लिकेशन हटाने के लिए कृपया पुष्टि करने हेतु \"{{phrase}}\" टाइप करें.", + "delete-application-title-short": "क्या आप वाकई एप्लिकेशन '{{name}}' को हटाना चाहते हैं?", + "delete-application-text-short": "सावधान रहें, पुष्टि के बाद एप्लिकेशन्स और उनसे संबंधित सभी डेटा हमेशा के लिए खो जाएँगे.", + "delete-application-phrase": "delete application", + "delete-applications-bundle-text": "सावधान रहें, पुष्टि के बाद मोबाइल बंडल और उससे संबंधित सभी डेटा हमेशा के लिए खो जाएगा.", + "delete-applications-bundle-title": "क्या आप वाकई मोबाइल बंडल '{{bundleName}}' को हटाना चाहते हैं?", + "generate-application-secret": "एप्लिकेशन सीक्रेट जनरेट करें", + "google-play-link": "Google Play लिंक", + "google-play-link-required": "Google Play लिंक आवश्यक है", + "latest-version": "नवीनतम संस्करण", + "min-version": "न्यूनतम संस्करण", + "invalid-version-pattern": "अमान्य संस्करण फ़ॉर्मेट. कृपया यह फ़ॉर्मेट उपयोग करें: major.minor.patch (उदा., 1.0.0).", + "mobile-center": "मोबाइल केंद्र", + "mobile-package": "एप्लिकेशन पैकेज", + "mobile-package-max-length": "एप्लिकेशन पैकेज 256 वर्णों से कम होना चाहिए", + "mobile-package-required": "एप्लिकेशन पैकेज आवश्यक है.", + "mobile-package-pattern": "एप्लिकेशन पैकेज का फ़ॉर्मेट अमान्य है", + "mobile-package-title": "एप्लिकेशन शीर्षक", + "mobile-package-title-max-length": "एप्लिकेशन शीर्षक 256 वर्णों से कम होना चाहिए", + "no-application": "कोई एप्लिकेशन नहीं मिला", + "no-bundles": "कोई बंडल नहीं मिला", + "platform-type": "प्लेटफ़ॉर्म प्रकार", + "search-application": "एप्लिकेशन्स खोजें", + "search-bundles": "बंडल खोजें", + "set": "सेट करें", + "sha256-certificate-fingerprints": "SHA256 सर्टिफिकेट फिंगरप्रिंट्स", + "sha256-certificate-fingerprints-required": "SHA256 सर्टिफिकेट फิงरप्रिंट्स आवश्यक हैं", + "sha256-certificate-fingerprints-pattern": "अमान्य SHA256 सर्टिफिकेट फिंगरप्रिंट फ़ॉर्मेट", + "show-hidden-pages": "छिपे हुए पेज दिखाएँ", + "status": "स्थिति", + "status-type": { + "deprecated": "अप्रचलित", + "draft": "ड्राफ्ट", + "published": "प्रकाशित", + "suspended": "निलंबित" + }, + "store-information": "स्टोर जानकारी", + "version-information": "संस्करण जानकारी", + "min-version-release-notes": "न्यूनतम संस्करण रिलीज़ नोट्स", + "latest-version-release-notes": "नवीनतम संस्करण रिलीज़ नोट्स", + "bundle": "बंडल", + "bundles": "बंडल्स", + "add-bundle": "बंडल जोड़ें", + "title": "शीर्षक", + "title-required": "शीर्षक आवश्यक है", + "title-cannot-contain-only-spaces": "शीर्षक में केवल स्पेस नहीं हो सकते", + "title-max-length": "शीर्षक 256 वर्णों से कम होना चाहिए", + "oauth-clients": "OAuth 2.0 क्लाइंट्स", + "android-app": "Android ऐप", + "android-application": "Android एप्लिकेशन", + "ios-app": "iOS ऐप", + "ios-application": "iOS एप्लिकेशन", + "invalid-store-link": "अमान्य स्टोर लिंक", + "enable-oauth": "OAuth 2.0 सक्षम करें", + "enable-self-registration": "स्व-पंजीकरण सक्षम करें", + "edit-bundle": "बंडल संपादित करें", + "description": "वर्णन", + "basic-settings": "मूल सेटिंग्स", + "no-application-matching": "'{{entity}}' से मेल खाता कोई एप्लिकेशन नहीं मिला.", + "no-bundle-matching": "'{{entity}}' से मेल खाता कोई बंडल नहीं मिला.", + "application-required": "एप्लिकेशन आवश्यक है.", + "bundle-required": "बंडल आवश्यक है.", + "no-application-text": "कोई एप्लिकेशन नहीं मिला", + "no-bundle-text": "कोई बंडल नहीं मिला", + "layout": "लेआउट", + "pages": "पेज", + "hide-all-pages": "सभी पेज छिपाएँ", + "reset-to-default-pages": "पेज को डिफ़ॉल्ट पर रीसेट करें", + "add-specific-page": "विशिष्ट पेज जोड़ें", + "visible": "दृश्यमान", + "hidden": "छिपा हुआ", + "reset-to-page-default": "पेज को वापस डिफ़ॉल्ट पर रीसेट करें", + "mobile-599": "मोबाइल (अधिकतम 599px)", + "tablet-959": "टैबलेट (अधिकतम 959px)", + "max-element-number": "एलिमेंट्स की अधिकतम संख्या", + "page-name": "पेज नाम", + "page-name-required": "पेज नाम आवश्यक है.", + "page-name-cannot-contain-only-spaces": "पेज नाम में केवल स्पेस नहीं हो सकते.", + "page-name-max-length": "पेज नाम 256 वर्णों से कम होना चाहिए", + "page-type": "पेज प्रकार", + "pages-types": { + "dashboard": "डैशबोर्ड", + "web-view": "वेब व्यू", + "custom": "कस्टम" + }, + "url": "URL", + "invalid-url-format": "अमान्य URL फ़ॉर्मेट", + "path": "पाथ", + "invalid-path-format": "अमान्य पाथ फ़ॉर्मेट", + "custom-page": "कस्टम पेज", + "edit-page": "पेज संपादित करें", + "edit-custom-page": "कस्टम पेज संपादित करें", + "delete-page": "पेज हटाएँ", + "qr-code-widget": "QR कोड विजेट", + "type-here": "यहाँ लिखें", + "configuration-dialog": "कॉन्फ़िगरेशन डायलॉग", + "configuration-app": "कॉन्फ़िगरेशन ऐप", + "configuration-step": { + "prepare-environment-title": "डेवलपमेंट एनवायरनमेंट तैयार करें", + "prepare-environment-text": "Flutter ThingsBoard Mobile Application के लिए Flutter SDK आवश्यक है. Flutter SDK सेटअप करने के लिए निर्देशों का पालन करें.", + "get-source-code-title": "ऐप सोर्स कोड प्राप्त करें", + "get-source-code-text": "आप GitHub रिपॉज़िटरी से क्लोन करके Flutter ThingsBoard Mobile Application का सोर्स कोड प्राप्त कर सकते हैं:", + "configure-app-settings-title": "ऐप सेटिंग्स कॉन्फ़िगर करें", + "configure-app-settings-text": "कॉन्फ़िगरेशन फ़ाइल डाउनलोड करें और उसे उस प्रोजेक्ट की रूट डायरेक्टरी में रखें जिसे आपने पिछले चरण में क्लोन किया था.", + "download-file": "फ़ाइल डाउनलोड करें", + "run-app-title": "ऐप चलाएँ", + "run-app-text": "अपने IDE में दिए गए निर्देशों के अनुसार ऐप चलाएँ.\nयदि आप टर्मिनल का उपयोग कर रहे हैं, तो ऐप को निम्न कमांड से चलाएँ:", + "more-information": "विस्तृत जानकारी हमारे Getting Started डॉक्यूमेंटेशन में मिल सकती है.", + "getting-started": "शुरुआत की गाइड" + } + }, + "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": "रेट लिमिट पार होने पर ट्रिगर सेटिंग्स", + "task-processing-failure-trigger-settings": "टास्क प्रोसेसिंग असफलता ट्रिगर सेटिंग्स", + "resources-shortage-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": "बनाया गया", + "customize-messages": "संदेशों को अनुकूलित करें", + "cpu-threshold": "CPU थ्रेशहोल्ड", + "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-preview": "ईमेल नोटिफिकेशन प्रीव्यू", + "slack": "स्लैक", + "slack-preview": "स्लैक नोटिफिकेशन प्रीव्यू", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Microsoft Teams नोटिफिकेशन प्रीव्यू", + "sms": "SMS", + "sms-preview": "SMS नोटिफिकेशन प्रीव्यू", + "web": "वेब", + "web-preview": "वेब नोटिफिकेशन प्रीव्यू", + "mobile-app": "मोबाइल ऐप", + "mobile-app-preview": "मोबाइल ऐप नोटिफिकेशन प्रीव्यू" + }, + "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": "अक्षम", + "edge-trigger-settings": "Edge ट्रिगर सेटिंग्स", + "edge-list-rule-hint": "यदि फ़ील्ड खाली है, तो ट्रिगर सभी Edge इंस्टेंसेज़ पर लागू होगा", + "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-max-length": "लिंक {{ length }} वर्णों से कम या बराबर होना चाहिए", + "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-recipients-matching": "'{{entity}}' से मेल खाता कोई प्राप्तकर्ता नहीं मिला.", + "no-recipients-text": "कोई प्राप्तकर्ता नहीं मिला", + "no-rule": "कोई नियम कॉन्फ़िगर नहीं है", + "no-rules-notification": "कोई नियम नोटिफिकेशन नहीं", + "no-severity-found": "कोई severity नहीं मिली", + "no-severity-matching": "'{{severity}}' नहीं मिला.", + "no-template-matching": "'{{template}}' से मेल खाता कोई संसाधन नहीं मिला.", + "create-new-template": "एक नया बनाएँ!", + "not-found-slack-recipient": "Slack प्राप्तकर्ता नहीं मिला", + "notification": "नोटिफिकेशन", + "notification-center": "अधिसूचना केंद्र", + "notification-tap-action": "नोटिफिकेशन टैप कार्रवाई", + "notification-tap-action-hint": "यदि सक्षम नहीं किया गया, तो डिफ़ॉल्ट अलार्म डैशबोर्ड का उपयोग किया जाएगा", + "notify": "सूचित करें", + "notify-again": "फिर से सूचित करें", + "notify-alarm-action": { + "acknowledged": "अलार्म स्वीकार किया गया", + "assigned": "अलार्म असाइन किया गया", + "cleared": "अलार्म क्लियर किया गया", + "created": "अलार्म बनाया गया", + "severity-changed": "अलार्म severity बदली गई", + "unassigned": "अलार्म अनअसाइन किया गया" + }, + "notify-on": "पर सूचित करें", + "notify-on-comment-update": "कमेंट अपडेट पर सूचित करें", + "notify-on-required": "'Notify on' आवश्यक है", + "notify-on-unassign": "अनअसाइन पर सूचित करें", + "notify-only-user-comments": "केवल उपयोगकर्ता कमेंट्स पर सूचित करें", + "only-rule-chain-lifecycle-failures": "केवल रूल चेन लाइफ़साइकल विफलताएँ", + "only-rule-node-lifecycle-failures": "केवल रूल नोड लाइफ़साइकल विफलताएँ", + "platform-users": "प्लेटफ़ॉर्म उपयोगकर्ता", + "ram-threshold": "RAM थ्रेशहोल्ड", + "rate-limits": "रेट लिमिट्स", + "rate-limits-hint": "यदि फ़ील्ड खाली है, तो ट्रिगर सभी रेट लिमिट्स पर लागू होगा", + "recipient": "प्राप्तकर्ता", + "recipient-group": "प्राप्तकर्ता समूह", + "recipient-type": { + "affected-tenant-administrators": "प्रभावित Tenant administrators", + "affected-user": "प्रभावित उपयोगकर्ता", + "all-users": "सभी उपयोगकर्ता", + "customer-users": "कस्टमर उपयोगकर्ता", + "system-administrators": "System administrators", + "tenant-administrators": "Tenant administrators", + "user-filters": "यूज़र फ़िल्टर", + "user-list": "यूज़र सूची", + "users-entity-owner": "एंटिटी मालिक के उपयोगकर्ता" + }, + "recipients": "प्राप्तकर्ता", + "notification-recipient": "नोटिफिकेशन प्राप्तकर्ता", + "notification-recipient-required": "नोटिफिकेशन प्राप्तकर्ता आवश्यक है.", + "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": "भेजा गया", + "setup": "सेटअप करें", + "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": "एस्केलेशन रोकें जब अलार्म स्टेटस यह हो जाए:", + "storage-threshold": "स्टोरेज थ्रेशहोल्ड", + "subject": "विषय", + "subject-required": "विषय आवश्यक है", + "subject-max-length": "विषय {{ length }} वर्णों से कम या बराबर होना चाहिए", + "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": "रेट लिमिट पार", + "edge-communication-failure": "Edge कम्युनिकेशन फेल्यर", + "edge-connection": "Edge कनेक्शन", + "task-processing-failure": "टास्क प्रोसेसिंग फेल्यर", + "resources-shortage": "रिसोर्सेज़ की कमी" + }, + "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": "रेट लिमिट पार", + "edge-connection": "Edge कनेक्शन", + "edge-communication-failure": "Edge कम्युनिकेशन फेल्यर", + "task-processing-failure": "टास्क प्रोसेसिंग फेल्यर", + "resources-shortage": "रिसोर्सेज़ की कमी", + "trigger": "ट्रिगर", + "trigger-required": "ट्रिगर आवश्यक है" + }, + "type": "प्रकार", + "unread": "अपठित", + "updated": "अपडेट किया गया", + "use-deprecated-webhook-connectors": "अप्रचलित Webhook कनेक्टर्स का उपयोग करें", + "use-old-api": "पुराने API का उपयोग करें", + "use-template": "टेम्पलेट का उपयोग करें", + "view-all": "सभी देखें", + "warning": "चेतावनी", + "webhook-url": "Webhook URL", + "webhook-url-required": "Webhook URL आवश्यक है", + "workflow-url": "वर्कफ़्लो URL", + "workflow-url-required": "वर्कफ़्लो 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": "पैकेज जोड़ें", + "assign-firmware": "असाइन किया गया फ़र्मवेयर", + "assign-firmware-required": "असाइन किया गया फ़र्मवेयर आवश्यक है", + "assign-software": "असाइन किया गया सॉफ़्टवेयर", + "assign-software-required": "असाइन किया गया सॉफ़्टवेयर आवश्यक है", + "auto-generate-checksum": "चेकसम स्वतः जनरेट करें", + "checksum": "चेकसम", + "checksum-hint": "यदि चेकसम खाली है, तो इसे स्वतः जनरेट किया जाएगा", + "checksum-algorithm": "चेकसम एल्गोरिदम", + "checksum-copied-message": "पैकेज चेकसम क्लिपबोर्ड पर कॉपी कर दिया गया है", + "change-firmware": "फ़र्मवेयर में बदलाव से { count, plural, =1 {1 डिवाइस} other {# डिवाइस} } अपडेट हो सकता है.", + "change-software": "सॉफ़्टवेयर में बदलाव से { count, plural, =1 {1 डिवाइस} other {# डिवाइस} } अपडेट हो सकता है.", + "change-ota-setting-title": "क्या आप वाकई OTA सेटिंग्स बदलना चाहते हैं?", + "chose-compatible-device-profile": "अपलोड किया गया पैकेज केवल चुने हुए डिवाइस प्रोफ़ाइल वाले डिवाइसेज़ के लिए उपलब्ध होगा.", + "chose-firmware-distributed-device": "वह फ़र्मवेयर चुनें जो डिवाइस पर वितरित किया जाएगा", + "chose-software-distributed-device": "वह सॉफ़्टवेयर चुनें जो डिवाइस पर वितरित किया जाएगा", + "content-type": "कंटेंट प्रकार", + "copy-checksum": "चेकसम कॉपी करें", + "copy-direct-url": "डायरेक्ट URL कॉपी करें", + "copyId": "पैकेज Id कॉपी करें", + "copied": "कॉपी हो गया!", + "delete": "पैकेज हटाएँ", + "delete-ota-update-text": "सावधान रहें, पुष्टि के बाद OTA अपडेट हमेशा के लिए खो जाएगा.", + "delete-ota-update-title": "क्या आप वाकई OTA अपडेट '{{title}}' को हटाना चाहते हैं?", + "delete-ota-updates-text": "सावधान रहें, पुष्टि के बाद सभी चयनित OTA अपडेट्स हटा दिए जाएँगे.", + "delete-ota-updates-title": "क्या आप वाकई { count, plural, =1 {1 OTA अपडेट} other {# OTA अपडेट्स} } को हटाना चाहते हैं?", + "description": "वर्णन", + "direct-url": "डायरेक्ट URL", + "direct-url-copied-message": "पैकेज डायरेक्ट URL क्लिपबोर्ड पर कॉपी कर दिया गया है", + "direct-url-required": "डायरेक्ट URL आवश्यक है", + "download": "पैकेज डाउनलोड करें", + "drop-file": "पैकेज फ़ाइल ड्रॉप करें या अपलोड करने के लिए क्लिक करके फ़ाइल चुनें.", + "drop-package-file-or": "पैकेज फ़ाइल को ड्रैग और ड्रॉप करें या", + "file-name": "फ़ाइल नाम", + "file-size": "फ़ाइल आकार", + "file-size-bytes": "फ़ाइल आकार (बाइट्स में)", + "idCopiedMessage": "पैकेज Id क्लिपबोर्ड पर कॉपी कर दिया गया है", + "no-firmware-matching": "कोई संगत Firmware OTA अपडेट पैकेज '{{entity}}' से मेल खाते नहीं मिले.", + "no-firmware-text": "कोई संगत Firmware OTA अपडेट पैकेज प्रोविज़न नहीं किए गए.", + "no-packages-text": "कोई पैकेज नहीं मिला", + "no-software-matching": "कोई संगत Software OTA अपडेट पैकेज '{{entity}}' से मेल खाते नहीं मिले.", + "no-software-text": "कोई संगत Software OTA अपडेट पैकेज प्रोविज़न नहीं किए गए.", + "ota-update": "OTA अपडेट", + "ota-update-details": "OTA अपडेट विवरण", + "ota-updates": "OTA अपडेट्स", + "package-file": "पैकेज फ़ाइल", + "package-type": "पैकेज प्रकार", + "packages-repository": "पैकेज रिपॉज़िटरी", + "search": "पैकेज खोजें", + "selected-package": "{ count, plural, =1 {1 पैकेज} other {# पैकेज} } चयनित", + "title": "शीर्षक", + "title-required": "शीर्षक आवश्यक है.", + "title-max-length": "शीर्षक 256 वर्णों से कम होना चाहिए", + "types": { + "firmware": "फ़र्मवेयर", + "software": "सॉफ़्टवेयर" + }, + "upload-binary-file": "बाइनरी फ़ाइल अपलोड करें", + "use-external-url": "एक्सटर्नल URL का उपयोग करें", + "version": "वर्ज़न", + "version-required": "वर्ज़न आवश्यक है.", + "version-tag": "वर्ज़न टैग", + "version-tag-hint": "कस्टम टैग आपके डिवाइस द्वारा रिपोर्ट किए गए पैकेज वर्ज़न से मेल खाना चाहिए.", + "version-max-length": "वर्ज़न 256 वर्णों से कम होना चाहिए", + "warning-after-save-no-edit": "एक बार पैकेज अपलोड हो जाने के बाद, आप शीर्षक, वर्ज़न, डिवाइस प्रोफ़ाइल और पैकेज प्रकार को संशोधित नहीं कर पाएँगे." + }, + "position": { + "top": "ऊपर", + "bottom": "नीचे", + "left": "बायाँ", + "right": "दायाँ" + }, + "profile": { + "profile": "प्रोफ़ाइल", + "last-login-time": "अंतिम लॉगिन", + "change-password": "पासवर्ड बदलें", + "current-password": "वर्तमान पासवर्ड", + "copy-jwt-token": "JWT टोकन कॉपी करें", + "jwt-token": "JWT टोकन", + "token-valid-till": "टोकन इस समय तक वैध है", + "tokenCopiedSuccessMessage": "JWT टोकन क्लिपबोर्ड पर कॉपी कर दिया गया है", + "tokenCopiedWarnMessage": "JWT टोकन की अवधि समाप्त हो गई है! कृपया पेज रीफ़्रेश करें." + }, + "profiles": { + "profiles": "प्रोफ़ाइल्स" + }, + "security": { + "security": "सुरक्षा", + "general-settings": "सामान्य सुरक्षा सेटिंग्स", + "access-token": "एक्सेस टोकन", + "access-token-required": "एक्सेस टोकन आवश्यक है", + "clientId": "Client ID", + "clientId-required": "Client ID आवश्यक है", + "username": "यूज़रनेम", + "username-required": "यूज़रनेम आवश्यक है", + "ca-cert": "CA प्रमाणपत्र", + "2fa": { + "2fa": "दो-कारक प्रमाणीकरण", + "2fa-description": "दो-कारक प्रमाणीकरण आपके अकाउंट को अनधिकृत पहुँच से बचाता है. आपको बस लॉगिन करते समय एक सिक्योरिटी कोड दर्ज करना होता है.", + "authenticate-with": "आप इनसे ऑथेंटिकेट कर सकते हैं:", + "disable-2fa-provider-text": "{{name}} को निष्क्रिय करने से आपका अकाउंट कम सुरक्षित हो जाएगा", + "disable-2fa-provider-title": "क्या आप वाकई {{name}} को निष्क्रिय करना चाहते हैं?", + "get-new-code": "नया कोड प्राप्त करें", + "main-2fa-method": "मुख्य दो-कारक प्रमाणीकरण विधि के रूप में उपयोग करें", + "dialog": { + "activation-step-description-email": "अगली बार जब आप लॉगिन करेंगे, तो आपसे वह सुरक्षा कोड दर्ज करने के लिए कहा जाएगा जो आपके ईमेल पते पर भेजा जाएगा.", + "activation-step-description-sms": "अगली बार जब आप लॉगिन करेंगे, तो आपसे वह सुरक्षा कोड दर्ज करने के लिए कहा जाएगा जो आपके फ़ोन नंबर पर भेजा जाएगा.", + "activation-step-description-totp": "अगली बार जब आप लॉगिन करेंगे, तो आपको दो-कारक प्रमाणीकरण कोड प्रदान करना होगा.", + "activation-step-label": "सक्रियकरण", + "backup-code-description": "इन कोड्स को प्रिंट कर लें ताकि जब आपको अपने अकाउंट में लॉगिन करने के लिए उनकी ज़रूरत पड़े, तो वे आपके पास हों. आप प्रत्येक बैकअप कोड का केवल एक बार उपयोग कर सकते हैं.", + "backup-code-warn": "एक बार जब आप इस पेज से बाहर निकल जाते हैं, तो ये कोड दोबारा नहीं दिखाए जा सकते. नीचे दिए गए विकल्पों का उपयोग करके इन्हें सुरक्षित रूप से स्टोर करें.", + "download-txt": "डाउनलोड (txt)", + "email-step-description": "अपने ऑथेंटिकेटर के रूप में उपयोग करने के लिए एक ईमेल दर्ज करें.", + "email-step-label": "ईमेल", + "enable-email-title": "ईमेल ऑथेंटिकेटर सक्षम करें", + "enable-sms-title": "SMS ऑथेंटिकेटर सक्षम करें", + "enable-totp-title": "ऑथेंटिकेटर ऐप सक्षम करें", + "enter-verification-code": "यहाँ 6-अंकों का कोड दर्ज करें", + "get-backup-code-title": "बैकअप कोड प्राप्त करें", + "next": "आगे", + "scan-qr-code": "अपने वेरिफिकेशन ऐप से इस QR कोड को स्कैन करें", + "send-code": "कोड भेजें", + "sms-step-description": "अपने ऑथेंटिकेटर के रूप में उपयोग करने के लिए एक फ़ोन नंबर दर्ज करें.", + "sms-step-label": "फ़ोन नंबर", + "success": "सफलता!", + "totp-step-description-install": "आप Google Authenticator, Authy या Duo जैसी ऐप्स इंस्टॉल कर सकते हैं.", + "totp-step-description-open": "अपने मोबाइल फ़ोन पर ऑथेंटिकेटर ऐप खोलें.", + "totp-step-label": "ऐप प्राप्त करें", + "verification-code": "6-अंकों का कोड", + "verification-code-invalid": "अमान्य वेरिफिकेशन कोड फ़ॉर्मेट", + "verification-code-incorrect": "वेरिफिकेशन कोड गलत है", + "verification-code-many-request": "बहुत अधिक अनुरोध, कृपया वेरिफिकेशन कोड की जाँच करें", + "verification-step-description": "वह 6-अंकों का कोड दर्ज करें जो हमने अभी {{address}} पर भेजा है", + "verification-step-label": "वेरिफिकेशन" + }, + "provider": { + "email": "ईमेल", + "email-description": "ऑथेंटिकेट करने के लिए अपने ईमेल पते पर भेजे गए सिक्योरिटी कोड का उपयोग करें.", + "email-hint": "ऑथेंटिकेशन कोड्स ईमेल के माध्यम से {{ info }} पर भेजे जाते हैं", + "sms": "SMS", + "sms-description": "ऑथेंटिकेट करने के लिए अपने फ़ोन का उपयोग करें. जब आप लॉगिन करेंगे तो हम आपको SMS संदेश के माध्यम से सिक्योरिटी कोड भेजेंगे.", + "sms-hint": "ऑथेंटिकेशन कोड्स टेक्स्ट संदेश द्वारा {{ info }} पर भेजे जाते हैं", + "totp": "ऑथेंटिकेटर ऐप", + "totp-description": "अपने फ़ोन पर Google Authenticator, Authy या Duo जैसी ऐप्स का उपयोग करके ऑथेंटिकेट करें. यह लॉगिन के लिए सिक्योरिटी कोड जनरेट करेगी.", + "totp-hint": "आपके अकाउंट के लिए ऑथेंटिकेटर ऐप सेटअप किया गया है", + "backup_code": "बैकअप कोड", + "backup-code-description": "ये प्रिंट करने योग्य एक-बार उपयोग होने वाले पासकोड्स आपको तब साइन-इन करने देते हैं जब आप अपने फ़ोन से दूर हों, जैसे यात्रा करते समय.", + "backup-code-hint": "{{ info }} सिंगल-यूज़ कोड इस समय सक्रिय हैं" + } + }, + "password-requirement": { + "at-least": "कम से कम:", + "character": "{ count, plural, =1 {1 अक्षर} other {# अक्षर} }", + "digit": "{ count, plural, =1 {1 अंक} other {# अंक} }", + "incorrect-password-try-again": "गलत पासवर्ड. दोबारा कोशिश करें", + "lowercase-letter": "{ count, plural, =1 {1 छोटे अक्षर} other {# छोटे अक्षर} }", + "new-passwords-not-match": "नए पासवर्ड आपस में मेल नहीं खाते", + "password-should-not-contain-spaces": "आपके पासवर्ड में स्पेस नहीं होना चाहिए", + "password-not-meet-requirements": "पासवर्ड आवश्यकताओं को पूरा नहीं करता", + "password-requirements": "पासवर्ड आवश्यकताएँ", + "password-should-difference": "नया पासवर्ड वर्तमान पासवर्ड से अलग होना चाहिए", + "special-character": "{ count, plural, =1 {1 विशेष वर्ण} other {# विशेष वर्ण} }", + "uppercase-letter": "{ count, plural, =1 {1 बड़े अक्षर} other {# बड़े अक्षर} }", + "at-most": "अधिकतम:" + } + }, + "relation": { + "relations": "रिलेशन", + "direction": "दिशा", + "clear-relation-type": "रिलेशन प्रकार साफ़ करें", + "search-direction": { + "FROM": "से", + "TO": "तक" + }, + "direction-type": { + "FROM": "से", + "TO": "तक" + }, + "from-relations": "आउटबाउंड रिलेशन", + "to-relations": "इनबाउंड रिलेशन", + "selected-relations": "{ count, plural, =1 {1 रिलेशन} other {# रिलेशन} } चयनित", + "type": "प्रकार", + "to-entity-type": "लक्ष्य एंटिटी प्रकार", + "to-entity-name": "लक्ष्य एंटिटी नाम", + "from-entity-type": "स्रोत एंटिटी प्रकार", + "from-entity-name": "स्रोत एंटिटी नाम", + "to-entity": "लक्ष्य एंटिटी", + "from-entity": "स्रोत एंटिटी", + "delete": "रिलेशन हटाएँ", + "relation-type": "रिलेशन प्रकार", + "relation-type-required": "रिलेशन प्रकार आवश्यक है.", + "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 {1 रिलेशन} other {# रिलेशन} } हटाना चाहते हैं?", + "delete-to-relations-text": "सावधान रहें, पुष्टि के बाद सभी चुने हुए रिलेशन हटा दिए जाएँगे और संबंधित एंटिटीज़ वर्तमान एंटिटी से असंबंधित हो जाएँगी.", + "delete-from-relation-title": "क्या आप वाकई एंटिटी '{{entityName}}' से रिलेशन हटाना चाहते हैं?", + "delete-from-relation-text": "सावधान रहें, पुष्टि के बाद वर्तमान एंटिटी एंटिटी '{{entityName}}' से असंबंधित हो जाएगी.", + "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": "रिलेशन फ़िल्टर्स", + "relation-filter": "रिलेशन फ़िल्टर", + "additional-info": "अतिरिक्त जानकारी (JSON)", + "invalid-additional-info": "अतिरिक्त जानकारी JSON को पार्स करने में असमर्थ.", + "no-relations-text": "कोई रिलेशन नहीं मिला", + "not": "नहीं" + }, + "resource": { + "add": "संसाधन जोड़ें", + "all-types": "सभी", + "copyId": "संसाधन Id कॉपी करें", + "delete": "संसाधन हटाएँ", + "delete-resource-text": "सावधान रहें, पुष्टि के बाद संसाधन हमेशा के लिए खो जाएगा.", + "delete-resource-title": "क्या आप वाकई संसाधन '{{resourceTitle}}' को हटाना चाहते हैं?", + "delete-resources-action-title": "हटाएँ { count, plural, =1 {1 संसाधन} other {# संसाधन} }", + "delete-resources-text": "कृपया ध्यान दें कि चयनित संसाधन, भले ही उनका उपयोग डिवाइस प्रोफ़ाइल्स में हो, हटा दिए जाएँगे.", + "delete-resources-title": "क्या आप वाकई { count, plural, =1 {1 संसाधन} other {# संसाधन} } को हटाना चाहते हैं?", + "download": "संसाधन डाउनलोड करें", + "drop-file": "संसाधन फ़ाइल ड्रॉप करें या अपलोड करने के लिए क्लिक करके फ़ाइल चुनें.", + "drop-resource-file-or": "संसाधन फ़ाइल को ड्रैग और ड्रॉप करें या", + "empty": "संसाधन खाली है", + "file-name": "फ़ाइल नाम", + "idCopiedMessage": "संसाधन Id क्लिपबोर्ड पर कॉपी कर दिया गया है", + "no-resource-matching": "'{{widgetsBundle}}' से मेल खाता कोई संसाधन नहीं मिला.", + "no-resource-text": "कोई संसाधन नहीं मिला", + "open-widgets-bundle": "विजेट बंडल खोलें", + "resource": "संसाधन", + "resource-file": "संसाधन फ़ाइल", + "resource-files": "संसाधन फ़ाइलें", + "resource-library-details": "संसाधन विवरण", + "resource-type": "संसाधन प्रकार", + "resources-library": "संसाधन लाइब्रेरी", + "search": "संसाधन खोजें", + "selected-resources": "{ count, plural, =1 {1 संसाधन} other {# संसाधन} } चयनित", + "system": "सिस्टम", + "title": "शीर्षक", + "title-required": "शीर्षक आवश्यक है.", + "title-max-length": "शीर्षक 256 वर्णों से कम होना चाहिए", + "type": { + "jks": "JKS", + "js-module": "JS मॉड्यूल", + "lwm2m-model": "LWM2M मॉडल", + "pkcs-12": "PKCS #12", + "general": "सामान्य" + }, + "resource-sub-type": "सब-टाइप", + "sub-type": { + "image": "इमेज", + "scada-symbol": "SCADA सिंबल", + "extension": "एक्सटेंशन", + "module": "मॉड्यूल" + }, + "resource-is-in-use": "संसाधन अन्य एंटिटीज़ द्वारा उपयोग में है", + "resources-are-in-use": "संसाधन अन्य एंटिटीज़ द्वारा उपयोग में हैं", + "resource-is-in-use-text": "संसाधन '{{title}}' को नहीं हटाया गया क्योंकि इसका उपयोग निम्नलिखित एंटिटीज़ द्वारा किया जा रहा है:", + "resources-are-in-use-text": "सभी संसाधनों को नहीं हटाया गया क्योंकि उनका उपयोग अन्य एंटिटीज़ द्वारा किया जा रहा है.
    आप संदर्भित एंटिटीज़ को संबंधित संसाधन पंक्ति में References बटन पर क्लिक करके देख सकते हैं.
    यदि आप फिर भी इन संसाधनों को हटाना चाहते हैं, तो उन्हें नीचे दी गई तालिका में चुनें और चयनित हटाएँ बटन पर क्लिक करें.", + "delete-resource-in-use-text": "यदि आप फिर भी संसाधन को हटाना चाहते हैं, तो फिर भी हटाएँ बटन पर क्लिक करें." + }, + "javascript": { + "add": "JavaScript संसाधन जोड़ें", + "delete": "JavaScript संसाधन हटाएँ", + "delete-javascript-resource-text": "सावधान रहें, पुष्टि के बाद JavaScript संसाधन हमेशा के लिए खो जाएगा.", + "delete-javascript-resource-title": "क्या आप वाकई JavaScript संसाधन '{{resourceTitle}}' को हटाना चाहते हैं?", + "delete-javascript-resources-action-title": "JavaScript { count, plural, =1 {1 संसाधन} other {# संसाधन} } हटाएँ", + "delete-javascript-resources-text": "कृपया ध्यान दें कि चयनित JavaScript संसाधन, भले ही उनका उपयोग JavaScript फ़ंक्शन्स में हो, हटा दिए जाएँगे.", + "delete-javascript-resources-title": "क्या आप वाकई JavaScript { count, plural, =1 {1 संसाधन} other {# संसाधन} } हटाना चाहते हैं?", + "delete-javascript-resource-in-use-text": "यदि आप फिर भी JavaScript संसाधन को हटाना चाहते हैं, तो फिर भी हटाएँ बटन पर क्लिक करें.", + "download": "JavaScript संसाधन डाउनलोड करें", + "upload-from-file": "फ़ाइल से JavaScript अपलोड करें", + "resource-file": "JavaScript संसाधन फ़ाइल", + "drop-file": "JavaScript फ़ाइल ड्रॉप करें या अपलोड करने के लिए क्लिक करके फ़ाइल चुनें.", + "drop-resource-file-or": "JavaScript फ़ाइल को ड्रैग और ड्रॉप करें या", + "javascript-library": "JavaScript लाइब्रेरी", + "javascript-type": "JavaScript प्रकार", + "javascript-resource-details": "JavaScript संसाधन विवरण", + "javascript-resource-is-in-use": "JavaScript संसाधन अन्य एंटिटीज़ द्वारा उपयोग में है", + "javascript-resources-are-in-use": "JavaScript संसाधन अन्य एंटिटीज़ द्वारा उपयोग में हैं", + "javascript-resource-is-in-use-text": "JavaScript संसाधन '{{title}}' को नहीं हटाया गया क्योंकि इसका उपयोग निम्नलिखित एंटिटीज़ द्वारा किया जा रहा है:", + "javascript-resources-are-in-use-text": "सभी JavaScript संसाधनों को नहीं हटाया गया क्योंकि उनका उपयोग अन्य एंटिटीज़ द्वारा किया जा रहा है.
    आप संदर्भित एंटिटीज़ को संबंधित संसाधन पंक्ति में References बटन पर क्लिक करके देख सकते हैं.
    यदि आप फिर भी इन JavaScript संसाधनों को हटाना चाहते हैं, तो उन्हें नीचे दी गई तालिका में चुनें और चयनित हटाएँ बटन पर क्लिक करें.", + "search": "JavaScript संसाधन खोजें", + "selected-javascript-resources": "{ count, plural, =1 {1 JavaScript संसाधन} other {# JavaScript संसाधन} } चयनित", + "no-javascript-resource-text": "कोई JavaScript संसाधन नहीं मिला", + "all-types": "सभी", + "module-script": "मॉड्यूल स्क्रिप्ट" + }, + "rpc": { + "error": { + "target-device-is-not-set": "टार्गेट डिवाइस सेट नहीं है!", + "invalid-target-entity": "RPC कमांड्स {{entityType}} एंटिटी द्वारा समर्थित नहीं हैं.", + "failed-to-resolve-target-device": "टार्गेट डिवाइस रिसॉल्व करने में विफल!", + "request-timeout": "रिक्वेस्ट टाइमआउट", + "rpc-http-error": "त्रुटि: {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "रूल चेन", + "rulechain-events": "रूल चेन इवेंट्स", + "rulechains": "रूल चेन्स", + "root": "रूट", + "delete": "रूल चेन हटाएँ", + "name": "नाम", + "name-required": "नाम आवश्यक है.", + "name-max-length": "नाम 256 वर्णों से कम होना चाहिए", + "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 रूल चेन} other {# रूल चेन्स} } को हटाना चाहते हैं?", + "delete-rulechains-action-title": "हटाएँ { count, plural, =1 {1 रूल चेन} 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": "रूल चेन Id कॉपी करें", + "idCopiedMessage": "रूल चेन Id क्लिपबोर्ड पर कॉपी कर दिया गया है", + "select-rulechain": "रूल चेन चुनें", + "no-rulechains-matching": "'{{entity}}' से मेल खाती कोई रूल चेन नहीं मिली.", + "rulechain-required": "रूल चेन आवश्यक है", + "management": "रूल्स मैनेजमेंट", + "debug-mode": "डिबग मोड", + "search": "रूल चेन्स खोजें", + "selected-rulechains": "{ count, plural, =1 {1 रूल चेन} other {# रूल चेन्स} } चयनित", + "open-rulechain": "रूल चेन खोलें", + "edge-template-root": "टेम्पलेट रूट", + "assign-to-edge": "Edge को असाइन करें", + "edge-rulechain": "Edge रूल चेन", + "unassign-rulechain-from-edge-text": "पुष्टि के बाद रूल चेन अनअसाइन हो जाएगी और Edge द्वारा एक्सेस नहीं की जा सकेगी।", + "unassign-rulechains-from-edge-title": "क्या आप वाकई { count, plural, =1 {1 रूल चेन} other {# रूल चेन्स} } को अनअसाइन करना चाहते हैं?", + "unassign-rulechains-from-edge-text": "पुष्टि के बाद सभी चयनित रूल चेन्स अनअसाइन हो जाएँगी और Edge द्वारा एक्सेस नहीं की जा सकेंगी।", + "assign-rulechain-to-edge-title": "Edge को रूल चेन(्स) असाइन करें", + "assign-rulechain-to-edge-text": "कृपया Edge को असाइन करने के लिए रूल चेन्स चुनें", + "set-edge-template-root-rulechain": "रूल चेन को Edge टेम्पलेट रूट बनाएं", + "set-edge-template-root-rulechain-title": "क्या आप वाकई रूल चेन '{{ruleChainName}}' को Edge टेम्पलेट रूट बनाना चाहते हैं?", + "set-edge-template-root-rulechain-text": "पुष्टि के बाद रूल चेन Edge टेम्पलेट रूट बन जाएगी और नए बनाए गए Edge के लिए रूट रूल चेन होगी।", + "invalid-rulechain-type-error": "रूल चेन इम्पोर्ट नहीं की जा सकी: अमान्य रूल चेन प्रकार। अपेक्षित प्रकार {{expectedRuleChainType}} है।", + "set-auto-assign-to-edge": "बनाते समय रूल चेन को Edge(s) पर असाइन करें", + "set-auto-assign-to-edge-title": "क्या आप वाकई बनाते समय Edge रूल चेन '{{ruleChainName}}' को Edge(s) पर असाइन करना चाहते हैं?", + "set-auto-assign-to-edge-text": "पुष्टि के बाद Edge रूल चेन बनाते समय Edge(s) पर अपने-आप असाइन हो जाएगी।", + "unset-auto-assign-to-edge": "बनाते समय रूल चेन को Edge(s) पर असाइन न करें", + "unset-auto-assign-to-edge-title": "क्या आप वाकई बनाते समय Edge रूल चेन '{{ruleChainName}}' को Edge(s) पर असाइन नहीं करना चाहते हैं?", + "unset-auto-assign-to-edge-text": "पुष्टि के बाद Edge रूल चेन बनाते समय Edge(s) पर अपने-आप असाइन नहीं होगी।", + "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 वर्णों से कम होना चाहिए", + "type": "प्रकार", + "rule-node-description": "रूल नोड विवरण", + "delete": "रूल नोड हटाएँ", + "select-all-objects": "सभी नोड्स और कनेक्शन्स चुनें", + "deselect-all-objects": "सभी नोड्स और कनेक्शन्स को अनचेक करें", + "delete-selected-objects": "चयनित नोड्स और कनेक्शन्स हटाएँ", + "delete-selected": "चयनित हटाएँ", + "create-nested-rulechain": "नेस्टेड रूल चेन बनाएँ", + "select-all": "सभी चुनें", + "copy-selected": "चयनित कॉपी करें", + "deselect-all": "सभी को अनचेक करें", + "rulenode-details": "रूल नोड विवरण", + "debug-mode": "डिबग मोड", + "singleton": "सिंगलटन", + "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-flow": "फ़्लो", + "type-flow-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": "स्क्रिप्ट फ़ंक्शन टेस्ट करें", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "मैसेज", + "message-type": "मैसेज प्रकार", + "select-message-type": "मैसेज प्रकार चुनें", + "message-type-required": "मैसेज प्रकार आवश्यक है", + "metadata": "मेटाडेटा", + "metadata-required": "मेटाडेटा एंट्रीज़ खाली नहीं हो सकतीं.", + "output": "आउटपुट", + "test": "टेस्ट", + "help": "सहायता", + "reset-debug-settings": "सभी नोड्स में डिबग सेटिंग्स रीसेट करें", + "test-with-this-message": "{{test}} इस मैसेज के साथ", + "queue-hint": "मैसेज को दूसरी क्व्यू पर फॉरवर्ड करने के लिए क्व्यू चुनें. 'Main' क्व्यू डिफ़ॉल्ट रूप से उपयोग की जाती है.", + "queue-singleton-hint": "मल्टी-इंस्टेंस एनवायरनमेंट्स में मैसेज फॉरवर्डिंग के लिए क्व्यू चुनें. 'Main' क्व्यू डिफ़ॉल्ट रूप से उपयोग की जाती है." + }, + "rule-node-config": { + "id": "Id", + "additional-info": "अतिरिक्त जानकारी", + "advanced-settings": "एडवांस्ड सेटिंग्स", + "create-entity-if-not-exists": "यदि एंटिटी मौजूद नहीं है तो नई एंटिटी बनाएँ", + "create-entity-if-not-exists-hint": "यदि सक्षम है, तो दिए गए पैरामीटर्स के साथ नई एंटिटी बनाई जाएगी, यदि वह पहले से मौजूद नहीं है। मौजूद एंटिटीज़ को रिलेशन के लिए जैसे हैं वैसा ही उपयोग किया जाएगा.", + "select-device-connectivity-event": "डिवाइस कनेक्टिविटी इवेंट चुनें", + "entity-name-pattern": "नाम पैटर्न", + "device-name-pattern": "डिवाइस नाम", + "asset-name-pattern": "एसेट नाम", + "entity-view-name-pattern": "एंटिटी व्यू नाम", + "customer-title-pattern": "कस्टमर शीर्षक", + "dashboard-name-pattern": "डैशबोर्ड शीर्षक", + "user-name-pattern": "उपयोगकर्ता ईमेल", + "edge-name-pattern": "Edge नाम", + "entity-name-pattern-required": "नाम पैटर्न आवश्यक है", + "entity-name-pattern-hint": "नाम पैटर्न फ़ील्ड टेम्पलेटाइजेशन सपोर्ट करता है। वैल्यू को मैसेज से निकालने के लिए $[messageKey] और मेटाडेटा से निकालने के लिए ${metadataKey} का उपयोग करें.", + "copy-message-type": "मैसेज प्रकार कॉपी करें", + "entity-type-pattern": "टाइप पैटर्न", + "entity-type-pattern-required": "टाइप पैटर्न आवश्यक है", + "message-type-value": "मैसेज टाइप वैल्यू", + "message-type-value-required": "मैसेज टाइप वैल्यू आवश्यक है", + "message-type-value-max-length": "मैसेज टाइप वैल्यू 256 वर्णों से कम होनी चाहिए", + "output-message-type": "आउटपुट मैसेज प्रकार", + "entity-cache-expiration": "एंटिटीज़ कैश एक्सपिरेशन समय (sec)", + "entity-cache-expiration-hint": "पाई गई एंटिटी रिकॉर्ड्स को स्टोर करने के लिए अधिकतम समय अंतराल निर्दिष्ट करता है। 0 का मतलब है कि रिकॉर्ड्स कभी एक्सपायर नहीं होंगे.", + "entity-cache-expiration-required": "एंटिटीज़ कैश एक्सपिरेशन समय आवश्यक है.", + "entity-cache-expiration-range": "एंटिटीज़ कैश एक्सपिरेशन समय 0 से अधिक या बराबर होना चाहिए.", + "customer-name-pattern": "कस्टमर शीर्षक", + "customer-name-pattern-required": "कस्टमर शीर्षक आवश्यक है", + "customer-name-pattern-hint": "मैसेज से वैल्यू निकालने के लिए $[messageKey] और मेटाडेटा से वैल्यू निकालने के लिए ${metadataKey} का उपयोग करें.", + "create-customer-if-not-exists": "यदि कस्टमर मौजूद नहीं है तो नया कस्टमर बनाएँ", + "unassign-from-customer": "यदि ओरिजिनेटर डैशबोर्ड है तो विशेष कस्टमर से अनअसाइन करें", + "unassign-from-customer-tooltip": "एक समय में कई कस्टमर्स को असाइन किए जाने वाले केवल डैशबोर्ड्स ही होते हैं। \nयदि मैसेज ओरिजिनेटर डैशबोर्ड है, तो आपको जिस कस्टमर से अनअसाइन करना है, उसका शीर्षक स्पष्ट रूप से बताना होगा.", + "customer-cache-expiration": "कस्टमर्स कैश एक्सपिरेशन समय (sec)", + "customer-cache-expiration-hint": "पाए गए कस्टमर रिकॉर्ड्स को स्टोर करने के लिए अधिकतम समय अंतराल निर्दिष्ट करता है। 0 का मतलब है कि रिकॉर्ड्स कभी एक्सपायर नहीं होंगे.", + "customer-cache-expiration-required": "कस्टमर्स कैश एक्सपिरेशन समय आवश्यक है.", + "customer-cache-expiration-range": "कस्टमर्स कैश एक्सपिरेशन समय 0 से अधिक या बराबर होना चाहिए.", + "interval-start": "इंटरवल शुरू", + "interval-end": "इंटरवल समाप्त", + "time-unit": "समय इकाई", + "fetch-mode": "फेच मोड", + "order-by-timestamp": "टाइमस्टैम्प के अनुसार क्रमबद्ध करें", + "limit": "सीमा", + "limit-hint": "न्यूनतम सीमा 2 और अधिकतम 1000 है। यदि आप केवल एक एंट्री फेच करना चाहते हैं, तो फेच मोड में 'First' या 'Last' चुनें.", + "limit-required": "सीमा आवश्यक है.", + "limit-range": "सीमा 2 से 1000 के बीच होनी चाहिए.", + "time-unit-milliseconds": "मिलीसेकंड", + "time-unit-seconds": "सेकंड", + "time-unit-minutes": "मिनट", + "time-unit-hours": "घंटे", + "time-unit-days": "दिन", + "time-value-range": "अनुमत सीमा 1 से 2147483647 तक है.", + "start-interval-value-required": "इंटरवल शुरू आवश्यक है.", + "end-interval-value-required": "इंटरवल समाप्त आवश्यक है.", + "filter": "फ़िल्टर", + "switch": "स्विच", + "math-templatization-tooltip": "यह फ़ील्ड टेम्पलेटाइजेशन सपोर्ट करता है. मैसेज से वैल्यू निकालने के लिए $[messageKey] और मेटाडेटा से वैल्यू निकालने के लिए ${metadataKey} का उपयोग करें.", + "add-message-type": "मैसेज प्रकार जोड़ें", + "select-message-types-required": "कम से कम एक मैसेज प्रकार चयनित होना चाहिए.", + "select-message-types": "मैसेज प्रकार चुनें", + "no-message-types-found": "कोई मैसेज प्रकार नहीं मिला", + "no-message-type-matching": "'{{messageType}}' नहीं मिला.", + "create-new-message-type": "नया बनाएँ.", + "message-types-required": "मैसेज प्रकार आवश्यक हैं.", + "client-attributes": "क्लाइंट विशेषताएँ", + "shared-attributes": "शेयर्ड विशेषताएँ", + "server-attributes": "सर्वर विशेषताएँ", + "attributes-keys": "विशेषताओं की कुंजियाँ", + "attributes-keys-required": "विशेषताओं की कुंजियाँ आवश्यक हैं", + "attributes-scope": "विशेषताओं का स्कोप", + "attributes-scope-value": "विशेषताओं के स्कोप की वैल्यू", + "attributes-scope-value-copy": "विशेषताओं के स्कोप की वैल्यू कॉपी करें", + "attributes-scope-hint": "प्रति मैसेज विशेषता स्कोप को डायनामिक रूप से सेट करने के लिए 'scope' मेटाडेटा कुंजी का उपयोग करें. यदि दिया गया है, तो यह कॉन्फ़िगरेशन में सेट स्कोप को ओवरराइड करेगा.", + "notify-device": "डिवाइस को ज़बरदस्ती नोटिफिकेशन भेजें", + "send-attributes-updated-notification": "अपडेटेड विशेषताओं की अधिसूचना भेजें", + "send-attributes-updated-notification-hint": "अपडेटेड विशेषताओं के बारे में नोटिफिकेशन को अलग मैसेज के रूप में रूल इंजन क्व्यू में भेजें.", + "send-attributes-deleted-notification": "डिलीट की गई विशेषताओं की अधिसूचना भेजें", + "send-attributes-deleted-notification-hint": "डिलीट की गई विशेषताओं के बारे में नोटिफिकेशन को अलग मैसेज के रूप में रूल इंजन क्व्यू में भेजें.", + "update-attributes-only-on-value-change": "केवल तब विशेषताएँ सेव करें जब वैल्यू बदलती है", + "update-attributes-only-on-value-change-hint": "हर इनकमिंग मैसेज पर विशेषताओं को अपडेट करता है, चाहे उनकी वैल्यू बदली हो या नहीं. इससे API उपयोग बढ़ता है और प्रदर्शन कम होता है.", + "update-attributes-only-on-value-change-hint-enabled": "केवल तब विशेषताओं को अपडेट करता है जब उनकी वैल्यू बदलती है. यदि वैल्यू नहीं बदली है, तो न तो विशेषता टाइमस्टैम्प अपडेट होगा और न ही विशेषता परिवर्तन नोटिफिकेशन भेजा जाएगा.", + "fetch-credentials-to-metadata": "क्रेडेंशियल्स को मेटाडेटा में फेच करें", + "notify-device-on-update-hint": "यदि सक्षम है, तो शेयर्ड विशेषताओं के अपडेट के बारे में डिवाइस को ज़बरदस्ती नोटिफिकेशन भेजें. यदि अक्षम है, तो नोटिफिकेशन व्यवहार इनकमिंग मैसेज मेटाडेटा के 'notifyDevice' पैरामीटर से नियंत्रित होगा. नोटिफिकेशन बंद करने के लिए मैसेज मेटाडेटा में 'notifyDevice' पैरामीटर 'false' पर सेट होना चाहिए. किसी भी अन्य स्थिति में डिवाइस को नोटिफिकेशन भेजा जाएगा.", + "notify-device-on-delete-hint": "यदि सक्षम है, तो शेयर्ड विशेषताओं के हटाए जाने के बारे में डिवाइस को ज़बरदस्ती नोटिफिकेशन भेजें. यदि अक्षम है, तो नोटिफिकेशन व्यवहार इनकमिंग मैसेज मेटाडेटा के 'notifyDevice' पैरामीटर से नियंत्रित होगा. नोटिफिकेशन चालू करने के लिए मैसेज मेटाडेटा में 'notifyDevice' पैरामीटर 'true' पर सेट होना चाहिए. किसी भी अन्य स्थिति में डिवाइस को नोटिफिकेशन नहीं भेजा जाएगा.", + "latest-timeseries": "नवीनतम टाइम सीरीज़ डेटा कीज़", + "timeseries-keys": "टाइम सीरीज़ कीज़", + "timeseries-keys-required": "कम से कम एक टाइम सीरीज़ की चयनित होनी चाहिए.", + "add-timeseries-key": "टाइम सीरीज़ की जोड़ें", + "add-message-field": "मैसेज फ़ील्ड जोड़ें", + "relation-search-parameters": "रिलेशन सर्च पैरामीटर्स", + "relation-parameters": "रिलेशन पैरामीटर्स", + "add-metadata-field": "मेटाडेटा फ़ील्ड जोड़ें", + "data-keys": "मैसेज फ़ील्ड नाम", + "copy-from": "से कॉपी करें", + "data-to-metadata": "डेटा से मेटाडेटा", + "metadata-to-data": "मेटाडेटा से डेटा", + "use-regular-expression-hint": "कीज़ को पैटर्न के अनुसार कॉपी करने के लिए रेगुलर एक्सप्रेशन का उपयोग करें.\n\nटिप्स और ट्रिक्स:\nफ़ील्ड नाम इनपुट पूरा करने के लिए 'Enter' दबाएँ.\nफ़ील्ड नाम हटाने के लिए 'Backspace' दबाएँ. कई फ़ील्ड नाम सपोर्टेड हैं.", + "interval": "इंटरवल", + "interval-required": "इंटरवल आवश्यक है", + "interval-hint": "डीडुप्लिकेशन इंटरवल (सेकंड में).", + "interval-min-error": "न्यूनतम अनुमत वैल्यू 1 है", + "max-pending-msgs": "अधिकतम पेंडिंग मैसेजेस", + "max-pending-msgs-hint": "प्रत्येक यूनिक डीडुप्लिकेशन Id के लिए मेमोरी में स्टोर किए जाने वाले मैसेजेस की अधिकतम संख्या.", + "max-pending-msgs-required": "अधिकतम पेंडिंग मैसेजेस आवश्यक हैं", + "max-pending-msgs-max-error": "अधिकतम अनुमत वैल्यू 1000 है", + "max-pending-msgs-min-error": "न्यूनतम अनुमत वैल्यू 1 है", + "max-retries": "अधिकतम रीट्राइज़", + "max-retries-required": "अधिकतम रीट्राइज़ आवश्यक हैं", + "max-retries-hint": "डीडुप्लिकेट किए गए मैसेजेस को क्व्यू में पुश करने के लिए अधिकतम रीट्राइज़ की संख्या. रीट्राइज़ के बीच 10 सेकंड की देरी उपयोग की जाती है", + "max-retries-max-error": "अधिकतम अनुमत वैल्यू 100 है", + "max-retries-min-error": "न्यूनतम अनुमत वैल्यू 0 है", + "strategy": "रणनीति", + "strategy-required": "रणनीति आवश्यक है", + "strategy-all-hint": "डीडुप्लिकेशन अवधि के दौरान आए सभी मैसेजेस को एक सिंगल JSON ऐरे मैसेज के रूप में रिटर्न करें. जहाँ प्रत्येक एलिमेंट msg और metadata इनर प्रॉपर्टीज़ वाला ऑब्जेक्ट होता है.", + "strategy-first-hint": "डीडुप्लिकेशन अवधि के दौरान आया पहला मैसेज रिटर्न करें.", + "strategy-last-hint": "डीडुप्लिकेशन अवधि के दौरान आया आखिरी मैसेज रिटर्न करें.", + "first": "पहला", + "last": "आखिरी", + "all": "सभी", + "output-msg-type-hint": "डीडुप्लिकेशन परिणाम का मैसेज प्रकार.", + "queue-name-hint": "वह क्व्यू नाम जहाँ डीडुप्लिकेशन परिणाम पब्लिश किया जाएगा.", + "keys": "कुंजियाँ", + "keys-required": "कुंजियाँ आवश्यक हैं", + "rename-keys-in": "इन में कुंजियों का नाम बदलें", + "data": "डेटा", + "message": "मैसेज", + "metadata": "मेटाडेटा", + "current-key-name": "मौजूदा कुंजी नाम", + "key-name-required": "कुंजी नाम आवश्यक है", + "new-key-name": "नई कुंजी का नाम", + "new-key-name-required": "नई कुंजी का नाम आवश्यक है", + "metadata-keys": "मेटाडेटा फ़ील्ड नाम", + "json-path-expression": "JSON पाथ एक्सप्रेशन", + "json-path-expression-required": "JSON पाथ एक्सप्रेशन आवश्यक है", + "json-path-expression-hint": "JSONPath, JSON स्ट्रक्चर में किसी एलिमेंट या एलिमेंट्स के सेट तक पाथ को स्पेसिफाई करता है. '$' रूट ऑब्जेक्ट या ऐरे को दर्शाता है.", + "relations-query": "रिलेशन्स क्वेरी", + "device-relations-query": "डिवाइस रिलेशन्स क्वेरी", + "max-relation-level": "अधिकतम रिलेशन लेवल", + "max-relation-level-error": "वैल्यू 0 से अधिक या अनिर्दिष्ट होनी चाहिए.", + "max-relation-level-invalid": "वैल्यू पूर्णांक होनी चाहिए.", + "relation-type": "रिलेशन टाइप", + "relation-type-pattern": "रिलेशन टाइप पैटर्न", + "relation-type-pattern-required": "रिलेशन टाइप पैटर्न आवश्यक है", + "relation-types-list": "प्रोपेगेट करने के लिए रिलेशन टाइप्स", + "relation-types-list-hint": "यदि 'रिलेशन टाइप्स प्रोपेगेट करें' चयनित नहीं हैं, तो अलार्म्स को रिलेशन टाइप द्वारा फ़िल्टर किए बिना प्रोपेगेट किया जाएगा.", + "unlimited-level": "अनलिमिटेड लेवल", + "latest-telemetry": "नवीनतम टेलीमेट्री", + "add-telemetry-key": "टेलीमेट्री की जोड़ें", + "delete-from": "से डिलीट करें", + "use-regular-expression-delete-hint": "कीज़ को पैटर्न के अनुसार डिलीट करने के लिए रेगुलर एक्सप्रेशन का उपयोग करें.\n\nटिप्स और ट्रिक्स:\nफ़ील्ड नाम इनपुट पूरा करने के लिए 'Enter' दबाएँ.\nफ़ील्ड नाम डिलीट करने के लिए 'Backspace' दबाएँ.\nएक से अधिक फ़ील्ड नाम सपोर्टेड हैं.", + "fetch-into": "में फेच करें", + "attr-mapping": "विशेषताओं की मैपिंग:", + "source-attribute": "सोर्स एट्रिब्यूट की", + "source-attribute-required": "सोर्स एट्रिब्यूट की आवश्यक है.", + "source-telemetry": "सोर्स टेलीमेट्री की", + "source-telemetry-required": "सोर्स टेलीमेट्री की आवश्यक है.", + "target-key": "टारगेट की", + "target-key-required": "टारगेट की आवश्यक है.", + "attr-mapping-required": "कम से कम एक मैपिंग एंट्री निर्धारित होनी चाहिए.", + "fields-mapping": "फ़ील्ड्स मैपिंग", + "fields-mapping-hint": "यदि मैसेज फ़ील्ड को $entityId पर सेट किया जाता है, तो मैसेज ओरिजिनेटर की Id निर्दिष्ट टेबल कॉलम में सेव की जाएगी.", + "relations-query-config-direction-suffix": "ओरिजिनेटर", + "profile-name": "प्रोफ़ाइल नाम", + "fetch-circle-parameter-info-from-metadata-hint": "मेटाडेटा फ़ील्ड '{{perimeterKeyName}}' को निम्न फ़ॉर्मेट में परिभाषित किया जाना चाहिए: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "मेटाडेटा फ़ील्ड '{{perimeterKeyName}}' को निम्न फ़ॉर्मेट में परिभाषित किया जाना चाहिए: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "मैसेज से वैल्यू निकालने के लिए $[messageKey] और मेटाडेटा से वैल्यू निकालने के लिए ${metadataKey} का उपयोग करें.", + "fields-mapping-required": "कम से कम एक फ़ील्ड मैपिंग निर्धारित होनी चाहिए.", + "at-least-one-field-required": "कम से कम एक इनपुट फ़ील्ड के लिए वैल्यू प्रदान की जानी चाहिए.", + "originator-fields-sv-map-hint": "टारगेट की फ़ील्ड्स टेम्पलेटाइजेशन सपोर्ट करती हैं. मैसेज से वैल्यू निकालने के लिए $[messageKey] और मेटाडेटा से वैल्यू निकालने के लिए ${metadataKey} का उपयोग करें.", + "sv-map-hint": "केवल टारगेट की फ़ील्ड्स टेम्पलेटाइजेशन सपोर्ट करती हैं. मैसेज से वैल्यू निकालने के लिए $[messageKey] और मेटाडेटा से वैल्यू निकालने के लिए ${metadataKey} का उपयोग करें.", + "source-field": "सोर्स फ़ील्ड", + "source-field-required": "सोर्स फ़ील्ड आवश्यक है.", + "originator-source": "ओरिजिनेटर सोर्स", + "new-originator": "नया ओरिजिनेटर", + "originator-customer": "कस्टमर", + "originator-tenant": "टेनेंट", + "originator-related": "संबंधित एंटिटी", + "originator-alarm-originator": "अलार्म ओरिजिनेटर", + "originator-entity": "नाम पैटर्न द्वारा एंटिटी", + "clone-message": "मैसेज क्लोन करें", + "transform": "ट्रांसफ़ॉर्म", + "default-ttl": "डिफ़ॉल्ट TTL", + "default-ttl-required": "डिफ़ॉल्ट TTL आवश्यक है.", + "default-ttl-hint": "रूल नोड टाइम-टू-लाइव (TTL) वैल्यू मैसेज मेटाडेटा से फ़ेच करेगा. यदि कोई वैल्यू मौजूद नहीं है, तो यह कॉन्फ़िगरेशन में निर्दिष्ट TTL को उपयोग करेगा. यदि वैल्यू 0 सेट है, तो टेनेंट प्रोफ़ाइल कॉन्फ़िगरेशन से TTL लागू होगी.", + "default-ttl-zero-hint": "यदि वैल्यू 0 सेट है तो TTL लागू नहीं होगी.", + "min-default-ttl-message": "केवल 0 न्यूनतम TTL की अनुमति है.", + "generation-parameters": "जेनेरेशन पैरामीटर्स", + "message-count": "जनरेटेड मैसेजेस लिमिट (0 - अनलिमिटेड)", + "message-count-required": "जनरेटेड मैसेजेस लिमिट आवश्यक है.", + "min-message-count-message": "केवल 0 न्यूनतम मैसेज काउंट की अनुमति है.", + "period-seconds": "पीरियड (सेकंड में)", + "period-seconds-required": "पीरियड आवश्यक है.", + "generation-frequency-seconds": "जेनेरेशन फ़्रीक्वेंसी (सेकंड में)", + "generation-frequency-required": "जेनेरेशन फ़्रीक्वेंसी आवश्यक है.", + "min-generation-frequency-message": "कम-से-कम 60 सेकंड का न्यूनतम मान अनुमत है.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "सेकंड में अवधि के पैटर्न का उपयोग करें", + "use-metadata-period-in-seconds-patterns-hint": "यदि चुना गया, तो रूल नोड मैसेज मेटाडेटा या डेटा से सेकंड में इंटरवल पैटर्न का उपयोग करेगा, यह मानते हुए कि इंटरवल सेकंड में हैं.", + "period-in-seconds-pattern": "सेकंड में अवधि का पैटर्न", + "period-in-seconds-pattern-required": "सेकंड में अवधि का पैटर्न आवश्यक है", + "min-period-seconds-message": "केवल 60 सेकंड की न्यूनतम अवधि की अनुमति है.", + "originator": "ओरिजिनेटर", + "message-body": "मैसेज बॉडी", + "message-metadata": "मैसेज मेटाडेटा", + "generate": "जनरेट करें", + "current-rule-node": "वर्तमान रूल नोड", + "current-tenant": "वर्तमान टेनेंट", + "generator-function": "जेनेरेटर फ़ंक्शन", + "test-generator-function": "जेनेरेटर फ़ंक्शन टेस्ट करें", + "generator": "जेनेरेटर", + "test-filter-function": "फ़िल्टर फ़ंक्शन टेस्ट करें", + "test-switch-function": "स्विच फ़ंक्शन टेस्ट करें", + "test-transformer-function": "ट्रांसफ़ॉर्मर फ़ंक्शन टेस्ट करें", + "transformer": "ट्रांसफ़ॉर्मर", + "alarm-create-condition": "अलार्म क्रिएट कंडीशन", + "test-condition-function": "कंडीशन फ़ंक्शन टेस्ट करें", + "alarm-clear-condition": "अलार्म क्लियर कंडीशन", + "alarm-details-builder": "अलार्म डीटेल्स बिल्डर", + "test-details-function": "डीटेल्स फ़ंक्शन टेस्ट करें", + "alarm-type": "अलार्म टाइप", + "select-entity-types": "एंटिटी टाइप्स चुनें", + "alarm-type-required": "अलार्म टाइप आवश्यक है.", + "alarm-severity": "अलार्म की गंभीरता", + "alarm-severity-required": "अलार्म की गंभीरता आवश्यक है", + "alarm-severity-pattern": "अलार्म गंभीरता पैटर्न", + "alarm-status-filter": "अलार्म स्टेटस फ़िल्टर", + "alarm-status-list-empty": "अलार्म स्टेटस सूची खाली है", + "no-alarm-status-matching": "कोई उपयुक्त अलार्म स्टेटस नहीं मिला.", + "propagate": "अलार्म को संबंधित एंटिटीज़ तक प्रोपेगेट करें", + "propagate-to-owner": "अलार्म को एंटिटी ओनर (कस्टमर या टेनेंट) तक प्रोपेगेट करें", + "propagate-to-tenant": "अलार्म को टेनेंट तक प्रोपेगेट करें", + "condition": "शर्त", + "details": "विवरण", + "to-string": "स्ट्रिंग में बदलें", + "test-to-string-function": "To string फ़ंक्शन टेस्ट करें", + "from-template": "प्रेषक", + "from-template-required": "प्रेषक आवश्यक है", + "message-to-metadata": "मैसेज से मेटाडेटा", + "metadata-to-message": "मेटाडेटा से मैसेज", + "from-message": "मैसेज से", + "from-metadata": "मेटाडेटा से", + "to-template": "प्राप्तकर्ता", + "to-template-required": "To टेम्पलेट आवश्यक है", + "mail-address-list-template-hint": "कॉमा सेपरेटेड एड्रेस सूची, मेटाडेटा से वैल्यू के लिए ${metadataKey} और मैसेज बॉडी से वैल्यू के लिए $[messageKey] का उपयोग करें", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "सब्जेक्ट", + "subject-template-required": "सब्जेक्ट टेम्पलेट आवश्यक है", + "body-template": "बॉडी", + "body-template-required": "बॉडी टेम्पलेट आवश्यक है", + "dynamic-mail-body-type": "डायनामिक मेल बॉडी टाइप", + "mail-body-type": "मेल बॉडी टाइप", + "body-type-template": "बॉडी टाइप टेम्पलेट", + "reply-routing-configuration": "रिप्लाई रूटिंग कॉन्फ़िगरेशन", + "rpc-reply-routing-configuration-hint": "ये कॉन्फ़िगरेशन पैरामीटर्स सर्विस, सेशन और रिक्वेस्ट की पहचान करने के लिए मेटाडेटा की के नाम निर्दिष्ट करते हैं, ताकि रिप्लाई वापस भेजी जा सके.", + "reply-routing-configuration-hint": "ये कॉन्फ़िगरेशन पैरामीटर्स सर्विस और रिक्वेस्ट की पहचान करने के लिए मेटाडेटा की के नाम निर्दिष्ट करते हैं, ताकि रिप्लाई वापस भेजी जा सके.", + "request-id-metadata-attribute": "रिक्वेस्ट Id", + "service-id-metadata-attribute": "सर्विस Id", + "session-id-metadata-attribute": "सेशन Id", + "timeout-sec": "टाइमआउट (सेकंड में)", + "timeout-required": "टाइमआउट आवश्यक है", + "min-timeout-message": "केवल 0 न्यूनतम टाइमआउट मान अनुमत है.", + "endpoint-url-pattern": "एंडपॉइंट URL पैटर्न", + "endpoint-url-pattern-required": "एंडपॉइंट URL पैटर्न आवश्यक है", + "request-method": "रिक्वेस्ट मेथड", + "use-simple-client-http-factory": "सिंपल क्लाइंट HTTP फ़ैक्टरी का उपयोग करें", + "ignore-request-body": "रिक्वेस्ट बॉडी के बिना", + "parse-to-plain-text": "प्लेन टेक्स्ट में पार्स करें", + "parse-to-plain-text-hint": "यदि चुना गया, तो रिक्वेस्ट बॉडी मैसेज पेलोड JSON स्ट्रिंग से प्लेन टेक्स्ट में बदल दिया जाएगा, उदाहरण: msg = \"नमस्ते,\\t\"दुनिया\"\" को पार्स करके नमस्ते, \"दुनिया\" बनाया जाएगा", + "read-timeout": "रीड टाइमआउट (मिलीसेकंड में)", + "read-timeout-hint": "मान 0 का अर्थ है अनलिमिटेड टाइमआउट", + "max-parallel-requests-count": "पैरलल रिक्वेस्ट्स की अधिकतम संख्या", + "max-parallel-requests-count-hint": "मान 0 का अर्थ है पैरलल प्रोसेसिंग पर कोई सीमा नहीं", + "max-response-size": "मैक्स रिस्पॉन्स साइज (KB में)", + "max-response-size-hint": "HTTP मैसेजेस (जैसे JSON या XML पेलोड्स) को डिकोड या एनकोड करते समय डेटा बफ़र करने के लिए आवंटित मेमोरी की अधिकतम मात्रा", + "headers": "हेडर्स", + "headers-hint": "मेटाडेटा से वैल्यू के लिए ${metadataKey} और मैसेज बॉडी से वैल्यू के लिए $[messageKey] का उपयोग हेडर/वैल्यू फ़ील्ड्स में करें", + "header": "हेडर", + "header-required": "हेडर आवश्यक है", + "value": "वैल्यू", + "value-required": "वैल्यू आवश्यक है", + "topic-pattern": "टॉपिक पैटर्न", + "key-pattern": "की पैटर्न", + "key-pattern-hint": "वैकल्पिक. यदि एक वैध पार्टिशन नंबर दिया गया है, तो रिकॉर्ड भेजते समय वही उपयोग होगा. यदि कोई पार्टिशन निर्दिष्ट नहीं है, तो की का उपयोग किया जाएगा. यदि दोनों में से कोई भी निर्दिष्ट नहीं है, तो पार्टिशन राउंड-रॉबिन तरीके से असाइन किया जाएगा.", + "topic-pattern-required": "टॉपिक पैटर्न आवश्यक है", + "topic": "टॉपिक", + "topic-required": "टॉपिक आवश्यक है", + "bootstrap-servers": "बूटस्ट्रैप सर्वर्स", + "bootstrap-servers-required": "बूटस्ट्रैप सर्वर्स का मान आवश्यक है", + "other-properties": "अन्य प्रॉपर्टीज़", + "key": "की", + "key-required": "की आवश्यक है", + "retries": "फेल होने पर ऑटोमैटिक रीट्राई की संख्या", + "min-retries-message": "केवल 0 न्यूनतम रीट्राई की अनुमति है.", + "batch-size-bytes": "प्रोड्यूस बैच साइज (बाइट्स में)", + "min-batch-size-bytes-message": "केवल 0 न्यूनतम बैच साइज की अनुमति है.", + "linger-ms": "लोकल बफ़र करने का समय (ms)", + "min-linger-ms-message": "केवल 0 ms न्यूनतम मान की अनुमति है.", + "buffer-memory-bytes": "क्लाइंट बफ़र का अधिकतम आकार (बाइट्स में)", + "min-buffer-memory-message": "केवल 0 न्यूनतम बफ़र आकार की अनुमति है.", + "memory-buffer-size-range": "मेमोरी बफ़र आकार 0 और {{max}} KB के बीच होना चाहिए", + "acks": "पुष्टिकरणों की संख्या", + "topic-arn-pattern": "टॉपिक ARN पैटर्न", + "topic-arn-pattern-required": "टॉपिक ARN पैटर्न आवश्यक है", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID आवश्यक है", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key आवश्यक है", + "aws-region": "AWS Region", + "aws-region-required": "AWS Region आवश्यक है", + "exchange-name-pattern": "एक्सचेंज नाम पैटर्न", + "routing-key-pattern": "रूटिंग की पैटर्न", + "message-properties": "संदेश प्रॉपर्टीज़", + "host": "होस्ट", + "host-required": "होस्ट आवश्यक है", + "port": "पोर्ट", + "port-required": "पोर्ट आवश्यक है", + "port-range": "पोर्ट 1 से 65535 की रेंज में होना चाहिए.", + "virtual-host": "वर्चुअल होस्ट", + "username": "यूज़रनेम", + "password": "पासवर्ड", + "automatic-recovery": "ऑटोमैटिक रिकवरी", + "connection-timeout-ms": "कनेक्शन टाइमआउट (ms)", + "min-connection-timeout-ms-message": "केवल 0 ms न्यूनतम मान की अनुमति है.", + "handshake-timeout-ms": "हैंडशेक टाइमआउट (ms)", + "min-handshake-timeout-ms-message": "केवल 0 ms न्यूनतम मान की अनुमति है.", + "client-properties": "क्लाइंट प्रॉपर्टीज़", + "queue-url-pattern": "क्यू URL पैटर्न", + "queue-url-pattern-required": "क्यू URL पैटर्न आवश्यक है", + "delay-seconds": "डिले (सेकंड)", + "min-delay-seconds-message": "केवल 0 सेकंड न्यूनतम मान की अनुमति है.", + "max-delay-seconds-message": "केवल 900 सेकंड अधिकतम मान की अनुमति है.", + "name": "नाम", + "name-required": "नाम आवश्यक है", + "queue-type": "क्यू टाइप", + "sqs-queue-standard": "स्टैंडर्ड", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP प्रोजेक्ट ID", + "gcp-project-id-required": "GCP प्रोजेक्ट ID आवश्यक है", + "gcp-service-account-key": "GCP सर्विस अकाउंट की फ़ाइल", + "gcp-service-account-key-required": "GCP सर्विस अकाउंट की फ़ाइल आवश्यक है", + "pubsub-topic-name": "टॉपिक नाम", + "pubsub-topic-name-required": "टॉपिक नाम आवश्यक है", + "message-attributes": "संदेश विशेषताएँ", + "message-attributes-hint": "मेटाडेटा से मान के लिए ${metadataKey} का उपयोग करें, name/value फ़ील्ड्स में मैसेज बॉडी से मान के लिए $[messageKey] का उपयोग करें", + "connect-timeout": "कनेक्शन टाइमआउट (सेकंड)", + "connect-timeout-required": "कनेक्शन टाइमआउट आवश्यक है.", + "connect-timeout-range": "कनेक्शन टाइमआउट 1 से 200 की रेंज में होना चाहिए.", + "client-id": "क्लाइंट ID", + "client-id-hint": "वैकल्पिक. ऑटो-जनरेटेड क्लाइंट ID के लिए इसे खाली छोड़ें. क्लाइंट ID निर्दिष्ट करते समय सावधान रहें. अधिकांश MQTT ब्रोकर्स समान क्लाइंट ID के साथ कई कनेक्शनों की अनुमति नहीं देंगे. ऐसे ब्रोकर्स से कनेक्ट होने के लिए आपका MQTT क्लाइंट ID यूनिक होना चाहिए. जब प्लेटफ़ॉर्म माइक्रो-सर्विस मोड में चल रहा होता है, तो प्रत्येक माइक्रो-सर्विस में रूल नोड की कॉपी लॉन्च होती है. यह अपने-आप ही समान ID वाले कई MQTT क्लाइंट्स की ओर ले जाता है और रूल नोड की विफलता का कारण बन सकता है. ऐसी विफलताओं से बचने के लिए नीचे दिए गए \"क्लाइंट ID के साथ सर्विस ID को उपसर्ग के रूप में जोड़ें\" विकल्प को सक्षम करें.", + "append-client-id-suffix": "क्लाइंट ID के साथ सर्विस ID को उपसर्ग के रूप में जोड़ें", + "client-id-suffix-hint": "वैकल्पिक. तब लागू होता है जब \"Client ID\" स्पष्ट रूप से निर्दिष्ट हो. अगर चुना गया, तो Service ID को क्लाइंट ID के साथ उपसर्ग के रूप में जोड़ा जाएगा. यह तब विफलताओं से बचने में मदद करता है जब प्लेटफ़ॉर्म माइक्रो-सर्विस मोड में चल रहा हो.", + "device-id": "डिवाइस ID", + "device-id-required": "डिवाइस ID आवश्यक है.", + "clean-session": "क्लीन सेशन", + "enable-ssl": "SSL सक्षम करें", + "credentials": "क्रेडेंशियल्स", + "credentials-type": "क्रेडेंशियल्स प्रकार", + "credentials-type-required": "क्रेडेंशियल्स प्रकार आवश्यक है.", + "credentials-anonymous": "Anonymous", + "credentials-basic": "Basic", + "credentials-pem": "PEM", + "credentials-pem-hint": "कम-से-कम सर्वर CA सर्टिफ़िकेट फ़ाइल या क्लाइंट सर्टिफ़िकेट और क्लाइंट प्राइवेट की फ़ाइलों की जोड़ी आवश्यक है", + "credentials-sas": "Shared Access Signature", + "sas-key": "SAS Key", + "sas-key-required": "SAS Key आवश्यक है.", + "hostname": "होस्टनेम", + "hostname-required": "होस्टनेम आवश्यक है.", + "azure-ca-cert": "CA सर्टिफ़िकेट फ़ाइल", + "username-required": "यूज़रनेम आवश्यक है.", + "password-required": "पासवर्ड आवश्यक है.", + "ca-cert": "सर्वर CA सर्टिफ़िकेट फ़ाइल", + "private-key": "क्लाइंट प्राइवेट की फ़ाइल", + "cert": "क्लाइंट सर्टिफ़िकेट फ़ाइल", + "no-file": "कोई फ़ाइल चयनित नहीं.", + "drop-file": "फ़ाइल को ड्रॉप करें या अपलोड करने के लिए फ़ाइल चुनने हेतु क्लिक करें.", + "private-key-password": "प्राइवेट की पासवर्ड", + "use-system-smtp-settings": "सिस्टम SMTP सेटिंग्स का उपयोग करें", + "use-metadata-dynamic-interval": "डायनेमिक इंटरवल का उपयोग करें", + "metadata-dynamic-interval-hint": "इंटरवल प्रारंभ और समाप्ति इनपुट फ़ील्ड्स टेम्पलेटाइज़ेशन को सपोर्ट करते हैं. ध्यान दें कि सब्स्टिट्यूट किया गया टेम्पलेट मान मिलीसेकंड्स में सेट होना चाहिए. मैसेज से मान निकालने के लिए $[messageKey] और मेटाडेटा से मान निकालने के लिए ${metadataKey} का उपयोग करें.", + "use-metadata-interval-patterns-hint": "अगर चुना गया, तो रूल नोड मैसेज मेटाडेटा या डेटा से प्रारंभ और समाप्ति इंटरवल पैटर्न का उपयोग करेगा, यह मानते हुए कि इंटरवल्स मिलीसेकंड्स में हैं.", + "use-message-alarm-data": "मैसेज अलार्म डेटा का उपयोग करें", + "overwrite-alarm-details": "अलार्म विवरण को ओवरराइट करें", + "use-alarm-severity-pattern": "अलार्म सीवियरिटी /पैटर्न का उपयोग करें", + "check-all-keys": "जाँचें कि सभी निर्दिष्ट फ़ील्ड मौजूद हैं", + "check-all-keys-hint": "यदि चुना गया, तो यह जाँचता है कि संदेश डेटा और मेटाडेटा में सभी निर्दिष्ट कुंजियाँ मौजूद हैं।", + "check-relation-to-specific-entity": "विशिष्ट एंटिटी के साथ संबंध की जाँच करें", + "check-relation-to-specific-entity-tooltip": "यदि सक्षम किया गया, तो यह किसी विशिष्ट एंटिटी के साथ संबंध की उपस्थिति की जाँच करता है, अन्यथा किसी भी एंटिटी के साथ संबंध की उपस्थिति की जाँच करता है। दोनों ही मामलों में, संबंध खोज कॉन्फ़िगर की गई दिशा और प्रकार पर आधारित होती है।", + "check-relation-hint": "दिशा और संबंध प्रकार के आधार पर, किसी विशिष्ट एंटिटी या किसी भी एंटिटी के साथ संबंध के अस्तित्व की जाँच करता है।", + "delete-relation-with-specific-entity": "विशिष्ट एंटिटी के साथ संबंध हटाएँ", + "delete-relation-with-specific-entity-hint": "यदि सक्षम किया गया, तो केवल एक विशिष्ट एंटिटी के साथ संबंध हटाया जाएगा। अन्यथा, संबंध सभी मिलती-जुलती एंटिटीज़ के साथ हटाया जाएगा।", + "delete-relation-hint": "निर्धारित दिशा और प्रकार के आधार पर, आने वाले संदेश के ओरिजिनेटर से निर्दिष्ट एंटिटी या एंटिटीज़ की सूची तक संबंध हटाता है।", + "remove-current-relations": "वर्तमान संबंध हटाएँ", + "remove-current-relations-hint": "दिशा और प्रकार के आधार पर, आने वाले संदेश के ओरिजिनेटर से वर्तमान संबंध हटाता है।", + "change-originator-to-related-entity": "ओरिजिनेटर को संबंधित एंटिटी में बदलें", + "change-originator-to-related-entity-hint": "सबमिट किए गए संदेश को किसी दूसरी एंटिटी से आए संदेश के रूप में प्रोसेस करने के लिए उपयोग किया जाता है।", + "start-interval": "इंटरवल प्रारंभ", + "end-interval": "इंटरवल समाप्ति", + "start-interval-required": "इंटरवल प्रारंभ आवश्यक है।", + "end-interval-required": "इंटरवल समाप्ति आवश्यक है।", + "smtp-protocol": "प्रोटोकॉल", + "smtp-host": "SMTP होस्ट", + "smtp-host-required": "SMTP होस्ट आवश्यक है।", + "smtp-port": "SMTP पोर्ट", + "smtp-port-required": "आपको SMTP पोर्ट प्रदान करना होगा।", + "smtp-port-range": "SMTP पोर्ट 1 से 65535 की रेंज में होना चाहिए।", + "timeout-msec": "टाइमआउट (ms)", + "min-timeout-msec-message": "केवल 0 ms न्यूनतम मान की अनुमति है।", + "enter-username": "यूज़रनेम दर्ज करें", + "enter-password": "पासवर्ड दर्ज करें", + "enable-tls": "TLS सक्षम करें", + "tls-version": "TLS संस्करण", + "enable-proxy": "प्रॉक्सी सक्षम करें", + "use-system-proxy-properties": "सिस्टम प्रॉक्सी गुणों का उपयोग करें", + "proxy-host": "प्रॉक्सी होस्ट", + "proxy-host-required": "प्रॉक्सी होस्ट आवश्यक है।", + "proxy-port": "प्रॉक्सी पोर्ट", + "proxy-port-required": "प्रॉक्सी पोर्ट आवश्यक है।", + "proxy-port-range": "प्रॉक्सी पोर्ट 1 से 65535 की रेंज में होना चाहिए।", + "proxy-user": "प्रॉक्सी उपयोगकर्ता", + "proxy-password": "प्रॉक्सी पासवर्ड", + "proxy-scheme": "प्रॉक्सी स्कीम", + "numbers-to-template": "फ़ोन नंबर 'To' टेम्पलेट", + "numbers-to-template-required": "फ़ोन नंबर 'To' टेम्पलेट आवश्यक है।", + "numbers-to-template-hint": "कॉमा से अलग किए गए फ़ोन नंबर, मेटाडेटा से मान लेने के लिए ${metadataKey} का उपयोग करें, और मैसेज बॉडी से मान लेने के लिए $[messageKey] का उपयोग करें", + "sms-message-template": "SMS मैसेज टेम्पलेट", + "sms-message-template-required": "SMS मैसेज टेम्पलेट आवश्यक है।", + "use-system-sms-settings": "सिस्टम SMS प्रोवाइडर सेटिंग्स का उपयोग करें", + "min-period-0-seconds-message": "केवल 0 सेकंड का न्यूनतम पीरियड अनुमत है।", + "max-pending-messages": "अधिकतम पेंडिंग मैसेज", + "max-pending-messages-required": "अधिकतम पेंडिंग मैसेज आवश्यक है।", + "max-pending-messages-range": "अधिकतम पेंडिंग मैसेज 1 से 100000 के बीच होना चाहिए।", + "originator-types-filter": "ओरिजिनेटर टाइप फ़िल्टर", + "interval-seconds": "इंटरवल (सेकंड में)", + "interval-seconds-required": "इंटरवल आवश्यक है।", + "int-range": "मान अधिकतम पूर्णांक सीमा (2147483648) से अधिक नहीं होना चाहिए।", + "min-interval-seconds-message": "केवल 1 सेकंड का न्यूनतम इंटरवल अनुमत है।", + "output-timeseries-key-prefix": "आउटपुट टाइम सीरीज़ की प्रीफ़िक्स", + "output-timeseries-key-prefix-required": "आउटपुट टाइम सीरीज़ की प्रीफ़िक्स आवश्यक है।", + "separator-hint": "फ़ील्ड इनपुट पूरा करने के लिए आपको \"एंटर\" दबाना होगा।", + "select-details": "विवरण चुनें", + "entity-details-id": "आईडी", + "entity-details-title": "टाइटल", + "entity-details-country": "देश", + "entity-details-state": "राज्य", + "entity-details-city": "शहर", + "entity-details-zip": "ज़िप", + "entity-details-address": "पता", + "entity-details-address2": "पता 2", + "entity-details-additional_info": "अतिरिक्त जानकारी", + "entity-details-phone": "फ़ोन", + "entity-details-email": "ईमेल", + "email-sender": "ईमेल प्रेषक", + "fields-to-check": "जाँचने वाले फ़ील्ड्स", + "add-detail": "डिटेल जोड़ें", + "check-all-keys-tooltip": "यदि सक्षम है, तो इनकमिंग संदेश और उसकी मेटाडाटा में दिए गए संदेश और मेटाडाटा फ़ील्ड-नामों की सूची में शामिल सभी फ़ील्ड्स की मौजूदगी की जाँच करता है।", + "fields-to-check-hint": "फ़ील्ड नाम इनपुट पूरा करने के लिए \"एंटर\" दबाएँ। एक से अधिक फ़ील्ड नाम समर्थित हैं।", + "entity-details-list-empty": "कम से कम एक डिटेल चुनी जानी चाहिए।", + "alarm-status": "अलार्म स्टेटस", + "alarm-required": "कम से कम एक अलार्म स्टेटस चुना जाना चाहिए।", + "no-entity-details-matching": "कोई मिलते-जुलते एंटिटी विवरण नहीं मिले।", + "custom-table-name": "कस्टम टेबल नाम", + "custom-table-name-required": "टेबल नाम आवश्यक है।", + "custom-table-hint": "टेबल आपके Cassandra क्लस्टर में बनाई जानी चाहिए और उसका नाम 'cs_tb_' प्रीफ़िक्स से शुरू होना चाहिए ताकि डेटा कॉमन TB टेबल्स में न जाए। यहाँ टेबल नाम 'cs_tb_' प्रीफ़िक्स के बिना दर्ज करें।", + "message-field": "मैसेज फ़ील्ड", + "message-field-required": "मैसेज फ़ील्ड आवश्यक है।", + "table-col": "टेबल कॉलम", + "table-col-required": "टेबल कॉलम आवश्यक है।", + "latitude-field-name": "लैटिट्यूड फ़ील्ड नाम", + "longitude-field-name": "लॉन्गिट्यूड फ़ील्ड नाम", + "latitude-field-name-required": "लैटिट्यूड फ़ील्ड नाम आवश्यक है।", + "longitude-field-name-required": "लॉन्गिट्यूड फ़ील्ड नाम आवश्यक है।", + "fetch-perimeter-info-from-metadata": "मेटाडेटा से परिमीटर की जानकारी प्राप्त करें", + "fetch-perimeter-info-from-metadata-tooltip": "यदि परिमीटर प्रकार 'पॉलीगॉन' पर सेट है, तो मेटाडेटा फ़ील्ड '{{perimeterKeyName}}' का मान बिना अतिरिक्त पार्सिंग के परिमीटर डेफ़िनिशन के रूप में उपयोग किया जाएगा। अन्यथा, यदि परिमीटर प्रकार 'सर्कल' पर सेट है, तो मेटाडेटा फ़ील्ड '{{perimeterKeyName}}' के मान को पार्स करके 'latitude', 'longitude', 'radius', 'radiusUnit' फ़ील्ड निकाले जाएँगे, जो सर्कल परिमीटर डेफ़िनिशन के लिए उपयोग होते हैं।", + "perimeter-key-name": "परिमीटर की कुंजी का नाम", + "perimeter-key-name-hint": "मेटाडेटा फ़ील्ड नाम जिसमें परिमीटर की जानकारी होती है।", + "perimeter-key-name-required": "परिमीटर की कुंजी का नाम आवश्यक है।", + "perimeter-circle": "सर्कल", + "perimeter-polygon": "पॉलीगॉन", + "perimeter-type": "परिमीटर प्रकार", + "circle-center-latitude": "केंद्र का लैटिट्यूड", + "circle-center-latitude-required": "केंद्र का लैटिट्यूड आवश्यक है।", + "circle-center-longitude": "केंद्र का लॉन्गिट्यूड", + "circle-center-longitude-required": "केंद्र का लॉन्गिट्यूड आवश्यक है।", + "range-unit-meter": "मीटर", + "range-unit-kilometer": "किलोमीटर", + "range-unit-foot": "फ़ुट", + "range-unit-mile": "माइल", + "range-unit-nautical-mile": "समुद्री माइल", + "range-units": "रेंज इकाइयाँ", + "range-units-required": "रेंज इकाइयाँ आवश्यक हैं।", + "range": "रेंज", + "range-required": "रेंज आवश्यक है।", + "polygon-definition": "पॉलीगॉन डेफ़िनिशन", + "polygon-definition-required": "पॉलीगॉन डेफ़िनिशन आवश्यक है।", + "polygon-definition-hint": "पॉलीगॉन को मैन्युअली परिभाषित करने के लिए निम्न फ़ॉर्मेट का उपयोग करें: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "न्यूनतम भीतर रहने की अवधि", + "min-inside-duration-value-required": "न्यूनतम भीतर रहने की अवधि आवश्यक है।", + "min-inside-duration-time-unit": "न्यूनतम भीतर रहने की अवधि की समय इकाई", + "min-outside-duration": "न्यूनतम बाहर रहने की अवधि", + "min-outside-duration-value-required": "न्यूनतम बाहर रहने की अवधि आवश्यक है।", + "min-outside-duration-time-unit": "न्यूनतम बाहर रहने की अवधि की समय इकाई", + "tell-failure-if-absent": "असफलता बताएँ", + "tell-failure-if-absent-hint": "यदि चुनी गई कुंजियों में से कम से कम एक भी मौजूद नहीं है, तो आउटबाउंड संदेश \"असफलता\" रिपोर्ट करेगा।", + "get-latest-value-with-ts": "लेटेस्ट टेलीमेट्री वैल्यूज़ के लिए टाइमस्टैम्प प्राप्त करें", + "get-latest-value-with-ts-hint": "यदि चयनित है, तो लेटेस्ट टेलीमेट्री वैल्यूज़ में टाइमस्टैम्प भी शामिल होगा, उदाहरण के लिए: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "नल (null) स्ट्रिंग्स को नज़रअंदाज़ करें", + "ignore-null-strings-hint": "यदि चयनित है, तो रूल नोड खाली मान वाले एंटिटी फ़ील्ड्स को नज़रअंदाज़ करेगा।", + "add-metadata-key-values-as-kafka-headers": "मैसेज मेटाडेटा की की-वैल्यू पेयर्स को Kafka रिकॉर्ड हेडर्स में जोड़ें", + "add-metadata-key-values-as-kafka-headers-hint": "यदि चयनित है, तो मैसेज मेटाडेटा से की-वैल्यू पेयर्स को आउटगोइंग रिकॉर्ड्स के हेडर्स में प्री-डिफ़ाइंड कैरेक्टर सेट एनकोडिंग के साथ बाइट ऐरेज़ के रूप में जोड़ा जाएगा।", + "charset-encoding": "कैरेक्टर सेट एनकोडिंग", + "charset-encoding-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": "क्यू का नाम ड्रॉप-डाउन सूची से चुना जा सकता है या आप कस्टम नाम जोड़ सकते हैं।", + "device-profile-node-hint": "यदि आपके पास अवधि या दोहराई जाने वाली कंडीशन हैं, तो अलार्म स्टेट के मूल्यांकन की निरंतरता सुनिश्चित करने के लिए यह उपयोगी है।", + "persist-alarm-rules": "अलार्म रूल्स की स्थिति को सुरक्षित रखें", + "persist-alarm-rules-hint": "यदि सक्षम है, तो रूल नोड प्रोसेसिंग की स्थिति को डेटाबेस में सहेजेगा।", + "fetch-alarm-rules": "अलार्म रूल्स की स्थिति प्राप्त करें", + "fetch-alarm-rules-hint": "यदि सक्षम है, तो रूल नोड इनिशियलाइज़ेशन पर प्रोसेसिंग की स्थिति को पुनर्स्थापित करेगा और यह सुनिश्चित करेगा कि सर्वर रीस्टार्ट के बाद भी अलार्म उठें। अन्यथा, स्थिति पहली बार डिवाइस से मैसेज आने पर पुनर्स्थापित होगी।", + "input-value-key": "इनपुट वैल्यू की", + "input-value-key-required": "इनपुट वैल्यू की आवश्यक है।", + "output-value-key": "आउटपुट वैल्यू की", + "output-value-key-required": "आउटपुट वैल्यू की आवश्यक है।", + "number-of-digits-after-floating-point": "दशमलव बिंदु के बाद अंकों की संख्या", + "number-of-digits-after-floating-point-range": "दशमलव बिंदु के बाद अंकों की संख्या 0 से 15 के बीच होनी चाहिए।", + "failure-if-delta-negative": "यदि डेल्टा निगेटिव हो तो असफलता बताएँ", + "failure-if-delta-negative-tooltip": "यदि डेल्टा मान निगेटिव है, तो रूल नोड मैसेज प्रोसेसिंग को असफल कर देगा।", + "use-caching": "कैशिंग का उपयोग करें", + "use-caching-tooltip": "परफॉर्मेंस सुधारने के लिए रूल नोड इनकमिंग मैसेज से आने वाली \"{{inputValueKey}}\" वैल्यू को कैश करेगा। ध्यान दें, यदि आप कहीं और \"{{inputValueKey}}\" वैल्यू बदलते हैं तो कैश अपडेट नहीं होगा।", + "add-time-difference-between-readings": "\"{{inputValueKey}}\" रीडिंग्स के बीच समयांतर जोड़ें", + "add-time-difference-between-readings-tooltip": "यदि सक्षम है, तो रूल नोड आउटबाउंड मैसेज में \"{{periodValueKey}}\" जोड़ देगा।", + "period-value-key": "पीरियड वैल्यू की", + "period-value-key-required": "पीरियड वैल्यू की आवश्यक है।", + "general-pattern-hint": "मेटाडेटा से मान के लिए ${metadataKey} और मैसेज बॉडी से मान के लिए $[messageKey] का उपयोग करें।", + "alarm-severity-pattern-hint": "मेटाडेटा से मान लेने के लिए ${metadataKey} और मैसेज बॉडी से मान लेने के लिए $[messageKey] का उपयोग करें। अलार्म की गंभीरता सिस्टम वैल्यू होनी चाहिए (CRITICAL, MAJOR आदि)।", + "output-node-name-hint": "रूल नोड नाम आउटपुट मैसेज के रिलेशन प्रकार के अनुरूप होता है और कॉलर रूल चेन में मैसेज को अन्य रूल नोड्स तक फॉरवर्ड करने के लिए उपयोग किया जाता है।", + "use-server-ts": "सर्वर टाइमस्टैम्प का उपयोग करें", + "use-server-ts-hint": "ऐसे टाइम-सीरीज़ डेटा के लिए जो स्पष्ट टाइमस्टैम्प नहीं भेजते, सर्वर का वर्तमान टाइमस्टैम्प उपयोग करें। यह कई स्रोतों से आने वाले या आउट-ऑफ-ऑर्डर मैसेजेस प्रोसेस करते समय सही क्रम बनाए रखने में मदद करता है।", + "kv-map-pattern-hint": "सभी इनपुट फ़ील्ड्स टेम्पलेटाइज़ेशन सपोर्ट करते हैं। मैसेज से मान निकालने के लिए $[messageKey] और मेटाडेटा से मान निकालने के लिए ${metadataKey} का उपयोग करें।", + "kv-map-single-pattern-hint": "इनपुट फ़ील्ड टेम्पलेटाइज़ेशन सपोर्ट करता है। मैसेज से मान निकालने के लिए $[messageKey] और मेटाडेटा से मान निकालने के लिए ${metadataKey} का उपयोग करें।", + "shared-scope": "शेयर्ड स्कोप", + "server-scope": "सर्वर स्कोप", + "client-scope": "क्लाइंट स्कोप", + "attribute-type": "विशेषता", + "attribute-type-description": "डेटाबेस से विशेषता मान प्राप्त करें", + "attribute-type-result-description": "परिणाम को डेटाबेस में एंटिटी विशेषता के रूप में सहेजें", + "constant-type": "कॉनस्टैंट", + "constant-type-description": "कॉनस्टैंट मान परिभाषित करें", + "time-series-type": "टाइम सीरीज़", + "time-series-type-description": "डाटाबेस से नवीनतम टाइम सीरीज़ मान प्राप्त करें", + "time-series-type-result-description": "परिणाम को डाटाबेस में एंटिटी टाइम सीरीज़ के रूप में सहेजें", + "message-body-type": "मैसेज", + "message-body-type-description": "इनकमिंग मैसेज से आर्ग्युमेंट मान प्राप्त करें", + "message-body-type-result-description": "परिणाम को आउटगोइंग मैसेज में जोड़ें", + "message-metadata-type": "मेटाडेटा", + "message-metadata-type-description": "इनकमिंग मैसेज मेटाडेटा से आर्ग्युमेंट मान प्राप्त करें", + "message-metadata-result-description": "परिणाम को आउटगोइंग मैसेज मेटाडेटा में जोड़ें", + "argument-tile": "आर्ग्युमेंट्स", + "no-arguments-prompt": "कोई आर्ग्युमेंट कॉन्फ़िगर नहीं किया गया है", + "result-title": "परिणाम", + "functions-field-input": "फ़ंक्शन्स", + "no-option-found": "कोई विकल्प नहीं मिला", + "argument-source-field-input": "स्रोत", + "argument-source-field-input-required": "आर्ग्युमेंट का स्रोत आवश्यक है.", + "argument-key-field-input": "की", + "argument-key-field-input-required": "आर्ग्युमेंट की आवश्यक है.", + "constant-value-field-input": "कॉनस्टैंट मान", + "constant-value-field-input-required": "कॉनस्टैंट मान आवश्यक है.", + "attribute-scope-field-input": "विशेषता स्कोप", + "attribute-scope-field-input-required": "विशेषता स्कोप आवश्यक है.", + "default-value-field-input": "डिफ़ॉल्ट मान", + "type-field-input": "टाइप", + "type-field-input-required": "टाइप आवश्यक है.", + "key-field-input": "की", + "add-entity-type": "एंटिटी टाइप जोड़ें", + "add-device-profile": "डिवाइस प्रोफ़ाइल जोड़ें", + "key-field-input-required": "की आवश्यक है.", + "number-floating-point-field-input": "दशमलव के बाद अंकों की संख्या", + "number-floating-point-field-input-hint": "परिणाम को पूर्णांक में बदलने के लिए 0 का उपयोग करें", + "add-to-message-field-input": "मैसेज में जोड़ें", + "add-to-metadata-field-input": "मेटाडेटा में जोड़ें", + "custom-expression-field-input": "गणितीय अभिव्यक्ति", + "custom-expression-field-input-required": "गणितीय अभिव्यक्ति आवश्यक है", + "custom-expression-field-input-hint": "जिसका मूल्यांकन करना है, वह गणितीय अभिव्यक्ति निर्दिष्ट करें। डिफ़ॉल्ट अभिव्यक्ति दिखाती है कि फ़ारेनहाइट को सेल्सियस में कैसे बदला जाए।", + "retained-message": "रिटेन्ड", + "attributes-mapping": "विशेषताओं की मैपिंग", + "latest-telemetry-mapping": "नवीनतम टेलीमेट्री मैपिंग", + "add-mapped-attribute-to": "मैप की गई विशेषताओं को इसमें जोड़ें", + "add-mapped-latest-telemetry-to": "मैप की गई नवीनतम टेलीमेट्री को इसमें जोड़ें", + "add-mapped-fields-to": "मैप किए गए फ़ील्ड्स को इसमें जोड़ें", + "add-selected-details-to": "चुने गए विवरण इसमें जोड़ें", + "clear-selected-types": "चुने गए टाइप्स साफ़ करें", + "clear-selected-details": "चुने गए विवरण साफ़ करें", + "clear-selected-fields": "चुने गए फ़ील्ड्स साफ़ करें", + "clear-selected-keys": "चुनी गई कीज़ साफ़ करें", + "geofence-configuration": "जियोफेंस कॉन्फ़िगरेशन", + "coordinate-field-names": "कोऑर्डिनेट फ़ील्ड नाम", + "coordinate-field-hint": "रूल नोड मैसेज से दिए गए फ़ील्ड प्राप्त करने की कोशिश करेगा। यदि वे वहाँ मौजूद नहीं हैं, तो उन्हें मेटाडेटा में खोजेगा।", + "presence-monitoring-strategy": "उपस्थिति मॉनिटरिंग स्ट्रैटेजी", + "presence-monitoring-strategy-on-first-message": "पहले मैसेज पर", + "presence-monitoring-strategy-on-each-message": "हर मैसेज पर", + "presence-monitoring-strategy-on-first-message-hint": "कॉन्फ़िगर की गई न्यूनतम अवधि, पिछली उपस्थिति स्थिति 'प्रवेश किया' या 'बाहर निकला' के अपडेट के बाद बीत जाने पर, पहले मैसेज पर उपस्थिति स्टेटस 'अंदर' या 'बाहर' की रिपोर्ट करता है।", + "presence-monitoring-strategy-on-each-message-hint": "उपस्थिति स्टेटस 'प्रवेश किया' या 'बाहर निकला' के अपडेट के बाद, हर मैसेज पर उपस्थिति स्टेटस 'अंदर' या 'बाहर' की रिपोर्ट करता है।", + "fetch-credentials-to": "क्रेडेंशियल्स यहाँ फ़ेच करें", + "add-originator-attributes-to": "ओरिजिनेटर विशेषताएँ इसमें जोड़ें", + "originator-attributes": "ओरिजिनेटर विशेषताएँ", + "fetch-latest-telemetry-with-timestamp": "नवीनतम टेलीमेट्री टाइमस्टैम्प सहित फ़ेच करें", + "fetch-latest-telemetry-with-timestamp-tooltip": "यदि चुना गया, तो नवीनतम टेलीमेट्री मान टाइमस्टैम्प सहित आउटबाउंड मेटाडेटा में जोड़ दिए जाएँगे, जैसे: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "यदि कोई भी विशेषता गायब हो तो विफलता बताएँ", + "tell-failure-tooltip": "यदि चुनी हुई कीज़ में से कम से कम एक भी मौजूद नहीं है, तो आउटबाउंड मैसेज \"विफलता\" रिपोर्ट करेगा।", + "created-time": "बनाए जाने का समय", + "chip-help": "फील्ड {{inputName}} का इनपुट पूरा करने के लिए 'Enter' दबाएँ।\n{{inputName}} को हटाने के लिए 'Backspace' दबाएँ।\nएक से अधिक मान समर्थित हैं।", + "detail": "विवरण", + "field-name": "फ़ील्ड नाम", + "device-profile": "डिवाइस प्रोफ़ाइल", + "entity-type": "एंटिटी टाइप", + "message-type": "मैसेज टाइप", + "timeseries-key": "टाइम सीरीज़ की", + "type": "टाइप", + "first-name": "पहला नाम", + "last-name": "अंतिम नाम", + "label": "लेबल", + "originator-fields-mapping": "ओरिजिनेटर फ़ील्ड मैपिंग", + "add-mapped-originator-fields-to": "मैप किए गए ओरिजिनेटर फ़ील्ड्स इसमें जोड़ें", + "fields": "फ़ील्ड्स", + "skip-empty-fields": "खाली फ़ील्ड्स को छोड़ें", + "skip-empty-fields-tooltip": "जिन फ़ील्ड्स के मान खाली होंगे, उन्हें आउटपुट मैसेज/आउटपुट मेटाडेटा में नहीं जोड़ा जाएगा।", + "fetch-interval": "फ़ेच इंटरवल", + "fetch-strategy": "फ़ेच स्ट्रैटेजी", + "fetch-timeseries-from-to": "टाइम सीरीज़ {{startInterval}} {{startIntervalTimeUnit}} पहले से {{endInterval}} {{endIntervalTimeUnit}} पहले तक फ़ेच करें।", + "fetch-timeseries-from-to-invalid": "टाइम सीरीज़ फ़ेच अमान्य है (\"इंटरवल स्टार्ट\" \"इंटरवल एंड\" से कम होना चाहिए)।", + "use-metadata-dynamic-interval-tooltip": "यदि चुना गया, तो रूल नोड मैसेज और मेटाडेटा पैटर्न के आधार पर डायनेमिक इंटरवल स्टार्ट और एंड का उपयोग करेगा।", + "all-mode-hint": "यदि फ़ेच मोड \"सभी\" चुना गया है, तो रूल नोड कॉन्फ़िगर किए गए क्वेरी पैरामीटर्स के साथ फ़ेच इंटरवल से टेलीमेट्री प्राप्त करेगा।", + "first-mode-hint": "यदि फ़ेच मोड \"पहला\" चुना गया है, तो रूल नोड फ़ेच इंटरवल की शुरुआत के सबसे नज़दीक की टेलीमेट्री प्राप्त करेगा।", + "last-mode-hint": "यदि फ़ेच मोड \"आख़िरी\" चुना गया है, तो रूल नोड फ़ेच इंटरवल के अंत के सबसे नज़दीक की टेलीमेट्री प्राप्त करेगा।", + "ascending": "आरोही", + "descending": "अवरोही", + "min": "न्यूनतम", + "max": "अधिकतम", + "average": "औसत", + "sum": "योग", + "count": "गिनती", + "none": "कोई नहीं", + "last-level-relation-tooltip": "यदि चुना गया, तो रूल नोड केवल अधिकतम रिलेशन लेवल में सेट किए गए लेवल पर ही संबंधित एंटिटीज़ को खोजेगा।", + "last-level-device-relation-tooltip": "यदि चुना गया, तो रूल नोड केवल अधिकतम रिलेशन लेवल में सेट किए गए लेवल पर ही संबंधित डिवाइस को खोजेगा।", + "data-to-fetch": "फ़ेच करने वाला डेटा", + "mapping-of-customers": "कस्टमर की मैपिंग", + "map-fields-required": "सभी मैपिंग फ़ील्ड ज़रूरी हैं।", + "attributes": "विशेषताएँ", + "related-device-attributes": "संबंधित डिवाइस की विशेषताएँ", + "add-selected-attributes-to": "चुनी हुई विशेषताएँ इसमें जोड़ें", + "device-profiles": "डिवाइस प्रोफ़ाइल्स", + "mapping-of-tenant": "टेनेंट की मैपिंग", + "add-attribute-key": "एट्रिब्यूट की जोड़ें", + "message-template": "संदेश टेम्पलेट", + "message-template-required": "संदेश टेम्पलेट ज़रूरी है", + "use-system-slack-settings": "सिस्टम Slack प्रदाता सेटिंग्स का उपयोग करें", + "slack-api-token": "Slack API टोकन", + "slack-api-token-required": "Slack API टोकन ज़रूरी है", + "keys-mapping": "कुंजियों की मैपिंग", + "add-key": "कुंजी जोड़ें", + "recipients": "प्राप्तकर्ता", + "message-subject-and-content": "संदेश का विषय और सामग्री", + "template-rules-hint": "दोनों इनपुट फ़ील्ड्स टेम्पलेटाइज़ेशन को सपोर्ट करते हैं। मैसेज से मान निकालने के लिए $[messageKey] और मैसेज मेटाडेटा से मान निकालने के लिए ${metadataKey} का उपयोग करें।", + "originator-customer-desc": "इनकमिंग मैसेज के ओरिजिनेटर के कस्टमर को नए ओरिजिनेटर के रूप में उपयोग करें।", + "originator-tenant-desc": "वर्तमान टेनेंट को नए ओरिजिनेटर के रूप में उपयोग करें।", + "originator-related-entity-desc": "संबंधित एंटिटी को नए ओरिजिनेटर के रूप में उपयोग करें। लुकअप कॉन्फ़िगर किए गए रिलेशन टाइप और डायरेक्शन के आधार पर होगा।", + "originator-alarm-originator-desc": "अलार्म ओरिजिनेटर को नए ओरिजिनेटर के रूप में उपयोग करें। केवल तब जब इनकमिंग मैसेज ओरिजिनेटर अलार्म एंटिटी हो।", + "originator-entity-by-name-pattern-desc": "डेटाबेस से प्राप्त एंटिटी को नए ओरिजिनेटर के रूप में उपयोग करें। लुकअप एंटिटी टाइप और दिए गए नाम पैटर्न के आधार पर होगा।", + "email-from-template-hint": "मैसेज से मान निकालने के लिए $[messageKey] और मेटाडेटा से मान निकालने के लिए ${metadataKey} का उपयोग करें।", + "recipients-block-main-hint": "कॉमा से अलग की गई एड्रेस सूची। सभी इनपुट फ़ील्ड्स टेम्पलेटाइज़ेशन को सपोर्ट करते हैं। मैसेज से मान निकालने के लिए $[messageKey] और मेटाडेटा से मान निकालने के लिए ${metadataKey} का उपयोग करें।", + "forward-msg-default-rule-chain": "मैसेज को ओरिजिनेटर की डिफ़ॉल्ट रूल चेन पर फ़ॉरवर्ड करें", + "forward-msg-default-rule-chain-tooltip": "यदि सक्षम किया गया है, तो मैसेज को ओरिजिनेटर की डिफ़ॉल्ट रूल चेन पर फ़ॉरवर्ड किया जाएगा, या कॉन्फ़िगरेशन में दी गई रूल चेन पर, यदि एंटिटी प्रोफ़ाइल में ओरिजिनेटर के लिए कोई डिफ़ॉल्ट रूल चेन परिभाषित नहीं है।", + "exclude-zero-deltas": "आउटबाउंड मैसेज से शून्य डेल्टा को निकालें", + "exclude-zero-deltas-hint": "यदि सक्षम किया गया है, तो आउटबाउंड मैसेज में \"{{outputValueKey}}\" आउटपुट कुंजी केवल तभी जोड़ी जाएगी जब इसका मान शून्य न हो।", + "exclude-zero-deltas-time-difference-hint": "यदि सक्षम किया गया है, तो आउटबाउंड मैसेज में \"{{outputValueKey}}\" और \"{{periodValueKey}}\" आउटपुट कुंजियाँ केवल तभी जोड़ी जाएँगी जब \"{{outputValueKey}}\" का मान शून्य न हो।", + "search-direction-from": "ओरिजिनेटर से टारगेट एंटिटी तक", + "search-direction-to": "टारगेट एंटिटी से ओरिजिनेटर तक", + "del-relation-direction-from": "ओरिजिनेटर से", + "del-relation-direction-to": "ओरिजिनेटर तक", + "target-entity": "टारगेट एंटिटी", + "function-configuration": "फ़ंक्शन कॉन्फ़िगरेशन", + "function-name": "फ़ंक्शन नाम", + "function-name-required": "फ़ंक्शन नाम आवश्यक है।", + "qualifier": "क्वालिफ़ायर", + "qualifier-hint": "अगर क्वालिफ़ायर निर्दिष्ट नहीं है, तो डिफ़ॉल्ट क्वालिफ़ायर \"$LATEST\" उपयोग किया जाएगा।", + "aws-credentials": "AWS क्रेडेंशियल्स", + "connection-timeout": "कनेक्शन टाइमआउट", + "connection-timeout-required": "कनेक्शन टाइमआउट आवश्यक है।", + "connection-timeout-min": "न्यूनतम कनेक्शन टाइमआउट 0 है।", + "connection-timeout-hint": "कनेक्शन स्थापित करते समय छोड़ने और टाइमआउट देने से पहले प्रतीक्षा करने का समय (सेकंड में)। 0 मान का अर्थ अनंत है, और अनुशंसित नहीं है।", + "request-timeout": "रिक्वेस्ट टाइमआउट", + "request-timeout-required": "रिक्वेस्ट टाइमआउट आवश्यक है।", + "request-timeout-min": "न्यूनतम रिक्वेस्ट टाइमआउट 0 है।", + "request-timeout-hint": "रिक्वेस्ट पूरा होने के लिए छोड़ने और टाइमआउट देने से पहले प्रतीक्षा करने का समय (सेकंड में)। 0 मान का अर्थ अनंत है, और अनुशंसित नहीं है।", + "units": "इकाइयाँ", + "tell-failure-aws-lambda": "यदि AWS Lambda फ़ंक्शन के निष्पादन में कोई अपवाद उत्पन्न हो तो Failure बताएं", + "tell-failure-aws-lambda-hint": "यदि AWS Lambda फ़ंक्शन के निष्पादन के दौरान कोई अपवाद (exception) उत्पन्न होता है, तो रूल नोड मैसेज प्रोसेसिंग की Failure को मजबूर करेगा।", + "basic-mode": "बेसिक", + "advanced-mode": "एडवांस्ड", + "save-time-series": { + "processing-settings": "प्रोसेसिंग सेटिंग्स", + "processing-settings-hint": "इनकमिंग मैसेज कैसे प्रोसेस किए जाएँगे, यह परिभाषित करें। बेसिक प्रोसेसिंग सेटिंग्स आपको पहले से कॉन्फ़िगर की गई स्ट्रैटेजीज़ चुनने देती हैं, जबकि एडवांस्ड सेटिंग्स में आप हर एक एक्शन के लिए अलग-अलग प्रोसेसिंग स्ट्रैटेजी चुन सकते हैं।", + "advanced-settings-hint": "प्रोसेसिंग स्ट्रैटेजीज़ कॉन्फ़िगर करते समय सावधान रहें। कुछ संयोजन अप्रत्याशित व्यवहार का कारण बन सकते हैं।", + "strategy": "स्ट्रैटेजी", + "deduplication-interval": "डेडुप्लीकेशन इंटरवल", + "deduplication-interval-required": "डेडुप्लीकेशन इंटरवल आवश्यक है।", + "deduplication-interval-min-max-range": "डेडुप्लीकेशन इंटरवल कम से कम 1 सेकंड और अधिकतम 1 दिन होना चाहिए।", + "strategy-type": { + "every-message": "हर मैसेज पर", + "skip": "स्किप करें", + "deduplicate": "डेडुप्लीकेट", + "web-sockets-only": "केवल WebSockets" + }, + "time-series": "टाइम सीरीज़", + "latest": "नवीनतम मान", + "web-sockets": "WebSockets" + }, + "save-attribute": { + "processing-settings": "प्रोसेसिंग सेटिंग्स", + "processing-settings-hint": "इनकमिंग मैसेज कैसे प्रोसेस किए जाएँगे, यह परिभाषित करें। बेसिक प्रोसेसिंग सेटिंग्स आपको पहले से कॉन्फ़िगर की गई स्ट्रैटेजीज़ चुनने देती हैं, जबकि एडवांस्ड सेटिंग्स में आप हर एक एक्शन के लिए अलग-अलग प्रोसेसिंग स्ट्रैटेजी चुन सकते हैं।", + "advanced-settings-hint": "प्रोसेसिंग स्ट्रैटेजीज़ कॉन्फ़िगर करते समय सावधान रहें। कुछ संयोजन अप्रत्याशित व्यवहार का कारण बन सकते हैं।", + "strategy": "स्ट्रैटेजी", + "deduplication-interval": "डेडुप्लीकेशन इंटरवल", + "deduplication-interval-required": "डेडुप्लीकेशन इंटरवल आवश्यक है।", + "deduplication-interval-min-max-range": "डेडुप्लीकेशन इंटरवल कम से कम 1 सेकंड और अधिकतम 1 दिन होना चाहिए।", + "scope": "स्कोप", + "strategy-type": { + "every-message": "हर मैसेज पर", + "skip": "स्किप करें", + "deduplicate": "डेडुप्लीकेट", + "web-sockets-only": "केवल WebSockets" + }, + "attributes": "विशेषताएँ" + }, + "key-val": { + "key": "कुंजी", + "value": "मान", + "see-examples": "उदाहरण देखें।", + "remove-entry": "एंट्री हटाएँ", + "remove-mapping-entry": "मैपिंग हटाएँ", + "add-mapping-entry": "मैपिंग जोड़ें", + "add-entry": "एंट्री जोड़ें", + "copy-key-values-from": "की-वैल्यूज़ यहाँ से कॉपी करें", + "delete-key-values": "की-वैल्यूज़ हटाएँ", + "delete-key-values-from": "यहाँ से की-वैल्यूज़ हटाएँ", + "at-least-one-key-error": "कम से कम एक कुंजी चुनी जानी चाहिए।", + "unique-key-value-pair-error": "'{{keyText}}' को '{{valText}}' से अलग होना चाहिए!" + }, + "mail-body-types": { + "plain-text": "सादा टेक्स्ट", + "html": "HTML", + "dynamic": "डायनेमिक", + "use-body-type-template": "बॉडी टाइप टेम्पलेट का उपयोग करें", + "plain-text-description": "सरल, बिना फ़ॉर्मैटिंग वाला टेक्स्ट, जिसमें कोई विशेष स्टाइलिंग या फ़ॉर्मैटिंग नहीं होती।", + "html-text-description": "आपको अपने मेल बॉडी में फ़ॉर्मैटिंग, लिंक और इमेज के लिए HTML टैग उपयोग करने की अनुमति देता है।", + "dynamic-text-description": "टेम्पलेटाइज़ेशन फ़ीचर के आधार पर Plain Text या HTML बॉडी टाइप को डायनेमिक रूप से उपयोग करने की अनुमति देता है।", + "after-template-evaluation-hint": "टेम्पलेट के मूल्यांकन के बाद मान HTML के लिए true और Plain text के लिए false होना चाहिए।" + }, + "ai": { + "ai-model": "एआई मॉडल", + "model": "मॉडल", + "ai-model-hint": "इस रूल नोड द्वारा भेजे गए रिक्वेस्ट प्रोसेस करने के लिए प्री-कॉन्फ़िगर एआई मॉडल चुनें, या नया मॉडल कॉन्फ़िगर करने के लिए \"Create new\" का उपयोग करें।", + "prompt-settings": "प्रॉम्प्ट सेटिंग्स", + "prompt-settings-hint": "वैकल्पिक सिस्टम प्रॉम्प्ट एआई की सामान्य भूमिका और सीमाएँ सेट करता है, जबकि यूज़र प्रॉम्प्ट करने वाला विशिष्ट कार्य परिभाषित करता है। दोनों फ़ील्ड टेम्पलेटाइज़ेशन को सपोर्ट करते हैं।", + "system-prompt": "सिस्टम प्रॉम्प्ट", + "system-prompt-max-length": "सिस्टम प्रॉम्प्ट 500000 अक्षरों से अधिक नहीं होना चाहिए।", + "system-prompt-blank": "सिस्टम प्रॉम्प्ट खाली नहीं होना चाहिए।", + "user-prompt": "यूज़र प्रॉम्प्ट", + "user-prompt-required": "यूज़र प्रॉम्प्ट आवश्यक है।", + "user-prompt-max-length": "यूज़र प्रॉम्प्ट 500000 अक्षरों से अधिक नहीं होना चाहिए।", + "user-prompt-blank": "यूज़र प्रॉम्प्ट खाली नहीं होना चाहिए।", + "response-format": "उत्तर फ़ॉर्मैट", + "response-text": "टेक्स्ट", + "response-json": "JSON", + "response-json-schema": "JSON Schema", + "response-format-hint-TEXT": "मॉडल को मनचाहा टेक्स्ट जनरेट करने की अनुमति देता है, जो वैध JSON ऑब्जेक्ट हो भी सकता है और नहीं भी। अगर आउटपुट वैध JSON ऑब्जेक्ट नहीं है, तो इसे अपने-आप \"response\" कुंजी के अंदर एक JSON ऑब्जेक्ट में लपेट दिया जाएगा।", + "response-format-hint-JSON": "मॉडल से आवश्यक है कि वह वैध JSON के रूप में उत्तर जनरेट करे। अगर आउटपुट वैध JSON ऑब्जेक्ट नहीं है, तो इसे अपने-आप \"response\" कुंजी के अंदर एक JSON ऑब्जेक्ट में लपेट दिया जाएगा।", + "response-format-hint-JSON_SCHEMA": "मॉडल से आवश्यक है कि वह दिए गए स्कीमा में परिभाषित स्ट्रक्चर और डेटा टाइप से मेल खाता JSON जनरेट करे। अगर आउटपुट वैध JSON ऑब्जेक्ट नहीं है, तो इसे अपने-आप \"response\" कुंजी के अंदर एक JSON ऑब्जेक्ट में लपेट दिया जाएगा।", + "response-json-schema-hint": "आप कोई भी वैध JSON Schema दर्ज कर सकते हैं, लेकिन यह रूल नोड उसके केवल सीमित फ़ीचर्स को सपोर्ट करता है। विवरण के लिए नोड डॉक्यूमेंटेशन देखें।", + "response-json-schema-required": "JSON Schema आवश्यक है।", + "advanced-settings": "एडवांस्ड सेटिंग्स", + "timeout": "टाइमआउट", + "timeout-hint": "रिक्वेस्ट समाप्त होने से पहले एआई मॉडल से उत्तर की प्रतीक्षा करने का अधिकतम समय।", + "timeout-required": "टाइमआउट आवश्यक है।", + "timeout-validation": "1 सेकंड से 10 मिनट के बीच होना चाहिए।", + "force-acknowledgement": "फ़ोर्स ऐक्नॉलेजमेंट", + "force-acknowledgement-hint": "अगर सक्षम है, तो इनकमिंग मैसेज को तुरंत ऐक्नॉलेज कर दिया जाएगा। इसके बाद मॉडल का उत्तर एक अलग, नए मैसेज के रूप में क्यू में डाला जाएगा।", + "ai-resources": "एआई रिसोर्सेज़" + } + }, + "timezone": { + "timezone": "समय ज़ोन", + "select-timezone": "समय ज़ोन चुनें", + "no-timezones-matching": "'{{timezone}}' से मेल खाते कोई समय ज़ोन नहीं मिले।", + "timezone-required": "समय ज़ोन आवश्यक है।", + "browser-time": "ब्राउज़र समय" + }, + "queue": { + "queue-name": "क्यू", + "no-queues-found": "कोई क्यू नहीं मिली।", + "no-queues-matching": "'{{queue}}' से मेल खाती कोई क्यू नहीं मिली।", + "select-name": "क्यू नाम चुनें", + "name": "नाम", + "name-required": "क्यू नाम आवश्यक है!", + "name-unique": "क्यू नाम अद्वितीय नहीं है!", + "name-pattern": "क्यू नाम में ASCII अल्फ़ान्यूमेरिक, '.', '_' और '-' के अलावा अन्य कैरेक्टर नहीं होना चाहिए!", + "queue-required": "क्यू आवश्यक है!", + "topic-required": "क्यू टॉपिक आवश्यक है!", + "poll-interval-required": "पोल इंटरवल आवश्यक है!", + "poll-interval-min-value": "पोल इंटरवल का मान 1 से कम नहीं हो सकता।", + "partitions-required": "पार्टिशन आवश्यक हैं!", + "partitions-min-value": "पार्टिशन का मान 1 से कम नहीं हो सकता।", + "pack-processing-timeout-required": "प्रोसेसिंग टाइमआउट आवश्यक है।", + "pack-processing-timeout-min-value": "प्रोसेसिंग टाइमआउट का मान 1 से कम नहीं हो सकता।", + "batch-size-required": "बैच साइज आवश्यक है!", + "batch-size-min-value": "बैच साइज का मान 1 से कम नहीं हो सकता।", + "retries-required": "रीट्राइज़ आवश्यक हैं!", + "retries-min-value": "रीट्राइज़ का मान ऋणात्मक नहीं हो सकता।", + "failure-percentage-required": "फेल्योर प्रतिशत आवश्यक है!", + "failure-percentage-min-value": "फेल्योर प्रतिशत का मान 0 से कम नहीं हो सकता।", + "failure-percentage-max-value": "फेल्योर प्रतिशत का मान 100 से ज़्यादा नहीं हो सकता।", + "pause-between-retries-required": "रीट्राइज़ के बीच का पॉज़ आवश्यक है!", + "pause-between-retries-min-value": "रीट्राइज़ के बीच पॉज़ का मान 1 से कम नहीं हो सकता।", + "max-pause-between-retries-required": "रीट्राइज़ के बीच अधिकतम पॉज़ आवश्यक है!", + "max-pause-between-retries-min-value": "रीट्राइज़ के बीच अधिकतम पॉज़ का मान 1 से कम नहीं हो सकता।", + "submit-strategy-type-required": "सबमिट स्ट्रैटजी टाइप आवश्यक है!", + "processing-strategy-type-required": "प्रोसेसिंग स्ट्रैटजी टाइप आवश्यक है!", + "queues": "क्यूज़", + "selected-queues": "{ count, plural, =1 {1 क्यू} other {# क्यूज़} } चयनित", + "delete-queue-title": "क्या आप वाकई क्यू '{{queueName}}' को हटाना चाहते हैं?", + "delete-queues-title": "क्या आप वाकई { count, plural, =1 {1 क्यू} other {# क्यूज़} } हटाना चाहते हैं?", + "delete-queue-text": "सावधान रहें, पुष्टि के बाद यह क्यू और इससे जुड़ा सारा डेटा हमेशा के लिए हट जाएगा।", + "delete-queues-text": "पुष्टि के बाद चुनी गई सभी क्यूज़ हटा दी जाएँगी और उपलब्ध नहीं रहेंगी।", + "search": "क्यू खोजें", + "add": "क्यू जोड़ें", + "details": "क्यू विवरण", + "topic": "टॉपिक", + "submit-settings": "सबमिट सेटिंग्स", + "submit-strategy": "स्ट्रैटजी टाइप *", + "grouping-parameter": "ग्रूपिंग पैरामीटर", + "processing-settings": "रीट्राइज़ प्रोसेसिंग सेटिंग्स", + "processing-strategy": "प्रोसेसिंग टाइप *", + "retries-settings": "रीट्राइज़ सेटिंग्स", + "polling-settings": "पोलिंग सेटिंग्स", + "batch-processing": "बैच प्रोसेसिंग", + "poll-interval": "पोल इंटरवल", + "partitions": "पार्टिशन", + "immediate-processing": "तुरंत प्रोसेसिंग", + "consumer-per-partition": "प्रत्येक कंज़्यूमर के लिए मैसेज पोल भेजें", + "consumer-per-partition-hint": "प्रत्येक पार्टिशन के लिए अलग-अलग कंज़्यूमर सक्षम करें", + "duplicate-msg-to-all-partitions": "संदेश को सभी पार्टिशन में डुप्लिकेट करें", + "processing-timeout": "प्रोसेसिंग समय, ms", + "batch-size": "बैच साइज", + "retries": "रीट्राइज़ की संख्या (0 – अनलिमिटेड)", + "failure-percentage": "रीट्राइज़ छोड़ने के लिए फेल्योर संदेश, %", + "pause-between-retries": "रीट्राइज़ अंतराल, सेकंड", + "max-pause-between-retries": "अतिरिक्त रीट्राइज़ अंतराल, सेकंड", + "delete": "क्यू हटाएँ", + "copyId": "क्यू Id कॉपी करें", + "idCopiedMessage": "क्यू Id क्लिपबोर्ड पर कॉपी कर दी गई है", + "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 के लिए पिछला संदेश अकनॉलेज न हो जाए", + "sequential-by-tenant-label": "टेनेंट के अनुसार सीक्वेंशियल", + "sequential-by-tenant-hint": "उदाहरण के लिए टेनेंट A के लिए नया संदेश तब तक सबमिट नहीं होगा जब तक टेनेंट A के लिए पिछला संदेश अकनॉलेज न हो जाए", + "sequential-label": "सीक्वेंशियल", + "sequential-hint": "नया संदेश तब तक सबमिट नहीं होगा जब तक पिछला संदेश अकनॉलेज न हो जाए", + "burst-label": "बर्स्ट", + "burst-hint": "सभी संदेश उनके आने के क्रम में रूल चेन्स को सबमिट किए जाते हैं", + "batch-label": "बैच", + "batch-hint": "नया बैच तब तक सबमिट नहीं होगा जब तक पिछला बैच अकनॉलेज न हो जाए", + "skip-all-failures-label": "सभी फेल्योर को स्किप करें", + "skip-all-failures-hint": "सभी फेल्योर को इग्नोर करें", + "skip-all-failures-and-timeouts-label": "सभी फेल्योर और टाइमआउट्स को स्किप करें", + "skip-all-failures-and-timeouts-hint": "सभी फेल्योर और टाइमआउट्स को इग्नोर करें", + "retry-all-label": "सब पर रीट्राइज़ करें", + "retry-all-hint": "प्रोसेसिंग पैक के सभी संदेशों पर रीट्राइज़ करें", + "retry-failed-label": "फेल्ड पर रीट्राइज़ करें", + "retry-failed-hint": "प्रोसेसिंग पैक के सभी फेल्ड संदेशों पर रीट्राइज़ करें", + "retry-timeout-label": "टाइमआउट पर रीट्राइज़ करें", + "retry-timeout-hint": "प्रोसेसिंग पैक के सभी टाइमआउट हुए संदेशों पर रीट्राइज़ करें", + "retry-failed-and-timeout-label": "फेल्ड और टाइमआउट पर रीट्राइज़ करें", + "retry-failed-and-timeout-hint": "प्रोसेसिंग पैक के सभी फेल्ड और टाइमआउट हुए संदेशों पर रीट्राइज़ करें" + } + }, + "queue-statistics": { + "queue-statistics": "क्यू सांख्यिकी", + "no-queue-statistics-matching": "कोई क्यू सांख्यिकी '{{entity}}' से मिलती नहीं मिली।", + "queue-statistics-required": "क्यू सांख्यिकी आवश्यक है।", + "list-of-queue-statistics": "{ count, plural, =1 {एक क्यू सांख्यिकी} other {# क्यू सांख्यिकी की सूची} }", + "selected-queue-statistics": "{ count, plural, =1 {1 क्यू सांख्यिकी} other {# क्यू सांख्यिकी} } चयनित", + "no-queue-statistics-text": "कोई क्यू सांख्यिकी नहीं मिली", + "queue-statistics-starts-with": "वे क्यू सांख्यिकी जिनके नाम '{{prefix}}' से शुरू होते हैं" + }, + "server-error": { + "general": "सामान्य सर्वर त्रुटि", + "authentication": "प्रमाणीकरण त्रुटि", + "jwt-token-expired": "JWT टोकन की समय-सीमा समाप्त हो गई", + "tenant-trial-expired": "टेनेंट ट्रायल की समय-सीमा समाप्त हो गई", + "credentials-expired": "क्रेडेंशियल्स की समय-सीमा समाप्त हो गई", + "permission-denied": "अनुमति अस्वीकृत", + "invalid-arguments": "अमान्य आर्ग्यूमेंट्स", + "bad-request-params": "खराब रिक्वेस्ट पैरामीटर्स", + "item-not-found": "आइटम नहीं मिला", + "too-many-requests": "बहुत अधिक रिक्वेस्ट", + "too-many-updates": "बहुत अधिक अपडेट्स" + }, + "tenant": { + "tenant": "टेनेंट", + "tenants": "टेनेंट्स", + "management": "टेनेंट प्रबंधन", + "add": "टेनेंट जोड़ें", + "admins": "प्रশासक", + "manage-tenant-admins": "टेनेंट प्रशासकों को प्रबंधित करें", + "delete": "टेनेंट हटाएँ", + "add-tenant-text": "नया टेनेंट जोड़ें", + "no-tenants-text": "कोई टेनेंट नहीं मिला", + "tenant-details": "टेनेंट विवरण", + "title-max-length": "शीर्षक 256 अक्षरों से कम होना चाहिए", + "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": "टेनेंट Id कॉपी करें", + "idCopiedMessage": "टेनेंट Id क्लिपबोर्ड पर कॉपी कर दिया गया है", + "select-tenant": "टेनेंट चुनें", + "no-tenants-matching": "कोई टेनेंट '{{entity}}' से मेल नहीं खाता मिला।", + "tenant-required": "टेनेंट आवश्यक है", + "search": "टेनेंट खोजें", + "selected-tenants": "{ count, plural, =1 {1 टेनेंट} other {# टेनेंट्स} } चयनित", + "isolated-tb-rule-engine": "आइसोलेटेड थिंग्सबोर्ड रूल इंजन क्यूज़ का उपयोग करें", + "isolated-tb-rule-engine-details": "हर टेनेंट के पास समर्पित रूल इंजन क्यूज़ होंगी।" + }, + "tenant-profile": { + "tenant-profile": "टेनेंट प्रोफ़ाइल", + "tenant-profiles": "टेनेंट प्रोफ़ाइल्स", + "add": "टेनेंट प्रोफ़ाइल जोड़ें", + "add-profile": "प्रोफ़ाइल जोड़ें", + "debug": "डीबग", + "edit": "टेनेंट प्रोफ़ाइल संपादित करें", + "tenant-profile-details": "टेनेंट प्रोफ़ाइल विवरण", + "no-tenant-profiles-text": "कोई टेनेंट प्रोफ़ाइल नहीं मिली", + "name-max-length": "नाम 256 अक्षरों से कम होना चाहिए", + "search": "टेनेंट प्रोफ़ाइल खोजें", + "selected-tenant-profiles": "{ count, plural, =1 {1 टेनेंट प्रोफ़ाइल} other {# टेनेंट प्रोफ़ाइल्स} } चयनित", + "no-tenant-profiles-matching": "कोई टेनेंट प्रोफ़ाइल '{{entity}}' से मेल नहीं खाती मिली।", + "tenant-profile-required": "टेनेंट प्रोफ़ाइल आवश्यक है", + "idCopiedMessage": "टेनेंट प्रोफ़ाइल Id क्लिपबोर्ड पर कॉपी कर दी गई है", + "set-default": "टेनेंट प्रोफ़ाइल को डिफ़ॉल्ट बनाएं", + "delete": "टेनेंट प्रोफ़ाइल हटाएँ", + "copyId": "टेनेंट प्रोफ़ाइल Id कॉपी करें", + "name": "नाम", + "name-required": "नाम आवश्यक है।", + "data": "प्रोफ़ाइल डेटा", + "profile-configuration": "प्रोफ़ाइल कॉन्फ़िगरेशन", + "description": "विवरण", + "default": "डिफ़ॉल्ट", + "delete-tenant-profile-title": "क्या आप वाकई टेनेंट प्रोफ़ाइल '{{tenantProfileName}}' को हटाना चाहते हैं?", + "delete-tenant-profile-text": "सावधान रहें, पुष्टि के बाद टेनेंट प्रोफ़ाइल और उससे जुड़ा सारा डेटा स्थायी रूप से मिट जाएगा।", + "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": "नई टेनेंट प्रोफ़ाइल बनाएँ", + "import": "टेनेंट प्रोफ़ाइल आयात करें", + "export": "टेनेंट प्रोफ़ाइल निर्यात करें", + "export-failed-error": "टेनेंट प्रोफ़ाइल निर्यात करने में असमर्थ: {{error}}", + "tenant-profile-file": "टेनेंट प्रोफ़ाइल फ़ाइल", + "invalid-tenant-profile-file-error": "टेनेंट प्रोफ़ाइल आयात करने में असमर्थ: अमान्य टेनेंट प्रोफ़ाइल डेटा संरचना।", + "advanced-settings": "उन्नत सेटिंग्स", + "entities": "एंटिटीज़", + "rule-engine": "रूल इंजन", + "time-to-live": "टाइम-टू-लाइव", + "calculated-fields": "कैल्क्युलेटेड फ़ील्ड्स", + "alarms-and-notifications": "अलार्म और अधिसूचनाएँ", + "ota-files-in-bytes": "फ़ाइलें", + "ws-title": "WS", + "unlimited": "(0 - अनलिमिटेड)", + "maximum-devices": "डिवाइस की अधिकतम संख्या", + "maximum-devices-required": "डिवाइस की अधिकतम संख्या आवश्यक है।", + "maximum-devices-range": "डिवाइस की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-assets": "एसेट की अधिकतम संख्या", + "maximum-assets-required": "एसेट की अधिकतम संख्या आवश्यक है।", + "maximum-assets-range": "एसेट की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-customers": "कस्टमर की अधिकतम संख्या", + "maximum-customers-required": "कस्टमर की अधिकतम संख्या आवश्यक है।", + "maximum-customers-range": "कस्टमर की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-users": "उपयोगकर्ताओं की अधिकतम संख्या", + "maximum-users-required": "उपयोगकर्ताओं की अधिकतम संख्या आवश्यक है।", + "maximum-users-range": "उपयोगकर्ताओं की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-dashboards": "डैशबोर्ड्स की अधिकतम संख्या", + "maximum-dashboards-required": "डैशबोर्ड्स की अधिकतम संख्या आवश्यक है।", + "maximum-dashboards-range": "डैशबोर्ड्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-edges": "एज की अधिकतम संख्या", + "maximum-edges-required": "एज की अधिकतम संख्या आवश्यक है।", + "maximum-edges-range": "एज की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-rule-chains": "रूल चेन्स की अधिकतम संख्या", + "maximum-rule-chains-required": "रूल चेन्स की अधिकतम संख्या आवश्यक है।", + "maximum-rule-chains-range": "रूल चेन्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "maximum-resources-sum-data-size": "रिसोर्स फ़ाइलों के कुल अधिकतम आकार (बाइट्स)", + "maximum-resources-sum-data-size-required": "रिसोर्स फ़ाइलों के कुल अधिकतम आकार की मान आवश्यक है।", + "maximum-resources-sum-data-size-range": "रिसोर्स फ़ाइलों के कुल अधिकतम आकार का मान नकारात्मक नहीं हो सकता", + "maximum-resource-size": "अधिकतम रिसोर्स फ़ाइल आकार (बाइट्स)", + "maximum-resource-size-required": "अधिकतम रिसोर्स फ़ाइल आकार आवश्यक है", + "maximum-resource-size-range": "अधिकतम रिसोर्स फ़ाइल आकार नकारात्मक नहीं हो सकता", + "maximum-ota-packages-sum-data-size": "OTA पैकेज फ़ाइलों के कुल अधिकतम आकार (बाइट्स)", + "maximum-ota-package-sum-data-size-required": "OTA पैकेज फ़ाइलों के कुल अधिकतम आकार की मान आवश्यक है।", + "maximum-ota-package-sum-data-size-range": "OTA पैकेज फ़ाइलों के कुल अधिकतम आकार का मान नकारात्मक नहीं हो सकता", + "maximum-debug-duration-min": "अधिकतम डीबग अवधि (मिनट)", + "maximum-debug-duration-min-range": "अधिकतम डीबग अवधि नकारात्मक नहीं हो सकती", + "rest-requests-for-tenant": "टेनेंट के लिए REST रिक्वेस्ट्स", + "transport-tenant-telemetry-msg-rate-limit": "ट्रांसपोर्ट टेनेंट टेलीमेट्री मैसेजेस", + "transport-tenant-telemetry-data-points-rate-limit": "ट्रांसपोर्ट टेनेंट टेलीमेट्री डेटा पॉइंट्स", + "transport-device-msg-rate-limit": "ट्रांसपोर्ट डिवाइस मैसेजेस", + "transport-device-telemetry-msg-rate-limit": "ट्रांसपोर्ट डिवाइस टेलीमेट्री मैसेजेस", + "transport-device-telemetry-data-points-rate-limit": "ट्रांसपोर्ट डिवाइस टेलीमेट्री डेटा पॉइंट्स", + "transport-gateway-msg-rate-limit": "ट्रांसपोर्ट Gateway संदेश", + "transport-gateway-telemetry-msg-rate-limit": "ट्रांसपोर्ट Gateway टेलीमेट्री संदेश", + "transport-gateway-telemetry-data-points-rate-limit": "ट्रांसपोर्ट Gateway टेलीमेट्री डेटा पॉइंट्स", + "transport-gateway-device-msg-rate-limit": "ट्रांसपोर्ट Gateway डिवाइस संदेश", + "transport-gateway-device-telemetry-msg-rate-limit": "ट्रांसपोर्ट Gateway डिवाइस टेलीमेट्री संदेश", + "transport-gateway-device-telemetry-data-points-rate-limit": "ट्रांसपोर्ट Gateway डिवाइस टेलीमेट्री डेटा पॉइंट्स", + "tenant-entity-export-rate-limit": "एंटिटी वर्ज़न क्रिएशन", + "tenant-entity-import-rate-limit": "एंटिटी वर्ज़न लोड", + "tenant-notification-request-rate-limit": "अधिसूचना रिक्वेस्ट्स", + "tenant-notification-requests-per-rule-rate-limit": "प्रति नोटिफिकेशन रूल अधिसूचना रिक्वेस्ट्स", + "max-calculated-fields": "प्रति एंटिटी कैलक्युलेटेड फ़ील्ड्स की अधिकतम संख्या", + "max-calculated-fields-range": "प्रति एंटिटी कैलक्युलेटेड फ़ील्ड्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-calculated-fields-required": "प्रति एंटिटी कैलक्युलेटेड फ़ील्ड्स की अधिकतम संख्या आवश्यक है", + "max-data-points-per-rolling-arg": "रोलिंग आर्ग्युमेंट्स में डेटा पॉइंट्स की अधिकतम संख्या", + "max-data-points-per-rolling-arg-range": "रोलिंग आर्ग्युमेंट्स में डेटा पॉइंट्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-data-points-per-rolling-arg-required": "रोलिंग आर्ग्युमेंट्स में डेटा पॉइंट्स की अधिकतम संख्या आवश्यक है", + "max-arguments-per-cf": "प्रति कैलक्युलेटेड फ़ील्ड आर्ग्युमेंट्स की अधिकतम संख्या", + "max-arguments-per-cf-range": "प्रति कैलक्युलेटेड फ़ील्ड आर्ग्युमेंट्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-arguments-per-cf-required": "प्रति कैलक्युलेटेड फ़ील्ड आर्ग्युमेंट्स की अधिकतम संख्या आवश्यक है", + "max-state-size": "स्टेट का अधिकतम आकार (KB में)", + "max-state-size-range": "स्टेट का अधिकतम आकार (KB में) नकारात्मक नहीं हो सकता", + "max-state-size-required": "स्टेट का अधिकतम आकार (KB में) आवश्यक है", + "max-value-argument-size": "सिंगल वैल्यू आर्ग्युमेंट का अधिकतम आकार (KB में)", + "max-value-argument-size-range": "सिंगल वैल्यू आर्ग्युमेंट का अधिकतम आकार (KB में) नकारात्मक नहीं हो सकता", + "max-value-argument-size-required": "सिंगल वैल्यू आर्ग्युमेंट का अधिकतम आकार (KB में) आवश्यक है", + "max-transport-messages": "ट्रांसपोर्ट मैसेजेस की अधिकतम संख्या", + "max-transport-messages-required": "ट्रांसपोर्ट मैसेजेस की अधिकतम संख्या आवश्यक है।", + "max-transport-messages-range": "ट्रांसपोर्ट मैसेजेस की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-transport-data-points": "ट्रांसपोर्ट डेटा पॉइंट्स की अधिकतम संख्या", + "max-transport-data-points-required": "ट्रांसपोर्ट डेटा पॉइंट्स की अधिकतम संख्या आवश्यक है।", + "max-transport-data-points-range": "ट्रांसपोर्ट डेटा पॉइंट्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-r-e-executions": "रूल इंजन निष्पादनों की अधिकतम संख्या", + "max-r-e-executions-required": "रूल इंजन निष्पादनों की अधिकतम संख्या आवश्यक है.", + "max-r-e-executions-range": "रूल इंजन निष्पादनों की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-j-s-executions": "जावास्क्रिप्ट निष्पादनों की अधिकतम संख्या", + "max-j-s-executions-required": "जावास्क्रिप्ट निष्पादनों की अधिकतम संख्या आवश्यक है.", + "max-j-s-executions-range": "जावास्क्रिप्ट निष्पादनों की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "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": "डेटा पॉइंट्स स्टोरेज दिनों की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "default-storage-ttl-days": "डिफॉल्ट स्टोरेज TTL (दिनों में)", + "default-storage-ttl-days-required": "डिफॉल्ट स्टोरेज TTL (दिनों में) आवश्यक है.", + "default-storage-ttl-days-range": "डिफॉल्ट स्टोरेज TTL (दिनों में) नकारात्मक नहीं हो सकता", + "alarms-ttl-days": "अलार्म्स TTL (दिनों में)", + "alarms-ttl-days-required": "अलार्म्स TTL (दिनों में) आवश्यक है", + "alarms-ttl-days-days-range": "अलार्म्स TTL (दिनों में) नकारात्मक नहीं हो सकता", + "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": "भेजे गए SMS की अधिकतम संख्या", + "max-sms-required": "भेजे गए SMS की अधिकतम संख्या आवश्यक है.", + "max-sms-range": "भेजे गए SMS की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "max-created-alarms": "बनाए गए अलार्म्स की अधिकतम संख्या", + "max-created-alarms-required": "बनाए गए अलार्म्स की अधिकतम संख्या आवश्यक है.", + "max-created-alarms-range": "बनाए गए अलार्म्स की अधिकतम संख्या नकारात्मक नहीं हो सकती", + "no-queue": "कोई कतार कॉन्फ़िगर नहीं है", + "add-queue": "कतार जोड़ें", + "queues-with-count": "कतारें ({{count}})", + "tenant-rest-limits": "टेनेंट के लिए REST अनुरोध", + "customer-rest-limits": "कस्टमर के लिए REST अनुरोध", + "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-write-tenant-core-limits-configuration": "REST API Cassandra लेखन क्वेरीज़", + "cassandra-read-tenant-core-limits-configuration": "REST API और WS टेलीमेट्री Cassandra पठन क्वेरीज़", + "cassandra-write-tenant-rule-engine-limits-configuration": "रूल इंजन टेलीमेट्री Cassandra लेखन क्वेरीज़", + "cassandra-read-tenant-rule-engine-limits-configuration": "रूल इंजन टेलीमेट्री Cassandra पठन क्वेरीज़", + "ws-limit-max-sessions-per-tenant": "प्रति टेनेंट सत्रों की अधिकतम संख्या", + "ws-limit-max-sessions-per-customer": "प्रति कस्टमर सत्रों की अधिकतम संख्या", + "ws-limit-max-sessions-per-regular-user": "प्रति सामान्य उपयोगकर्ता सत्रों की अधिकतम संख्या", + "ws-limit-max-sessions-per-public-user": "प्रति सार्वजनिक उपयोगकर्ता सत्रों की अधिकतम संख्या", + "ws-limit-queue-per-session": "प्रति सत्र संदेश कतार का अधिकतम आकार", + "ws-limit-max-subscriptions-per-tenant": "प्रति टेनेंट सब्स्क्रिप्शन्स की अधिकतम संख्या", + "ws-limit-max-subscriptions-per-customer": "प्रति कस्टमर सब्स्क्रिप्शन्स की अधिकतम संख्या", + "ws-limit-max-subscriptions-per-regular-user": "प्रति सामान्य उपयोगकर्ता सब्स्क्रिप्शन्स की अधिकतम संख्या", + "ws-limit-max-subscriptions-per-public-user": "प्रति सार्वजनिक उपयोगकर्ता सब्स्क्रिप्शन्स की अधिकतम संख्या", + "ws-limit-updates-per-session": "प्रति सत्र WS अपडेट्स", + "rate-limits": { + "add-limit": "लिमिट जोड़ें", + "and-also-less-than": "और साथ ही इससे कम", + "advanced-settings": "एडवांस्ड सेटिंग्स", + "edit-limit": "लिमिट एडिट करें", + "calculated-field-debug-event-rate-limit": "कैलक्युलेटेड फ़ील्ड डिबग इवेंट्स", + "edit-calculated-field-debug-event-rate-limit": "कैलक्युलेटेड फ़ील्ड डिबग इवेंट्स के रेट लिमिट्स एडिट करें", + "edit-transport-tenant-msg-title": "ट्रांसपोर्ट टेनेंट संदेशों के रेट लिमिट्स एडिट करें", + "edit-transport-tenant-telemetry-msg-title": "ट्रांसपोर्ट टेनेंट टेलीमेट्री संदेशों के रेट लिमिट्स एडिट करें", + "edit-transport-tenant-telemetry-data-points-title": "ट्रांसपोर्ट टेनेंट टेलीमेट्री डेटा पॉइंट्स के रेट लिमिट्स एडिट करें", + "edit-transport-device-msg-title": "ट्रांसपोर्ट डिवाइस संदेशों के रेट लिमिट्स एडिट करें", + "edit-transport-device-telemetry-msg-title": "ट्रांसपोर्ट डिवाइस टेलीमेट्री संदेशों के रेट लिमिट्स एडिट करें", + "edit-transport-device-telemetry-data-points-title": "ट्रांसपोर्ट डिवाइस टेलीमेट्री डेटा पॉइंट्स के रेट लिमिट्स एडिट करें", + "edit-transport-gateway-msg-title": "ट्रांसपोर्ट Gateway संदेशों की रेट लिमिट्स संपादित करें", + "edit-transport-gateway-telemetry-msg-title": "ट्रांसपोर्ट Gateway टेलीमेट्री संदेशों की रेट लिमिट्स संपादित करें", + "edit-transport-gateway-telemetry-data-points-title": "ट्रांसपोर्ट Gateway टेलीमेट्री डेटा पॉइंट्स की रेट लिमिट्स संपादित करें", + "edit-transport-gateway-device-msg-title": "ट्रांसपोर्ट Gateway डिवाइस संदेशों की रेट लिमिट्स संपादित करें", + "edit-transport-gateway-device-telemetry-msg-title": "ट्रांसपोर्ट Gateway डिवाइस टेलीमेट्री संदेशों की रेट लिमिट्स संपादित करें", + "edit-transport-gateway-device-telemetry-data-points-title": "ट्रांसपोर्ट Gateway डिवाइस टेलीमेट्री डेटा पॉइंट्स की रेट लिमिट्स संपादित करें", + "edit-tenant-rest-limits-title": "टेनेंट के REST अनुरोधों के रेट लिमिट्स एडिट करें", + "edit-customer-rest-limits-title": "कस्टमर के REST अनुरोधों के रेट लिमिट्स एडिट करें", + "edit-ws-limit-updates-per-session-title": "प्रति सत्र WS अपडेट्स के रेट लिमिट्स एडिट करें", + "edit-cassandra-write-tenant-core-limits-configuration": "REST API Cassandra लेखन क्वेरीज़ के रेट लिमिट्स एडिट करें", + "edit-cassandra-read-tenant-core-limits-configuration": "REST API और WS टेलीमेट्री Cassandra पठन क्वेरीज़ के रेट लिमिट्स एडिट करें", + "edit-cassandra-write-tenant-rule-engine-limits-configuration": "रूल इंजन टेलीमेट्री Cassandra लेखन क्वेरीज़ के रेट लिमिट्स एडिट करें", + "edit-cassandra-read-tenant-rule-engine-limits-configuration": "रूल इंजन टेलीमेट्री 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": "प्रति नोटिफिकेशन रूल अधिसूचना रिक्वेस्ट्स के रेट लिमिट्स एडिट करें", + "edit-edge-events-rate-limit": "एज इवेंट्स के रेट लिमिट्स एडिट करें", + "edit-edge-events-per-edge-rate-limit": "प्रति एज एज इवेंट्स के रेट लिमिट्स एडिट करें", + "edge-events-rate-limit": "एज इवेंट्स", + "edge-events-per-edge-rate-limit": "प्रति एज एज इवेंट्स", + "edit-edge-uplink-messages-rate-limit": "एज अपलिंक संदेशों के रेट लिमिट्स एडिट करें", + "edit-edge-uplink-messages-per-edge-rate-limit": "प्रति एज एज अपलिंक संदेशों के रेट लिमिट्स एडिट करें", + "edge-uplink-messages-rate-limit": "एज अपलिंक संदेश", + "edge-uplink-messages-per-edge-rate-limit": "प्रति एज एज अपलिंक संदेश", + "messages-per": "संदेश प्रति", + "not-set": "सेट नहीं है", + "number-of-messages": "संदेशों की संख्या", + "number-of-messages-required": "संदेशों की संख्या आवश्यक है.", + "number-of-messages-min": "न्यूनतम मान 1 है.", + "preview": "प्रीव्यू", + "per-seconds": "प्रति सेकंड", + "per-seconds-required": "टाइम रेट आवश्यक है।", + "per-seconds-min": "न्यूनतम मान 1 है।", + "per-seconds-duplicate": "डुप्लिकेट टाइम रेट। हर टाइम इंटरवल यूनिक होना चाहिए।", + "rate-limits": "रेट लिमिट्स", + "remove-limit": "लिमिट हटाएँ", + "transport-tenant-msg": "ट्रांसपोर्ट टेनेंट संदेश", + "transport-tenant-telemetry-msg": "ट्रांसपोर्ट टेनेंट टेलीमेट्री संदेश", + "transport-tenant-telemetry-data-points": "ट्रांसपोर्ट टेनेंट टेलीमेट्री डेटा पॉइंट्स", + "transport-device-msg": "ट्रांसपोर्ट डिवाइस संदेश", + "transport-device-telemetry-msg": "ट्रांसपोर्ट डिवाइस टेलीमेट्री संदेश", + "transport-device-telemetry-data-points": "ट्रांसपोर्ट डिवाइस टेलीमेट्री डेटा पॉइंट्स", + "transport-gateway-msg": "ट्रांसपोर्ट Gateway संदेश", + "transport-gateway-telemetry-msg": "ट्रांसपोर्ट Gateway टेलीमेट्री संदेश", + "transport-gateway-telemetry-data-points": "ट्रांसपोर्ट Gateway टेलीमेट्री डेटा पॉइंट्स", + "transport-gateway-device-msg": "ट्रांसपोर्ट Gateway डिवाइस संदेश", + "transport-gateway-device-telemetry-msg": "ट्रांसपोर्ट Gateway डिवाइस टेलीमेट्री संदेश", + "transport-gateway-device-telemetry-data-points": "ट्रांसपोर्ट Gateway डिवाइस टेलीमेट्री डेटा पॉइंट्स", + "sec": "सेकंड" + } + }, + "timeinterval": { + "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": "मिनट", + "seconds": "सेकंड", + "advanced": "एडवांस्ड", + "custom": "कस्टम", + "predefined": { + "yesterday": "कल", + "day-before-yesterday": "परसों", + "this-day-last-week": "पिछले सप्ताह का यही दिन", + "previous-week": "पिछला सप्ताह (रवि - शनि)", + "previous-week-iso": "पिछला सप्ताह (सोम - रवि)", + "previous-month": "पिछला महीना", + "previous-quarter": "पिछली तिमाही", + "previous-half-year": "पिछला आधा वर्ष", + "previous-year": "पिछला वर्ष", + "current-hour": "वर्तमान घंटा", + "current-day": "आज", + "current-day-so-far": "आज तक", + "current-week": "वर्तमान सप्ताह (रवि - शनि)", + "current-week-iso": "वर्तमान सप्ताह (सोम - रवि)", + "current-week-so-far": "वर्तमान सप्ताह अब तक (रवि - शनि)", + "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": "वर्तमान वर्ष अब तक" + }, + "type": { + "week": "सप्ताह (रवि - शनि)", + "week-iso": "सप्ताह (सोम - रवि)", + "month": "महीना", + "quarter": "तिमाही" + } + }, + "timeunit": { + "milliseconds": "मिलीसेकंड", + "seconds": "सेकंड", + "minutes": "मिनट", + "hours": "घंटे", + "days": "दिन" + }, + "timewindow": { + "timewindow": "टाइम विंडो", + "timewindow-settings": "टाइम विंडो सेटिंग्स", + "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": { + "years": "{ years, plural, =1 {1 वर्ष } other {# वर्ष } }", + "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": "अंतराल", + "just-now": "अभी-अभी", + "just-now-lower": "अभी-अभी", + "ago": "पहले", + "style": "टाइम विंडो शैली", + "icon": "आइकन", + "icon-position": "आइकन स्थिति", + "icon-position-left": "बाएँ", + "icon-position-right": "दाएँ", + "font": "फ़ॉन्ट", + "color": "रंग", + "displayTypePrefix": "रीयलटाइम/हिस्ट्री प्रीफ़िक्स दिखाएँ", + "preview": "पूर्वावलोकन", + "relative": "सापेक्ष", + "range": "रेंज", + "hide-timewindow-section": "अंतिम उपयोगकर्ताओं से टाइम विंडो सेक्शन छिपाएँ", + "hide-last-interval": "अंतिम उपयोगकर्ताओं से \"पिछले\" अंतराल छिपाएँ", + "hide-relative-interval": "अंतिम उपयोगकर्ताओं से सापेक्ष अंतराल छिपाएँ", + "hide-fixed-interval": "अंतिम उपयोगकर्ताओं से स्थिर अंतराल छिपाएँ", + "hide-aggregation": "अंतिम उपयोगकर्ताओं से एग्रीगेशन छिपाएँ", + "hide-group-interval": "अंतिम उपयोगकर्ताओं से ग्रुपिंग अंतराल छिपाएँ", + "hide-max-values": "अंतिम उपयोगकर्ताओं से अधिकतम मान छिपाएँ", + "hide-timezone": "अंतिम उपयोगकर्ताओं से टाइम ज़ोन छिपाएँ", + "disable-custom-interval": "कस्टम अंतराल चयन निष्क्रिय करें", + "edit-aggregation-functions-list": "एग्रीगेशन फ़ंक्शन्स की सूची संपादित करें", + "edit-aggregation-functions-list-hint": "उपलब्ध विकल्पों की सूची निर्दिष्ट की जा सकती है।", + "allowed-aggregation-functions": "अनुमत एग्रीगेशन फ़ंक्शन्स", + "edit-intervals-list": "अंतराल सूची संपादित करें", + "allowed-agg-intervals": "ग्रुपिंग अंतराल", + "default-agg-interval": "डिफ़ॉल्ट ग्रुपिंग अंतराल", + "edit-intervals-list-hint": "उपलब्ध अंतराल विकल्पों की सूची निर्दिष्ट की जा सकती है।", + "edit-grouping-intervals-list-hint": "ग्रुपिंग अंतरालों की सूची और डिफ़ॉल्ट ग्रुपिंग अंतराल को कॉन्फ़िगर करना संभव है।", + "all": "सभी" + }, + "tooltip": { + "trigger": "ट्रिगर", + "trigger-point": "बिंदु", + "trigger-axis": "अक्ष", + "label": "लेबल", + "value": "मान", + "date": "तिथि", + "show-date-time-interval": "तिथि-समय अंतराल दिखाएँ", + "show-date-time-interval-hint": "डेटा एग्रीगेशन के अनुसार तिथि-समय अंतराल दिखाएँ।", + "hide-zero-tooltip-values": "शून्य मान छिपाएँ", + "show-stack-total": "स्टैक मोड में कुल मान दिखाएँ", + "background-color": "पृष्ठभूमि रंग", + "background-blur": "पृष्ठभूमि ब्लर" + }, + "unit": { + "set-unit-conversion": "इकाई रूपांतरण सेट करें", + "unit-settings": { + "unit-settings": "इकाई सेटिंग्स", + "source-unit": "स्रोत इकाई", + "source-unit-hint": "यह संग्रहित मान की इकाई है, यानी वह इकाई जिससे आप रूपांतरण कर रहे हैं। वह प्रतीक दर्ज करें जो आपका स्रोत डेटा उपयोग करता है (जैसे m, km, ft, in)।", + "target-metric-unit": "टारगेट मेट्रिक इकाई", + "target-metric-unit-hint": "वह मेट्रिक (SI) इकाई चुनें जिसमें आप अपने स्रोत मान को बदलना चाहते हैं (जैसे cm, mm, km)।", + "target-imperial-unit": "टारगेट इम्पीरियल इकाई", + "target-imperial-unit-hint": "वह इम्पीरियल इकाई चुनें जिसमें आप अपने स्रोत मान को बदलना चाहते हैं (जैसे in, ft, yd)।", + "target-hybrid-unit": "टारगेट हाइब्रिड इकाई", + "target-hybrid-unit-hint": "वह हाइब्रिड इकाई चुनें जिसमें आप अपने स्रोत मान को बदलना चाहते हैं (जैसे cm, in, km)। हाइब्रिड इकाइयाँ मेट्रिक या इम्पीरियल इकाइयों को मिलाती हैं।", + "enable-unit-conversion": "इकाई रूपांतरण सक्षम करें", + "enable-unit-conversion-hint": "रूपांतरण सक्रिय करने के लिए ऑन करें। ऑफ होने पर आपका स्रोत मान बिना बदले चला जाएगा। यदि संबंधित माप समूह (जैसे Luminous flux, AQI) में केवल एक ही इकाई हो, तो यह निष्क्रिय रहता है।" + }, + "unit-system": "इकाई सिस्टम", + "unit-system-type": { + "AUTO": "ऑटो", + "METRIC": "मेट्रिक", + "IMPERIAL": "इम्पीरियल", + "HYBRID": "हाइब्रिड" + }, + "measures": { + "absorbed-dose-rate": "अवशोषित डोज दर", + "acceleration": "त्वरण", + "acidity": "अम्लता", + "air-quality-index": "वायु गुणवत्ता सूचकांक", + "amount-of-substance": "पदार्थ की मात्रा", + "angle": "कोण", + "angular-acceleration": "कोणीय त्वरण", + "area": "क्षेत्रफल", + "area-density": "क्षेत्रीय घनत्व", + "capacitance": "धारिता", + "catalytic-activity": "उत्प्रेरक सक्रियता", + "catalytic-concentration": "उत्प्रेरक सान्द्रता", + "charge": "आवेश", + "current-density": "धारा घनत्व", + "data-transfer-rate": "डेटा ट्रांसफ़र दर", + "density": "घनत्व", + "digital": "डिजिटल", + "dimension-ratio": "आयाम अनुपात", + "dynamic-viscosity": "गतिशील श्यानता", + "earthquake-magnitude": "भूकंप परिमाण", + "electric-charge-density": "विद्युत आवेश घनत्व", + "electric-current": "विद्युत धारा", + "electric-dipole-moment": "विद्युत द्विध्रुव आघूर्ण", + "electric-field-strength": "विद्युत क्षेत्र की तीव्रता", + "electric-flux": "विद्युत फ्लक्स", + "electric-permittivity": "विद्युत पारगम्यता", + "electric-polarizability": "विद्युत ध्रुवणक्षमता", + "electrical-conductance": "विद्युत चालकता", + "electrical-conductivity": "विद्युत चालकता गुणांक", + "energy": "ऊर्जा", + "energy-density": "ऊर्जा घनत्व", + "force": "बल", + "frequency": "आवृत्ति", + "fuel-efficiency": "ईंधन दक्षता", + "heat-capacity": "ऊष्मा धारिता", + "illuminance": "प्रकाशितता", + "inductance": "प्रेरकत्व", + "kinematic-viscosity": "गतिज श्यानता", + "length": "लंबाई", + "light-exposure": "प्रकाश एक्सपोज़र", + "linear-charge-density": "रेखीय आवेश घनत्व", + "logarithmic-ratio": "लघुगणकीय अनुपात", + "luminous-efficacy": "आलोक दक्षता", + "luminous-flux": "आलोक फ्लक्स", + "luminous-intensity": "आलोक तीव्रता", + "magnetic-field-gradient": "चुंबकीय क्षेत्र ग्रेडिएंट", + "magnetic-flux": "चुंबकीय फ्लक्स", + "magnetic-flux-density": "चुंबकीय फ्लक्स घनत्व", + "magnetic-moment": "चुंबकीय आघूर्ण", + "magnetic-permeability": "चुंबकीय पारगम्यता", + "mass": "द्रव्यमान", + "mass-fraction": "द्रव्यमान अंश", + "molar-concentration": "मोलर सांद्रता", + "molar-energy": "मोलर ऊर्जा", + "molar-heat-capacity": "मोलर ऊष्मा धारिता", + "molar-mass": "मोलर द्रव्यमान", + "number-concentration": "संख्या सांद्रता", + "parts-per-million": "पार्ट्स पर मिलियन", + "power": "शक्ति", + "power-density": "शक्ति घनत्व", + "pressure": "दाब", + "radiance": "दीप्ति", + "radiant-intensity": "दीप्त तीव्रता", + "radiation-dose": "विकिरण मात्रा", + "radioactive-decay": "रेडियोधर्मी अपक्षय", + "radioactivity": "रेडियोधर्मिता", + "radioactivity-concentration": "रेडियोधर्मिता सांद्रता", + "reciprocal-length": "प्रतिलोम लंबाई", + "resistance": "प्रतिरोध", + "reynolds-number": "रेनॉल्ड्स संख्या", + "signal-level": "सिग्नल स्तर", + "solid-angle": "घन कोण", + "specific-energy": "विशिष्ट ऊर्जा", + "specific-heat-capacity": "विशिष्ट ऊष्मा धारिता", + "specific-humidity": "विशिष्ट आर्द्रता", + "specific-volume": "विशिष्ट आयतन", + "speed": "वेग", + "surface-charge-density": "सतही आवेश घनत्व", + "surface-tension": "पृष्ठ तनाव", + "temperature": "तापमान", + "thermal-conductivity": "ऊष्मीय चालकता", + "time": "समय", + "torque": "टॉर्क", + "turbidity": "मलिनता", + "voltage": "वोल्टेज", + "volume": "आयतन", + "volume-flow": "आयतनिक प्रवाह" + }, + "millimeter": "मिलीमीटर", + "centimeter": "सेंटीमीटर", + "decimeter": "डेसीमीटर", + "angstrom": "आंग्स्ट्रॉम", + "nanometer": "नैनोमीटर", + "micrometer": "माइक्रोमीटर", + "meter": "मीटर", + "kilometer": "किलोमीटर", + "inch": "इंच", + "foot": "फुट", + "foot-us": "फुट (US सर्वे)", + "yard": "यार्ड", + "mile": "मील", + "nautical-mile": "समुद्री मील", + "astronomical-unit": "खगोलीय इकाई", + "reciprocal-metre": "प्रति मीटर", + "meter-per-meter": "मीटर प्रति मीटर", + "steradian": "स्टेरैडियन", + "thou": "थाउ", + "barleycorn": "बार्लीकॉर्न", + "hand": "हैंड", + "chain": "चेन", + "furlong": "फर्लांग", + "league": "लीग", + "fathom": "फैथम", + "cable": "केबल", + "link": "लिंक", + "rod": "रॉড", + "nanogram": "नैनोग्राम", + "microgram": "माइक्रोग्राम", + "milligram": "मिलिग्राम", + "gram": "ग्राम", + "kilogram": "किलोग्राम", + "tonne": "टन", + "ounce": "औंस", + "pound": "पाउंड", + "stone": "स्टोन", + "hundredweight-count": "हंड्रेडवेट काउंट", + "short-tons": "शॉर्ट टन", + "dalton": "डाल्टन", + "grain": "ग्रेन", + "drachm": "ड्राम", + "quarter": "क्वार्टर", + "slug": "स्लग", + "carat": "कैरेट", + "cubic-millimeter": "घन मिलीमीटर", + "cubic-centimeter": "घन सेंटीमीटर", + "cubic-meter": "घन मीटर", + "cubic-kilometer": "घन किलोमीटर", + "microliter": "माइक्रोलीटर", + "milliliter": "मिलीलीटर", + "liter": "लीटर", + "hectoliter": "हेक्टोलीटर", + "cubic-inch": "घन इंच", + "cubic-foot": "घन फुट", + "cubic-yard": "घन यार्ड", + "fluid-ounce": "फ्लूइड औंस", + "fluid-ounce-per-second": "फ्लूइड औंस प्रति सेकंड", + "pint": "पिंट", + "quart": "क्वार्ट", + "gallon": "गैलन", + "oil-barrels": "ऑयल बैरल", + "cubic-meter-per-kilogram": "किलोग्राम प्रति घन मीटर", + "gill": "गिल", + "hogshead": "हॉग्सहेड", + "teaspoon": "टीस्पून", + "tablespoon": "टेबलस्पून", + "cup": "कप", + "celsius": "सेल्सियस", + "kelvin": "केल्विन", + "rankine": "रैंकिन", + "fahrenheit": "फारेनहाइट", + "percent": "प्रतिशत", + "meter-per-second": "मीटर प्रति सेकंड", + "kilometer-per-hour": "किलोमीटर प्रति घंटा", + "foot-per-second": "फुट प्रति सेकंड", + "foot-per-minute": "फुट प्रति मिनट", + "mile-per-hour": "मील प्रति घंटा", + "knot": "नॉट", + "inch-per-second": "इंच प्रति सेकंड", + "inch-per-hour": "इंच प्रति घंटा", + "millimeters-per-minute": "मिलीमीटर प्रति मिनट", + "meter-per-minute": "मीटर प्रति मिनट", + "kilometer-per-hour-squared": "किलोमीटर प्रति घंटा वर्ग", + "foot-per-second-squared": "फुट प्रति सेकंड वर्ग", + "pascal": "पास्कल", + "kilopascal": "किलोपास्कल", + "megapascal": "मेगापास्कल", + "gigapascal": "गिगापास्कल", + "millibar": "मिलीबार", + "bar": "बार", + "kilobar": "किलोबार", + "newton": "न्यूटन", + "newton-meter": "न्यूटन मीटर", + "foot-pounds": "फुट-पाउंड", + "inch-pounds": "इंच-पाउंड", + "newton-per-meter": "न्यूटन प्रति मीटर", + "atmospheres": "एटमॉस्फियर", + "pounds-per-square-inch": "पाउंड प्रति वर्ग इंच", + "kilopound-per-square-inch": "किलोपाउंड प्रति वर्ग इंच", + "torr": "टॉर", + "inches-of-mercury": "इंच ऑफ मर्करी", + "pascal-per-square-meter": "पास्कल प्रति वर्ग मीटर", + "pound-per-square-inch": "पाउंड प्रति वर्ग इंच", + "newton-per-square-meter": "न्यूटन प्रति वर्ग मीटर", + "kilogram-force-per-square-meter": "किलोग्राम-फोर्स प्रति वर्ग मीटर", + "pascal-per-square-centimeter": "पास्कल प्रति वर्ग सेंटीमीटर", + "ton-force-per-square-inch": "टन-फोर्स प्रति वर्ग इंच", + "kilonewton-per-square-meter": "किलोन्यूटन प्रति वर्ग मीटर", + "newton-per-square-millimeter": "न्यूटन प्रति वर्ग मिलीमीटर", + "microjoule": "माइक्रोजूल", + "millijoule": "मिलीजूल", + "joule": "जूल", + "kilojoule": "किलोजूल", + "megajoule": "मेगाजूल", + "gigajoule": "गिगाजूल", + "watt-hour": "वॉट-घंटा", + "watt-minute": "वॉट-मिनट", + "kilowatt-hour": "किलोवॉट-घंटा", + "milliwatt-hour": "मिलीवॉट-घंटा", + "megawatt-hour": "मेगावॉट-घंटा", + "gigawatt-hour": "गिगावॉट-घंटा", + "electron-volts": "इलेक्ट्रॉन वोल्ट", + "joules-per-coulomb": "जूल प्रति कूलॉम", + "british-thermal-unit": "ब्रिटिश थर्मल यूनिट", + "thousand-british-thermal-unit": "हज़ार ब्रिटिश थर्मल यूनिट", + "million-british-thermal-unit": "मिलियन ब्रिटिश थर्मल यूनिट", + "foot-pound": "फुट-पाउंड", + "calorie": "कैलोरी", + "small-calorie": "छोटी कैलोरी", + "kilocalorie": "किलोकैलोरी", + "joule-per-kelvin": "जूल प्रति केल्विन", + "joule-per-kilogram-kelvin": "जूल प्रति किलोग्राम-केल्विन", + "joule-per-kilogram": "जूल प्रति किलोग्राम", + "watt-per-meter-kelvin": "वॉट प्रति मीटर-केल्विन", + "joule-per-cubic-meter": "जूल प्रति घन मीटर", + "therm": "थर्म", + "electric-dipole-moment": "विद्युत द्विध्रुव आघूर्ण", + "magnetic-dipole-moment": "चुंबकीय द्विध्रुव आघूर्ण", + "debye": "डेबाई", + "coulomb-per-square-meter-per-volt": "कूलॉम प्रति वर्ग मीटर प्रति वोल्ट", + "milliwatt": "मिलिवॉट", + "microwatt": "माइक्रोवॉट", + "watt": "वॉट", + "kilowatt": "किलोवॉट", + "megawatt": "मेगावॉट", + "gigawatt": "गिगावॉट", + "metric-horsepower": "मेट्रिक हॉर्सपावर", + "milliwatt-per-square-centimeter": "मिलिवॉट प्रति वर्ग सेंटीमीटर", + "watt-per-square-centimeter": "वॉट प्रति वर्ग सेंटीमीटर", + "kilowatt-per-square-centimeter": "किलोवॉट प्रति वर्ग सेंटीमीटर", + "milliwatt-per-square-meter": "मिलिवॉट प्रति वर्ग मीटर", + "watt-per-square-meter": "वॉट प्रति वर्ग मीटर", + "kilowatt-per-square-meter": "किलोवॉट प्रति वर्ग मीटर", + "watt-per-square-inch": "वॉट प्रति वर्ग इंच", + "kilowatt-per-square-inch": "किलोवॉट प्रति वर्ग इंच", + "horsepower": "हॉर्सपावर", + "btu-per-hour": "ब्रिटिश थर्मल यूनिट प्रति घंटा", + "btu-per-second": "ब्रिटिश थर्मल यूनिट प्रति सेकंड", + "btu-per-day": "ब्रिटिश थर्मल यूनिट प्रति दिन", + "mbtu-per-hour": "हज़ार ब्रिटिश थर्मल यूनिट प्रति घंटा", + "mbtu-per-second": "हज़ार ब्रिटिश थर्मल यूनिट प्रति सेकंड", + "mbtu-per-day": "हज़ार ब्रिटिश थर्मल यूनिट प्रति दिन", + "mmbtu-per-hour": "मिलियन ब्रिटिश थर्मल यूनिट प्रति घंटा", + "mmbtu-per-second": "मिलियन ब्रिटिश थर्मल यूनिट प्रति सेकंड", + "mmbtu-per-day": "मिलियन ब्रिटिश थर्मल यूनिट प्रति दिन", + "foot-pound-per-second": "फुट-पाउंड प्रति सेकंड", + "coulomb": "कूलॉम", + "millicoulomb": "मिलीकूलॉम", + "microcoulomb": "माइक्रोकूलॉम", + "nanocoulomb": "नैनोकूलॉम", + "picocoulomb": "पिको कूलॉम", + "coulomb-per-meter": "कूलॉम प्रति मीटर", + "coulomb-per-cubic-meter": "कूलॉम प्रति घन मीटर", + "coulomb-per-square-meter": "कूलॉम प्रति वर्ग मीटर", + "square-millimeter": "वर्ग मिलीमीटर", + "square-centimeter": "वर्ग सेंटीमीटर", + "square-meter": "वर्ग मीटर", + "hectare": "हेक्टेयर", + "square-kilometer": "वर्ग किलोमीटर", + "square-inch": "वर्ग इंच", + "square-foot": "वर्ग फुट", + "square-yard": "वर्ग गज", + "acre": "एकड़", + "square-mile": "वर्ग मील", + "are": "आर", + "barn": "बार्न", + "circular-inch": "परिपत्र इंच", + "milliampere-hour": "मिलिअंपियर-घंटा", + "ampere-hours": "अंपियर-घंटे", + "kiloampere-hours": "किलोअंपियर-घंटे", + "nanoampere": "नैनोअंपियर", + "picoampere": "पिकोअंपियर", + "microampere": "माइक्रोअंपियर", + "milliampere": "मिलिअंपियर", + "ampere": "अंपियर", + "kiloampere": "किलोअंपियर", + "megaampere": "मेगा-अंपियर", + "gigaampere": "गिगा-अंपियर", + "microampere-per-square-centimeter": "माइक्रोअंपियर प्रति वर्ग सेंटीमीटर", + "ampere-per-square-meter": "अंपियर प्रति वर्ग मीटर", + "ampere-per-meter": "अंपियर प्रति मीटर", + "oersted": "ओएरस्टेड", + "bohr-magneton": "बोर मैग्नेटॉन", + "ampere-meter-squared": "अंपियर-मीटर स्क्वेर्ड", + "nanovolt": "नैनोवोल्ट", + "picovolt": "पिकोवोल्ट", + "millivolt": "मिलिवोल्ट", + "microvolt": "माइक्रोवोल्ट", + "volt": "वोल्ट", + "kilovolt": "किलोवोल्ट", + "megavolt": "मेगावोल्ट", + "dbmV": "डेसीबेल वोल्ट", + "dbm": "डेसीबेल-मिलिवॉट्स", + "volt-meter": "वोल्ट-मीटर", + "kilovolt-meter": "किलोवोल्ट-मीटर", + "megavolt-meter": "मेगावोल्ट-मीटर", + "microvolt-meter": "माइक्रोवोल्ट-मीटर", + "millivolt-meter": "मिलिवोल्ट-मीटर", + "nanovolt-meter": "नैनोवोल्ट-मीटर", + "ohm": "ओम", + "microohm": "माइक्रोओम", + "milliohm": "मिलिओम", + "kilohm": "किलोओम", + "megohm": "मेगाओम", + "gigohm": "गिगाओम", + "millihertz": "मिलिहर्ट्ज़", + "hertz": "हर्ट्ज़", + "kilohertz": "किलोहर्ट्ज़", + "megahertz": "मेगाहर्ट्ज़", + "gigahertz": "गिगाहर्ट्ज़", + "terahertz": "टेराहर्ट्ज़", + "rpm": "प्रति मिनट घूर्णन", + "candela-per-square-meter": "कैंडेला प्रति वर्ग मीटर", + "candela": "कैंडेला", + "lumen": "लुमेन", + "lux": "लक्स", + "foot-candle": "फ़ुट-कैंडल", + "lumen-per-square-meter": "लुमेन प्रति वर्ग मीटर", + "lux-second": "लक्स सेकंड", + "lumen-second": "लुमेन सेकंड", + "lumens-per-watt": "लुमेन प्रति वॉट", + "mole": "मोल", + "nanomole": "नैनोमोल", + "micromole": "माइक्रोमोल", + "millimole": "मिलिमोल", + "kilomole": "किलोमोल", + "mole-per-cubic-meter": "मोल प्रति घन मीटर", + "rssi": "प्राप्त सिग्नल शक्ति सूचक", + "ppm": "पार्ट्स पर मिलियन", + "ppb": "पार्ट्स पर बिलियन", + "micrograms-per-cubic-meter": "माइक्रोग्राम प्रति घन मीटर", + "aqi": "AQI", + "gram-per-cubic-meter": "ग्राम प्रति घन मीटर", + "gram-per-kilogram": "विशिष्ट आर्द्रता", + "millimeters-per-second": "मिलीमीटर प्रति सेकंड", + "neper": "नेपर", + "bel": "बेल", + "decibel": "डेसीबेल", + "meters-per-second-squared": "मीटर प्रति सेकंड वर्ग", + "becquerel": "बेकरेल", + "curie": "क्यूरी", + "gray": "ग्रे", + "sievert": "सीवर्ट", + "roentgen": "रोएंटजन", + "cps": "प्रति सेकंड काउंट", + "rad": "रैड", + "rem": "रेम", + "dps": "प्रति सेकंड विघटन", + "rutherford": "रदरफोर्ड", + "coulombs-per-kilogram": "कूलॉम्ब प्रति किलोग्राम", + "becquerels-per-cubic-meter": "बेकरेल प्रति घन मीटर", + "curies-per-liter": "क्यूरी प्रति लीटर", + "becquerels-per-second": "बेकरेल प्रति सेकंड", + "curies-per-second": "क्यूरी प्रति सेकंड", + "gy-per-second": "ग्रे प्रति सेकंड", + "watt-per-steradian": "वॉट प्रति स्टीरेडियन", + "watt-per-square-metre-steradian": "वॉट प्रति वर्ग मीटर-स्टीरेडियन", + "ph-level": "pH स्तर", + "turbidity": "टर्बिडिटी", + "mg-per-liter": "मिलिग्राम प्रति लीटर", + "microsiemens-per-centimeter": "माइक्रोसीमेन्स प्रति सेंटीमीटर", + "millisiemens-per-meter": "मिलिसीमेन्स प्रति मीटर", + "siemens-per-meter": "सीमेन्स प्रति मीटर", + "kilogram-per-cubic-meter": "किलोग्राम प्रति घन मीटर", + "gram-per-cubic-centimeter": "ग्राम प्रति घन सेंटीमीटर", + "kilogram-per-square-meter": "किलोग्राम प्रति वर्ग मीटर", + "milligram-per-milliliter": "मिलिग्राम प्रति मिलीलीटर", + "milligram-per-cubic-meter": "मिलिग्राम प्रति घन मीटर", + "pound-per-cubic-foot": "पाउंड प्रति घन फ़ुट", + "ounces-per-cubic-inch": "औंस प्रति घन इंच", + "tons-per-cubic-yard": "टन प्रति घन यार्ड", + "particle-density": "कण घनत्व", + "kilometers-per-liter": "किलोमीटर प्रति लीटर", + "miles-per-gallon": "मील प्रति गैलन", + "liters-per-100-km": "100 किलोमीटर पर लीटर", + "gallons-per-mile": "गैलन प्रति मील", + "liters-per-hour": "लीटर प्रति घंटा", + "gallons-per-hour": "गैलन प्रति घंटा", + "beats-per-minute": "बीट्स प्रति मिनट", + "millimeters-of-mercury": "पारे के मिलीमीटर", + "milligrams-per-deciliter": "मिलिग्राम प्रति डेसीलीटर", + "g-force": "जी-फ़ोर्स", + "kilonewton": "किलो न्यूटन", + "kilogram-force": "किलोग्राम-फ़ोर्स", + "pound-force": "पाउंड-फ़ोर्स", + "kilopound-force": "किलोपाउंड-फ़ोर्स", + "dyne": "डाइन", + "poundal": "पाउंडल", + "kip": "किप", + "gal": "गैल", + "gravity": "गुरुत्वाकर्षण", + "hectopascal": "हेक्टोपास्कल", + "atmosphere": "एटमॉस्फ़ियर", + "millibars": "मिलिबार", + "inch-of-mercury": "पारे का इंच", + "richter-scale": "रिक्टर स्केल", + "nanosecond": "नैनोसेकंड", + "microsecond": "माइक्रोसेकंड", + "millisecond": "मिलीसेकंड", + "second": "सेकंड", + "minute": "मिनट", + "hour": "घंटा", + "day": "दिन", + "week": "सप्ताह", + "month": "महीना", + "year": "साल", + "cubic-foot-per-minute": "घन फ़ुट प्रति मिनट", + "cubic-meters-per-hour": "घन मीटर प्रति घंटा", + "cubic-meters-per-second": "घन मीटर प्रति सेकंड", + "liter-per-second": "लीटर प्रति सेकंड", + "liter-per-minute": "लीटर प्रति मिनट", + "gallons-per-minute": "गैलन प्रति मिनट", + "cubic-foot-per-second": "घन फ़ुट प्रति सेकंड", + "milliliters-per-minute": "मिलीलीटर प्रति मिनट", + "cubic-decimeter-per-second": "घन डेसीमीटर प्रति सेकंड", + "bit": "बिट", + "byte": "बाइट", + "kilobyte": "किलोबाइट", + "megabyte": "मेगाबाइट", + "gigabyte": "गीगाबाइट", + "terabyte": "टेराबाइट", + "petabyte": "पेटाबाइट", + "exabyte": "एग्ज़ाबाइट", + "zettabyte": "ज़ेटाबाइट", + "yottabyte": "योटाबाइट", + "bit-per-second": "बिट प्रति सेकंड", + "kilobit-per-second": "किलोबिट प्रति सेकंड", + "megabit-per-second": "मेगाबिट प्रति सेकंड", + "gigabit-per-second": "गीगाबिट प्रति सेकंड", + "terabit-per-second": "टेराबिट प्रति सेकंड", + "byte-per-second": "बाइट प्रति सेकंड", + "kilobyte-per-second": "किलोबाइट प्रति सेकंड", + "megabyte-per-second": "मेगाबाइट प्रति सेकंड", + "gigabyte-per-second": "गीगाबाइट प्रति सेकंड", + "degree": "डिग्री", + "radian": "रेडियन", + "gradian": "ग्रेडियन", + "arcminute": "आर्कमिनट", + "arcsecond": "आर्कसेकंड", + "milliradian": "मिलीरैडियन", + "revolution": "रेवोल्यूशन", + "siemens": "सीमेन्स", + "millisiemens": "मिलीसीमेन्स", + "microsiemens": "माइक्रोसीमेन्स", + "kilosiemens": "किलोसीमेन्स", + "megasiemens": "मेगासीमेन्स", + "gigasiemens": "गीगासीमेन्स", + "farad": "फैराड", + "millifarad": "मिलिफैराड", + "microfarad": "माइक्रोफैराड", + "nanofarad": "नैनोफैराड", + "picofarad": "पिकोफैराड", + "kilofarad": "किलोफैराड", + "megafarad": "मेगाफैराड", + "gigafarad": "गीगाफैराड", + "terfarad": "टेराफैराड", + "farad-per-meter": "फैराड प्रति मीटर", + "tesla": "टेस्ला", + "gauss": "गॉस", + "kilogauss": "किलोगॉस", + "millitesla": "मिलिटेस्ला", + "microtesla": "माइक्रोटेस्ला", + "nanotesla": "नैनोटेस्ला", + "kilotesla": "किलोटेस्ला", + "megatesla": "मेगाटेस्ला", + "millitesla-square-meters": "मिलिटेस्ला वर्ग मीटर", + "gamma": "गामा", + "lambda": "लैम्ब्डा", + "square-meter-per-second": "वर्ग मीटर प्रति सेकंड", + "square-centimeter-per-second": "वर्ग सेंटीमीटर प्रति सेकंड", + "stoke": "स्टोक", + "centistokes": "सेंटीस्टोक्स", + "square-foot-per-second": "वर्ग फ़ुट प्रति सेकंड", + "square-inch-per-second": "वर्ग इंच प्रति सेकंड", + "pascal-second": "पास्कल-सेकंड", + "centipoise": "सेंटिपॉइज़", + "poise": "पॉइज़", + "reynolds": "रेनॉल्ड्स", + "pound-per-foot-hour": "पाउंड प्रति फ़ुट-घंटा", + "newton-second-per-square-meter": "न्यूटन सेकंड प्रति वर्ग मीटर", + "dyne-second-per-square-centimeter": "डाइन सेकंड प्रति वर्ग सेंटीमीटर", + "kilogram-per-meter-second": "किलोग्राम प्रति मीटर-सेकंड", + "tesla-square-meters": "टेस्ला वर्ग मीटर", + "maxwell": "मैक्सवेल", + "tesla-per-meter": "टेस्ला प्रति मीटर", + "gauss-per-centimeter": "गॉस प्रति सेंटीमीटर", + "weber": "वेबर", + "microweber": "माइक्रोवेबर", + "milliweber": "मिलिवेबर", + "gauss-square-centimeter": "गॉस वर्ग सेंटीमीटर", + "kilogauss-square-centimeter": "किलोगॉस वर्ग सेंटीमीटर", + "henry": "हेनरी", + "millihenry": "मिलिहेनरी", + "microhenry": "माइक्रोहेनरी", + "nanohenry": "नैनोहेनरी", + "henry-per-meter": "हेनरी प्रति मीटर", + "tesla-meter-per-ampere": "टेस्ला मीटर प्रति एंपियर", + "gauss-per-oersted": "गॉस प्रति ओएर्स्टेड", + "kilogram-per-mole": "किलोग्राम प्रति मोल", + "gram-per-mole": "ग्राम प्रति मोल", + "milligram-per-mole": "मिलिग्राम प्रति मोल", + "joule-per-mole": "जूल प्रति मोल", + "joule-per-mole-kelvin": "जूल प्रति मोल-केल्विन", + "millivolts-per-meter": "मिलिवोल्ट प्रति मीटर", + "volts-per-meter": "वोल्ट प्रति मीटर", + "kilovolts-per-meter": "किलोवोल्ट प्रति मीटर", + "radian-per-second": "रेडियन प्रति सेकंड", + "radian-per-second-squared": "रेडियन प्रति सेकंड स्क्वेयर", + "revolutions-per-minute-per-second": "कोणीय त्वरण", + "deg-per-second": "डिग्री प्रति सेकंड", + "rotation-per-minute": "रोटेशन प्रति मिनट", + "degrees-brix": "डिग्री ब्रिक्स", + "katal": "कैटल", + "katal-per-cubic-metre": "कैटल प्रति क्यूबिक मीटर", + "paris-inch": "पेरिस इंच" + }, + "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": "उपयोगकर्ता को सक्रिय करने के लिए निम्न सक्रियण लिंक का उपयोग करें ({{activationLinkTtl}} में समाप्त होगा) :", + "copy-activation-link": "सक्रियण लिंक कॉपी करें", + "activation-link-copied-message": "उपयोगकर्ता सक्रियण लिंक क्लिपबोर्ड पर कॉपी कर दिया गया है", + "details": "विवरण", + "login-as-tenant-admin": "टेनेंट एडमिन के रूप में लॉगिन करें", + "login-as-customer-user": "कस्टमर उपयोगकर्ता के रूप में लॉगिन करें", + "search": "उपयोगकर्ताओं को खोजें", + "selected-users": "{ count, plural, =1 {1 उपयोगकर्ता} other {# उपयोगकर्ताओं} } चुने गए", + "disable-account": "उपयोगकर्ता खाता निष्क्रिय करें", + "enable-account": "उपयोगकर्ता खाता सक्रिय करें", + "enable-account-message": "उपयोगकर्ता खाता सफलतापूर्वक सक्रिय कर दिया गया!", + "disable-account-message": "उपयोगकर्ता खाता सफलतापूर्वक निष्क्रिय कर दिया गया!", + "copyId": "उपयोगकर्ता Id कॉपी करें", + "idCopiedMessage": "उपयोगकर्ता Id क्लिपबोर्ड पर कॉपी कर दी गई है", + "user-list": "उपयोगकर्ताओं की सूची", + "user-list-required": "उपयोगकर्ताओं की सूची आवश्यक है" + }, + "value": { + "type": "मान का प्रकार", + "string": "स्ट्रिंग", + "string-value": "स्ट्रिंग मान", + "string-value-required": "स्ट्रिंग मान आवश्यक है", + "integer": "इंटीजर", + "integer-value": "इंटीजर मान", + "integer-value-required": "इंटीजर मान आवश्यक है", + "invalid-integer-value": "अमान्य इंटीजर मान", + "double": "डबल", + "double-value": "डबल मान", + "double-value-required": "डबल मान आवश्यक है", + "boolean": "बूलियन", + "boolean-value": "बूलियन मान", + "false": "फॉल्स", + "true": "ट्रू", + "long": "लॉन्ग", + "json": "JSON", + "json-value": "JSON मान", + "json-value-invalid": "JSON मान का फ़ॉर्मेट अमान्य है", + "json-value-required": "JSON मान आवश्यक है।" + }, + "version-control": { + "version-control": "वर्ज़न कंट्रोल", + "management": "वर्ज़न कंट्रोल प्रबंधन", + "search": "वर्ज़न खोजें", + "branch": "ब्रांच", + "default": "डिफ़ॉल्ट", + "select-branch": "ब्रांच चुनें", + "branch-required": "ब्रांच आवश्यक है", + "create-entity-version": "एंटिटी वर्ज़न बनाएँ", + "version-name": "वर्ज़न नाम", + "version-name-required": "वर्ज़न नाम आवश्यक है", + "author": "लेखक", + "export-relations": "रिलेशन एक्सपोर्ट करें", + "export-attributes": "विशेषताएँ एक्सपोर्ट करें", + "export-credentials": "क्रेडेंशियल्स एक्सपोर्ट करें", + "export-calculated-fields": "कैलक्युलेटेड फ़ील्ड्स एक्सपोर्ट करें", + "entity-versions": "एंटिटी वर्ज़न", + "versions": "वर्ज़न", + "created-time": "बनाने का समय", + "version-id": "वर्ज़न ID", + "no-entity-versions-text": "कोई एंटिटी वर्ज़न नहीं मिला", + "no-versions-text": "कोई वर्ज़न नहीं मिला", + "copy-full-version-id": "पूरा वर्ज़न ID कॉपी करें", + "create-version": "वर्ज़न बनाएँ", + "creating-version": "वर्ज़न बनाया जा रहा है... कृपया प्रतीक्षा करें", + "nothing-to-commit": "कमीट करने के लिए कोई बदलाव नहीं", + "restore-version": "वर्ज़न पुनर्स्थापित करें", + "restore-entity-from-version": "वर्ज़न '{{versionName}}' से एंटिटी पुनर्स्थापित करें", + "restoring-entity-version": "एंटिटी वर्ज़न पुनर्स्थापित किया जा रहा है... कृपया प्रतीक्षा करें", + "load-relations": "रिलेशन लोड करें", + "load-attributes": "विशेषताएँ लोड करें", + "load-credentials": "क्रेडेंशियल्स लोड करें", + "load-calculated-fields": "कैलक्युलेटेड फ़ील्ड्स लोड करें", + "compare-with-current": "वर्तमान से तुलना करें", + "diff-entity-with-version": "एंटिटी वर्ज़न '{{versionName}}' से तुलना", + "previous-difference": "पिछला अंतर", + "next-difference": "अगला अंतर", + "current": "वर्तमान", + "differences": "{ count, plural, =1 {1 अंतर} other {# अंतर} }", + "create-entities-version": "एंटिटीज़ वर्ज़न बनाएँ", + "default-sync-strategy": "डिफ़ॉल्ट सिंक स्ट्रैटेजी", + "sync-strategy-merge": "मर्ज", + "sync-strategy-overwrite": "ओवरराइट", + "entities-to-export": "एक्सपोर्ट करने के लिए एंटिटीज़", + "entities-to-restore": "रिस्टोर करने के लिए एंटिटीज़", + "sync-strategy": "सिंक स्ट्रैटेजी", + "all-entities": "सभी एंटिटीज़", + "no-entities-to-export-prompt": "कृपया एक्सपोर्ट करने के लिए एंटिटीज़ निर्दिष्ट करें", + "no-entities-to-restore-prompt": "कृपया रिस्टोर करने के लिए एंटिटीज़ निर्दिष्ट करें", + "add-entity-type": "एंटिटी टाइप जोड़ें", + "remove-all": "सभी हटाएँ", + "version-create-result": "{ added, plural, =0 {कोई एंटिटी नहीं} =1 {1 एंटिटी} other {# एंटिटीज़} } जोड़ी गईं.
    { modified, plural, =0 {कोई एंटिटी नहीं} =1 {1 एंटिटी} other {# एंटिटीज़} } बदली गईं.
    { removed, plural, =0 {कोई एंटिटी नहीं} =1 {1 एंटिटी} other {# एंटिटीज़} } हटाई गईं.", + "remove-other-entities": "अन्य एंटिटीज़ हटाएँ", + "find-existing-entity-by-name": "नाम से मौजूदा एंटिटी खोजें", + "restore-entities-from-version": "वर्ज़न '{{versionName}}' से एंटिटीज़ रिस्टोर करें", + "restoring-entities-from-version": "एंटिटीज़ रिस्टोर की जा रही हैं... कृपया प्रतीक्षा करें", + "no-entities-restored": "कोई एंटिटी रिस्टोर नहीं की गई", + "created": "{{created}} बनाए गए", + "updated": "{{updated}} अपडेट किए गए", + "deleted": "{{deleted}} डिलीट किए गए", + "remove-other-entities-confirm-text": "सावधान रहें! इससे उन सभी वर्तमान एंटिटीज़ को स्थायी रूप से डिलीट कर दिया जाएगा
    जो उस वर्ज़न में मौजूद नहीं हैं जिसे आप रिस्टोर करना चाहते हैं।

    कन्फ़र्म करने के लिए कृपया \"अन्य एंटिटीज़ हटाएँ\" टाइप करें।", + "auto-commit-to-branch": "ऑटो-कमिट {{ branch }} ब्रांच पर", + "default-create-entity-version-name": "{{entityName}} अपडेट", + "sync-strategy-merge-hint": "रिपॉज़िटरी में चुनी गई एंटिटीज़ को बनाता या अपडेट करता है। अन्य सभी रिपॉज़िटरी एंटिटीज़ परिवर्तित नहीं की जातीं।", + "sync-strategy-overwrite-hint": "रिपॉज़िटरी में चुनी गई एंटिटीज़ को बनाता या अपडेट करता है। अन्य सभी रिपॉज़िटरी एंटिटीज़ डिलीट कर दी जाती हैं।", + "device-credentials-conflict": "एक्सटर्नल ID {{entityId}} वाले डिवाइस को लोड करने में विफल
    क्योंकि वही क्रेडेंशियल्स पहले से किसी दूसरे डिवाइस के लिए डेटाबेस में मौजूद हैं।
    कृपया रिस्टोर फॉर्म में लोड क्रेडेंशियल्स सेटिंग को डिसेबल करने पर विचार करें।", + "missing-referenced-entity": "एक्सटर्नल ID {{sourceEntityId}} वाली {{sourceEntityTypeName}} को लोड करने में विफल
    क्योंकि यह ID {{targetEntityId}} वाली गुम {{targetEntityTypeName}} को रेफ़र करती है।", + "runtime-failed": "विफल: {{message}}", + "auto-commit-settings-read-only-hint": "रिपॉज़िटरी सेटिंग्स में रीड-ओनली विकल्प सक्षम होने पर ऑटो-कमिट फ़ीचर काम नहीं करता।", + "rollback-on-error": "एरर होने पर रोलबैक", + "rollback-on-error-hint": "यदि आपके पास रिस्टोर करने के लिए एंटिटीज़ की संख्या बहुत अधिक है, तो प्रदर्शन बढ़ाने के लिए इस विकल्प को डिसेबल करने पर विचार करें।\n ध्यान दें, वर्ज़न लोड करते समय यदि कोई एरर हो जाता है, तो पहले से सेव की गई एंटिटीज़ (रिलेशन, विशेषताओं आदि के साथ) जैसी हैं वैसी ही बनी रहेंगी" + }, + "widget": { + "widget-library": "विजेट लाइब्रेरी", + "widget-bundle": "विजेट बंडल", + "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": "आपके पास असहेजे हुए विजेट सेटिंग्स हैं।
    क्या आप वाकई इस पेज से बाहर जाना चाहते हैं?", + "widget-type-not-found": "विजेट कॉन्फ़िगरेशन लोड करते समय समस्या हुई।
    संभवतः संबंधित\n विजेट प्रकार हटा दिया गया है।", + "widget-type-load-error": "निम्नलिखित त्रुटियों के कारण विजेट लोड नहीं किया जा सका:", + "remove": "विजेट हटाएँ", + "delete": "विजेट डिलीट करें", + "edit": "विजेट संपादित करें", + "remove-widget-title": "क्या आप वाकई विजेट '{{widgetTitle}}' हटाना चाहते हैं?", + "remove-widget-text": "पुष्टि के बाद यह विजेट और इससे संबंधित सभी डेटा पुनर्प्राप्त नहीं किया जा सकेगा।", + "replace-reference-with-widget-copy": "रेफ़्रेन्स को विजेट कॉपी से बदलें", + "timeseries": "टाइम सीरीज़", + "search-data": "डेटा खोजें", + "no-data-found": "कोई डेटा नहीं मिला", + "latest": "नवीनतम मान", + "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": "विजेट को इस रूप में सहेजें", + "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-extension": "एक्सटेंशन है", + "remove-resource": "संसाधन हटाएँ", + "add-resource": "संसाधन जोड़ें", + "html": "HTML", + "tidy": "साफ़ करें", + "css": "CSS", + "settings-form": "सेटिंग्स फ़ॉर्म", + "data-key-settings-form": "डेटा कुंजी सेटिंग्स फ़ॉर्म", + "latest-data-key-settings-form": "नवीनतम डेटा कुंजी सेटिंग्स फ़ॉर्म", + "widget-settings": "विजेट सेटिंग्स", + "description": "विवरण", + "tags": "टैग", + "image-preview": "छवि पूर्वावलोकन", + "settings-form-selector": "सेटिंग्स फ़ॉर्म चयनकर्ता", + "data-key-settings-form-selector": "डेटा कुंजी सेटिंग्स फ़ॉर्म चयनकर्ता", + "latest-data-key-settings-form-selector": "नवीनतम डेटा कुंजी सेटिंग्स फ़ॉर्म चयनकर्ता", + "all": "सभी", + "actual": "वास्तविक", + "scada": "SCADA प्रतीक", + "deprecated": "अप्रचलित", + "has-basic-mode": "बेसिक मोड उपलब्ध", + "basic-mode-form-selector": "बेसिक मोड फ़ॉर्म चयनकर्ता", + "basic-mode": "बेसिक", + "advanced-mode": "उन्नत", + "javascript": "JavaScript", + "js": "JS", + "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": "विजेट टेम्पलेट लोड करने में विफल!", + "details": "विवरण", + "widget-details": "विजेट विवरण", + "add": "विजेट जोड़ें", + "add-existing-widget": "मौजूदा विजेट जोड़ें", + "add-new-widget": "नया विजेट जोड़ें", + "search-widgets": "विजेट खोजें", + "selected-widgets": "{ count, plural, =1 {1 विजेट} other {# विजेट} } चयनित", + "undo": "विजेट परिवर्तनों को पूर्ववत करें", + "export": "विजेट निर्यात करें", + "export-prompt": "विजेट छवियों और संसाधनों को एम्बेड करें", + "export-widgets": "विजेट निर्यात करें", + "export-widgets-prompt": "विजेट की छवियाँ और संसाधन एम्बेड करें", + "import": "विजेट आयात करें", + "no-data": "विजेट पर दिखाने के लिए कोई डेटा नहीं", + "data-overflow": "विजेट {{total}} में से {{count}} एंटिटीज़ प्रदर्शित कर रहा है", + "alarm-data-overflow": "विजेट {{totalEntities}} एंटिटीज़ में से {{allowedEntities}} (अधिकतम अनुमत) एंटिटीज़ के लिए अलार्म प्रदर्शित कर रहा है", + "search": "विजेट खोजें", + "filter": "विजेट फ़िल्टर प्रकार", + "loading-widgets": "विजेट लोड किए जा रहे हैं...", + "widget-template-error": "अमान्य विजेट HTML टेम्पलेट।", + "reference": "संदर्भ" + }, + "widget-action": { + "header-button": "विजेट हेडर बटन", + "do-nothing": "कुछ न करें", + "open-dashboard-state": "नए डैशबोर्ड स्टेट पर जाएँ", + "update-dashboard-state": "वर्तमान डैशबोर्ड स्टेट अपडेट करें", + "open-dashboard": "अन्य डैशबोर्ड पर जाएँ", + "custom": "कस्टम एक्शन", + "custom-pretty": "कस्टम एक्शन (HTML टेम्पलेट सहित)", + "custom-pretty-error-title": "कस्टम डायलॉग त्रुटि", + "custom-pretty-template-error": "अमान्य कस्टम डायलॉग टेम्पलेट।", + "custom-pretty-controller-error": "कस्टम डायलॉग फ़ंक्शन का मूल्यांकन करते समय त्रुटि हुई।", + "mobile-action": "मोबाइल एक्शन", + "target-dashboard-state": "टार्गेट डैशबोर्ड स्टेट", + "target-dashboard-state-required": "टार्गेट डैशबोर्ड स्टेट आवश्यक है", + "set-entity-from-widget": "विजेट से एंटिटी सेट करें", + "target-dashboard": "टार्गेट डैशबोर्ड", + "select-target-dashboard": "टार्गेट डैशबोर्ड चुनें", + "target-dashboard-required": "टार्गेट डैशबोर्ड आवश्यक है।", + "open-right-layout": "दाएँ डैशबोर्ड लेआउट खोलें (मोबाइल व्यू)", + "state-display-type": "डैशबोर्ड स्टेट डिस्प्ले विकल्प", + "open-normal": "सामान्य", + "open-in-separate-dialog": "अलग डायलॉग में खोलें", + "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": "दाएँ", + "popover-placement-rightTop": "दाएँ-ऊपर", + "popover-placement-rightBottom": "दाएँ-नीचे", + "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": "पॉपओवर चौड़ाई", + "popover-height": "पॉपओवर ऊँचाई", + "popover-style": "पॉपओवर शैली", + "open-new-browser-tab": "नए ब्राउज़र टैब में खोलें", + "open-URL": "URL खोलें", + "URL": "URL", + "url-required": "URL आवश्यक है।", + "mobile": { + "device-provision": "डिवाइस प्रोविज़न", + "action-type": "मोबाइल एक्शन प्रकार", + "select-action-type": "मोबाइल एक्शन प्रकार चुनें", + "action-type-required": "मोबाइल एक्शन प्रकार आवश्यक है", + "take-picture-from-gallery": "गैलरी से चित्र लें", + "take-photo": "फ़ोटो लें", + "map-direction": "मैप दिशानिर्देश खोलें", + "map-location": "मैप स्थान खोलें", + "scan-qr-code": "QR कोड स्कैन करें", + "make-phone-call": "फ़ोन कॉल करें", + "get-location": "फ़ोन लोकेशन प्राप्त करें", + "take-screenshot": "स्क्रीनशॉट लें" + }, + "custom-action-function": "कस्टम एक्शन फ़ंक्शन", + "custom-pretty-function": "कस्टम एक्शन (HTML टेम्पलेट सहित) फ़ंक्शन", + "map-item-type": "मैप आइटम प्रकार", + "map-item": { + "marker": "मार्कर", + "polygon": "बहुभुज", + "rectangle": "आयत", + "circle": "वृत्त" + }, + "place-map-item": "मैप आइटम रखें", + "map-item-tooltip": { + "customize-map-item-tooltips": "मैप आइटम टूलटिप्स अनुकूलित करें", + "place-marker": "मार्कर रखें", + "start-draw-rectangle": "आयत बनाना शुरू करें", + "finish-draw-rectangle": "आयत बनाना समाप्त करें", + "start-draw-polygon": "बहुभुज बनाना शुरू करें", + "continue-draw-polygon": "बहुभुज बनाना जारी रखें", + "finish-draw-polygon": "बहुभुज बनाना समाप्त करें", + "start-draw-circle": "वृत्त बनाना शुरू करें", + "finish-draw-circle": "वृत्त बनाना समाप्त करें" + } + }, + "widgets-bundle": { + "current": "वर्तमान बंडल", + "widgets-bundles": "विजेट बंडल्स", + "widgets-bundle-widgets": "विजेट बंडल के विजेट", + "add": "विजेट बंडल जोड़ें", + "delete": "विजेट बंडल हटाएँ", + "title": "शीर्षक", + "title-required": "शीर्षक आवश्यक है।", + "title-max-length": "शीर्षक 256 अक्षरों से कम होना चाहिए", + "description": "विवरण", + "image-preview": "छवि पूर्वावलोकन", + "scada": "SCADA विजेट बंडल", + "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 {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": "निर्यात किए गए डेटा में बंडल के विजेट शामिल करें (अन्यथा केवल संदर्भित विजेट FQNs निर्यात किए जाएँगे)", + "export-failed-error": "विजेट बंडल निर्यात नहीं किया जा सका: {{error}}", + "create-new-widgets-bundle": "नया विजेट बंडल बनाएँ", + "widgets-bundle-file": "विजेट बंडल फ़ाइल", + "invalid-widgets-bundle-file-error": "विजेट बंडल आयात नहीं किया जा सका: अमान्य विजेट बंडल डेटा संरचना।", + "search": "विजेट बंडल खोजें", + "selected-widgets-bundles": "{ count, plural, =1 {1 विजेट बंडल} other {# विजेट बंडल्स} } चयनित", + "open-widgets-bundle": "विजेट बंडल खोलें", + "loading-widgets-bundles": "विजेट बंडल्स लोड किए जा रहे हैं...", + "create-new": "नया विजेट बंडल बनाएँ" + }, + "widget-config": { + "data": "डेटा", + "settings": "सेटिंग्स", + "advanced": "उन्नत", + "appearance": "दिखावट", + "widget-card": "विजेट कार्ड", + "mobile": "मोबाइल", + "title": "शीर्षक", + "title-tooltip": "शीर्षक टूलटिप", + "general-settings": "सामान्य सेटिंग्स", + "display-title": "विजेट शीर्षक प्रदर्शित करें", + "card-title": "कार्ड शीर्षक", + "drop-shadow": "ड्रॉप शैडो", + "enable-fullscreen": "फुलस्क्रीन सक्षम करें", + "background-color": "पृष्ठभूमि रंग", + "text-color": "पाठ रंग", + "border-radius": "बॉर्डर रेडियस", + "padding": "पैडिंग", + "margin": "मार्जिन", + "widget-style": "विजेट स्टाइल", + "widget-css": "विजेट CSS", + "title-style": "शीर्षक स्टाइल", + "mobile-mode-settings": "मोबाइल मोड", + "order": "क्रम", + "height": "ऊँचाई", + "mobile-hide": "मोबाइल मोड में विजेट छुपाएँ", + "desktop-hide": "डेस्कटॉप मोड में विजेट छुपाएँ", + "units": "मान के पास दिखाने हेतु विशेष प्रतीक", + "units-by-default": "डिफ़ॉल्ट इकाइयाँ", + "decimals": "दशमलव के बाद अंकों की संख्या", + "decimals-by-default": "डिफ़ॉल्ट दशमलव", + "default-data-key-parameter-hint": "यह पैरामीटर सभी विजेट मानों पर लागू होता है, जब तक कि डेटा कुंजी कॉन्फ़िगरेशन द्वारा अधिलेखित न किया जाए", + "units-short": "इकाइयाँ", + "decimals-short": "दशमलव", + "decimals-suffix": "दशमलव", + "digits-suffix": "अंक", + "timewindow": "टाइम विंडो", + "use-dashboard-timewindow": "डैशबोर्ड टाइम विंडो का उपयोग करें", + "use-widget-timewindow": "विजेट टाइम विंडो का उपयोग करें", + "display-timewindow": "टाइम विंडो प्रदर्शित करें", + "legend": "लीजेंड", + "display-legend": "लीजेंड प्रदर्शित करें", + "datasources": "डेटासोर्स", + "datasource": "डेटासोर्स", + "maximum-datasources": "अधिकतम { count, plural, =1 {1 डेटासोर्स की अनुमति है।} other {# डेटासोर्स की अनुमति है} }", + "timeseries-key-error": "कम से कम एक टाइम सीरीज़ डेटा कुंजी निर्दिष्ट होनी चाहिए", + "datasource-type": "प्रकार", + "datasource-parameters": "पैरामीटर्स", + "remove-datasource": "डेटासोर्स हटाएँ", + "add-datasource": "डेटासोर्स जोड़ें", + "target-device": "लक्षित डिवाइस", + "alarm-source": "अलार्म स्रोत", + "actions": "एक्शन्स", + "action": "एक्शन", + "add-action": "एक्शन जोड़ें", + "search-actions": "एक्शन खोजें", + "no-actions-text": "कोई एक्शन नहीं मिला", + "action-source": "एक्शन स्रोत", + "select-action-source": "एक्शन स्रोत चुनें", + "action-source-required": "एक्शन स्रोत आवश्यक है।", + "column-index": "कॉलम इंडेक्स", + "select-column-index": "कॉलम इंडेक्स चुनें", + "column-index-required": "कॉलम इंडेक्स आवश्यक है।", + "not-set": "सेट नहीं", + "action-name": "नाम", + "action-name-required": "एक्शन नाम आवश्यक है।", + "action-name-not-unique": "इस नाम वाला अन्य एक्शन पहले से मौजूद है।\nएक्शन नाम समान एक्शन स्रोत के भीतर अद्वितीय होना चाहिए।", + "action-icon": "आइकन", + "header-button": { + "button-settings": "बटन सेटिंग्स", + "button-type": "बटन प्रकार", + "button-type-basic": "बेसिक", + "button-type-raised": "रेज़्ड", + "button-type-stroked": "स्ट्रोक्ड", + "button-type-flat": "फ्लैट", + "button-type-icon": "आइकन", + "button-type-mini-fab": "FAB", + "colors": "रंग", + "color": "रंग", + "background": "पृष्ठभूमि", + "border": "बॉर्डर", + "advanced-button-style": "उन्नत बटन स्टाइल", + "button-style": "बटन स्टाइल" + }, + "show-hide-action-using-function": "फ़ंक्शन के माध्यम से एक्शन दिखाएँ/छुपाएँ", + "show-action-function": "एक्शन दिखाने का फ़ंक्शन", + "action-type": "प्रकार", + "action-type-required": "एक्शन प्रकार आवश्यक है।", + "edit-action": "एक्शन संपादित करें", + "delete-action": "एक्शन हटाएँ", + "delete-action-title": "विजेट एक्शन हटाएँ", + "delete-action-text": "क्या आप वाकई '{{actionName}}' नाम वाले विजेट एक्शन को हटाना चाहते हैं?", + "title-icon": "शीर्षक आइकन", + "display-icon": "शीर्षक आइकन प्रदर्शित करें", + "card-icon": "कार्ड आइकन", + "icon": "आइकन", + "icon-color": "आइकन रंग", + "icon-size": "आइकन आकार", + "advanced-settings": "उन्नत सेटिंग्स", + "data-settings": "डेटा सेटिंग्स", + "limits": "सीमाएँ", + "no-data-display-message": "\"दिखाने के लिए कोई डेटा नहीं\" का वैकल्पिक संदेश", + "data-page-size": "प्रति डेटासोर्स अधिकतम एंटिटीज़", + "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-padding": "कार्ड पैडिंग", + "card-appearance": "कार्ड दिखावट", + "color": "रंग", + "tooltip": "टूलटिप", + "units-required": "इकाई आवश्यक है।", + "list-layout": "सूची लेआउट", + "layout": "लेआउट", + "resize-options": "रीसाइज़ विकल्प", + "resizable": "रीसाइज़ योग्य", + "preserve-aspect-ratio": "आस्पेक्ट अनुपात बनाए रखें" + }, + "widget-type": { + "import": "विजेट प्रकार आयात करें", + "export": "विजेट प्रकार निर्यात करें", + "export-failed-error": "विजेट निर्यात नहीं किया जा सका: {{error}}", + "widget-file": "विजेट फ़ाइल", + "invalid-widget-file-error": "विजेट आयात नहीं किया जा सका: अमान्य विजेट डेटा संरचना।" + }, + "markdown": { + "edit": "संपादित करें", + "preview": "पूर्वावलोकन", + "copy-code": "कॉपी करने के लिए क्लिक करें", + "copied": "कॉपी किया गया!" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "कॉन्फ़िगरेशन प्लेटफ़ॉर्म की मुख्य सेटिंग्स में मोबाइल ऐप QR कोड विजेट पर निर्भर करता है", + "get-it-on-google-play": "Google Play पर प्राप्त करें", + "download-on-the-app-store": "App Store से डाउनलोड करें" + }, + "action-button": { + "behavior": "व्यवहार", + "on-click": "क्लिक पर", + "on-click-hint": "बटन क्लिक होने पर ट्रिगर होने वाली कार्रवाई", + "first-button-click": "पहले बटन का क्लिक", + "first-button-click-hint": "पहले बटन को दबाने पर कार्रवाई।", + "second-button-click": "दूसरे बटन का क्लिक", + "second-button-click-hint": "दूसरे बटन को दबाने पर कार्रवाई।", + "button-click-hint": "विजेट को दबाने पर कार्रवाई।" + }, + "command-button": { + "behavior": "व्यवहार", + "on-click": "क्लिक पर", + "on-click-hint": "बटन क्लिक होने पर की जाने वाली कार्रवाई।" + }, + "power-button": { + "behavior": "व्यवहार", + "power-on": "Power 'On'", + "power-on-hint": "कंपोनेंट को ON करने के लिए की जाने वाली कार्रवाई।", + "power-off": "Power 'Off'", + "power-off-hint": "कंपोनेंट को OFF करने के लिए की जाने वाली कार्रवाई।", + "on-label": "On", + "off-label": "Off", + "layout": "लेआउट", + "layout-default": "डिफ़ॉल्ट", + "layout-simplified": "सरलीकृत", + "layout-outlined": "आउटलाइन", + "layout-default-volume": "Default.Volume", + "layout-simplified-volume": "Simplified.Volume", + "layout-outlined-volume": "Outlined.Volume", + "layout-default-icon": "Default.Icon", + "layout-simplified-icon": "Simplified.Icon", + "layout-outlined-icon": "Outlined.Icon", + "main": "मुख्य", + "background": "पृष्ठभूमि", + "button-icon-on": "बटन आइकन 'On'", + "button-icon-off": "बटन आइकन 'Off'", + "power-on-colors": "Power 'On' रंग", + "power-off-colors": "Power 'Off' रंग", + "disabled-colors": "अक्षम रंग", + "button": "बटन" + }, + "toggle-button": { + "behavior": "व्यवहार", + "checked": "चेक किया हुआ", + "unchecked": "अनचेक", + "check": "चेक", + "check-hint": "कंपोनेंट को चेक करने के लिए की जाने वाली कार्रवाई।", + "uncheck": "अनचेक", + "uncheck-hint": "कंपोनेंट को अनचेक करने के लिए की जाने वाली कार्रवाई।", + "auto-scale": "ऑटो स्केल", + "horizontal-fill": "क्षैतिज भराव", + "vertical-fill": "ऊर्ध्वाधर भराव", + "button-appearance": "बटन दिखावट" + }, + "segmented-button": { + "layout": "लेआउट", + "layout-squared": "स्क्वायर्ड", + "layout-rounded": "राउंडेड", + "card-border": "कार्ड बॉर्डर", + "button-appearance": "बटन दिखावट", + "first": "पहला", + "second": "दूसरा", + "color-styles": "रंग शैलियाँ", + "selected": "चयनित", + "unselected": "अचयनित" + }, + "button": { + "layout": "लेआउट", + "outlined": "आउटलाइन", + "filled": "भरा हुआ", + "underlined": "अंडरलाइन", + "basic": "बेसिक", + "auto-scale": "ऑटो स्केल", + "label": "लेबल", + "icon": "आइकन", + "border-radius": "बॉर्डर रेडियस", + "color-palette": "रंग पैलेट", + "main": "मुख्य", + "background": "पृष्ठभूमि", + "border": "बॉर्डर", + "custom-styles": "कस्टम शैलियाँ", + "clear-style": "स्टाइल साफ़ करें", + "shadow": "छाया", + "enabled": "सक्रिय", + "disabled": "निष्क्रिय", + "preview": "पूर्वावलोकन", + "copy-style-from": "यहाँ से स्टाइल कॉपी करें" + }, + "value-stepper": { + "behavior": "व्यवहार", + "simplified": "सरलीकृत", + "filled": "भरा हुआ", + "outlined": "आउटलाइन", + "volume": "वॉल्यूम", + "initial-state": "प्रारंभिक स्थिति", + "initial-state-hint": "प्रारंभिक मान प्राप्त करने हेतु कार्रवाई।", + "disabled-state": "अक्षम स्थिति", + "disabled-state-hint": "वह स्थिति कॉन्फ़िगर करें जिसमें कंपोनेंट अक्षम हो जाए।", + "right-button-click": "दाएँ बटन का क्लिक", + "right-button-click-hint": "दाएँ बटन दबाने पर कार्रवाई।", + "left-button-click": "बाएँ बटन का क्लिक", + "left-button-click-hint": "बाएँ बटन दबाने पर कार्रवाई।", + "auto-scale": "ऑटो स्केल", + "value-range": "सीमा", + "min-range": "न्यूनतम", + "max-range": "अधिकतम", + "value-increment-decrement-step": "मान बढ़ाने/घटाने का चरण", + "value": "मान", + "value-box-background": "मान बॉक्स पृष्ठभूमि", + "border": "बॉर्डर", + "button-appearance": "बटन दिखावट", + "left": "बाएँ", + "right": "दाएँ", + "left-button": "बाएँ बटन", + "right-button": "दाएँ बटन", + "icon": "आइकन", + "color-palette": "रंग पैलेट", + "main": "मुख्य", + "background": "पृष्ठभूमि", + "button-icon-on": "बटन आइकन 'On'", + "button-on-colors": "Power 'On' रंग", + "disabled-colors": "अक्षम रंग" + }, + "button-state": { + "activated-state": "सक्रिय स्थिति", + "activated-state-hint": "वह स्थिति कॉन्फ़िगर करें जिसमें बटन सक्रिय हो।", + "disabled-state": "अक्षम स्थिति", + "disabled-state-hint": "वह स्थिति कॉन्फ़िगर करें जिसमें बटन अक्षम हो।", + "selected-state": "बटन चयन", + "selected-state-hint": "वह स्थिति कॉन्फ़िगर करें जिसमें बटन चयनित हो।", + "enabled": "सक्रिय", + "hovered": "होवर किया हुआ", + "pressed": "दबाया हुआ", + "activated": "सक्रिय", + "disabled": "निष्क्रिय", + "initial": "पहला बटन", + "first": "पहला", + "second": "दूसरा" + }, + "background": { + "background": "पृष्ठभूमि", + "background-settings": "पृष्ठभूमि सेटिंग्स", + "background-type-image": "छवि", + "background-type-color": "रंग", + "image-url": "छवि URL", + "overlay": "ओवरले", + "enable-overlay": "ओवरले सक्षम करें", + "blur": "ब्लर", + "preview": "पूर्वावलोकन" + }, + "bar-chart": { + "bar-appearance": "बार दिखावट", + "label-on-bar": "बार पर लेबल", + "value-on-bar": "बार पर मान", + "bar-chart-style": "बार चार्ट शैली", + "bar-axis": "बार अक्ष" + }, + "polar-area-chart": { + "polar-axis": "पोलर अक्ष", + "start-angle": "प्रारंभ कोण", + "polar-area-chart-style": "पोलर एरिया चार्ट शैली" + }, + "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": "वाई-फाई", + "layout-cellular-bar": "सेलुलर बार", + "icon": "आइकन", + "date": "तारीख", + "active-bars-color": "सक्रिय सिग्नल बार्स का रंग", + "inactive-bars-color": "निष्क्रिय सिग्नल बार्स का रंग", + "signal-strength-card-style": "सिग्नल शक्ति कार्ड शैली", + "no-signal-rssi-value": "\"कोई सिग्नल नहीं\" rssi मान" + }, + "status-widget": { + "behavior": "व्यवहार", + "layout": "लेआउट", + "layout-default": "डिफ़ॉल्ट", + "layout-center": "केंद्र", + "layout-icon": "आइकन", + "on": "On", + "off": "Off", + "label": "लेबल", + "status": "स्थिति", + "icon": "आइकन", + "color-palette": "रंग पैलेट", + "disabled-color-palette": "अक्षम रंग पैलेट", + "primary": "प्राथमिक", + "primary-color-hint": "आइकन और लेबल का रंग", + "secondary": "द्वितीयक", + "secondary-color-hint": "स्थिति का रंग", + "background": "पृष्ठभूमि" + }, + "chart": { + "common-settings": "सामान्य सेटिंग्स", + "enable-stacking-mode": "स्टैकिंग मोड सक्षम करें", + "selection": "समय सीमा चयन", + "enable-selection-mode": "चयन मोड सक्षम करें", + "line-shadow-size": "लाइन छाया आकार", + "display-smooth-lines": "स्मूथ (घुमावदार) लाइनों को प्रदर्शित करें", + "default-bar-width": "गैर-संकलित डेटा के लिए डिफ़ॉल्ट बार चौड़ाई (मिलीसेकंड)", + "bar-alignment": "बार संरेखण", + "bar-alignment-left": "बाएँ", + "bar-alignment-right": "दाएँ", + "bar-alignment-center": "केंद्र", + "default-font": "डिफ़ॉल्ट फ़ॉन्ट", + "default-font-size": "डिफ़ॉल्ट फ़ॉन्ट आकार", + "default-font-color": "डिफ़ॉल्ट फ़ॉन्ट रंग", + "thresholds-line-width": "सभी थ्रेशहोल्ड्स के लिए डिफ़ॉल्ट लाइन चौड़ाई", + "tooltip-settings": "टूलटिप सेटिंग्स", + "tooltip": "टूलटिप", + "show-tooltip": "टूलटिप दिखाएँ", + "hover-individual-points": "व्यक्तिगत बिंदुओं पर होवर करें", + "show-cumulative-values": "स्टैकिंग मोड में संचयी मान दिखाएँ", + "hide-zero-false-values": "टूलटिप से शून्य/गलत मान छुपाएँ", + "tooltip-value-format-function": "टूलटिप मान फ़ॉर्मेट फ़ंक्शन", + "grid-settings": "ग्रिड सेटिंग्स", + "show-vertical-lines": "वर्टिकल लाइनें दिखाएँ", + "show-horizontal-lines": "हॉरिज़ॉन्टल लाइनें दिखाएँ", + "grid-outline-border-width": "ग्रिड आउटलाइन/बॉर्डर चौड़ाई (px)", + "primary-color": "प्राथमिक रंग", + "background-color": "पृष्ठभूमि रंग", + "ticks-color": "टिक्स रंग", + "xaxis-settings": "X अक्ष सेटिंग्स", + "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-days": "एक दिन पहले", + "time-for-comparison-weeks": "एक सप्ताह पहले", + "time-for-comparison-months": "एक महीने पहले", + "time-for-comparison-years": "एक वर्ष पहले", + "time-for-comparison-custom-interval": "कस्टम अंतराल", + "custom-interval-value": "कस्टम अंतराल मान (ms)", + "comparison-x-axis-settings": "तुलना X अक्ष सेटिंग्स", + "axis-position": "अक्ष स्थिति", + "axis-position-top": "ऊपर (डिफ़ॉल्ट)", + "axis-position-bottom": "नीचे", + "custom-legend-settings": "कस्टम लेजेंड सेटिंग्स", + "enable-custom-legend": "कस्टम लेजेंड सक्षम करें (यह आपको की लेबल्स में attribute/टाइम सीरीज़ मानों का उपयोग करने की अनुमति देता है)", + "key-name": "कुंजी नाम", + "key-name-required": "कुंजी नाम आवश्यक है", + "key-type": "कुंजी प्रकार", + "key-type-attribute": "विशेषता", + "key-type-timeseries": "टाइम सीरीज़", + "label-keys-list": "लेबल्स में उपयोग की जाने वाली कुंजियों की सूची", + "no-label-keys": "कोई कुंजियाँ कॉन्फ़िगर नहीं की गईं", + "add-label-key": "नई कुंजी जोड़ें", + "line-width": "लाइन चौड़ाई", + "color": "रंग", + "data-is-hidden-by-default": "डेटा डिफ़ॉल्ट रूप से छिपा हुआ है", + "disable-data-hiding": "डेटा छिपाना अक्षम करें", + "remove-from-legend": "लेजेंड से डेटा कुंजी हटाएँ", + "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": "क्रॉस", + "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": "दाएँ", + "thresholds": "थ्रेशहोल्ड्स", + "no-thresholds": "कोई थ्रेशहोल्ड कॉन्फ़िगर नहीं किया गया", + "add-threshold": "थ्रेशहोल्ड जोड़ें", + "show-values-for-comparison": "तुलना के लिए ऐतिहासिक मान दिखाएँ", + "comparison-values-label": "ऐतिहासिक मान लेबल", + "comparison-line-color": "तुलना लाइन रंग", + "threshold-settings": "थ्रेशहोल्ड सेटिंग्स", + "use-as-threshold": "कुंजी मान को थ्रेशहोल्ड के रूप में उपयोग करें", + "threshold-line-width": "थ्रेशहोल्ड लाइन चौड़ाई", + "threshold-color": "थ्रेशहोल्ड रंग", + "common-pie-settings": "सामान्य पाई सेटिंग्स", + "radius": "त्रिज्या", + "inner-radius": "आंतरिक त्रिज्या", + "tilt": "झुकाव", + "common-pie-settings-range-error": "मान 0 से 1 की सीमा में होना चाहिए", + "stroke-settings": "स्ट्रोक सेटिंग्स", + "width-pixels": "चौड़ाई (पिक्सेल)", + "show-labels": "लेबल दिखाएँ", + "animation-settings": "एनिमेशन सेटिंग्स", + "animated-pie": "पाई एनिमेशन सक्षम करें (प्रायोगिक)", + "border-settings": "बॉर्डर सेटिंग्स", + "border-width": "बॉर्डर चौड़ाई", + "border-color": "बॉर्डर रंग", + "legend-settings": "लेजेंड सेटिंग्स", + "display-legend": "लेजेंड प्रदर्शित करें", + "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": "हॉरिज़ॉन्टल अक्ष", + "shape-empty-circle": "खाली वृत्त", + "shape-circle": "वृत्त", + "shape-rect": "आयत", + "shape-round-rect": "गोल किनारों वाला आयत", + "shape-triangle": "त्रिभुज", + "shape-diamond": "डायमंड", + "shape-pin": "पिन", + "shape-arrow": "तीर", + "shape-none": "कोई नहीं", + "line-type-solid": "ठोस", + "line-type-dashed": "डैश्ड", + "line-type-dotted": "डॉटेड", + "label-position-top": "ऊपर", + "label-position-bottom": "नीचे", + "label-position-outside": "बाहर", + "label-position-inside": "अंदर", + "fill": "भराव", + "fill-type-none": "कोई नहीं", + "fill-type-solid": "ठोस", + "fill-type-opacity": "अपारदर्शिता", + "fill-type-gradient": "ग्रेडिएंट", + "background": "पृष्ठभूमि", + "opacity": "अपारदर्शिता", + "gradient-stops": "ग्रेडिएंट स्टॉप्स", + "gradient-start": "प्रारंभ", + "gradient-end": "अंत", + "animation": { + "animation": "एनीमेशन", + "animation-threshold": "एनीमेशन थ्रेशहोल्ड", + "animation-duration": "एनीमेशन अवधि", + "animation-easing": "एनीमेशन ईज़िंग", + "animation-delay": "एनीमेशन विलंब", + "update-animation-duration": "अपडेट एनीमेशन अवधि", + "update-animation-easing": "अपडेट एनीमेशन ईज़िंग", + "update-animation-delay": "अपडेट एनीमेशन विलंब" + }, + "chart-axis": { + "scale": "स्केल", + "scale-min": "न्यूनतम", + "scale-max": "अधिकतम", + "scale-auto": "स्वतः" + }, + "bar": { + "show-border": "बॉर्डर दिखाएँ", + "border-width": "बॉर्डर चौड़ाई", + "border-radius": "बॉर्डर रेडियस", + "bar-width": "बार चौड़ाई", + "label": "लेबल", + "label-hint": "बार के ऊपर लेबल प्रदर्शित करें।", + "series-label-hint": "बार के ऊपर मान सहित लेबल प्रदर्शित करें।", + "label-background": "लेबल पृष्ठभूमि" + } + }, + "color": { + "color-settings": "रंग सेटिंग्स", + "color-type-constant": "स्थायी", + "color-type-gradient": "ग्रेडिएंट", + "color-type-range": "सीमा", + "color-type-function": "फ़ंक्शन", + "color": "रंग", + "value-range": "मान सीमा", + "from": "से", + "to": "तक", + "color-function": "रंग फ़ंक्शन", + "copy-color-settings-from": "यहाँ से रंग सेटिंग्स कॉपी करें", + "copy-from": "यहाँ से कॉपी करें", + "settings-type": "सेटिंग्स प्रकार", + "basic-mode": "बेसिक", + "advanced-mode": "उन्नत", + "entity-alias": "एंटिटी उर्फ़", + "entity-attribute": "एंटिटी विशेषता", + "gradient-color": "ग्रेडिएंट रंग", + "gradient-color-min": "रंग", + "gradient-start": "ग्रेडिएंट प्रारंभ रंग", + "gradient-start-min": "प्रारंभ", + "gradient-end": "ग्रेडिएंट अंतिम रंग", + "gradient-end-min": "अंत", + "start-value": "प्रारंभ मान", + "end-value": "अंत मान", + "gradient-type": "ग्रेडिएंट प्रकार" + }, + "dashboard-state": { + "dashboard-state-settings": "डैशबोर्ड स्टेट सेटिंग्स", + "dashboard-state": "डैशबोर्ड स्टेट ID", + "autofill-state-layout": "डिफ़ॉल्ट रूप से स्टेट लेआउट ऊँचाई स्वतः भरें", + "default-margin": "डिफ़ॉल्ट विजेट मार्जिन", + "default-background-color": "डिफ़ॉल्ट पृष्ठभूमि रंग", + "sync-parent-state-params": "पैरेंट डैशबोर्ड के साथ स्टेट पैरामीटर्स सिंक करें" + }, + "date-range-navigator": { + "date-range-picker-settings": "डेट रेंज पिकर सेटिंग्स", + "hide-date-range-picker": "डेट रेंज पिकर छुपाएँ", + "picker-one-panel": "डेट रेंज पिकर एक पैनल", + "picker-auto-confirm": "डेट रेंज पिकर ऑटो पुष्टि", + "picker-show-template": "डेट रेंज पिकर टेम्पलेट दिखाएँ", + "first-day-of-week": "सप्ताह का पहला दिन", + "interval-settings": "अवधि सेटिंग्स", + "hide-interval": "अवधि छुपाएँ", + "initial-interval": "प्रारंभिक अवधि", + "interval-hour": "घंटा", + "interval-day": "दिन", + "interval-week": "सप्ताह", + "interval-two-weeks": "2 सप्ताह", + "interval-month": "महीना", + "interval-three-months": "3 महीने", + "interval-six-months": "6 महीने", + "step-settings": "चरण सेटिंग्स", + "hide-step-size": "चरण आकार छुपाएँ", + "initial-step-size": "प्रारंभिक चरण आकार", + "hide-labels": "लेबल छुपाएँ", + "use-session-storage": "सेशन स्टोरेज उपयोग करें", + "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": "ओके" + } + }, + "doughnut": { + "doughnut-appearance": "डोनट दिखावट", + "layout": "लेआउट", + "layout-default": "डिफ़ॉल्ट", + "layout-with-total": "कुल सहित", + "central-total-value": "केंद्रीय कुल मान", + "doughnut-card-style": "डोनट कार्ड शैली" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "हाइरार्की डेटा सेटिंग्स", + "relations-query-function": "नोड संबंध क्वेरी फ़ंक्शन", + "has-children-function": "नोड में बच्चे हैं या नहीं फ़ंक्शन", + "node-state-settings": "नोड स्थिति सेटिंग्स", + "node-opened-function": "डिफ़ॉल्ट नोड खुला है फ़ंक्शन", + "node-disabled-function": "नोड अक्षम फ़ंक्शन", + "display-settings": "प्रदर्शन सेटिंग्स", + "node-icon-function": "नोड आइकन फ़ंक्शन", + "node-text-function": "नोड टेक्स्ट फ़ंक्शन", + "sort-settings": "सॉर्ट सेटिंग्स", + "nodes-sort-function": "नोड्स सॉर्ट फ़ंक्शन" + }, + "edge": { + "display-default-title": "डिफ़ॉल्ट शीर्षक प्रदर्शित करें" + }, + "gateway": { + "general-settings": "सामान्य सेटिंग्स", + "widget-title": "विजेट शीर्षक", + "default-archive-file-name": "डिफ़ॉल्ट आर्काइव फ़ाइल नाम", + "device-type-for-new-gateway": "नए Gateway के लिए डिवाइस प्रकार", + "messages-settings": "संदेश सेटिंग्स", + "save-config-success-message": "Gateway कॉन्फ़िगरेशन सफलतापूर्वक सहेजे जाने के बारे में टेक्स्ट संदेश", + "device-name-exists-message": "जब दर्ज किए गए नाम वाला डिवाइस पहले से मौजूद हो, तब का टेक्स्ट संदेश", + "gateway-title": "Gateway फ़ॉर्म", + "read-only": "केवल पढ़ने के लिए", + "events-title": "Gateway इवेंट्स फ़ॉर्म शीर्षक", + "events-filter": "इवेंट्स फ़िल्टर", + "event-key-contains": "इवेंट कुंजी में शामिल है...", + "show-connector": "कनेक्टर के लिए दिखाएँ", + "connector-state-param-key": "कनेक्टर स्टेट पैरामीटर कुंजी", + "message": "संदेश", + "level": "स्तर", + "created-time": "निर्मित समय" + }, + "gauge": { + "default-color": "डिफ़ॉल्ट रंग", + "radial-gauge-settings": "रेडियल गेज सेटिंग्स", + "ticks-settings": "टिक्स सेटिंग्स", + "min-value": "न्यूनतम मान", + "max-value": "अधिकतम मान", + "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": "मान बॉक्स सेटिंग्स", + "show-value-box": "मान बॉक्स दिखाएँ", + "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": "प्लेट रंग", + "needle-settings": "सुई सेटिंग्स", + "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": "हाइलाइट जोड़ें", + "animation-settings": "एनिमेशन सेटिंग्स", + "enable-animation": "एनिमेशन", + "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": "प्रोग्रेस बार", + "progress-bar-color-start": "प्रोग्रेस बार रंग - प्रारंभ ग्रेडिएंट", + "progress-bar-color-end": "प्रोग्रेस बार रंग - अंत ग्रेडिएंट", + "major-ticks-names": "मुख्य टिक्स नाम", + "show-stroke-ticks": "टिक्स स्ट्रोक दिखाएँ", + "major-ticks-font": "मुख्य टिक्स फ़ॉन्ट", + "border-color": "बॉर्डर रंग", + "border-width": "बॉर्डर चौड़ाई", + "needle-circle": "सुई सर्कल", + "needle-circle-color": "सुई सर्कल रंग", + "animation-target": "एनिमेशन लक्ष्य", + "animation-target-needle": "सुई", + "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)", + "neon-glow-brightness-hint": "0 - प्रभाव अक्षम", + "stripes-thickness": "पट्टियों की मोटाई", + "stripes-thickness-hint": "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": "गेज शीर्षक फ़ॉन्ट", + "unit-title-and-timestamp-settings": "यूनिट शीर्षक और टाइमस्टैंप सेटिंग्स", + "show-timestamp": "टाइमस्टैंप दिखाएँ", + "timestamp-format": "टाइमस्टैंप फ़ॉर्मेट", + "label-font": "मान के नीचे दिखने वाले लेबल का फ़ॉन्ट", + "value-settings": "मान सेटिंग्स", + "show-value": "मान टेक्स्ट दिखाएँ", + "min-max-settings": "न्यूनतम/अधिकतम लेबल सेटिंग्स", + "show-min-max": "न्यूनतम और अधिकतम मान दिखाएँ", + "min-max-font": "न्यूनतम और अधिकतम लेबल का फ़ॉन्ट", + "show-ticks": "टिक्स दिखाएँ", + "tick-width": "टिक चौड़ाई", + "tick-color": "टिक रंग", + "tick-values": "टिक मान", + "no-tick-values": "कोई टिक मान कॉन्फ़िगर नहीं किया गया", + "add-tick-value": "टिक मान जोड़ें", + "gauge-appearance": "गेज दिखावट", + "units-title": "यूनिट शीर्षक", + "value": "मान", + "ticks": "टिक्स", + "arrow-and-scale-color": "तीर और स्केल का डिफ़ॉल्ट रंग", + "scale-settings": "स्केल सेटिंग्स", + "scale": "स्केल", + "scale-color": "स्केल रंग", + "compass-appearance": "कम्पास दिखावट", + "label": "लेबल", + "labels": "लेबल्स", + "label-style": "लेबल शैली", + "simple-gauge-type": "प्रकार", + "gauge-bar-background": "गेज बार पृष्ठभूमि", + "bar-color": "बार रंग", + "min-and-max-value": "न्यूनतम और अधिकतम मान", + "min-and-max-label": "न्यूनतम और अधिकतम लेबल", + "font": "फ़ॉन्ट", + "tick-width-and-color": "टिक चौड़ाई और रंग", + "min-max-validation-text": "अधिकतम मान न्यूनतम मान से बड़ा होना चाहिए" + }, + "gpio": { + "pin": "पिन", + "label": "लेबल", + "row": "पंक्ति", + "column": "स्तंभ", + "color": "रंग", + "panel-settings": "पैनल सेटिंग्स", + "background-color": "पृष्ठभूमि रंग", + "gpio-switches": "GPIO स्विचेज़", + "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": "GPIO LEDs", + "no-gpio-leds": "कोई GPIO LED कॉन्फ़िगर नहीं की गई", + "add-gpio-led": "GPIO LED जोड़ें" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "यह विजेट Attribute पैरामीटर का उपयोग नहीं कर सकता", + "blocked-location": "आपके ब्राउज़र में जियोलोकेशन अवरुद्ध है", + "claim-device": "डिवाइस क्लेम करें", + "claim-failed": "डिवाइस को क्लेम करने में विफल!", + "claim-not-found": "डिवाइस नहीं मिला!", + "claim-successful": "डिवाइस सफलतापूर्वक क्लेम किया गया!", + "date": "तारीख", + "device-name": "डिवाइस नाम", + "device-name-required": "डिवाइस नाम आवश्यक है", + "discard-changes": "परिवर्तन रद्द करें", + "entity-attribute-required": "एंटिटी विशेषता आवश्यक है", + "entity-coordinate-required": "दोनों फ़ील्ड — अक्षांश और देशांतर — आवश्यक हैं", + "entity-timeseries-required": "एंटिटी टाइम सीरीज़ आवश्यक है", + "get-location": "वर्तमान स्थान प्राप्त करें", + "invalid-date": "अमान्य तारीख", + "latitude": "अक्षांश", + "longitude": "देशांतर", + "min-value-error": "न्यूनतम मान {{value}} है", + "max-value-error": "अधिकतम मान {{value}} है", + "not-allowed-entity": "चयनित एंटिटी में शेयर की गई विशेषताएँ नहीं हो सकतीं", + "no-attribute-selected": "कोई विशेषता चयनित नहीं है", + "no-datakey-selected": "कोई डेटा-कुंजी चयनित नहीं है", + "no-coordinate-specified": "अक्षांश/देशांतर के लिए डेटाकुंजी निर्दिष्ट नहीं है", + "no-entity-selected": "कोई एंटिटी चयनित नहीं है", + "no-image": "कोई छवि नहीं", + "no-support-geolocation": "आपका ब्राउज़र जियोलोकेशन का समर्थन नहीं करता", + "no-support-web-camera": "आपका ब्राउज़र कैमरा का समर्थन नहीं करता", + "enable-https-use-widget": "कृपया इस विजेट का उपयोग करने के लिए HTTPS सक्षम करें", + "no-found-your-camera": "आपका कैमरा नहीं मिल सका", + "no-permission-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": "मान", + "general-settings": "सामान्य सेटिंग्स", + "widget-title": "विजेट शीर्षक", + "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": "'सीक्रेट कुंजी आवश्यक है' त्रुटि संदेश", + "show-label": "लेबल दिखाएँ", + "label": "लेबल", + "required": "आवश्यक", + "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": "चेकबॉक्स सेटिंग्स", + "true-label": "चेक्ड लेबल", + "false-label": "अनचेक्ड लेबल", + "image-input-settings": "छवि इनपुट सेटिंग्स", + "display-preview": "पूर्वावलोकन दिखाएँ", + "display-clear-button": "क्लियर बटन दिखाएँ", + "display-apply-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": "अक्षांश के लिए लेबल", + "longitude-label": "देशांतर के लिए लेबल", + "input-fields-alignment": "इनपुट फ़ील्ड्स संरेखण", + "input-fields-alignment-column": "स्तंभ (डिफ़ॉल्ट)", + "input-fields-alignment-row": "पंक्ति", + "layout": "लेआउट", + "row-gap": "पंक्तियों के बीच का अंतर (पिक्सेल में)", + "column-gap": "स्तंभों के बीच का अंतर (पिक्सेल में)", + "latitude-field-required": "अक्षांश फ़ील्ड आवश्यक है", + "longitude-field-required": "देशांतर फ़ील्ड आवश्यक है", + "attribute-settings": "विशेषता सेटिंग्स", + "widget-mode": "विजेट मोड", + "widget-mode-update-attribute": "विशेषता अपडेट करें", + "widget-mode-update-timeseries": "टाइम सीरीज़ अपडेट करें", + "attribute-scope": "विशेषता स्कोप", + "attribute-scope-server": "सर्वर विशेषता", + "attribute-scope-shared": "शेयर की गई विशेषता", + "value-required": "मान आवश्यक है", + "image-settings": "छवि सेटिंग्स", + "image-format": "छवि फ़ॉर्मेट", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "छवि गुणवत्ता (हानिपूर्ण संपीड़न वाले फ़ॉर्मेट जैसे JPEG और WEBP के लिए)", + "max-image-width": "अधिकतम छवि चौड़ाई", + "max-image-height": "अधिकतम छवि ऊँचाई", + "action-buttons": "एक्शन बटन", + "show-action-buttons": "एक्शन बटन दिखाएँ", + "update-all-values": "सिर्फ़ बदलाव नहीं, सभी मान अपडेट करें", + "save-button-label": "'SAVE' बटन लेबल", + "reset-button-label": "'UNDO' बटन लेबल", + "group-settings": "समूह सेटिंग्स", + "show-group-title": "विभिन्न एंटिटीज़ से संबंधित फ़ील्ड्स के समूह का शीर्षक दिखाएँ", + "group-title": "समूह शीर्षक", + "fields-alignment": "फ़ील्ड्स संरेखण", + "fields-alignment-row": "पंक्ति (डिफ़ॉल्ट)", + "fields-alignment-column": "स्तंभ", + "fields-in-row": "पंक्ति में फ़ील्ड्स की संख्या", + "option-value": "मान (खाली विकल्प बनाने के लिए 'null' लिखें)", + "option-label": "लेबल", + "hide-input-field": "इनपुट फ़ील्ड छुपाएँ", + "datakey-type": "डेटाकुंजी प्रकार", + "datakey-type-server": "सर्वर विशेषता (डिफ़ॉल्ट)", + "datakey-type-shared": "शेयर की गई विशेषता", + "datakey-type-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-radio": "रेडियो", + "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": "अन्य डेटाकुंजी के false मान पर अक्षम करें (डेटाकुंजी नाम निर्दिष्ट करें)", + "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": "आइकन सेटिंग्स", + "dialog-editor-settings": "डायलॉग संपादक सेटिंग्स", + "use-custom-icon": "कस्टम आइकन उपयोग करें", + "input-cell-icon": "इनपुट सेल से पहले दिखाने वाला आइकन", + "value-conversion-settings": "मान परिवर्तन सेटिंग्स", + "get-value-settings": "मान प्राप्त करने की सेटिंग्स", + "use-get-value-function": "getValue फ़ंक्शन उपयोग करें", + "get-value-function": "getValue फ़ंक्शन", + "set-value-settings": "मान सेट करने की सेटिंग्स", + "use-set-value-function": "setValue फ़ंक्शन उपयोग करें", + "set-value-function": "setValue फ़ंक्शन", + "json-invalid": "JSON मान का फ़ॉर्मेट अमान्य है", + "title": "शीर्षक", + "cancel-button-label": "'Cancel' बटन लेबल", + "radio-button-settings": "रेडियो बटन सेटिंग्स", + "color": "रंग", + "columns": "स्तंभ", + "radio-options": "रेडियो विकल्प", + "no-radio-options": "कोई रेडियो विकल्प कॉन्फ़िगर नहीं किए गए", + "add-radio-option": "रेडियो विकल्प जोड़ें", + "radio-label-position": "लेबल स्थिति", + "radio-label-position-before": "पहले", + "radio-label-position-after": "बाद में" + }, + "invalid-qr-code-text": "QR कोड के लिए इनपुट टेक्स्ट अमान्य है। इनपुट का प्रकार स्ट्रिंग होना चाहिए", + "qr-code": { + "use-qr-code-text-function": "QR कोड टेक्स्ट फ़ंक्शन उपयोग करें", + "qr-code-text-pattern": "QR कोड टेक्स्ट पैटर्न (उदाहरण: '${entityName} | ${keyName} - कुछ टेक्स्ट.')", + "qr-code-text-pattern-hint": "QR कोड टेक्स्ट पैटर्न एंटिटी उपनाम में एंटिटीज़ के भीतर पहली मिली कुंजी के मान का उपयोग करता है।", + "qr-code-text-pattern-required": "QR कोड टेक्स्ट पैटर्न आवश्यक है।", + "qr-code-text-function": "QR कोड टेक्स्ट फ़ंक्शन" + }, + "label-widget": { + "label-pattern": "पैटर्न", + "label-pattern-hint": "संकेत: उदाहरण: 'टेक्स्ट ${keyName} यूनिट्स.' या ${#<key index>} units'", + "label-pattern-required": "पैटर्न आवश्यक है", + "label-position": "स्थिति (पृष्ठभूमि के सापेक्ष प्रतिशत)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "पृष्ठभूमि रंग", + "font-settings": "फ़ॉन्ट सेटिंग्स", + "background-image": "पृष्ठभूमि छवि", + "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": "फ़िल्टर करने के लिए URLs दर्ज करें..." + }, + "persistent-table": { + "rpc-id": "RPC ID", + "message-type": "संदेश प्रकार", + "method": "मेथड", + "params": "पैरामीटर्स", + "created-time": "निर्माण समय", + "expiration-time": "समाप्ति समय", + "retries": "पुनः प्रयास", + "status": "स्थिति", + "filter": "फ़िल्टर", + "refresh": "रिफ्रेश", + "add": "Persistent RPC अनुरोध जोड़ें", + "details": "विवरण", + "delete": "हटाएँ", + "delete-request-title": "Persistent RPC अनुरोध हटाएँ", + "delete-request-text": "क्या आप वाकई इस अनुरोध को हटाना चाहते हैं?", + "details-title": "विवरण RPC ID: ", + "additional-info": "अतिरिक्त जानकारी", + "response": "प्रतिक्रिया", + "any-status": "कोई भी स्थिति", + "rpc-status-list": "RPC स्थिति सूची", + "no-request-prompt": "प्रदर्शित करने के लिए कोई अनुरोध नहीं", + "send-request": "अनुरोध भेजें", + "add-title": "Persistent RPC अनुरोध बनाएँ", + "method-error": "मेथड आवश्यक है।", + "timeout-error": "न्यूनतम टाइमआउट मान 5000 (5 सेकंड) है।", + "white-space-error": "Whitespace की अनुमति नहीं है.", + "rpc-status": { + "QUEUED": "कतारबद्ध", + "SENT": "भेजा गया", + "DELIVERED": "डिलीवर किया गया", + "SUCCESSFUL": "सफल", + "TIMEOUT": "टाइमआउट", + "EXPIRED": "समाप्त", + "FAILED": "विफल" + }, + "rpc-search-status-all": "सभी", + "message-types": { + "false": "टू-वे", + "true": "वन-वे" + }, + "general-settings": "सामान्य सेटिंग्स", + "enable-filter": "फ़िल्टर सक्षम करें", + "enable-sticky-header": "स्क्रॉल करते समय हेडर दिखाएँ", + "enable-sticky-action": "स्क्रॉल करते समय एक्शन कॉलम दिखाएँ", + "display-request-details": "अनुरोध विवरण दिखाएँ", + "allow-send-request": "RPC अनुरोध भेजने की अनुमति दें", + "allow-delete-request": "अनुरोध हटाने की अनुमति दें", + "columns-settings": "कॉलम सेटिंग्स", + "display-columns": "दिखाने के लिए कॉलम", + "column": "कॉलम", + "no-columns-found": "कोई कॉलम नहीं मिला", + "no-columns-matching": "'{{column}}' नहीं मिला." + }, + "range-chart": { + "chart": "चार्ट", + "data-zoom": "डेटा ज़ूम", + "range-chart-appearance": "रेंज चार्ट दिखावट", + "range-colors": "रेंज रंग", + "out-of-range-color": "रेंज से बाहर का रंग", + "show-range-thresholds": "रेंज थ्रेशहोल्ड्स दिखाएँ", + "range-thresholds-settings": "रेंज थ्रेशहोल्ड्स सेटिंग्स", + "fill-area": "क्षेत्र भरें", + "fill-area-opacity": "क्षेत्र की अपारदर्शिता", + "range-chart-style": "रेंज चार्ट शैली" + }, + "knob": { + "behavior": "व्यवहार", + "initial-value": "प्रारंभिक मान", + "initial-value-hint": "नॉब के प्रारंभिक मान को प्राप्त करने के लिए क्रिया।", + "on-value-change": "मान बदलने पर", + "on-value-change-hint": "जब नॉब का मान बदलता है तब क्रिया ट्रिगर होती है।", + "range": "सीमा", + "min": "न्यूनतम", + "max": "अधिकतम", + "value": "मान", + "fallback-initial-value": "फॉलबैक प्रारंभिक मान" + }, + "rpc": { + "value-settings": "मान सेटिंग्स", + "initial-value": "प्रारंभिक मान", + "retrieve-value-settings": "On/Off मान प्राप्त करने की सेटिंग्स", + "retrieve-value-method": "मान प्राप्त करने की विधि", + "retrieve-value-method-none": "प्राप्त न करें", + "retrieve-value-method-rpc": "RPC get value मेथड कॉल करें", + "retrieve-value-method-attribute": "एट्रिब्यूट को सब्सक्राइब करें", + "retrieve-value-method-timeseries": "टाइम सीरीज़ को सब्सक्राइब करें", + "attribute-value-key": "एट्रिब्यूट कुंजी", + "timeseries-value-key": "टाइम सीरीज़ कुंजी", + "get-value-method": "RPC get value मेथड", + "parse-value-function": "मान पार्स करने का फ़ंक्शन", + "update-value-settings": "मान अपडेट करने की सेटिंग्स", + "set-value-method": "RPC set value मेथड", + "convert-value-function": "मान रूपांतरण फ़ंक्शन", + "rpc-settings": "RPC सेटिंग्स", + "request-timeout": "RPC अनुरोध टाइमआउट (ms)", + "persistent-rpc-settings": "Persistent RPC सेटिंग्स", + "request-persistent": "Persistent RPC अनुरोध", + "persistent-polling-interval": "Persistent RPC प्रतिक्रिया प्राप्त करने के लिए पोलिंग अंतराल (ms)", + "common-settings": "सामान्य सेटिंग्स", + "switch-title": "स्विच शीर्षक", + "show-on-off-labels": "On/Off लेबल दिखाएँ", + "slide-toggle-label": "स्लाइड टॉगल लेबल", + "label-position": "लेबल स्थिति", + "label-position-before": "पहले", + "label-position-after": "बाद में", + "slider-color": "स्लाइडर रंग", + "slider-color-primary": "प्राथमिक", + "slider-color-accent": "एक्सेंट", + "slider-color-warn": "चेतावनी", + "button-style": "बटन शैली", + "button-raised": "रेज़्ड बटन", + "button-primary": "प्राथमिक रंग", + "button-background-color": "बटन पृष्ठभूमि रंग", + "button-text-color": "बटन टेक्स्ट रंग", + "widget-title": "विजेट शीर्षक", + "button-label": "बटन लेबल", + "device-attribute-scope": "डिवाइस एट्रिब्यूट स्कोप", + "server-attribute": "सर्वर एट्रिब्यूट", + "shared-attribute": "शेयर की गई एट्रिब्यूट", + "device-attribute-parameters": "डिवाइस एट्रिब्यूट पैरामीटर्स", + "is-one-way-command": "वन-वे कमांड है", + "rpc-method": "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": "LED स्टेटस मान पार्स करने का फ़ंक्शन", + "knob-title": "नॉब शीर्षक" + }, + "maps": { + "map-type": { + "type": "मैप प्रकार", + "map": "मैप", + "image": "छवि" + }, + "image": { + "image-source": "छवि स्रोत", + "image-source-image": "छवि", + "image-source-entity-key": "एंटिटी कुंजी", + "source-entity-alias": "सोर्स एंटिटी उपनाम", + "image-url-key": "छवि URL कुंजी", + "image-url-key-required": "छवि URL कुंजी आवश्यक है" + }, + "control": { + "map-controls": "मैप नियंत्रण", + "position": "स्थिति", + "position-topleft": "ऊपर-बाएँ", + "position-topright": "ऊपर-दाएँ", + "position-bottomleft": "नीचे-बाएँ", + "position-bottomright": "नीचे-दाएँ", + "zoom-actions": "ज़ूम क्रियाएँ", + "zoom-scroll": "स्क्रॉल", + "zoom-double-click": "डबल-क्लिक", + "zoom-control-buttons": "कंट्रोल बटन", + "scale": "स्केल", + "scale-metric": "मेट्रिक", + "scale-imperial": "इम्पीरियल", + "switch-to-drag-mode-using-button": "बटन का उपयोग करके ड्रैग मोड पर स्विच करें" + }, + "timeline": { + "control-panel": "टाइमलाइन नियंत्रण पैनल", + "time-step": "समय चरण", + "speed-options": "गति विकल्प", + "timestamp": "टाइमस्टैम्प", + "snap-to-real-location": "वास्तविक स्थान पर स्नैप करें", + "location-snap-filter-function": "लोकेशन स्नैप फ़िल्टर फ़ंक्शन", + "no-trips-data-available": "कोई ट्रिप डेटा उपलब्ध नहीं" + }, + "map-action": { + "map-action-buttons": "मैप एक्शन बटन", + "label": "लेबल", + "icon": "आइकन", + "color": "रंग", + "action": "एक्शन", + "add-button": "बटन जोड़ें", + "no-action-buttons-configured": "कोई एक्शन बटन कॉन्फ़िगर नहीं किया गया", + "remove-action-button": "एक्शन बटन हटाएँ", + "map-action-button": "मैप एक्शन बटन", + "button-requires": "बटन के लिए लेबल या आइकन आवश्यक है" + }, + "common": { + "common-map-settings": "सामान्य मैप सेटिंग्स", + "fit-map-bounds": "सभी मार्कर्स को कवर करने के लिए मैप सीमा फिट करें", + "default-map-center-position": "डिफ़ॉल्ट मैप केंद्र स्थिति", + "default-map-zoom-level": "डिफ़ॉल्ट मैप ज़ूम स्तर", + "entities-limit": "लोड करने के लिए एंटिटीज़ की सीमा" + }, + "layer": { + "label": "लेबल", + "layer": "लेयर", + "layers": "लेयर्स", + "map-layers": "मैप लेयर्स", + "add-layer": "लेयर जोड़ें", + "layer-settings": "लेयर सेटिंग्स", + "remove-layer": "लेयर हटाएँ", + "no-layers": "कोई लेयर्स कॉन्फ़िगर नहीं की गईं", + "roadmap": "रोडमैप", + "satellite": "सैटेलाइट", + "hybrid": "हाइब्रिड", + "reference": { + "reference-layer": "रेफरेंस लेयर", + "no-layer": "कोई लेयर नहीं", + "openstreetmap-hybrid": "OpenStreetMap हाइब्रिड", + "world-edition-hybrid": "वर्ल्ड एडिशन हाइब्रिड", + "enhanced-contrast-hybrid": "एन्हांस्ड कॉन्ट्रास्ट हाइब्रिड" + }, + "provider": { + "provider": "प्रोवाइडर", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Roadmap", + "satellite": "Satellite", + "hybrid": "Hybrid", + "terrain": "Terrain" + }, + "here": { + "title": "HERE", + "normal-day": "Normal day", + "normal-night": "Normal night", + "hybrid-day": "Hybrid day", + "terrain-day": "Terrain day" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satellite", + "terrain": "Terrain" + }, + "custom": { + "title": "Custom", + "tile-url": "Tile URL" + } + }, + "credentials": { + "credentials": "Credentials", + "api-key": "API Key" + } + }, + "overlays": { + "overlays": "ओवरलेज़", + "overlays-hint": "मैप एंटिटीज़ के लिए डेटा स्रोत, दिखावट, व्यवहार, संपादन विकल्प और ग्रुपिंग कॉन्फ़िगर करें", + "trips": "ट्रिप्स", + "markers": "मार्कर्स", + "polygons": "पॉलीगॉन्स", + "circles": "सर्कल्स" + }, + "data-layer": { + "source": "सोर्स", + "filter": "फ़िल्टर", + "additional-data-keys": "अतिरिक्त डेटा कीज़", + "additional-datasources": "अतिरिक्त डेटा सोर्सेज़", + "additional-datasources-hint": "उन एंटिटीज़ के एट्रिब्यूट्स या टेलीमेट्री तक पहुँचने के लिए डेटा सोर्स जिन्हें मैप पर प्रदर्शित नहीं किया जाता; इन्हें मैप ओवरले फ़ंक्शंस में उपयोग किया जा सकता है।", + "more-datasources": "अधिक डेटा सोर्सेज़", + "data-keys": "डेटा कीज़", + "add-datasource": "डेटा सोर्स जोड़ें", + "no-datasources": "कोई डेटा सोर्स कॉन्फ़िगर नहीं किया गया", + "remove-datasource": "डेटा सोर्स हटाएँ", + "behavior": "व्यवहार", + "on-click": "क्लिक पर", + "on-click-hint": "जब उपयोगकर्ता मैप आइटम पर क्लिक करता है तब क्रिया प्रारंभ होती है।", + "groups": "ग्रुप्स", + "groups-hint": "ओवरले को सौंपे गए ग्रुप नामों की सूची, जिनसे मैप पर उसकी दृश्यता को टॉगल किया जाता है।", + "color": "रंग", + "color-settings": "रंग सेटिंग्स", + "color-type-constant": "स्थिर", + "color-type-range": "रेंज", + "color-type-function": "फ़ंक्शन", + "color-range-source-key": "रंग रेंज सोर्स की", + "color-range-source-key-required": "रंग रेंज सोर्स की आवश्यक है", + "color-range": "रंग रेंज", + "color-function": "रंग फ़ंक्शन", + "label": "लेबल", + "tooltip": "टूलटिप", + "pattern-type-pattern": "पैटर्न", + "pattern-type-function": "फ़ंक्शन", + "label-pattern": "लेबल (पैटर्न उदाहरण: '${entityName}', '${entityName}: (टेक्स्ट ${keyName} यूनिट्स.)')", + "label-function": "लेबल फ़ंक्शन", + "tooltip-pattern": "टूलटिप (उदाहरण: 'टेक्स्ट ${keyName} यूनिट्स.' या लिंक टेक्स्ट)", + "tooltip-function": "टूलटिप फ़ंक्शन", + "tooltip-trigger": "टूलटिप ट्रिगर", + "tooltip-trigger-click": "क्लिक पर टूलटिप दिखाएँ", + "tooltip-trigger-hover": "होवर पर टूलटिप दिखाएँ", + "auto-close-tooltips": "टूलटिप्स स्वतः बंद करें", + "tooltip-offset": "टूलटिप ऑफ़सेट", + "tooltip-offset-horizontal": "क्षैतिज", + "tooltip-offset-vertical": "ऊर्ध्वाधर", + "tooltip-tag-actions": "टैग क्रियाएँ", + "add-tooltip-tag-action": "टैग क्रिया जोड़ें", + "edit-tooltip-tag-action": "टैग क्रिया संपादित करें", + "remove-tooltip-tag-action": "टैग क्रिया हटाएँ", + "action-add": "जोड़ें", + "action-edit": "संपादित करें", + "action-move": "स्थानांतरित करें", + "action-remove": "हटाएँ", + "edit-instruments": "उपकरण", + "persist-location-attribute-scope": "लोकेशन को संरक्षित करने के लिए एट्रिब्यूट का स्कोप", + "enable-snapping": "सटीक ड्रॉइंग के लिए अन्य वर्टिसेस के साथ स्नैपिंग सक्षम करें", + "enable-snapping-hint": "नई पॉइंट्स को मौजूदा आकृतियों के साथ स्वचालित रूप से संरेखित करता है जिससे ड्रॉइंग अधिक आसान और सटीक हो जाती है।", + "drag-drop-mode": "ड्रैग-ड्रॉप मोड", + "trip": { + "no-trips": "कोई ट्रिप कॉन्फ़िगर नहीं की गई", + "add-trip": "ट्रिप जोड़ें", + "trip-configuration": "ट्रिप कॉन्फ़िगरेशन", + "remove-trip": "ट्रिप हटाएँ" + }, + "marker": { + "marker": "मार्कर", + "latitude-key": "Latitude कुंजी", + "longitude-key": "Longitude कुंजी", + "x-pos-key": "X स्थिति कुंजी", + "y-pos-key": "Y स्थिति कुंजी", + "latitude-key-required": "Latitude कुंजी आवश्यक है", + "longitude-key-required": "Longitude कुंजी आवश्यक है", + "x-pos-key-required": "X स्थिति कुंजी आवश्यक है", + "y-pos-key-required": "Y स्थिति कुंजी आवश्यक है", + "no-markers": "कोई मार्कर्स कॉन्फ़िगर नहीं किए गए", + "add-marker": "मार्कर जोड़ें", + "marker-configuration": "मार्कर कॉन्फ़िगरेशन", + "remove-marker": "मार्कर हटाएँ", + "marker-type": "मार्कर प्रकार", + "marker-type-shape": "आकृति", + "marker-type-icon": "आइकन", + "marker-type-image": "छवि", + "shape": "आकृति", + "icon": "आइकन", + "image": "छवि", + "marker-shapes": "मार्कर आकृतियाँ", + "marker-icon": "मार्कर आइकन", + "marker-appearance": "मार्कर दिखावट", + "marker-image": "मार्कर छवि", + "marker-image-type-image": "छवि", + "marker-image-type-function": "फ़ंक्शन", + "custom-marker-image-size": "कस्टम मार्कर इमेज आकार", + "marker-image-function": "मार्कर इमेज फ़ंक्शन", + "marker-images": "मार्कर छवियाँ", + "marker-offset": "मार्कर ऑफ़सेट", + "offset-horizontal": "क्षैतिज", + "offset-vertical": "ऊर्ध्वाधर", + "rotate-marker": "मार्कर घुमाएँ", + "offset-angle": "ऑफ़सेट कोण", + "position-conversion": "स्थिति रूपांतरण", + "position-conversion-function": "स्थिति रूपांतरण फ़ंक्शन, जो 0 से 1 तक प्रत्येक के लिए double के रूप में x,y निर्देशांक वापस करता है", + "clustering": { + "use-map-markers-clustering": "मैप मार्कर्स क्लस्टरिंग का उपयोग करें", + "zoom-on-cluster-click": "क्लस्टर पर क्लिक करने पर ज़ूम करें", + "max-zoom": "अधिकतम ज़ूम स्तर जिस पर एक मार्कर क्लस्टर का हिस्सा हो सकता है (0 - 18)", + "max-radius": "अधिकतम त्रिज्या जो एक क्लस्टर कवर करेगा", + "zoom-animation": "ज़ूम करते समय मार्कर्स पर एनीमेशन", + "bounds-on-cluster-mouse-over": "क्लस्टर पर माउस ओवर होने पर मार्कर्स की सीमाएँ", + "spiderfy-max-zoom-level": "मैक्स ज़ूम स्तर पर स्पाइडरफाई (सभी क्लस्टर मार्कर्स देखने के लिए)", + "load-optimization": "लोड ऑप्टिमाइज़ेशन", + "chunked-load": "मार्कर्स जोड़ने के लिए चंक्स का उपयोग करें ताकि पेज फ्रीज़ न हो", + "lazy-load": "मार्कर्स जोड़ने के लिए लेज़ी लोड का उपयोग करें", + "use-cluster-marker-color-function": "क्लस्टर मार्कर्स का रंग फ़ंक्शन उपयोग करें", + "marker-color-function": "मार्कर रंग फ़ंक्शन" + }, + "edit": "मार्कर संपादित करें", + "remove-marker-for": "'{{entityName}}' के लिए मार्कर हटाएँ", + "place-marker": "मार्कर रखें", + "place-marker-hint": "मार्कर रखने के लिए क्लिक करें", + "place-marker-hint-with-entity": "'{{entityName}}' एंटिटी को रखने के लिए क्लिक करें" + }, + "path": { + "path": "पाथ", + "path-decorator": "पाथ डेकोरेटर", + "decorator-symbol": "डेकोरेटर प्रतीक", + "decorator-symbol-arrow-head": "तीर", + "decorator-symbol-dash": "डैश", + "decorator-arrangement": "डेकोरेटर व्यवस्था", + "decorator-offset": "आरंभ", + "decorator-end-offset": "समाप्ति", + "decorator-repeat": "दोहराएँ" + }, + "points": { + "points": "प्वाइंट्स", + "point-tooltip": "प्वाइंट टूलटिप" + }, + "shape": { + "fill": "भराव", + "fill-type-color": "रंग", + "fill-type-stripe": "स्ट्राइप", + "fill-type-image": "छवि", + "color": "रंग", + "stripe": "स्ट्राइप", + "image": "छवि", + "stroke": "स्ट्रोक", + "fill-image": "भराव छवि", + "fill-image-type-image": "छवि", + "fill-image-type-function": "फ़ंक्शन", + "preserve-aspect-ratio": "आस्पेक्ट अनुपात बनाए रखें", + "opacity": "अपारदर्शिता", + "angle": "रोटेशन कोण", + "scale": "स्केल", + "fill-image-function": "आकार भरने की छवि फ़ंक्शन", + "fill-images": "आकार भरने की छवियाँ", + "stripe-pattern": "स्ट्राइप पैटर्न", + "first-stripe": "पहला स्ट्राइप", + "second-stripe": "दूसरा स्ट्राइप" + }, + "polygon": { + "polygon-key": "पॉलीगॉन कुंजी", + "polygon-key-required": "पॉलीगॉन कुंजी आवश्यक है", + "no-polygons": "कोई पॉलीगॉन्स कॉन्फ़िगर नहीं किए गए", + "add-polygon": "पॉलीगॉन जोड़ें", + "polygon-configuration": "पॉलीगॉन कॉन्फ़िगरेशन", + "remove-polygon": "पॉलीगॉन हटाएँ", + "edit": "पॉलीगॉन संपादित करें", + "remove-polygon-for": "'{{entityName}}' के लिए पॉलीगॉन हटाएँ", + "cut": "पॉलीगॉन क्षेत्र काटें", + "rotate": "पॉलीगॉन घुमाएँ", + "draw-rectangle": "रेक्टैंगल बनाएं", + "draw-polygon": "पॉलीगॉन बनाएं", + "polygon-place-first-point-cut-hint": "पहला बिंदु रखने के लिए क्लिक करें", + "continue-polygon-cut-hint": "ड्रॉइंग जारी रखने के लिए क्लिक करें", + "finish-polygon-cut-hint": "समाप्त करने और सहेजने के लिए पहले मार्कर पर क्लिक करें", + "polygon-place-first-point-hint": "पॉलीगॉन: पहला बिंदु रखने के लिए क्लिक करें", + "polygon-place-first-point-hint-with-entity": "'{{entityName}}' के लिए पॉलीगॉन: पहला बिंदु रखने के लिए क्लिक करें", + "continue-polygon-hint": "पॉलीगॉन: ड्रॉइंग जारी रखने के लिए क्लिक करें", + "continue-polygon-hint-with-entity": "'{{entityName}}' के लिए पॉलीगॉन: ड्रॉइंग जारी रखने के लिए क्लिक करें", + "finish-polygon-hint": "पॉलीगॉन: ड्रॉइंग समाप्त करने के लिए पहले मार्कर पर क्लिक करें", + "finish-polygon-hint-with-entity": "'{{entityName}}' के लिए पॉलीगॉन: समाप्त करने और सहेजने के लिए पहले मार्कर पर क्लिक करें", + "rectangle-place-first-point-hint": "रेक्टैंगल: पहला बिंदु रखने के लिए क्लिक करें", + "rectangle-place-first-point-hint-with-entity": "'{{entityName}}' के लिए रेक्टैंगल: पहला बिंदु रखने के लिए क्लिक करें", + "finish-rectangle-hint": "रेक्टैंगल: ड्रॉइंग समाप्त करने के लिए क्लिक करें", + "finish-rectangle-hint-with-entity": "'{{entityName}}' के लिए रेक्टैंगल: समाप्त करने और सहेजने के लिए क्लिक करें" + }, + "circle": { + "circle-key": "सर्कल कुंजी", + "circle-key-required": "सर्कल कुंजी आवश्यक है", + "no-circles": "कोई सर्कल कॉन्फ़िगर नहीं किए गए", + "add-circle": "सर्कल जोड़ें", + "circle-configuration": "सर्कल कॉन्फ़िगरेशन", + "remove-circle": "सर्कल हटाएँ", + "edit": "सर्कल संपादित करें", + "remove-circle-for": "'{{entityName}}' के लिए सर्कल हटाएँ", + "draw-circle": "सर्कल बनाएं", + "place-circle-center-hint-with-entity": "'{{entityName}}' के लिए सर्कल: केंद्र रखने के लिए क्लिक करें", + "place-circle-center-hint": "सर्कल: केंद्र रखने के लिए क्लिक करें", + "finish-circle-hint-with-entity": "'{{entityName}}' के लिए सर्कल: समाप्त करने और सहेजने के लिए क्लिक करें", + "finish-circle-hint": "सर्कल: ड्रॉइंग समाप्त करने के लिए क्लिक करें" + }, + "select-entity": "एंटिटी चुनें", + "select-entity-hint": "संकेत: चयन के बाद स्थिति सेट करने के लिए मैप पर क्लिक करें" + }, + "select-entity": "एंटिटी चुनें", + "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": "सर्कल मार्कर रखने के लिए क्लिक करें" + }, + "actions": { + "finish": "समाप्त करें", + "cancel": "रद्द करें", + "removeLastVertex": "अंतिम बिंदु हटाएँ" + }, + "buttonTitles": { + "drawMarkerButton": "एंटिटी रखें", + "drawPolyButton": "पॉलीगॉन बनाएँ", + "drawLineButton": "पॉलीलाइन बनाएँ", + "drawCircleButton": "सर्कल बनाएँ", + "drawRectButton": "रेक्टैंगल बनाएँ", + "editButton": "संपादन मोड", + "dragButton": "ड्रैग-ड्रॉप मोड", + "cutButton": "पॉलीगॉन क्षेत्र काटें", + "deleteButton": "हटाएँ", + "drawCircleMarkerButton": "सर्कल मार्कर बनाएँ", + "rotateButton": "पॉलीगॉन घुमाएँ" + }, + "map-provider-settings": "मैप प्रोवाइडर सेटिंग्स", + "map-provider": "मैप प्रोवाइडर", + "map-provider-google": "Google maps", + "map-provider-openstreet": "OpenStreet maps", + "map-provider-here": "HERE maps", + "map-provider-image": "Image map", + "map-provider-tencent": "Tencent maps", + "openstreet-provider": "OpenStreet map provider", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Default)", + "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": "कस्टम प्रोवाइडर उपयोग करें", + "custom-provider-tile-url": "कस्टम प्रोवाइडर टाइल URL", + "google-maps-api-key": "Google Maps API Key", + "default-map-type": "डिफ़ॉल्ट मैप प्रकार", + "google-map-type-roadmap": "Roadmap", + "google-map-type-satelite": "Satellite", + "google-map-type-hybrid": "Hybrid", + "google-map-type-terrain": "Terrain", + "map-layer": "मैप लेयर", + "here-map-normal-day": "HERE.normalDay (Default)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "क्रेडेंशियल्स", + "here-app-id": "HERE app id", + "here-app-code": "HERE app code", + "here-api-key": "HERE API key", + "here-use-new-version-api-3": "API version 3 उपयोग करें", + "tencent-maps-api-key": "Tencent Maps API Key", + "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": "सामान्य मैप सेटिंग्स", + "x-pos-key-name": "X पोज़िशन कुंजी नाम", + "y-pos-key-name": "Y पोज़िशन कुंजी नाम", + "latitude-key-name": "Latitude कुंजी नाम", + "longitude-key-name": "Longitude कुंजी नाम", + "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": "लोड करने के लिए एंटिटीज़ की सीमा", + "markers-settings": "मार्कर सेटिंग्स", + "marker-offset-x": "मार्कर X ऑफ़सेट (स्थिति × मार्कर चौड़ाई)", + "marker-offset-y": "मार्कर Y ऑफ़सेट (स्थिति × मार्कर ऊँचाई)", + "position-function": "स्थिति रूपांतरण फ़ंक्शन, जो x,y को 0 से 1 की सीमा में double के रूप में लौटाए", + "draggable-marker": "ड्रैग करने योग्य मार्कर", + "label": "लेबल", + "show-label": "लेबल दिखाएँ", + "use-label-function": "लेबल फ़ंक्शन उपयोग करें", + "label-pattern": "लेबल (पैटर्न उदाहरण: '${entityName}', '${entityName}: (टेक्स्ट ${keyName} यूनिट्स.)' )", + "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} यूनिट्स.' या लिंक टेक्स्ट)", + "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": "कस्टम मार्कर इमेज आकार (px)", + "marker-image-function": "मार्कर इमेज फ़ंक्शन", + "marker-images": "मार्कर इमेजेज़", + "polygon-settings": "पॉलीगॉन सेटिंग्स", + "show-polygon": "पॉलीगॉन दिखाएँ", + "polygon-key-name": "पॉलीगॉन की key", + "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} यूनिट्स.' या लिंक टेक्स्ट)", + "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": "सर्कल key नाम", + "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} यूनिट्स.' या लिंक टेक्स्ट)", + "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": "सभी एडिट कंट्रोल बटन छिपाएँ", + "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": "टूलटिप स्वतः बंद करें", + "rotation-angle": "मार्कर के लिए अतिरिक्त रोटेशन ऐंगल सेट करें (deg)", + "path-settings": "पाथ सेटिंग्स", + "path-color": "पाथ रंग", + "use-path-color-function": "पाथ रंग फ़ंक्शन उपयोग करें", + "path-color-function": "पाथ रंग फ़ंक्शन", + "path-decorator": "पाथ डेकोरेटर", + "use-path-decorator": "पाथ डेकोरेटर उपयोग करें", + "decorator-symbol": "डेकोरेटर प्रतीक", + "decorator-symbol-arrow-head": "तीर", + "decorator-symbol-dash": "डैश", + "decorator-symbol-size": "डेकोरेटर प्रतीक आकार (px)", + "use-path-decorator-custom-color": "पाथ डेकोरेटर कस्टम रंग उपयोग करें", + "decorator-custom-color": "डेकोरेटर कस्टम रंग", + "decorator-offset": "डेकोरेटर ऑफ़सेट", + "end-decorator-offset": "अंतिम डेकोरेटर ऑफ़सेट", + "decorator-repeat": "डेकोरेटर दोहराव", + "points-settings": "पॉइंट सेटिंग्स", + "show-points": "पॉइंट्स दिखाएँ", + "point-color": "पॉइंट रंग", + "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": "मार्कर्स रंग फ़ंक्शन उपयोग करें", + "marker-color-function": "मार्कर रंग फ़ंक्शन" + }, + "markdown": { + "use-markdown-text-function": "मार्कडाउन/HTML वैल्यू फ़ंक्शन उपयोग करें", + "markdown-text-function": "मार्कडाउन/HTML वैल्यू फ़ंक्शन", + "markdown-text-pattern": "मार्कडाउन/HTML पैटर्न (मार्कडाउन या HTML वेरिएबल्स के साथ, उदाहरण: '${entityName} या ${keyName} - कुछ टेक्स्ट.')", + "apply-default-markdown-style": "डिफ़ॉल्ट मार्कडाउन स्टाइल लागू करें", + "markdown-css": "मार्कडाउन/HTML CSS" + }, + "simple-card": { + "label": "लेबल", + "label-position": "लेबल स्थिति", + "label-position-left": "बाएँ", + "label-position-top": "ऊपर" + }, + "single-switch": { + "behavior": "व्यवहार", + "layout": "लेआउट", + "layout-right": "दाएँ", + "layout-left": "बाएँ", + "layout-centered": "सेंटर", + "auto-scale": "ऑटो स्केल", + "label": "लेबल", + "icon": "आइकन", + "switch-color": "स्विच रंग", + "on": "ऑन", + "off": "ऑफ़", + "disabled": "अक्षम", + "tumbler-color": "टंबलर रंग", + "on-label": "ऑन लेबल", + "off-label": "ऑफ़ लेबल", + "switch": "स्विच" + }, + "slider": { + "behavior": "व्यवहार", + "initial-value": "प्रारंभिक मान", + "initial-value-hint": "स्लाइडर का प्रारंभिक मान प्राप्त करने की कार्रवाई।", + "on-value-change": "मान बदलने पर", + "on-value-change-hint": "जब स्लाइडर का मान बदला जाता है तो ट्रिगर होने वाली कार्रवाई।", + "layout": "लेआउट", + "layout-default": "डिफ़ॉल्ट", + "layout-extended": "विस्तारित", + "layout-simplified": "सरलीकृत", + "auto-scale": "ऑटो स्केल", + "icon": "आइकन", + "value": "मान", + "range": "सीमा", + "min": "न्यूनतम", + "max": "अधिकतम", + "range-ticks": "सीमा टिक", + "tick-marks": "टिक मार्क्स", + "colors": "रंग", + "main": "मुख्य", + "background": "पृष्ठभूमि", + "left-icon": "बाएँ आइकन", + "right-icon": "दाएँ आइकन", + "slider": "स्लाइडर" + }, + "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": "ऑटो स्केल" + }, + "label-card": { + "auto-scale": "ऑटो स्केल", + "label": "लेबल", + "icon": "आइकन", + "label-card-style": "लेबल कार्ड स्टाइल" + }, + "label-value-card": { + "value": "मान", + "label-value-card-style": "लेबल और मान कार्ड शैली" + }, + "liquid-level-card": { + "layout-simple": "सरल", + "layout-percentage": "प्रतिशत", + "layout-absolute": "निरपेक्ष", + "layout": "लेआउट", + "background-overlay": "मान बैकग्राउंड ओवरले", + "total-volume": "कुल मात्रा", + "total-volume-units": "कुल मात्रा की इकाइयाँ", + "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": "प्रोग्रेस बार कार्ड शैली" + }, + "notification": { + "max-notification-display": "दिखाने के लिए अधिकतम नोटिफ़िकेशन", + "counter": "काउंटर", + "counter-hint": "यदि \"विजेट शीर्षक\" सक्षम है तो काउंटर प्रदर्शित होगा", + "icon": "आइकन", + "counter-value": "मान", + "counter-color": "रंग", + "notification-button": "नोटिफ़िकेशन बटन", + "button-view-all": "सभी देखें", + "button-filter": "फ़िल्टर", + "type-filter": "प्रकार फ़िल्टर", + "button-mark-read": "सभी को पढ़ा हुआ चिह्नित करें", + "notification-types": "नोटिफ़िकेशन प्रकार", + "notification-type": "नोटिफ़िकेशन प्रकार", + "search-type": "खोज प्रकार", + "any-type": "कोई भी प्रकार" + }, + "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": "सामान्य टेबल सेटिंग्स", + "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-pagination": "पेजिनेशन दिखाएँ", + "default-page-size": "डिफ़ॉल्ट पृष्ठ आकार", + "page-step-settings": "पृष्ठ स्टेप सेटिंग्स", + "page-step-count": "स्टेप की संख्या", + "page-step-increment": "स्टेप वृद्धि", + "page-step-count-format-message": "मान 1 से 100 की सीमा में पूर्णांक होना चाहिए।", + "page-step-increment-format-message": "1 या उससे अधिक का पूर्णांक मान होना चाहिए।", + "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": "सेल सामग्री फ़ंक्शन", + "show-latest-data-column": "नवीनतम डेटा कॉलम दिखाएँ", + "latest-data-column-order": "नवीनतम डेटा कॉलम क्रम", + "entities-table-title": "एंटिटी टेबल शीर्षक", + "enable-select-column-display": "दिखाने के लिए कॉलम चुनना सक्षम करें", + "display-entity-name": "एंटिटी नाम कॉलम दिखाएँ", + "entity-name-column-title": "एंटिटी नाम कॉलम शीर्षक", + "display-entity-label": "एंटिटी लेबल कॉलम दिखाएँ", + "entity-label-column-title": "एंटिटी लेबल कॉलम शीर्षक", + "display-entity-type": "एंटिटी प्रकार कॉलम दिखाएँ", + "default-sort-order": "डिफ़ॉल्ट क्रमबद्धता क्रम", + "custom-title": "कस्टम हेडर शीर्षक", + "column-width": "कॉलम चौड़ाई (px या %)", + "default-column-visibility": "डिफ़ॉल्ट कॉलम दृश्यता", + "column-visibility-visible": "दृश्यमान", + "column-visibility-hidden": "छिपा हुआ", + "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": "अलार्म साफ़ करने की अनुमति दें", + "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": "मोबाइल मोड में सेल एक्शन ड्रॉपडाउन मेनू दिखाएँ", + "disable-sorting": "सॉर्टिंग अक्षम करें" + }, + "latest-chart": { + "total": "कुल", + "auto-scale": "ऑटो स्केल", + "clockwise-layout": "क्लॉकवाइज़ लेआउट", + "sort-series": "लेबल के अनुसार सीरीज़ को सॉर्ट करें", + "tooltip-value-type-absolute": "एब्सोल्यूट", + "tooltip-value-type-percentage": "प्रतिशत" + }, + "pie-chart": { + "pie-chart-appearance": "पाई चार्ट उपस्थिति", + "label": "लेबल", + "border": "बॉर्डर", + "radius": "रेडियस", + "pie-chart-card-style": "पाई चार्ट कार्ड स्टाइल" + }, + "radar-chart": { + "radar-appearance": "रडार उपस्थिति", + "shape": "आकार", + "shape-polygon": "बहुभुज", + "shape-circle": "वृत्त", + "color": "रंग", + "line": "लाइन", + "points": "पॉइंट्स", + "points-label": "पॉइंट्स लेबल", + "radar-axis": "रडार अक्ष", + "axis-label": "अक्ष लेबल", + "ticks-label": "टिक लेबल", + "radar-chart-style": "रडार चार्ट स्टाइल", + "max-axes-scaling": "अधिकतम अक्ष स्केलिंग", + "max-axes-scaling-hint": "चुनें कि प्रत्येक रडार अक्ष का अपना अधिकतम मान हो (Separate) या विजेट डेटासेट के आधार पर सभी अक्ष सबसे बड़े मान को साझा करें (Common)।", + "separate": "अलग-अलग", + "common": "सामान्य" + }, + "time-series-chart": { + "chart": "चार्ट", + "chart-style": "चार्ट स्टाइल", + "data-zoom": "डेटा ज़ूम", + "stack-mode": "स्टैक मोड", + "stack-mode-hint": "चार्ट पर सीरीज़ को स्टैक करता है। समान यूनिट वाली सीरीज़ एक-दूसरे के ऊपर रखी जाएँगी।", + "axes": "अक्ष", + "y-axes": "Y अक्ष", + "line-type": "लाइन प्रकार", + "line-width": "लाइन चौड़ाई", + "type-line": "लाइन", + "type-bar": "बार", + "type-point": "प्वाइंट", + "no-aggregation-bar-width-strategy": "ग़ैर-एग्रीगेटेड डेटा के लिए बार चौड़ाई रणनीति", + "no-aggregation-bar-width-strategy-group": "ग्रुप", + "no-aggregation-bar-width-strategy-separate": "अलग-अलग", + "bar-group-width": "बार ग्रुप चौड़ाई", + "bar-width": "बार चौड़ाई", + "bar-width-relative": "टाइम विंडो का प्रतिशत", + "bar-width-absolute": "एब्सोल्यूट (ms)", + "comparison": { + "comparison": "तुलना", + "comparison-hint": "तुलना केवल ऐतिहासिक डेटा के साथ काम करती है!", + "show": "दिखाएँ", + "settings": "तुलना सेटिंग्स", + "show-values-for-comparison": "तुलना के लिए ऐतिहासिक डेटा दिखाएँ", + "comparison-values-label": "तुलना की कुंजी लेबल", + "comparison-values-label-auto": "ऑटो", + "comparison-data-color": "तुलना डेटा रंग" + }, + "threshold": { + "thresholds": "थ्रेशहोल्ड्स", + "source": "स्रोत", + "key-value": "कुंजी / मान", + "no-thresholds": "कोई थ्रेशहोल्ड कॉन्फ़िगर नहीं है", + "add-threshold": "थ्रेशहोल्ड जोड़ें", + "type-constant": "कॉनस्टेंट", + "type-latest-key": "कुंजी", + "type-entity": "एंटिटी", + "threshold-settings": "थ्रेशहोल्ड सेटिंग्स", + "remove-threshold": "थ्रेशहोल्ड हटाएँ", + "threshold-value-required": "थ्रेशहोल्ड मान आवश्यक है।", + "key-required": "कुंजी आवश्यक है।", + "entity-key-required": "एंटिटी कुंजी आवश्यक है।", + "line-appearance": "लाइन स्वरूप", + "line-color": "लाइन रंग", + "start-symbol": "स्टार्ट प्रतीक", + "end-symbol": "एंड प्रतीक", + "symbol-size": "आकार", + "label": "लेबल", + "label-position-start": "आरंभ", + "label-position-middle": "मध्य", + "label-position-end": "अंत", + "label-position-inside-start": "अंदर आरंभ", + "label-position-inside-start-top": "अंदर आरंभ शीर्ष", + "label-position-inside-start-bottom": "अंदर आरंभ नीचे", + "label-position-inside-middle": "अंदर मध्य", + "label-position-inside-middle-top": "अंदर मध्य शीर्ष", + "label-position-inside-middle-bottom": "अंदर मध्य नीचे", + "label-position-inside-end": "अंदर अंत", + "label-position-inside-end-top": "अंदर अंत शीर्ष", + "label-position-inside-end-bottom": "अंदर अंत नीचे", + "label-background": "लेबल बैकग्राउंड" + }, + "state": { + "states": "स्टेट्स", + "label": "लेबल", + "ticks-value": "टिक्स मान", + "source": "स्रोत", + "value-range": "मान / रेंज", + "no-states": "कोई स्टेट कॉन्फ़िगर नहीं है", + "add-state": "स्टेट जोड़ें", + "type-constant": "कॉनस्टेंट", + "type-range": "रेंज", + "from": "से", + "to": "तक", + "remove-state": "स्टेट हटाएँ" + }, + "grid": { + "grid": "ग्रिड", + "background-color": "बैकग्राउंड रंग", + "border": "बॉर्डर" + }, + "axis": { + "axes": "धुरियाँ", + "x-axis": "X धुरी", + "y-axis": "Y धुरी", + "y-axis-settings": "Y धुरी सेटिंग्स", + "comparison-x-axis-settings": "तुलना X धुरी सेटिंग्स", + "remove-y-axis": "Y धुरी हटाएँ", + "id": "आईडी", + "label": "लेबल", + "position": "स्थिति", + "position-left": "बाएँ", + "position-right": "दाएँ", + "position-top": "ऊपर", + "position-bottom": "नीचे", + "tick-labels": "टिक लेबल", + "ticks-formatter-function": "टिक्स फ़ॉर्मेटर फ़ंक्शन", + "ticks-generator-function": "टिक्स जनरेटर फ़ंक्शन", + "show-ticks": "टिक्स दिखाएँ", + "show-line": "लाइन दिखाएँ", + "show-split-lines": "स्प्लिट लाइन्स दिखाएँ", + "show-split-lines-x-axis-hint": "यदि सक्षम किया गया, तो चार्ट पर वर्टिकल लाइन्स दिखेंगी।", + "show-split-lines-y-axis-hint": "यदि सक्षम किया गया, तो चार्ट पर हॉरिज़ॉन्टल लाइन्स दिखेंगी।", + "ticks-interval": "टिक्स अंतराल", + "ticks-interval-hint": "धुरी के लिए विभाजन अंतराल को बाध्यतापूर्वक सेट करें।", + "split-number": "विभाजन संख्या", + "split-number-hint": "धुरी को जितने खंडों में विभाजित किया जाएगा उनकी संख्या।", + "min": "न्यूनतम", + "max": "अधिकतम", + "show": "दिखाएँ", + "add-y-axis": "Y धुरी जोड़ें" + }, + "series": { + "legend-settings": "लीजेंड सेटिंग्स", + "show-in-legend": "लीजेंड में दिखाएँ", + "show-in-legend-hint": "लीजेंड में सीरीज़ नाम और डेटा दिखाएँ।", + "hidden-by-default": "डिफ़ॉल्ट रूप से छिपा हुआ", + "hidden-by-default-hint": "सीरीज़ को डिफ़ॉल्ट रूप से लीजेंड में छिपाएँ।", + "series-type": "सीरीज़ प्रकार", + "type": "प्रकार", + "type-line": "लाइन", + "type-bar": "बार", + "line": { + "line": "लाइन", + "show-line": "लाइन दिखाएँ", + "step-line": "स्टेप लाइन", + "step-type-start": "शुरुआत", + "step-type-middle": "मध्य", + "step-type-end": "अंत", + "smooth-line": "स्मूथ लाइन" + }, + "point": { + "points": "पॉइंट्स", + "show-points": "पॉइंट्स दिखाएँ", + "point-label": "पॉइंट लेबल", + "point-label-hint": "सीरीज़ पॉइंट के ऊपर मान वाला लेबल दिखाएँ।", + "point-label-background": "पॉइंट लेबल बैकग्राउंड", + "point-shape": "पॉइंट आकार", + "point-size": "पॉइंट आकार" + } + } + }, + "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": "मान स्रोत", + "predefined-value": "स्थिर", + "entity-attribute": "एंटिटी विशेषता", + "value": "मान", + "value-required": "मान आवश्यक है।", + "key-required": "की आवश्यक है।", + "entity-key-required": "एंटिटी की आवश्यक है।", + "source-entity-alias": "स्रोत एंटिटी उपनाम", + "source-entity-attribute": "स्रोत एंटिटी विशेषता", + "type-constant": "स्थिर", + "type-latest-key": "की", + "type-entity": "एंटिटी" + }, + "rpc-state": { + "initial-state": "प्रारंभिक स्थिति", + "initial-state-hint": "कम्पोनेंट की प्रारंभिक स्थिति (On/Off) प्राप्त करने की क्रिया।", + "disabled-state": "अक्षम स्थिति", + "disabled-state-hint": "वह शर्त कॉन्फ़िगर करें जिसके तहत कम्पोनेंट अक्षम होगा।", + "turn-on": "'On' करें", + "turn-on-hint": "जब स्लाइडर 'On' पर स्विच किया जाता है तब ट्रिगर होने वाली क्रिया।", + "turn-off": "'Off' करें", + "turn-off-hint": "जब स्लाइडर 'Off' पर स्विच किया जाता है तब ट्रिगर होने वाली क्रिया।", + "on": "On", + "off": "Off", + "disabled": "Disabled" + }, + "value-action": { + "do-nothing": "कुछ न करें", + "execute-rpc": "RPC निष्पादित करें", + "get-attribute": "विशेषता प्राप्त करें", + "set-attribute": "विशेषता सेट करें", + "get-time-series": "टाइम सीरीज़ प्राप्त करें", + "get-alarm-status": "अलार्म स्थिति प्राप्त करें", + "get-dashboard-state": "डैशबोर्ड स्टेट ID प्राप्त करें", + "get-dashboard-state-object": "डैशबोर्ड स्टेट ऑब्जेक्ट प्राप्त करें", + "add-time-series": "टाइम सीरीज़ जोड़ें", + "execute-rpc-text": "RPC मेथड '{{methodName}}' निष्पादित करें", + "get-time-series-text": "टाइम सीरीज़ '{{key}}' का उपयोग करें", + "get-attribute-text": "विशेषता '{{key}}' का उपयोग करें", + "get-alarm-status-text": "अलार्म स्थिति का उपयोग करें", + "get-dashboard-state-text": "डैशबोर्ड स्टेट का उपयोग करें", + "get-dashboard-state-object-text": "डैशबोर्ड स्टेट ऑब्जेक्ट का उपयोग करें", + "when-dashboard-state-is-text": "जब डैशबोर्ड स्टेट ID '{{state}}' हो", + "when-dashboard-state-function-is-text": "जब f(डैशबोर्ड स्टेट ID) '{{state}}' हो", + "when-dashboard-state-object-function-is-text": "जब f(डैशबोर्ड स्टेट ऑब्जेक्ट) '{{state}}' हो", + "set-attribute-to-value-text": "'{{key}}' विशेषता को सेट करें: {{value}}", + "add-time-series-value-text": "'{{key}}' टाइम सीरीज़ मान जोड़ें: {{value}}", + "set-attribute-text": "'{{key}}' विशेषता सेट करें", + "add-time-series-text": "'{{key}}' टाइम सीरीज़ जोड़ें", + "action": "क्रिया", + "value": "मान", + "init-value-hint": "वह मान जो तब तक सेट रहेगा जब तक डिवाइस डेटा नहीं भेजता।", + "method": "मेथड", + "method-name-required": "मेथड नाम आवश्यक है।", + "request-timeout-ms": "RPC अनुरोध टाइमआउट (ms)", + "request-timeout-required": "अनुरोध टाइमआउट आवश्यक है।", + "min-request-timeout-error": "अनुरोध टाइमआउट मान 5000 ms (5 सेकंड) या उससे अधिक होना चाहिए।", + "request-persistent": "स्थायी RPC अनुरोध", + "persistent-polling-interval": "स्थायी पोलिंग अंतराल (ms)", + "persistent-polling-interval-hint": "स्थायी RPC प्रतिक्रिया प्राप्त करने के लिए पोलिंग अंतराल (ms)", + "persistent-polling-interval-required": "स्थायी पोलिंग अंतराल आवश्यक है।", + "min-persistent-polling-interval-error": "स्थायी पोलिंग अंतराल मान 1000 ms (1 सेकंड) या उससे अधिक होना चाहिए।", + "attribute-scope": "विशेषता स्कोप", + "attribute-key": "विशेषता की", + "attribute-key-required": "विशेषता की आवश्यक है।", + "time-series-key": "टाइम सीरीज़ की", + "time-series-key-required": "टाइम सीरीज़ की आवश्यक है।", + "action-result-converter": "क्रिया परिणाम कन्वर्टर", + "converter-none": "कोई नहीं", + "converter-function": "फ़ंक्शन", + "converter-constant": "स्थिर", + "converter-value": "मान", + "parse-value-function": "मान पार्स करने का फ़ंक्शन", + "state-when-result-is": "'{{state}}' जब परिणाम हो", + "parameters": "पैरामीटर्स", + "convert-value-function": "मान कन्वर्ट करने का फ़ंक्शन", + "error": { + "target-entity-is-not-set": "लक्ष्य एंटिटी सेट नहीं है!", + "failed-to-perform-action": "{{ actionLabel }} क्रिया निष्पादित करने में विफल।", + "invalid-attribute-scope": "{{scope}} विशेषता स्कोप {{entityType}} एंटिटी द्वारा समर्थित नहीं है।" + } + }, + "widget-font": { + "font-settings": "फ़ॉन्ट सेटिंग्स", + "font-family": "फ़ॉन्ट परिवार", + "size": "आकार", + "relative-font-size": "सापेक्ष फ़ॉन्ट आकार (प्रतिशत)", + "font-style": "शैली", + "font-style-normal": "सामान्य", + "font-style-italic": "इटैलिक", + "font-style-oblique": "ओब्लिक", + "font-weight": "वज़न", + "font-weight-normal": "सामान्य", + "font-weight-bold": "बोल्ड", + "font-weight-bolder": "अधिक बोल्ड", + "font-weight-lighter": "हल्का", + "color": "रंग", + "shadow-color": "छाया रंग", + "preview": "पूर्वावलोकन", + "line-height": "लाइन ऊँचाई", + "auto": "ऑटो" + }, + "home": { + "no-data-available": "कोई डेटा उपलब्ध नहीं है" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "डिस्क", + "cpu-warning-text": "CPU उपयोग बहुत अधिक है। सिस्टम विफलता से बचने के लिए सिस्टम प्रदर्शन को ऑप्टिमाइज़ करें।", + "cpu-critical-text": "CPU उपयोग अत्यधिक उच्च है। सिस्टम विफलता से बचने के लिए सिस्टम प्रदर्शन को ऑप्टिमाइज़ करें।", + "ram-warning-text": "RAM का रिज़र्व कम हो रहा है। सिस्टम विफलता से बचने के लिए प्रदर्शन को ऑप्टिमाइज़ करें या RAM बढ़ाएँ।", + "ram-critical-text": "RAM का रिज़र्व बेहद कम है। सिस्टम विफलता से बचने के लिए प्रदर्शन को ऑप्टिमाइज़ करें या RAM बढ़ाएँ।", + "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": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "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\nProfessional Edition में", + "switch-to-pe": "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": "फ़ीचर कॉन्फ़िगर करें: SMS प्रदाता", + "content": "

    SMS प्रदाताओं को कॉन्फ़िगर करें ताकि कस्टमर को अलार्म SMS द्वारा सूचित किया जा सके।

    इसे कैसे करना है, इसकी डॉक्यूमेंटेशन देखें:

    ", + "how-to-configure-sms-provider": "SMS प्रदाता कैसे कॉन्फ़िगर करें" + }, + "step4": { + "title": "फ़ीचर कॉन्फ़िगर करें: व्हाइट लेबलिंग", + "content": "

    बिना कोडिंग और बिना सेवा को रीस्टार्ट किए, अपनी कंपनी या प्रोडक्ट का लोगो और रंग योजना आसानी से अनुकूलित करें।

    इसे कैसे करना है, इसकी डॉक्यूमेंटेशन देखें:

    " + }, + "step5": { + "title": "फ़ीचर कॉन्फ़िगर करें: 2FA", + "content": "

    दो-स्तरीय प्रमाणीकरण का उपयोग करके प्लेटफ़ॉर्म अकाउंट्स की सुरक्षा बढ़ाएँ।

    इसे कैसे करना है, इसकी डॉक्यूमेंटेशन देखें:

    " + }, + "step6": { + "title": "फ़ीचर कॉन्फ़िगर करें: OAuth 2", + "content": "

    OAuth 2.0 के माध्यम से सिंगल साइन-ऑन फ़ंक्शन का उपयोग करके टेनेंट और कस्टमर उपयोगकर्ताओं के लिए लॉगिन प्रक्रिया सरल करें।

    इसे कैसे करना है, इसकी डॉक्यूमेंटेशन देखें:

    " + } + }, + "tenant-admin": { + "step1": { + "title": "डिवाइस बनाएँ", + "content": "

    चलिए UI के माध्यम से आपका पहला डिवाइस प्लेटफ़ॉर्म पर प्रोविजन करते हैं। इसे कैसे करना है, इसके लिए डॉक्यूमेंटेशन देखें:

    ", + "how-to-create-device": "डिवाइस कैसे बनाएँ" + }, + "step2": { + "title": "डिवाइस कनेक्ट करें", + "content-before": "

    डिवाइस को कनेक्ट करने के लिए आपको डिवाइस के क्रेडेंशियल्स प्राप्त करने होंगे। इस गाइड के लिए हम डिफ़ॉल्ट ऑटो-जनरेटेड क्रेडेंशियल्स (एक्सेस टोकन) का उपयोग करने की सलाह देते हैं।

    • डिवाइस टेबल पर जाएँ
    • डिवाइस की पंक्ति पर क्लिक करें ताकि डिवाइस विवरण खुल जाए
    • \"एक्सेस टोकन कॉपी करें\" बटन दबाएँ

    HTTP के माध्यम से डेटा प्रकाशित करने के लिए इन सरल commands का उपयोग करें। ध्यान रखें कि $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°C या इससे अधिक का नया टेलीमेट्री मान भेजें।

    ", + "replace-access-token": "$ACCESS_TOKEN को अपने डिवाइस के टोकन से बदलें:", + "content-after": "

    इसे कैसे करना है, इसके लिए डॉक्यूमेंटेशन देखें:

    ", + "how-to-create-alarm": "अलार्म कैसे बनाएँ" + }, + "step6": { + "title": "कस्टमर बनाएँ और डैशबोर्ड साझा करें", + "content": "

    एंड-यूज़र डैशबोर्ड बनाए जाने पर, कस्टमर उपयोगकर्ता केवल अपने डिवाइस देख सकेगा, और अन्य कस्टमर का डेटा छुपा रहेगा।

    इसे कैसे करना है, इसके लिए डॉक्यूमेंटेशन देखें:

    " + } + } + } + }, + "icon": { + "icon": "आइकन", + "icons": "आइकन्स", + "select-icon": "आइकन चुनें", + "material-icons": "मटेरियल आइकन्स", + "show-all": "सभी आइकन्स दिखाएँ", + "search-icon": "आइकन खोजें", + "no-icons-found": "'{{iconSearch}}' के लिए कोई आइकन नहीं मिला" + }, + "phone-input": { + "phone-input-label": "फ़ोन नंबर", + "phone-input-required": "फ़ोन नंबर आवश्यक है", + "phone-input-validation": "फ़ोन नंबर अमान्य है या संभव नहीं है", + "phone-input-pattern": "अमान्य फ़ोन नंबर। यह E.164 फ़ॉर्मेट में होना चाहिए, उदाहरण: {{phoneNumber}}", + "phone-input-hint": "E.164 फ़ॉर्मेट में फ़ोन नंबर, उदाहरण: {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "एक्शन सेल बटन", + "row-click": "पंक्ति पर क्लिक", + "cell-click": "सेल पर क्लिक", + "polygon-click": "पॉलीगॉन पर क्लिक", + "marker-click": "मार्कर पर क्लिक", + "circle-click": "सर्कल पर क्लिक", + "tooltip-tag-action": "टूलटिप टैग एक्शन", + "node-selected": "नोड चयनित होने पर", + "element-click": "HTML एलिमेंट पर क्लिक", + "pie-slice-click": "स्लाइस पर क्लिक", + "row-double-click": "पंक्ति पर डबल क्लिक", + "cell-double-click": "सेल पर डबल क्लिक", + "card-click": "कार्ड पर क्लिक", + "click": "क्लिक पर" + } + }, + "paginator": { + "items-per-page": "प्रति पृष्ठ आइटम:", + "first-page-label": "पहला पृष्ठ", + "last-page-label": "अंतिम पृष्ठ", + "next-page-label": "अगला पृष्ठ", + "previous-page-label": "पिछला पृष्ठ", + "items-per-page-separator": "में से" + }, + "language": { + "language": "भाषा" + } +} \ No newline at end of file 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 45f2819123..ab07b5ec48 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -78,6 +78,7 @@ "show-more": "Mostra di più", "dont-show-again": "Non mostrare più", "see-documentation": "Vedi documentazione", + "see-debug-events": "Vedi eventi di debug", "clear": "Pulisci", "upload": "Carica", "delete-anyway": "Elimina comunque", @@ -485,6 +486,7 @@ "2fa": { "2fa": "Autenticazione a due fattori", "available-providers": "Provider disponibili", + "available-providers-required": "Deve essere configurato almeno un provider 2FA.", "issuer-name": "Nome dell'emittente", "issuer-name-required": "Il nome dell'emittente è obbligatorio.", "max-verification-failures-before-user-lockout": "Numero massimo di verifiche fallite prima del blocco dell'utente", @@ -513,7 +515,9 @@ "verification-message-template-required": "Il template del messaggio di verifica è obbligatorio.", "within-time": "Entro il tempo (sec)", "within-time-pattern": "Il tempo deve essere un numero intero positivo.", - "within-time-required": "Il tempo è obbligatorio." + "within-time-required": "Il tempo è obbligatorio.", + "force-2fa": "Forza l'autenticazione a due fattori", + "enforce-for": "Forza per" }, "jwt": { "security-settings": "Impostazioni di sicurezza JWT", @@ -545,16 +549,11 @@ "slack-settings": "Impostazioni di Slack", "mobile-settings": "Impostazioni mobile", "firebase-service-account-file": "File JSON delle credenziali dell'account di servizio Firebase", - "select-firebase-service-account-file": "Trascina e rilascia il file delle credenziali dell'account di servizio Firebase oppure", - "trendz": "Trendz", - "trendz-settings": "Impostazioni di Trendz", - "trendz-url": "URL di Trendz", - "trendz-url-required": "L'URL di Trendz è obbligatorio", - "trendz-api-key": "Chiave API di Trendz", - "trendz-enable": "Abilita Trendz" + "select-firebase-service-account-file": "Trascina e rilascia il file delle credenziali dell'account di servizio Firebase oppure" }, "alarm": { "alarm": "Allarme", + "alarm-list": "Elenco allarmi", "alarms": "Allarmi", "all-alarms": "Tutti gli allarmi", "select-alarm": "Seleziona allarme", @@ -655,7 +654,16 @@ "alarm-type": "Tipo di allarme", "enter-alarm-type": "Inserisci tipo di allarme", "no-alarm-types-matching": "Nessun tipo di allarme corrispondente a '{{entitySubtype}}' trovato.", - "alarm-type-list-empty": "Nessun tipo di allarme selezionato." + "alarm-type-list-empty": "Nessun tipo di allarme selezionato.", + "system-comments": { + "acked-by-user": "L'allarme è stato confermato dall'utente {{userName}}", + "cleared-by-user": "L'allarme è stato ripristinato dall'utente {{userName}}", + "assigned-to-user": "L'allarme è stato assegnato dall'utente {{userName}} all'utente {{assigneeName}}", + "unassigned-to-user": "L'allarme è stato rimosso dall'utente {{userName}}", + "unassigned-from-deleted-user": "L'allarme è stato rimosso perché l'utente {{userName}} è stato eliminato", + "comment-deleted": "L'utente {{userName}} ha eliminato il suo commento", + "severity-changed": "La gravità dell'allarme è stata aggiornata da {{oldSeverity}} a {{newSeverity}}" + } }, "alarm-activity": { "add": "Aggiungi un commento...", @@ -760,6 +768,7 @@ "name-max-length": "Il nome deve essere inferiore a 256 caratteri", "label-max-length": "L'etichetta deve essere inferiore a 256 caratteri", "description": "Descrizione", + "description-required": "La descrizione è obbligatoria.", "type": "Tipo", "type-required": "Il tipo è obbligatorio.", "details": "Dettagli", @@ -873,6 +882,9 @@ "alarms-created-monthly-activity": "Attività mensile di allarmi creati", "data-points": "Punti dati", "data-points-storage-days": "Giorni di conservazione dei punti dati", + "data-points-storage-days-hourly-activity": "Attività oraria dei giorni di conservazione dei punti dati", + "data-points-storage-days-daily-activity": "Attività giornaliera dei giorni di conservazione dei punti dati", + "data-points-storage-days-monthly-activity": "Attività mensile dei giorni di conservazione dei punti dati", "device-api": "API dispositivo", "email": "Email", "email-messages": "Messaggi email", @@ -906,6 +918,7 @@ "rule-node": "Nodo regola", "sms": "SMS", "sms-messages": "Messaggi SMS", + "sms-messages-hourly-activity": "Attività oraria dei messaggi SMS", "sms-messages-daily-activity": "Attività giornaliera messaggi SMS", "sms-messages-monthly-activity": "Attività mensile messaggi SMS", "successful": "${entityName} riusciti", @@ -915,13 +928,40 @@ "telemetry-persistence-hourly-activity": "Attività oraria persistenza telemetria", "telemetry-persistence-monthly-activity": "Attività mensile persistenza telemetria", "transport": "Trasporto", + "transport-msg-hourly-activity": "Transportberichten: uurlijkse activiteit", + "transport-msg-daily-activity": "Transportberichten: dagelijkse activiteit", + "transport-msg-monthly-activity": "Transportberichten: maandelijkse activiteit", "transport-daily-activity": "Attività giornaliera del trasporto", "transport-data-points": "Punti dati del trasporto", - "transport-hourly-activity": "Attività oraria del trasporto", - "transport-messages": "Messaggi del trasporto", - "transport-monthly-activity": "Attività mensile del trasporto", + "transport-data-points-hourly-activity": "Attività oraria dei punti dati di trasporto", + "transport-data-points-daily-activity": "Attività giornaliera dei punti dati di trasporto", + "transport-data-points-monthly-activity": "Attività mensile dei punti dati di trasporto", "view-details": "Visualizza dettagli", - "view-statistics": "Visualizza statistiche" + "view-statistics": "Visualizza statistiche", + "transport-messages": "Messaggi di trasporto", + "transport-messages-hourly-activity": "Attività oraria dei messaggi di trasporto", + "transport-data-point-hourly-activity": "Attività oraria del punto dati di trasporto", + "javascript-function-executions": "Esecuzioni delle funzioni JavaScript", + "javascript-function-executions-hourly-activity": "Attività oraria delle esecuzioni delle funzioni JavaScript", + "javascript-function-executions-daily-activity": "Attività giornaliera delle esecuzioni delle funzioni JavaScript", + "javascript-function-executions-monthly-activity": "Attività mensile delle esecuzioni delle funzioni JavaScript", + "tbel-function-executions": "Esecuzioni delle funzioni TBEL", + "tbel-function-executions-hourly-activity": "Attività oraria delle esecuzioni delle funzioni TBEL", + "tbel-function-executions-daily-activity": "Attività giornaliera delle esecuzioni delle funzioni TBEL", + "tbel-function-executions-monthly-activity": "Attività mensile delle esecuzioni delle funzioni TBEL", + "created-reports": "Report creati", + "created-reports-hourly-activity": "Attività oraria dei report creati", + "created-reports-daily-activity": "Attività giornaliera dei report creati", + "created-reports-monthly-activity": "Attività mensile dei report creati", + "emails": "Email", + "emails-hourly-activity": "Attività oraria delle email", + "emails-daily-activity": "Attività giornaliera delle email", + "emails-monthly-activity": "Attività mensile delle email", + "status": { + "enabled": "Abilitato", + "disabled": "Disabilitato", + "warning": "Avviso" + } }, "api-limit": { "cassandra-write-queries-core": "Query di scrittura Cassandra tramite Rest API", @@ -946,6 +986,40 @@ "edge-uplink-messages": "Messaggi uplink Edge", "edge-uplink-messages-per-edge": "Messaggi uplink Edge per nodo Edge" }, + "api-key": { + "api-key": "Chiave API", + "api-keys": "Chiavi API", + "delete-api-key-title": "Sei sicuro di voler eliminare la chiave API '{{name}}'?", + "delete-api-key-text": "Attenzione, dopo la conferma la chiave sarà irrecuperabile.", + "delete-api-keys-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 chiave API} other {# chiavi API} }?", + "delete-api-keys-text": "Attenzione, dopo la conferma tutte le chiavi selezionate saranno irrecuperabili.", + "expiration-date": "Data di scadenza", + "date": "Data", + "description": "Descrizione", + "disable": "Disabilita", + "edit-description": "Modifica descrizione", + "enable": "Abilita chiave API", + "expiration-time": "Tempo di scadenza", + "expiration-time-never": "Mai", + "expiration-time-custom": "Personalizzato", + "generate": "Genera", + "generate-title": "Genera chiave API", + "generate-text": "Nota: La chiave API eredita i permessi dell'utente per cui è stata creata.", + "generated-api-key-title": "Chiave API generata. Controlliamo la connettività!", + "generated-api-key-copy": "Assicurati di copiare e salvare la tua chiave API ora, poiché non potrai vederla di nuovo.", + "generated-api-key-command": "Usa le seguenti istruzioni per verificare la connettività. Come risultato, dovresti ricevere le informazioni dell'utente corrente:", + "generated-api-key-insecure-url": "Eseguire comandi tramite una connessione HTTP non sicura invierà la tua chiave API non criptata, rendendola vulnerabile all'intercettazione.", + "list": "{ count, plural, =1 {Una chiave API} other {Elenco di # chiavi API} }", + "manage": "Gestisci", + "manage-api-keys": "Gestisci chiavi API", + "no-found": "Nessuna chiave API trovata", + "selected-api-keys": "{ count, plural, =1 {1 chiave API} other {# chiavi API} } selezionata/e", + "search": "Cerca chiavi API", + "status": "Stato", + "status-active": "Attivo", + "status-inactive": "Inattivo", + "status-expired": "Scaduto" + }, "audit-log": { "audit": "Audit", "audit-logs": "Log di audit", @@ -999,7 +1073,11 @@ "type-provision-failure": "Provisioning dispositivo fallito", "type-timeseries-updated": "Telemetria aggiornata", "type-timeseries-deleted": "Telemetria eliminata", - "type-sms-sent": "SMS inviato" + "type-sms-sent": "SMS inviato", + "any-type": "Qualsiasi tipo", + "audit-log-filter-title": "Filtro log di audit", + "filter-title": "Filtro", + "filter-types": "Tipi di log di audit" }, "debug-settings": { "label": "Configurazione debug", @@ -1020,12 +1098,25 @@ "selected-fields": "{ count, plural, =1 {1 campo calcolato} other {# campi calcolati} } selezionato/i", "type": { "simple": "Semplice", - "script": "Script" + "simple-hint": "Calcolo aritmetico semplice basato sugli argomenti di input.", + "script": "Script", + "script-hint": "Calcolo sugli argomenti definiti utilizzando uno script TBEL.", + "geofencing": "Geofencing", + "geofencing-hint": "Valutazione della posizione GPS dell'entità e delle transizioni contro i gruppi di zone di geofencing configurati.", + "propagation": "Propagazione", + "propagation-hint": "Propagazione dei dati a entità superiori o inferiori in base alla direzione e al tipo di relazione.", + "related-entities-aggregation": "Aggregazione entità correlate", + "related-entities-aggregation-hint": "Aggregazione dei dati più recenti dalle entità correlate.", + "time-series-data-aggregation": "Aggregazione dei dati delle serie temporali", + "time-series-data-aggregation-hint": "Aggregazione dei dati storici da un'entità corrente." }, + "preview": "Anteprima", "arguments": "Argomenti", "decimals-by-default": "Decimali di default", "debugging": "Debug del campo calcolato", + "calculated-field-details": "Dettagli campo calcolato", "argument-name": "Nome argomento", + "name": "Nome", "datasource": "Fonte dati", "add-argument": "Aggiungi argomento", "test-script-function": "Testa funzione script", @@ -1037,8 +1128,9 @@ "argument-asset": "Asset", "argument-customer": "Cliente", "argument-tenant": "Tenant corrente", + "argument-owner": "Proprietario corrente", + "argument-relation-query": "Entità correlate", "argument-type": "Tipo di argomento", - "see-debug-events": "Vedi eventi di debug", "attribute": "Attributo", "copy-argument-name": "Copia nome argomento", "timeseries-key": "Chiave serie temporale", @@ -1051,12 +1143,14 @@ "shared-attributes": "Attributi condivisi", "attribute-key": "Chiave attributo", "default-value": "Valore di default", + "default-value-required": "Il valore predefinito è obbligatorio.", "limit": "Valori massimi", "time-window": "Finestra temporale", "customer-name": "Nome cliente", "asset-name": "Nome asset", "timeseries": "Serie temporale", "output": "Output", + "output-hint": "Definisce come viene elaborato l'output.", "create": "Crea nuovo campo calcolato", "file": "File campo calcolato", "invalid-file-error": "Formato file non valido. Assicurati che il file sia un file JSON valido.", @@ -1070,23 +1164,395 @@ "delete-multiple-text": "Attenzione, dopo la conferma tutti i campi calcolati selezionati saranno eliminati e tutti i dati correlati saranno irrimediabilmente persi.", "test-with-this-message": "Testa con questo messaggio", "use-latest-timestamp": "Usa il timestamp più recente", + "entity-coordinates": "Coordinate entità", + "latitude-time-series-key": "Chiave serie temporale latitudine", + "latitude-time-series-key-required": "La chiave serie temporale latitudine è obbligatoria.", + "longitude-time-series-key": "Chiave serie temporale longitudine", + "longitude-time-series-key-required": "La chiave serie temporale longitudine è obbligatoria.", + "geofencing-zone-groups": "Gruppi di zone geofencing", + "geofencing-zone-groups-settings": "Impostazioni gruppi di zone geofencing", + "target-zone": "Zona target", + "perimeter-key": "Chiave perimetro", + "report-strategy": "Strategia di report", + "no-zone-configured": "È richiesta almeno una zona.", + "no-zone-configured-required": "Deve essere configurato almeno un gruppo di zone.", + "add-zone-group": "Aggiungi gruppo di zone", + "report-transition-event-only": "Solo eventi di transizione", + "report-presence-status-only": "Solo stato di presenza", + "report-transition-event-and-presence": "Stato di presenza e eventi di transizione", + "perimeter-attribute-key": "Chiave attributo perimetro", + "perimeter-attribute-key-required": "La chiave attributo perimetro è obbligatoria.", + "perimeter-attribute-key-pattern": "La chiave attributo perimetro non è valida.", + "entity-zone-relationship": "Percorso dall'entità alle zone", + "direction": "Direzione relazione", + "direction-from": "Dall'entità alla zona", + "direction-to": "Dalla zona all'entità", + "relation-type": "Tipo di relazione", + "create-relation-with-matched-zones": "Crea relazioni per l'entità sorgente con le zone corrispondenti", + "relation-level": "Livello di relazione", + "fetch-last-available-level": "Recupera solo l'ultimo livello disponibile", + "zone-group-refresh-interval": "Intervallo di aggiornamento gruppi di zone", + "copy-zone-group-name": "Copia nome gruppo di zone", + "open-details-page": "Apri la pagina dei dettagli dell'entità", + "level": "Livello", + "direction-level": "Direzione", + "direction-up": "Su", + "direction-up-parent": "Su verso il genitore", + "direction-down": "Giù", + "direction-down-child": "Giù verso il figlio", + "add-level": "Aggiungi livello", + "delete-level": "Elimina livello", + "no-level": "Nessun livello configurato", + "levels-required": "Deve essere configurato almeno un livello.", + "max-allowed-levels-error": "Il livello di relazione supera il massimo consentito.", + "propagation-path-related-entities": "Percorso di propagazione verso le entità correlate", + "propagate-type": { + "arguments-only": "Solo argomenti", + "expression-result": "Risultato del calcolo" + }, + "script": "Script", + "data-propagate": "Dati da propagare", + "output-key": "Chiave di output", + "copy-output-key": "Copia chiave di output", + "aggregation-path-related-entities": "Percorso di aggregazione verso le entità correlate", + "deduplication-interval": "Intervallo di deduplicazione", + "deduplication-interval-min": "L'intervallo di deduplicazione deve essere almeno di {{ sec }} secondi.", + "deduplication-interval-hint": "Tempo minimo tra le aggregazioni di telemetria.", + "deduplication-interval-required": "L'intervallo di deduplicazione è obbligatorio.", + "calculated-field-filter-title": "Filtro campo calcolato", + "filter-title": "Filtro", + "calculated-field-types": "Tipi di campo calcolato", + "events": "Eventi", + "any-type": "Qualsiasi tipo", + "metrics": { + "metrics": "Metriche", + "metrics-empty": "È richiesta almeno una metrica.", + "metric-name": "Nome metrica", + "metric-name-required": "Il nome della metrica è obbligatorio.", + "metric-name-pattern": "Il nome della metrica non è valido.", + "metric-name-duplicate": "Una metrica con questo nome esiste già.", + "metric-name-max-length": "Il nome della metrica deve essere inferiore a 256 caratteri.", + "metric-name-forbidden": "Il nome della metrica è riservato e non può essere utilizzato.", + "copy-metric-name": "Copia nome metrica", + "argument-name": "Nome argomento", + "aggregation": "Aggregazione", + "aggregation-type": { + "avg": "Media", + "min": "Minimo", + "max": "Massimo", + "sum": "Somma", + "count": "Conteggio", + "count-unique": "Conteggio univoco" + }, + "filtered": "Filtrato", + "value-source": "Fonte del valore", + "value-source-hint": "Definisce come viene ottenuto il valore per l'aggregazione.", + "value-source-type": { + "key": "Chiave", + "function": "Funzione" + }, + "no-metrics-configured": "È richiesta almeno una metrica.", + "add-metric": "Aggiungi metrica", + "max-metrics": "Numero massimo di metriche raggiunto.", + "metric-settings": "Impostazioni metrica", + "filter": "Filtro", + "filter-hint": "Abilita il filtro delle entità durante l'aggregazione. La funzione del filtro deve restituire un valore booleano e può utilizzare tutti gli argomenti configurati." + }, + "output-strategy": { + "strategy": "Strategia", + "process-right-away": "Elabora immediatamente", + "process-rule-chains": "Elabora tramite Rule Chains", + "save-time-series": "Salva nella serie temporale", + "save-database": "Salva nel database", + "save-latest-values": "Salva nei valori più recenti", + "send-web-sockets": "Invia a WebSockets", + "save-calculated-fields": "Invia a Campi calcolati", + "update-attribute-only-on-value-change": "Aggiorna l'attributo solo se il valore cambia", + "send-attributes-updated-notification": "Invia notifica di aggiornamento degli attributi", + "ttl": "TTL personalizzato", + "ttl-required": "TTL è obbligatorio", + "ttl-min": "È consentito solo un TTL minimo di 0", + "processing-parameters": "Parametri di elaborazione", + "hint": { + "strategy": "Controlla se il risultato viene elaborato immediatamente o inviato a una catena di regole per ulteriori elaborazioni.", + "processing-options": "Opzioni di elaborazione", + "update-attribute-only-on-value-change": "Aggiorna l'attributo per ogni messaggio in ingresso, indipendentemente dal fatto che il valore sia cambiato. Ciò aumenta l'uso delle API e riduce le prestazioni.", + "update-attribute-only-on-value-change-enabled": "Aggiorna l'attributo solo quando il valore cambia. Se il valore non cambia, i timestamp non vengono aggiornati e non vengono inviate notifiche.", + "send-attributes-updated-notification": "Invia un evento di aggiornamento attributi alla catena di regole predefinita.", + "save-time-series": "Salva i dati della serie temporale nella tabella ts_kv del database.", + "save-database": "Salva i dati degli attributi nel database.", + "save-latest-values": "Aggiorna i dati della serie temporale nella tabella ts_kv_latest del database se il nuovo valore è più recente.", + "send-web-sockets-attribute": "Notifica le sottoscrizioni WebSocket sugli aggiornamenti dei dati dell'attributo.", + "send-web-sockets-time-series": "Notifica le sottoscrizioni WebSocket sugli aggiornamenti dei dati della serie temporale.", + "save-calculated-fields-attribute": "Notifica i campi calcolati sugli aggiornamenti dei dati dell'attributo.", + "save-calculated-fields-time-series": "Notifica i campi calcolati sugli aggiornamenti dei dati della serie temporale.", + "ttl": "Definisce il periodo di conservazione dei dati della serie temporale. Se disabilitato, viene utilizzato il TTL del Profilo tenant." + } + }, + "aggregate-interval-type": "Tipo intervallo di aggregazione", + "aggregate-interval-value": "Valore intervallo di aggregazione", + "aggregate-interval-value-required": "Il valore dell'intervallo di aggregazione è obbligatorio.", + "aggregate-interval-value-min": "Il valore dell'intervallo di aggregazione deve essere almeno { sec, plural, =0 {0 secondo} =1 {1 secondo} other {# secondi} }.", + "aggregate-interval-value-step-multiple-of": "Il valore dell'intervallo di aggregazione deve essere un divisore o un multiplo di 1 giorno.", + "aggregate-period": { + "hour": "Ora", + "day": "Giorno", + "week": "Settimana (Lun - Dom)", + "week-sun-sat": "Settimana (Dom - Sab)", + "month": "Mese", + "quarter": "Trimestre", + "year": "Anno", + "custom": "Personalizzato" + }, + "aggregate-period-hint-offset": "Il tuo intervallo di aggregazione sarà: {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "Il tuo intervallo di aggregazione sarà: {{ interval }} e così via.", + "entity-aggregation": { + "argument-hint": "I dati saranno recuperati dall'entità corrente.", + "argument-title-hint": "Definisce gli argomenti di input utilizzati per l'aggregazione.", + "argument-setting-hint": "L'ultima telemetria è l'unico tipo di argomento disponibile per questo campo calcolato.", + "aggregation-interval": "Intervallo di aggregazione", + "aggregation-interval-hint": "Definisce la frequenza con cui viene eseguita l'aggregazione. Esempio: ogni 1 ora, aggrega i dati alle 00:00, 01:00, 02:00, ecc. I risultati dell'aggregazione sono memorizzati con il timestamp corrispondente all'inizio dell'intervallo di aggregazione.", + "apply-offset": "Applica offset all'intervallo di aggregazione", + "apply-offset-hint": "Definisce di quanto spostare l'inizio di ogni periodo di aggregazione (ad esempio, +10 minuti - 00:10, 01:10).", + "offset-value": "Valore offset", + "offset-value-required": "Il valore offset è obbligatorio.", + "offset-value-min": "Il valore offset deve essere un numero intero positivo.", + "offset-value-max": "Il valore offset deve essere inferiore al valore dell'intervallo di aggregazione.", + "wait-delay": "Applica timeout di attesa per telemetria ritardata", + "wait-delay-hint": "Definisce quanto tempo attendere per la telemetria ritardata dopo che l'intervallo è terminato. Se tale telemetria arriva, il risultato per quell'intervallo verrà ricalcolato.", + "duration": "Durata", + "duration-required": "La durata è obbligatoria.", + "duration-min": "La durata deve essere almeno di 1 minuto.", + "duration-hint": "Quanto tempo attendere per i dati ritardati dopo che l'intervallo è terminato.", + "produce-intermediate-result": "Produci risultato intermedio", + "produce-intermediate-result-hint": "Calcola le metriche durante l'intervallo corrente per produrre un risultato intermedio. Gli aggiornamenti avvengono non più frequentemente di una volta ogni {{ time }}." + }, "hint": { - "arguments-simple-with-rolling": "Il campo calcolato di tipo semplice non deve contenere chiavi con tipo rolling delle serie temporali.", - "arguments-empty": "Gli argomenti non devono essere vuoti.", - "expression-required": "Espressione obbligatoria.", - "expression-invalid": "Espressione non valida", + "arguments-simple-with-rolling": "Un campo calcolato di tipo semplice non dovrebbe contenere chiavi con tipo di serie temporale rolling.", + "arguments-propagate-arguments-with-rolling": "Il tipo 'Serie temporale rolling' è incompatibile con la propagazione 'Solo argomenti'.", + "arguments-propagate-argument-entity-type": "Il tipo entità è incompatibile con la propagazione 'Solo argomenti'.", + "arguments-propagate-argument-must-current-entity": "Almeno un argomento deve essere configurato con il tipo entità sorgente 'Entità corrente'.", + "arguments-empty": "Almeno un argomento deve essere specificato.", + "expression-required": "L'espressione è obbligatoria.", + "expression-invalid": "L'espressione non è valida.", "expression-max-length": "La lunghezza dell'espressione deve essere inferiore a 255 caratteri.", "argument-name-required": "Il nome dell'argomento è obbligatorio.", "argument-name-pattern": "Il nome dell'argomento non è valido.", "argument-name-duplicate": "Esiste già un argomento con questo nome.", "argument-name-max-length": "Il nome dell'argomento deve essere inferiore a 256 caratteri.", "argument-name-forbidden": "Il nome dell'argomento è riservato e non può essere utilizzato.", + "output-key-required": "La chiave di output è obbligatoria.", + "output-key-pattern": "La chiave di output non è valida.", + "output-key-duplicate": "Esiste già una chiave con questo nome.", + "output-key-max-length": "La chiave di output deve essere inferiore a 256 caratteri.", + "output-key-forbidden": "La chiave di output è riservata e non può essere utilizzata.", + "entity-type-required": "Il tipo entità è obbligatorio.", + "name-required": "Il nome è obbligatorio.", + "name-pattern": "Il nome non è valido.", + "name-duplicate": "Esiste già un nome con questo nome.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri.", + "name-forbidden": "Il nome è riservato e non può essere utilizzato.", "argument-type-required": "Il tipo di argomento è obbligatorio.", "max-args": "Numero massimo di argomenti raggiunto.", - "decimals-range": "Per impostazione predefinita, i decimali devono essere un numero compreso tra 0 e 15.", + "decimals-range": "I decimali di default devono essere un numero compreso tra 0 e 15.", "expression": "L'espressione predefinita dimostra come trasformare una temperatura da Fahrenheit a Celsius.", "arguments-entity-not-found": "Entità di destinazione dell'argomento non trovata.", - "use-latest-timestamp": "Se abilitato, il valore calcolato verrà salvato utilizzando il timestamp più recente dalla telemetria degli argomenti, invece dell'orario del server." + "use-latest-timestamp": "Se abilitato, il valore calcolato verrà memorizzato utilizzando il timestamp più recente dalla telemetria degli argomenti, invece del tempo del server.", + "entity-coordinates": "Specifica le chiavi della serie temporale che forniscono le coordinate GPS dell'entità (latitudine e longitudine).", + "geofencing-zone-groups": "Definisci uno o più gruppi di zone di geofencing da verificare (ad esempio, 'allowedZones', 'restrictedZones'). Ogni gruppo deve avere un nome univoco, che viene utilizzato come prefisso per le chiavi di telemetria di output del campo calcolato.", + "perimeter-attribute-key": "Imposta la chiave attributo che contiene la definizione del perimetro della zona di geofencing. Il perimetro viene sempre preso dagli attributi lato server dell'entità zona.", + "report-strategy": "Lo stato di presenza segnala se l'entità è attualmente DENTRO o FUORI dal gruppo di zone. Gli eventi di transizione segnalano quando l'entità È ENTRATA o È USCITA dal gruppo di zone.", + "create-relation-with-matched-zones": "Crea automaticamente e mantieni le relazioni tra l'entità e le zone in cui si trova attualmente. Le relazioni vengono rimosse quando l'entità lascia una zona e create quando entra in una nuova.", + "relation-type-required": "Il tipo di relazione è obbligatorio.", + "relation-level-required": "Il livello della relazione è obbligatorio.", + "relation-level-min": "Il valore minimo del livello di relazione è 1.", + "relation-level-max": "Il valore massimo del livello di relazione è {{max}}.", + "geofencing-empty": "È richiesto configurare almeno un gruppo di zone.", + "geofencing-entity-not-found": "Entità target di geofencing non trovata.", + "max-geofencing-zone": "Numero massimo di zone di geofencing raggiunto.", + "zone-group-refresh-interval": "Definisce con quale frequenza vengono aggiornati i gruppi di zone configurati tramite entità correlate.", + "zone-group-refresh-interval-required": "L'intervallo di aggiornamento dei gruppi di zone è obbligatorio.", + "zone-group-refresh-interval-min": "L'intervallo di aggiornamento del gruppo di zone deve essere almeno di {{ min }} secondi.", + "propagation-path-related-entities": "Definisce un percorso diretto a un'entità correlata basato sulla direzione e sul tipo di relazione selezionati. Sono supportate solo le relazioni tra entità dispositivo, asset, cliente e tenant. Il numero massimo di entità risolte tramite il percorso di relazione è {{ max }}.", + "data-propagate": "Definisce i dati da propagare dagli argomenti configurati di seguito. 'Solo argomenti' utilizza direttamente i dati recuperati, mentre 'Risultato espressione' calcola un nuovo valore da quei dati.", + "aggregation-path-related-entities": "Definisce un percorso di aggregazione a livello singolo tramite relazioni dirette con entità genitore o figlio, basato sulla direzione e sul tipo di relazione. Sono supportate solo le relazioni tra entità dispositivo, asset, cliente e tenant. Il numero massimo di entità risolte tramite il percorso di relazione è {{ max }}.", + "arguments-aggregation": "Definisce gli argomenti di input utilizzati per il filtraggio e l'aggregazione.", + "setting-arguments-aggregation": "I dati saranno recuperati dalle entità correlate configurate nel percorso di aggregazione.", + "metrics": "Definisce le metriche aggregate in base agli argomenti configurati.", + "entity-aggregation-metrics": "Definisce le metriche aggregate in base agli argomenti configurati sugli intervalli di tempo specificati.", + "import-invalid-calculated-field-type": "Impossibile importare il campo calcolato: struttura del campo calcolato non valida.", + "simple-expression-title": "Espressione aritmetica che definisce come viene calcolato il valore.", + "script-title": "Script TBEL che definisce la logica di calcolo e i valori di output.", + "simple-arguments": "Espressione aritmetica che definisce come viene calcolato il valore.", + "script-arguments": "Definisce gli argomenti di input disponibili per lo script." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Regole allarme", + "alarm-rule": "Regola allarme", + "alarm-rules": "Regole allarme", + "alarm-rules-old": "Vecchie", + "alarm-rules-actual": "Attuali", + "severities": "Gravità", + "cleared": "Condizione di cancellazione", + "delete-title": "Sei sicuro di voler eliminare la regola allarme '{{title}}'?", + "delete-text": "Attenzione, dopo la conferma la regola allarme e tutti i dati correlati diventeranno irrecuperabili.", + "delete-multiple-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 regola allarme} other {# regole allarme} }?", + "delete-multiple-text": "Attenzione, dopo la conferma tutte le regole allarme selezionate verranno rimosse e tutti i dati correlati diventeranno irrecuperabili.", + "create": "Crea nuova regola allarme", + "add": "Aggiungi regola allarme", + "copy": "Copia configurazione regola allarme", + "details": "Dettagli regola allarme", + "no-found": "Nessuna regola allarme trovata", + "list": "{ count, plural, =1 {Una regola allarme} other {Elenco di # regole allarme} }", + "selected-fields": "{ count, plural, =1 {1 regola allarme} other {# regole allarme} } selezionata/e", + "import": "Importa regola allarme", + "file": "File regola allarme", + "export": "Esporta regola allarme", + "export-failed-error": "Impossibile esportare la regola allarme: {{error}}", + "entity-type": "Tipo entità", + "entity-type-required": "Il tipo entità è obbligatorio.", + "alarm-type": "Tipo di allarme", + "alarm-type-hint": "Identificatore unico (ad esempio, HighTempAlarm) nell'ambito dell'origine dell'allarme (Dispositivo, Asset, ecc.) per evitare conflitti.", + "alarm-type-required": "Il tipo di allarme è obbligatorio.", + "alarm-type-pattern": "Il tipo di allarme non è valido.", + "alarm-type-max-length": "Il tipo di allarme deve essere inferiore a 256 caratteri.", + "clear-alarm": "Cancella allarme", + "value-argument": "Argomento", + "value-argument-required": "L'argomento è obbligatorio.", + "static-settings": "Impostazioni statiche", + "configuration": "Configurazione", + "static-schedule": "Statico", + "dynamic-schedule": "Dinamico", + "operation-and": "E", + "operation-or": "O", + "condition-during": "Durante {{during}}", + "condition-during-dynamic": "Durante \"{{ attribute }}\"", + "condition-repeat-times": "Si ripete { count, plural, =1 {1 volta} other {# volte} }", + "condition-repeat-times-dynamic": "Si ripete \"{{ attribute }}\" volte", + "filter-preview": "Anteprima filtro", + "condition-settings": "Impostazioni condizione", + "static": "Statico", + "dynamic": "Dinamico", + "argument-filters": "Filtri argomento", + "argument-name": "Nome argomento", + "value-type": "Tipo di valore", + "general": "Generale", + "filters": "Filtri", + "date-time-hint": "L'argomento deve essere in millisecondi epoch. Esempio: 1698839340000 corrisponde a 2023-11-01 12:49:00 UTC.", + "operation": "Operazione", + "value-source": "Fonte del valore", + "value": "Valore", + "ignore-case": "Ignora maiuscole/minuscole", + "condition": "Condizione", + "script": "Script", + "add-filter": "Aggiungi filtro argomento", + "edit-filter": "Filtro argomento", + "remove-filter": "Rimuovi filtro argomento", + "no-filter": "È richiesto almeno un filtro.", + "conditions": { + "simple": "Semplice", + "duration": "Durata", + "repeating": "Ripetizione" + }, + "schedule-title": "Pianificazione", + "edit-schedule": "Modifica pianificazione allarme", + "schedule-type": "Tipo di pianificatore", + "schedule-type-required": "Il tipo di pianificatore è obbligatorio.", + "schedule": { + "any-time": "Attivo tutto il tempo", + "specific-time": "Attivo a un orario specifico", + "custom": "Personalizzato" + }, + "schedule-day": { + "monday": "Lunedì", + "tuesday": "Martedì", + "wednesday": "Mercoledì", + "thursday": "Giovedì", + "friday": "Venerdì", + "saturday": "Sabato", + "sunday": "Domenica" + }, + "schedule-days": "Giorni", + "schedule-time": "Orario", + "schedule-time-from": "Da", + "schedule-time-to": "A", + "schedule-days-of-week-required": "È necessario selezionare almeno un giorno della settimana.", + "tbel": "TBEL", + "expression-type": { + "simple": "Semplice", + "script": "Script" + }, + "operation-type": { + "and": "E", + "or": "O" + }, + "filter-predicate-type": { + "string": "Stringa", + "numeric": "Numerico", + "boolean": "Booleano", + "complex": "Complesso" + }, + "alarm-rule-additional-info": "Informazioni aggiuntive", + "edit-alarm-rule-additional-info": "Modifica informazioni aggiuntive", + "alarm-rule-additional-info-placeholder": "Fornisci i tuoi commenti e aggiustamenti qui per mostrarli nei dettagli dell'allarme sotto Informazioni aggiuntive", + "alarm-rule-additional-info-hint": "Suggerimento: usa ${Nome argomento} per sostituire i valori degli argomenti usati nella condizione della regola allarme.", + "alarm-rule-additional-info-icon-hint": "Usa Nome argomento per sostituire i valori degli argomenti usati nella condizione della regola allarme.", + "alarm-rule-mobile-dashboard": "Dashboard mobile", + "alarm-rule-mobile-dashboard-hint": "Utilizzato dall'app mobile come dashboard dei dettagli dell'allarme.", + "alarm-rule-no-mobile-dashboard": "Nessuna dashboard selezionata", + "alarm-rule-condition": "Condizione della regola allarme", + "enter-alarm-rule-condition-prompt": "Aggiungi condizione", + "enter-alarm-rule-clear-condition-prompt": "Aggiungi condizione di cancellazione", + "edit-alarm-rule-condition": "Condizione allarme", + "condition-type": "Tipo di condizione", + "condition-type-hint": "Le opzioni \"Durata\" e \"Ripetizione\" non sono disponibili quando l'operazione \"Missing for\" è utilizzata nel filtro.", + "select-alarm-severity": "Seleziona gravità allarme", + "add-create-alarm-rule-prompt": "È richiesta almeno una condizione di attivazione.", + "add-create-alarm-rule": "Aggiungi condizione di attivazione", + "add-clear-alarm-rule": "Aggiungi condizione di cancellazione", + "condition-duration": "Durata condizione", + "condition-duration-value": "Valore durata", + "condition-duration-time-unit": "Unità di tempo", + "condition-duration-value-range": "Il valore della durata deve essere compreso tra 1 e 2147483647.", + "condition-duration-value-pattern": "Il valore della durata deve essere un numero intero.", + "condition-duration-value-required": "Il valore della durata è obbligatorio.", + "condition-duration-time-unit-required": "L'unità di tempo è obbligatoria.", + "condition-repeating-value": "Conteggio eventi", + "condition-repeating-value-hint": "L'aggiornamento di qualsiasi argomento della regola allarme sarà conteggiato come evento", + "condition-repeating-value-range": "Il conteggio degli eventi deve essere compreso tra 1 e 2147483647.", + "condition-repeating-value-pattern": "Il conteggio degli eventi deve essere un numero intero.", + "condition-repeating-value-required": "Il conteggio degli eventi è obbligatorio.", + "create-conditions": "Condizioni di attivazione", + "clear-condition": "Condizione di cancellazione", + "no-clear-alarm-rule": "Nessuna condizione di cancellazione configurata.", + "advanced-settings": "Impostazioni avanzate", + "propagate-alarm": "Propaga allarme alle entità correlate", + "alarm-rule-relation-types-list": "Tipi di relazione", + "alarm-rule-relation-types-list-hint": "Definisce i tipi di relazione per filtrare le entità correlate. Se non impostato, l'allarme verrà propagato a tutte le entità correlate.", + "propagate-alarm-to-owner": "Propaga allarme al proprietario dell'entità (Cliente o Tenant)", + "propagate-alarm-to-tenant": "Propaga allarme al Tenant", + "alarm-rule-filter-title": "Filtro regola allarme", + "filter-title": "Filtro", + "debugging": "Debugging regola allarme", + "any-type": "Qualsiasi tipo", + "enter-alarm-rule-type": "Inserisci tipo di allarme", + "no-alarm-rule-types-matching": "Nessun tipo di allarme corrispondente a '{{entitySubtype}}' trovato.", + "alarm-rule-type-list-empty": "Nessun tipo di allarme selezionato.", + "alarm-rule-type-list": "Elenco tipi di allarme", + "alarm-rule-entity-list": "Elenco entità", + "missing-for": "mancante per", + "time-unit": "Unità di tempo", + "mode": "Modalità", + "type": "Tipo", + "value-required": "Il valore è obbligatorio.", + "min-value": "Il valore deve essere 1 o maggiore.", + "argument-in-use": "L'argomento è utilizzato come argomento generale.", + "import-invalid-alarm-rule-type": "Impossibile importare la regola allarme: struttura della regola allarme non valida.", + "no-filter-preview": "Nessun filtro specificato", + "filter-operation": { + "and": "E", + "or": "O" } }, "ai-models": { @@ -1193,6 +1659,7 @@ "contact": { "country": "Paese", "country-required": "Il paese è obbligatorio.", + "country-object-required": "Seleziona un paese valido dalla lista.", "city": "Città", "state": "Stato / Provincia", "postal-code": "CAP / Codice postale", @@ -1229,6 +1696,8 @@ "documentation": "Documentazione", "time-left": "{{time}} rimanenti", "output": "Output", + "sort-asc": "Ascendente", + "sort-desc": "Discendente", "suffix": { "s": "s", "ms": "ms" @@ -1365,6 +1834,8 @@ "mobile-order": "Ordine della dashboard nell'app mobile", "mobile-hide": "Nascondi dashboard nell'app mobile", "update-image": "Aggiorna immagine dashboard", + "update-new-version": "Carica nuova versione", + "upload-file-to-update": "Carica file per aggiornamento", "take-screenshot": "Cattura screenshot", "select-widget-title": "Seleziona widget", "select-widget-value": "{{title}}: seleziona widget", @@ -1733,6 +2204,8 @@ "bootstrap-tab": "Client bootstrap", "bootstrap-server": "Server Bootstrap", "lwm2m-server": "Server LwM2M", + "client-reboot": "Attivazione aggiornamento registrazione", + "bootstrap-reboot": "Attivazione richiesta bootstrap", "client-publicKey-or-id": "Chiave pubblica o ID del client", "client-publicKey-or-id-required": "Chiave pubblica o ID del client è obbligatoria.", "client-publicKey-or-id-tooltip-psk": "L'identificatore PSK è un identificatore arbitrario fino a 128 byte, come descritto nello standard [RFC7925].\nL'identificatore PSK DEVE essere prima convertito in una stringa e poi codificato in UTF-8.", @@ -1780,7 +2253,6 @@ "unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
    {{widgetsList}}", "is-gateway": "È un gateway", "overwrite-activity-time": "Sovrascrivi tempo di attività per il dispositivo connesso", - "device-filter": "Filtro dispositivo", "device-filter-title": "Filtro dispositivo", "filter-title": "Filtro", "device-state": "Stato dispositivo", @@ -2234,7 +2706,8 @@ "short-id-required": "ID server breve obbligatorio.", "short-id-range": "L'ID server breve deve essere compreso tra {{ min }} e {{ max }}.", "short-id-pattern": "L'ID server breve deve essere un numero intero positivo.", - "lifetime": "Durata registrazione client", + "short-id-pattern-bs": "Il breve ID del server deve essere solo nullo", + "lifetime": "Durata della registrazione del client", "lifetime-required": "Durata registrazione client obbligatoria.", "lifetime-pattern": "La durata della registrazione deve essere un numero intero positivo.", "default-min-period": "Periodo minimo tra due notifiche (s)", @@ -2328,7 +2801,9 @@ "composite-all-description": "Tutte le risorse sono osservate con un’unica richiesta Composite Observe (più efficiente, meno flessibile)", "composite-by-object": "Composita per oggetti", "composite-by-object-description": "Le risorse sono raggruppate per tipo di oggetto e osservate tramite richieste Composite Observe separate (approccio bilanciato)" - } + }, + "init-attr-tel-as-obs-strategy": "Inizializza attributi e telemetria utilizzando la strategia Observe", + "init-attr-tel-as-obs-strategy-hint": "Se falso - gli attributi e la telemetria vengono inizializzati leggendo i loro valori uno per uno.\\nSe vero - gli attributi e la telemetria vengono inizializzati sottoscrivendo i loro valori utilizzando la strategia Observe." }, "snmp": { "add-communication-config": "Aggiungi configurazione di comunicazione", @@ -2644,6 +3119,8 @@ "type-rulenodes": "Nodi rule", "list-of-rulenodes": "{ count, plural, =1 {Un nodo rule} other {Elenco di # nodi rule} }", "rulenode-name-starts-with": "Nodi rule i cui nomi iniziano con '{{prefix}}'", + "type-api-key": "Chiave API", + "type-api-keys": "Chiavi API", "type-current-customer": "Cliente corrente", "type-current-tenant": "Tenant corrente", "type-current-user": "Utente corrente", @@ -2665,6 +3142,7 @@ "details": "Dettagli entità", "no-entities-prompt": "Nessuna entità trovata", "no-data": "Nessun dato da visualizzare", + "show-all-columns": "Mostra tutto", "columns-to-display": "Colonne da visualizzare", "type-api-usage-state": "Stato utilizzo API", "type-edge": "Edge", @@ -2710,7 +3188,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {Un'applicazione mobile} other {Elenco di # applicazioni mobili} }", "type-mobile-app-bundle": "Pacchetto mobile", "type-mobile-app-bundles": "Pacchetti mobili", - "list-of-mobile-app-bundles": "{ count, plural, =1 {Un pacchetto mobile} other {Elenco di # pacchetti mobili} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {Un pacchetto mobile} other {Elenco di # pacchetti mobili} }", + "limit-reached": "Limite raggiunto", + "limit-reached-text": "Hai raggiunto il limite di {{ entities }}. Per aggiungere altri, chiedi al tuo Amministratore di Sistema di aumentare il limite di {{ entity }}.", + "request-limit-increase": "Richiedi aumento del limite", + "request-sysadmin-text": "Sei l'Amministratore di Sistema?", + "login-here": "Accedi qui", + "to-increase-limit": "per aumentare il limite.", + "increase-limit-request-sent-title": "Abbiamo inviato una richiesta automatica al tuo Amministratore di Sistema per aumentare il limite", + "increase-limit-request-sent-text": "Consenti del tempo per la revisione della richiesta e l'aggiornamento delle impostazioni. Potrebbe essere necessario aggiornare questa pagina per vedere le modifiche." }, "entity-field": { "created-time": "Data di creazione", @@ -3798,7 +4284,8 @@ "password-link-sent-message": "Link di reimpostazione inviato", "email": "Email", "invalid-email-format": "Formato email non valido.", - "login-with": "Accedi con {{name}}", + "sign-in-with": "Accedi con {{name}}", + "sign-in-to-your-account": "Accedi al tuo account", "or": "oppure", "error": "Errore di accesso", "verify-your-identity": "Verifica la tua identità", @@ -3817,7 +4304,51 @@ "activation-link-expired": "Il link di attivazione è scaduto", "activation-link-expired-message": "Il link per attivare il tuo profilo è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email.", "reset-password-link-expired": "Il link per reimpostare la password è scaduto", - "reset-password-link-expired-message": "Il link per reimpostare la password è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email." + "reset-password-link-expired-message": "Il link per reimpostare la password è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email.", + "two-fa": "Autenticazione a due fattori", + "two-fa-required": "L'autenticazione a due fattori è obbligatoria", + "set-up-verification-method": "Configura un metodo di verifica per continuare", + "set-up-verification-method-login": "Configura un metodo di verifica o accedi", + "enable-authenticator-app": "Abilita l'app di autenticazione", + "enable-authenticator-app-description": "Inserisci il codice di sicurezza dalla tua app di autenticazione", + "enable-authenticator-sms": "Abilita l'autenticatore SMS", + "enable-authenticator-sms-description": "Inserisci un codice a 6 cifre che ti abbiamo appena inviato a ", + "enable-authenticator-email": "Abilita l'autenticatore email", + "enable-authenticator-email-description": "Un codice di sicurezza è stato inviato al tuo indirizzo email a ", + "enter-key-manually": "oppure inserisci manualmente questa chiave a 32 cifre:", + "continue": "Continua", + "confirm": "Conferma", + "authenticator-app-success": "App di autenticazione abilitata con successo", + "authenticator-app-success-description": "La prossima volta che effettuerai l'accesso, dovrai fornire un codice di autenticazione a due fattori", + "authenticator-sms-success": "Autenticatore SMS abilitato con successo", + "authenticator-sms-success-description": "La prossima volta che effettuerai l'accesso, ti verrà richiesto di inserire il codice di sicurezza che verrà inviato al numero di telefono", + "authenticator-email-success": "Autenticatore email abilitato con successo", + "authenticator-email-success-description": "La prossima volta che effettuerai l'accesso, ti verrà richiesto di inserire il codice di sicurezza che verrà inviato al tuo indirizzo email", + "authenticator-backup-code-success": "Codice di backup abilitato con successo", + "authenticator-backup-code-success-description": "La prossima volta che effettuerai l'accesso, ti verrà richiesto di inserire il codice di sicurezza o utilizzare uno dei codici di backup.", + "add-verification-method": "Aggiungi metodo di verifica", + "get-backup-code": "Ottieni codice di backup", + "copy-key": "Copia chiave", + "send-code": "Invia codice", + "email-label": "Email", + "email-description": "Inserisci un'email da utilizzare come tuo autenticatore.", + "sms-description": "Inserisci un numero di telefono da utilizzare come tuo autenticatore.", + "backup-code-description": "Stampa i codici in modo da averli a portata di mano quando ne avrai bisogno per accedere al tuo account. Puoi utilizzare ogni codice di backup una sola volta.", + "backup-code-warn": "Una volta che lasci questa pagina, questi codici non potranno essere mostrati di nuovo. Conservali in modo sicuro utilizzando le opzioni sottostanti.", + "download-txt": "Scarica (txt)", + "print": "Stampa", + "verification-code": "Codice a 6 cifre", + "verification-code-invalid": "Formato del codice di verifica non valido", + "verification-code-incorrect": "Codice di verifica errato", + "verification-code-many-request": "Troppe richieste per verificare il codice", + "scan-qr-code": "Scansiona questo codice QR con la tua app di verifica", + "phone-input": { + "phone-input-label": "Numero di telefono", + "phone-input-required": "Il numero di telefono è obbligatorio", + "phone-input-validation": "Il numero di telefono non è valido o non possibile", + "phone-input-pattern": "Numero di telefono non valido. Deve essere nel formato E.164, es. {{phoneNumber}}", + "phone-input-hint": "Numero di telefono nel formato E.164, es. {{phoneNumber}}" + } }, "mobile": { "add-application": "Aggiungi applicazione", @@ -4185,6 +4716,7 @@ "api-usage-limit": "Limite utilizzo API", "device-activity": "Attività dispositivo", "entities-limit": "Limite entità", + "entities-limit-increase-request": "Richiesta aumento limite entità", "entity-action": "Azione entità", "general": "Generale", "rule-engine-lifecycle-event": "Evento ciclo di vita Rule Engine", @@ -4402,6 +4934,12 @@ "at-least": "Almeno:", "character": "{ count, plural, =1 {1 carattere} other {# caratteri} }", "digit": "{ count, plural, =1 {1 cifra} other {# cifre} }", + "password-tooltip-min-length": "Almeno {{minimumLength}} caratteri", + "password-tooltip-max-length": "Al massimo {{maximumLength}} caratteri", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} carattere maiuscolo", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} carattere minuscolo", + "password-tooltip-digit": "{{minimumDigits}} numero", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} carattere speciale", "incorrect-password-try-again": "Password errata. Riprova", "lowercase-letter": "{ count, plural, =1 {1 lettera minuscola} other {# lettere minuscole} }", "new-passwords-not-match": "La nuova password non corrisponde", @@ -4460,7 +4998,8 @@ "additional-info": "Informazioni aggiuntive (JSON)", "invalid-additional-info": "Impossibile analizzare il JSON delle informazioni aggiuntive.", "no-relations-text": "Nessuna relazione trovata", - "not": "Non" + "not": "Non", + "copy-type": "Tipo di copia" }, "resource": { "add": "Aggiungi risorsa", @@ -5411,7 +5950,7 @@ "time-series": "Serie temporale", "latest": "Ultimi valori", "web-sockets": "WebSockets", - "calculated-fields": "Campi calcolati" + "calculated-fields-and-alarm-rules": "Campi calcolati e regole allarme" }, "save-attribute": { "processing-settings": "Impostazioni di elaborazione", @@ -5606,7 +6145,8 @@ "bad-request-params": "Parametri di richiesta errati", "item-not-found": "Elemento non trovato", "too-many-requests": "Troppe richieste", - "too-many-updates": "Troppe modifiche" + "too-many-updates": "Troppe modifiche", + "entities-limit-exceeded": "Limite entità superato" }, "tenant": { "tenant": "Tenant", @@ -5744,6 +6284,27 @@ "max-arguments-per-cf": "Numero massimo di argomenti per campo calcolato", "max-arguments-per-cf-range": "Il numero massimo di argomenti per campo calcolato non può essere negativo", "max-arguments-per-cf-required": "Il numero massimo di argomenti per campo calcolato è richiesto", + "max-related-level-per-argument": "Numero massimo di livello di relazione per l'argomento 'Entità correlate'", + "max-related-level-per-argument-range": "Il numero massimo di livello di relazione per l'argomento 'Entità correlate' non può essere inferiore a '1'", + "max-related-level-per-argument-required": "Il numero massimo di livello di relazione per l'argomento 'Entità correlate' è obbligatorio", + "min-allowed-scheduled-update-interval": "Intervallo minimo consentito per l'aggiornamento dell'argomento 'Entità correlate' (secondi)", + "min-allowed-scheduled-update-interval-range": "Il numero minimo dell'intervallo di aggiornamento consentito non può essere negativo", + "min-allowed-deduplication-interval": "Intervallo minimo consentito di deduplicazione (secondi)", + "min-allowed-deduplication-interval-range": "Il valore dell'intervallo minimo consentito di deduplicazione non può essere negativo", + "min-allowed-deduplication-interval-required": "L'intervallo minimo consentito di deduplicazione è obbligatorio", + "intermediate-aggregation-interval": "Intervallo di aggregazione intermedio (secondi)", + "intermediate-aggregation-interval-range": "Il valore dell'intervallo di aggregazione intermedio non può essere inferiore a '1'", + "intermediate-aggregation-interval-required": "L'intervallo di aggregazione intermedio è obbligatorio", + "reevaluation-check-interval": "Intervallo di controllo della rivalutazione (secondi)", + "reevaluation-check-interval-range": "Il valore dell'intervallo di controllo della rivalutazione non può essere inferiore a '1'", + "reevaluation-check-interval-required": "L'intervallo di controllo della rivalutazione è obbligatorio", + "alarms-reevaluation-interval": "Intervallo di rivalutazione degli allarmi (secondi)", + "alarms-reevaluation-interval-range": "Il valore dell'intervallo di rivalutazione degli allarmi non può essere inferiore a '1'", + "alarms-reevaluation-interval-required": "L'intervallo di rivalutazione degli allarmi è obbligatorio", + "min-allowed-aggregation-interval": "Intervallo minimo consentito di aggregazione (secondi)", + "min-allowed-aggregation-interval-range": "Il valore dell'intervallo minimo consentito di aggregazione non può essere negativo", + "min-allowed-aggregation-interval-required": "L'intervallo minimo consentito di aggregazione è obbligatorio", + "min-allowed-scheduled-update-interval-required": "Il numero minimo dell'intervallo di aggiornamento consentito è obbligatorio", "max-state-size": "Dimensione massima dello stato in KB", "max-state-size-range": "La dimensione massima dello stato in KB non può essere negativa", "max-state-size-required": "La dimensione massima dello stato in KB è richiesta", @@ -5819,6 +6380,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Numero massimo di sottoscrizioni per utente regolare", "ws-limit-max-subscriptions-per-public-user": "Numero massimo di sottoscrizioni per utente pubblico", "ws-limit-updates-per-session": "Aggiornamenti WS per sessione", + "relation-search-entity-limit": "Limite entità ricerca relazione", + "relation-search-entity-limit-hint": "Limita il numero di entità risolte all'ultimo livello del percorso di relazione. Si applica agli argomenti 'Entità correlate' e ai campi di propagazione.", + "relation-search-entity-limit-required": "Il limite delle entità nella ricerca di relazione è obbligatorio", + "relation-search-entity-limit-range": "Il limite delle entità nella ricerca di relazione non può essere inferiore a '1'", "rate-limits": { "add-limit": "Aggiungi limite", "and-also-less-than": "e anche inferiore a", @@ -6004,7 +6569,9 @@ "default-agg-interval": "Intervallo di raggruppamento predefinito", "edit-intervals-list-hint": "È possibile specificare un elenco di opzioni di intervallo disponibili.", "edit-grouping-intervals-list-hint": "È possibile configurare l'elenco degli intervalli di raggruppamento e l'intervallo di raggruppamento predefinito.", - "all": "Tutti" + "all": "Tutti", + "save-current-settings-as-default": "Salva le impostazioni correnti come finestra temporale predefinita", + "hide-option-from-end-users": "Nascondi l'opzione dagli utenti finali" }, "tooltip": { "trigger": "Trigger", @@ -6658,7 +7225,8 @@ "export-relations": "Esporta relazioni", "export-attributes": "Esporta attributi", "export-credentials": "Esporta credenziali", - "export-calculated-fields": "Esporta campi calcolati", + "export-calculated-fields": "Esporta campi calcolati \ne regole allarme", + "export-alarm-rules": "Esporta regole allarme", "entity-versions": "Versioni entità", "versions": "Versioni", "created-time": "Ora di creazione", @@ -6676,6 +7244,7 @@ "load-attributes": "Carica attributi", "load-credentials": "Carica credenziali", "load-calculated-fields": "Carica campi calcolati", + "load-alarm-rules": "Carica regole allarme", "compare-with-current": "Confronta con l'attuale", "diff-entity-with-version": "Differenze con la versione '{{versionName}}'", "previous-difference": "Differenza precedente", @@ -6885,7 +7454,23 @@ "scan-qr-code": "Scansiona codice QR", "make-phone-call": "Effettua una chiamata", "get-location": "Ottieni la posizione del telefono", - "take-screenshot": "Cattura schermata" + "take-screenshot": "Cattura schermata", + "handle-provision-success-function": "Gestisci la funzione di successo della provision", + "get-location-function": "Funzione per ottenere la posizione", + "process-launch-result-function": "Funzione per elaborare il risultato del lancio", + "get-phone-number-function": "Funzione per ottenere il numero di telefono", + "process-image-function": "Funzione per elaborare l'immagine", + "process-qr-code-function": "Funzione per elaborare il codice QR", + "process-location-function": "Funzione per elaborare la posizione", + "handle-empty-result-function": "Funzione per gestire il risultato vuoto", + "handle-error-function": "Funzione per gestire l'errore", + "handle-non-mobile-fallback-function": "Funzione per gestire il fallback Non-Mobile", + "save-to-gallery": "Salva nella galleria", + "provision-type": "Tipo di provision", + "auto": "Auto", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Funzione azione personalizzata", "custom-pretty-function": "Funzione azione personalizzata (con template HTML)", @@ -6894,7 +7479,8 @@ "marker": "Indicatore", "polygon": "Poligono", "rectangle": "Rettangolo", - "circle": "Cerchio" + "circle": "Cerchio", + "polyline": "Polilinea" }, "place-map-item": "Posiziona elemento sulla mappa", "map-item-tooltip": { @@ -6906,7 +7492,9 @@ "continue-draw-polygon": "Continua disegno poligono", "finish-draw-polygon": "Termina disegno poligono", "start-draw-circle": "Inizia a disegnare cerchio", - "finish-draw-circle": "Termina disegno cerchio" + "finish-draw-circle": "Termina disegno cerchio", + "start-draw-polyline": "Inizia a disegnare la polilinea", + "finish-draw-polyline": "Termina il disegno della polilinea" } }, "widgets-bundle": { @@ -7472,6 +8060,14 @@ "update-animation-delay": "Ritardo aggiornamento animazione" }, "chart-axis": { + "limit": "Limite", + "source": "Fonte", + "key-value": "Chiave / Valore", + "value-required": "Il valore è obbligatorio.", + "entity-key-required": "La chiave dell'entità è obbligatoria.", + "key-required": "La chiave è obbligatoria.", + "scale-limits": "Limiti scala", + "scale-appearance": "Aspetto scala", "scale": "Scala", "scale-min": "min", "scale-max": "max", @@ -8016,7 +8612,10 @@ "add-radio-option": "Aggiungi opzione radio", "radio-label-position": "Posizione dell'etichetta", "radio-label-position-before": "Prima", - "radio-label-position-after": "Dopo" + "radio-label-position-after": "Dopo", + "save-image": "Salva immagine", + "save-to-gallery": "Archivia automaticamente le immagini catturate nella Galleria Immagini", + "public-image": "Rende l'immagine disponibile per qualsiasi utente non autorizzato" }, "invalid-qr-code-text": "Testo non valido per codice QR. L'input deve essere di tipo stringa", "qr-code": { @@ -8311,7 +8910,8 @@ "trips": "Percorsi", "markers": "Marker", "polygons": "Poligoni", - "circles": "Cerchi" + "circles": "Cerchi", + "polylines": "Polilinee" }, "data-layer": { "source": "Sorgente", @@ -8508,8 +9108,27 @@ "finish-circle-hint-with-entity": "Cerchio per '{{entityName}}': clicca per terminare e salvare", "finish-circle-hint": "Cerchio: clicca per terminare il disegno" }, + "polyline": { + "polyline-key": "Chiave polilinea", + "polyline-key-required": "Chiave polilinea obbligatoria", + "no-polylines": "Nessuna polilinea configurata", + "add-polylines": "Aggiungi polilinea", + "polyline-configuration": "Configurazione polilinea", + "remove-polyline": "Rimuovi polilinea", + "edit": "Modifica polilinea", + "cut": "Taglia area polilinea", + "rotate": "Ruota polilinea", + "remove-polyline-for": "Rimuovi polilinea per '{{entityName}}'", + "draw-polyline": "Disegna polilinea", + "polyline-place-first-point-hint-with-entity": "Polilinea per '{{entityName}}': clicca per posizionare il primo punto", + "polyline-place-first-point-hint": "Polilinea: clicca per posizionare il primo punto", + "finish-polyline-hint-with-entity": "Polilinea per '{{entityName}}': clicca per terminare il disegno", + "finish-polyline-hint": "Polilinea: clicca per terminare il disegno", + "polyline-place-first-point-cut-hint": "Clicca per posizionare il primo punto", + "finish-polyline-cut-hint": "Clicca sul primo marcatore per terminare e salvare" + }, "select-entity": "Seleziona entità", - "select-entity-hint": "Suggerimento: dopo la selezione, clicca sulla mappa per impostare la posizione" + "select-entity-hint": "Suggerimento: dopo la selezione clicca sulla mappa per impostare la posizione" }, "select-entity": "Seleziona entità", "select-entity-hint": "Suggerimento: dopo la selezione clicca sulla mappa per impostare la posizione", @@ -8949,6 +9568,7 @@ "show-empty-space-hidden-action": "Mostra spazio vuoto al posto del pulsante di azione nascosto", "dont-reserve-space-hidden-action": "Non riservare spazio per i pulsanti di azione nascosti", "display-timestamp": "Timestamp", + "timestamp-column-name": "Timestamp", "display-pagination": "Mostra paginazione", "default-page-size": "Dimensione pagina predefinita", "page-step-settings": "Impostazioni passo di pagina", @@ -9010,7 +9630,9 @@ "alarm-column-error": "Deve essere specificata almeno una colonna allarmi", "table-tabs": "Schede della tabella", "show-cell-actions-menu-mobile": "Mostra menu azioni cella in modalità mobile", - "disable-sorting": "Disabilita ordinamento" + "disable-sorting": "Disabilita ordinamento", + "sort-by": "Ordina schede per", + "sort-timestamp-option": "Orario di creazione" }, "latest-chart": { "total": "Totale", @@ -9502,11 +10124,28 @@ "content": "

    Creando dashboard per gli utenti finali, ogni cliente può vedere solo i propri dispositivi; i dati degli altri clienti saranno nascosti.

    Segui la documentazione per sapere come fare:

    " } } + }, + "api-usage": { + "api-usage": "Uso API", + "label": "Etichetta", + "state-name": "Nome stato", + "status": "Stato", + "status-required": "Lo stato è obbligatorio.", + "limit": "Limite massimo", + "limit-required": "Il limite massimo è obbligatorio.", + "current-number": "Numero attuale", + "current-number-required": "Il numero attuale è obbligatorio.", + "add-key": "Aggiungi chiave", + "no-key": "Nessuna chiave", + "delete-key": "Elimina chiave", + "target-dashboard-state": "Stato della dashboard di destinazione", + "go-to-main-state": "Vai alla vista predefinita" } }, "icon": { "icon": "Icona", "icons": "Icone", + "custom": "Personalizzato", "select-icon": "Seleziona icona", "material-icons": "Icone Material", "show-all": "Mostra tutte le icone", @@ -9547,6 +10186,7 @@ "items-per-page-separator": "di" }, "language": { + "auto": "Auto", "language": "Lingua" } } \ No newline at end of file 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 73b4751ba4..d4726ac3e9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ja_JP.json +++ b/ui-ngx/src/assets/locale/locale.constant-ja_JP.json @@ -1,1512 +1,10192 @@ { - "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": "[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}}" - }, - "aggregation": { - "aggregation": "集約", - "function": "データ集約機能", - "limit": "上限", - "group-interval": "グループ化の間隔", - "min": "最小", - "max": "最大", - "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": "テストメールを送信する" - }, - "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": "Active Acknowledged", - "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 alarm} other {# alarms} }選択された", - "no-data": "表示するデータがありません", - "polling-interval": "アラームポーリング間隔(秒)", - "polling-interval-required": "アラームのポーリング間隔が必要です。", - "min-polling-interval-message": "少なくとも1秒間のポーリング間隔が許可されます。", - "aknowledge-alarms-title": "{ count, plural, =1 {1 alarm} other {# alarms} }", - "aknowledge-alarms-text": "{ count, plural, =1 {1 alarm} other {# alarms} }?", - "clear-alarms-title": "{ count, plural, =1 {1 alarm} other {# alarms} }", - "clear-alarms-text": "{ count, plural, =1 {1 alarm} other {# alarms} }?" - }, - "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-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}}", - "entity-filter": "エンティティフィルタ", - "resolve-multiple": "複数のエンティティとして解決する", - "filter-type": "フィルタタイプ", - "filter-type-required": "フィルタタイプが必要です。", - "entity-filter-no-entity-matched": "指定されたフィルタに一致するエンティティは見つかりませんでした。", - "no-entity-filter-specified": "エンティティフィルタが指定されていない", - "root-state-entity": "ルートとしてダッシュボードの状態エンティティを使用する", - "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 asset} other {# assets} }顧客に", - "delete-assets": "アセットを削除する", - "unassign-assets": "アセットの割り当てを解除する", - "unassign-assets-action-title": "{ count, plural, =1 {1 asset} other {# assets} }顧客から", - "assign-new-asset": "新しいアセットを割り当てる", - "delete-asset-title": "'{{assetName}}'?", - "delete-asset-text": "確認後、アセットと関連するすべてのデータが回復不能になることに注意してください。", - "delete-assets-title": "{ count, plural, =1 {1 asset} other {# assets} }?", - "delete-assets-action-title": "{ count, plural, =1 {1 asset} other {# assets} }", - "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 asset} other {# assets} }?", - "unassign-assets-text": "確認後、選択されたすべてのアセットが割り当て解除され、顧客がアクセスできなくなります。", - "copyId": "アセットIDをコピーする", - "idCopiedMessage": "アセットIDがクリップボードにコピーされました", - "select-asset": "アセットを選択", - "no-assets-matching": "'{{entity}}'発見されました。", - "asset-required": "アセットが必要です", - "name-starts-with": "次で始まるアセット名", - "label": "ラベル" - }, - "attribute": { - "attributes": "属性", - "latest-telemetry": "最新テレメトリ", - "attributes-scope": "エンティティ属性のスコープ", - "scope-telemetry": "テレメトリー", - "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 attribute} other {# attributes} }?", - "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 {1 attribute} other {# attributes} }選択された", - "selected-telemetry": "{ count, plural, =1 {1 telemetry unit} other {# telemetry units} }選択された" - }, - "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": "クリアされた", - "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": "Eメール", - "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": "顧客デバイス", - "assets": "顧客アセット", - "public-dashboards": "パブリックダッシュボード", - "public-devices": "パブリックデバイス", - "public-assets": "パブリックアセット", - "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 customer} other {# customers} }?", - "delete-customers-action-title": "{ count, plural, =1 {1 customer} other {# customers} }", - "delete-customers-text": "確認後、選択したすべての顧客は削除され、関連するすべてのデータは回復不能になります。", - "manage-users": "ユーザーを管理する", - "manage-assets": "アセットを管理する", - "manage-devices": "デバイスを管理する", - "manage-dashboards": "ダッシュボードの管理", - "title": "タイトル", - "title-required": "タイトルは必須です。", - "description": "説明", - "details": "詳細", - "events": "イベント", - "copyId": "顧客IDをコピー", - "idCopiedMessage": "顧客IDがクリップボードにコピーされました", - "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": "ウィジェットを選択", - "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 dashboard} other {# dashboards} }顧客に", - "unassign-dashboards-action-text": "{ count, plural, =1 {1 dashboard} other {# dashboards} }顧客から", - "delete-dashboards": "ダッシュボードの削除", - "unassign-dashboards": "ダッシュボードの割り当てを解除する", - "unassign-dashboards-action-title": "{ count, plural, =1 {1 dashboard} other {# dashboards} }顧客から", - "delete-dashboard-title": "'{{dashboardTitle}}'?", - "delete-dashboard-text": "確認後、ダッシュボードとすべての関連データが回復不能になるので注意してください。", - "delete-dashboards-title": "{ count, plural, =1 {1 dashboard} other {# dashboards} }?", - "delete-dashboards-action-title": "{ count, plural, =1 {1 dashboard} other {# dashboards} }", - "delete-dashboards-text": "注意してください。確認後、選択したダッシュボードはすべて削除され、関連するすべてのデータは回復不能になります。", - "unassign-dashboard-title": "'{{dashboardTitle}}'?", - "unassign-dashboard-text": "確認後、ダッシュボードは割り当てられなくなり、顧客はアクセスできなくなります。", - "unassign-dashboard": "ダッシュボードの割り当てを解除する", - "unassign-dashboards-title": "{ count, plural, =1 {1 dashboard} other {# dashboards} }?", - "unassign-dashboards-text": "確認の後、選択したすべてのダッシュボードは割り当てられなくなり、顧客はアクセスできなくなります。", - "public-dashboard-title": "ダッシュボードは公開されました", - "public-dashboard-text": "{{dashboardTitle}} is now public and accessible via next public link:", - "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ピクセルしか許可されません。", - "max-mobile-row-height-message": "移動可能な行の高さの最大値として許可されるのは200ピクセルだけです。", - "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 dashboard state} other {# dashboard states} }選択された", - "edit-state": "ダッシュボードの状態を編集する", - "delete-state": "ダッシュボードの状態を削除する", - "add-state": "ダッシュボードの状態を追加する", - "state": "ダッシュボードの状態", - "state-name": "名", - "state-name-required": "ダッシュボードの状態名は必須です。", - "state-id": "状態ID", - "state-id-required": "ダッシュボードの状態IDは必須です。", - "state-id-exists": "同じIDを持つダッシュボードの状態は既に存在します。", - "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": "属性", - "alarm": "アラームフィールド", - "timeseries-required": "エンティティの時系列データが必要です。", - "timeseries-or-attributes-required": "エンティティのtimeseries /属性は必須です。", - "maximum-timeseries-or-attributes": "{ count, plural, =1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", - "alarm-fields-required": "アラームフィールドが必要です。", - "function-types": "関数型", - "function-types-required": "関数型が必要です。", - "maximum-function-types": "{ count, plural, =1 {1 function type is allowed.} other {# function types are allowed} }" - }, - "datasource": { - "type": "データソースタイプ", - "name": "名", - "add-datasource-prompt": "データソースを追加してください" - }, - "details": { - "edit-mode": "編集モード", - "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 device} other {# devices} }顧客に", - "delete-devices": "デバイスを削除する", - "unassign-from-customer": "顧客からの割り当て解除", - "unassign-devices": "デバイスの割り当てを解除する", - "unassign-devices-action-title": "{ count, plural, =1 {1 device} other {# devices} }顧客から", - "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 device} other {# devices} }?", - "delete-devices-action-title": "{ count, plural, =1 {1 device} other {# devices} }", - "delete-devices-text": "注意してください。確認後、選択したすべてのデバイスが削除され、関連するすべてのデータは回復不能になります。", - "unassign-device-title": "'{{deviceName}}'?", - "unassign-device-text": "確認の後、デバイスは割り当てが解除され、顧客がアクセスできなくなります。", - "unassign-device": "デバイスの割り当てを解除する", - "unassign-devices-title": "{ count, plural, =1 {1 device} other {# devices} }?", - "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": "デバイスIDをコピーする", - "copyAccessToken": "コピーアクセストークン", - "idCopiedMessage": "デバイスIDがクリップボードにコピーされました", - "accessTokenCopiedMessage": "デバイスアクセストークンがクリップボードにコピーされました", - "assignedToCustomer": "顧客に割り当てられた", - "unable-delete-device-alias-title": "デバイスエイリアスを削除できません", - "unable-delete-device-alias-text": "'{{deviceAlias}}'{{widgetsList}}", - "is-gateway": "ゲートウェイです", - "public": "パブリック", - "device-public": "デバイスは公開されています", - "select-device": "デバイスの選択" - }, - "dialog": { - "close": "ダイアログを閉じる" - }, - "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-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 {One device} other {List of # devices} }", - "device-name-starts-with": "'{{prefix}}'", - "type-asset": "アセット", - "type-assets": "アセット", - "list-of-assets": "{ count, plural, =1 {One asset} other {List of # assets} }", - "asset-name-starts-with": "'{{prefix}}'", - "type-rule": "ルール", - "type-rules": "ルール", - "list-of-rules": "{ count, plural, =1 {One rule} other {List of # rules} }", - "rule-name-starts-with": "'{{prefix}}'", - "type-plugin": "プラグイン", - "type-plugins": "プラグイン", - "list-of-plugins": "{ count, plural, =1 {One plugin} other {List of # plugins} }", - "plugin-name-starts-with": "'{{prefix}}'", - "type-tenant": "テナント", - "type-tenants": "テナント", - "list-of-tenants": "{ count, plural, =1 {One tenant} other {List of # tenants} }", - "tenant-name-starts-with": "'{{prefix}}'", - "type-customer": "顧客", - "type-customers": "顧客", - "list-of-customers": "{ count, plural, =1 {One customer} other {List of # customers} }", - "customer-name-starts-with": "'{{prefix}}'", - "type-user": "ユーザー", - "type-users": "ユーザー", - "list-of-users": "{ count, plural, =1 {One user} other {List of # users} }", - "user-name-starts-with": "'{{prefix}}'", - "type-dashboard": "ダッシュボード", - "type-dashboards": "ダッシュボード", - "list-of-dashboards": "{ count, plural, =1 {One dashboard} other {List of # dashboards} }", - "dashboard-name-starts-with": "'{{prefix}}'", - "type-alarm": "アラーム", - "type-alarms": "アラーム", - "list-of-alarms": "{ count, plural, =1 {One alarms} other {List of # alarms} }", - "alarm-name-starts-with": "'{{prefix}}'", - "type-rulechain": "ルールチェーン", - "type-rulechains": "ルールチェーン", - "list-of-rulechains": "{ count, plural, =1 {One rule chain} other {List of # rule chains} }", - "rulechain-name-starts-with": "'{{prefix}}'", - "type-rulenode": "ルールノード", - "type-rulenodes": "ルールノード", - "list-of-rulenodes": "{ count, plural, =1 {One rule node} other {List of # rule nodes} }", - "rulenode-name-starts-with": "'{{prefix}}'", - "type-current-customer": "現在の顧客", - "search": "検索エンティティ", - "selected-entities": "{ count, plural, =1 {1 entity} other {# entities} }選択された", - "entity-name": "エンティティ名", - "details": "エンティティの詳細", - "no-entities-prompt": "エンティティが見つかりません", - "no-data": "表示するデータがありません" - }, - "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": "メッセージID", - "message-type": "メッセージタイプ", - "data-type": "データ・タイプ", - "relation-type": "関係タイプ", - "metadata": "メタデータ", - "data": "データ", - "event": "イベント", - "status": "状態", - "success": "成功", - "failed": "失敗", - "messages-processed": "処理されたメッセージ", - "errors-occurred": "エラーが発生しました", + "access": { + "unauthorized": "未認証", + "unauthorized-access": "未認証アクセス", + "unauthorized-access-text": "このリソースにアクセスするにはサインインする必要があります!", + "access-forbidden": "アクセス禁止", + "access-forbidden-text": "この場所へのアクセス権がありません!
    この場所にアクセスしたい場合は、別のユーザーでサインインしてみてください。", + "refresh-token-expired": "セッションの有効期限が切れました", + "refresh-token-failed": "セッションを更新できません", + "permission-denied": "権限がありません", + "permission-denied-text": "この操作を実行する権限がありません!" + }, + "account": { + "account": "アカウント", + "notification-settings": "通知設定" + }, + "action": { + "activate": "有効化", + "suspend": "一時停止", + "save": "保存", + "saveAs": "名前を付けて保存", + "move": "移動", + "cancel": "キャンセル", + "ok": "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": "変更を破棄", + "decline": "拒否", + "close": "閉じる", + "back": "戻る", + "run": "実行", + "sign-in": "サインイン!", + "edit": "編集", + "view": "表示", + "create": "作成", + "drag": "ドラッグ", + "refresh": "更新", + "undo": "元に戻す", + "copy": "コピー", + "paste": "貼り付け", + "copy-reference": "参照をコピー", + "paste-reference": "参照を貼り付け", + "import": "インポート", + "export": "エクスポート", + "share-via": "{{provider}}で共有", + "select": "選択", + "continue": "続行", + "discard-changes": "変更を破棄", + "download": "ダウンロード", + "next": "次へ", + "next-with-label": "次へ: {{label}}", + "read-more": "詳細を見る", + "hide": "非表示", + "test": "テスト", + "done": "完了", + "print": "印刷", + "restore": "復元", + "confirm": "確認", + "more": "もっと見る", + "less": "折りたたむ", + "skip": "スキップ", + "send": "送信", + "reset": "リセット", + "show-more": "もっと表示", + "dont-show-again": "今後表示しない", + "see-documentation": "ドキュメントを見る", + "see-debug-events": "デバッグイベントを表示", + "clear": "クリア", + "upload": "アップロード", + "delete-anyway": "それでも削除", + "delete-selected": "選択したものを削除", + "set": "設定" + }, + "aggregation": { + "aggregation": "集計", + "function": "データ集計関数", + "limit": "最大値数", + "group-interval": "グループ化間隔", + "min": "最小", + "max": "最大", + "avg": "平均", + "sum": "合計", + "count": "件数", + "none": "なし" + }, + "admin": { + "settings": "設定", + "general": "一般", + "general-settings": "一般設定", + "home-settings": "ホーム設定", + "home": "ホーム", + "outgoing-mail": "メールサーバー", + "outgoing-mail-settings": "送信メールサーバー設定", + "system-settings": "システム設定", + "test-mail-sent": "テストメールが正常に送信されました!", + "base-url": "ベースURL", + "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プロトコル", + "smtp-host": "SMTPホスト", + "smtp-host-required": "SMTPホストは必須です。", + "smtp-port": "SMTPポート", + "smtp-port-required": "SMTPポートを指定する必要があります。", + "smtp-port-invalid": "有効なSMTPポートではないようです。", + "timeout-msec": "タイムアウト (msec)", + "timeout-required": "タイムアウトは必須です。", + "timeout-invalid": "有効なタイムアウトではないようです。", + "enable-tls": "TLSを有効化", + "tls-version": "TLSバージョン", + "enable-proxy": "プロキシを有効化", + "proxy-host": "プロキシホスト", + "proxy-host-required": "プロキシホストは必須です。", + "proxy-port": "プロキシポート", + "proxy-port-required": "プロキシポートは必須です。", + "proxy-port-range": "プロキシポートは1〜65535の範囲である必要があります。", + "proxy-user": "プロキシユーザー", + "proxy-password": "プロキシパスワード", + "change-password": "パスワードを変更", + "send-test-mail": "テストメールを送信", + "sms-provider": "SMSプロバイダー", + "sms-provider-settings": "SMSプロバイダー設定", + "sms-provider-type": "SMSプロバイダータイプ", + "sms-provider-type-required": "SMSプロバイダータイプは必須です。", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWSアクセスキーID", + "aws-access-key-id-required": "AWSアクセスキーIDは必須です", + "aws-secret-access-key": "AWSシークレットアクセスキー", + "aws-secret-access-key-required": "AWSシークレットアクセスキーは必須です", + "aws-region": "AWSリージョン", + "aws-region-required": "AWSリージョンは必須です", + "number-from": "送信元電話番号", + "number-from-required": "送信元電話番号は必須です。", + "number-to": "送信先電話番号", + "number-to-required": "送信先電話番号は必須です。", + "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メッセージ", + "sms-message-required": "SMSメッセージは必須です。", + "sms-message-max-length": "SMSメッセージは1600文字を超えることはできません", + "twilio-account-sid": "TwilioアカウントSID", + "twilio-account-sid-required": "TwilioアカウントSIDは必須です", + "twilio-account-token": "Twilioアカウントトークン", + "twilio-account-token-required": "Twilioアカウントトークンは必須です", + "send-test-sms": "テストSMSを送信", + "test-sms-sent": "テストSMSが正常に送信されました!", + "security-settings": "セキュリティ設定", + "password-policy": "パスワードポリシー", + "minimum-password-length": "最小パスワード長", + "minimum-password-length-required": "最小パスワード長は必須です", + "minimum-password-length-range": "最小パスワード長は6〜50の範囲である必要があります", + "maximum-password-length": "最大パスワード長", + "maximum-password-length-min": "最大パスワード長は少なくとも6である必要があります", + "maximum-password-length-less-min": "最大パスワード長は最小長より大きい必要があります", + "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": "パスワード再利用間隔日数は負の値にはできません", + "allow-whitespace": "空白を許可", + "force-reset-password-if-no-valid": "無効な場合はパスワードのリセットを強制", + "force-reset-password-if-no-valid-hint": "この機能を有効にする場合は注意してください。有効にすると、無効なパスワードのユーザーはemailでパスワードをリセットする必要があります。", + "general-policy": "一般ポリシー", + "max-failed-login-attempts": "アカウントがロックされるまでのログイン失敗回数の上限", + "minimum-max-failed-login-attempts-range": "ログイン失敗回数の上限は負の値にはできません", + "user-lockout-notification-email": "ユーザーアカウントがロックアウトされた場合、emailに通知を送信", + "user-activation-token-ttl": "ユーザー有効化リンクのTTL(時間)", + "user-activation-token-ttl-range": "ユーザー有効化リンクのTTLは1〜24時間の範囲である必要があります", + "password-reset-token-ttl": "パスワードリセットリンクのTTL(時間)", + "password-reset-token-ttl-range": "パスワードリセットリンクのTTLは1〜24時間の範囲である必要があります", + "mobile-secret-key-length": "モバイルシークレットキー長", + "mobile-secret-key-length-range": "モバイルシークレットキー長は正の値である必要があります", + "domain-name": "ドメイン名", + "domain-name-unique": "ドメイン名とプロトコルは一意である必要があります。", + "domain-name-max-length": "ドメイン名は256未満である必要があります", + "error-verification-url": "ドメイン名に記号'/'および':'を含めることはできません。例: thingsboard.io", + "connection-settings": "接続設定", + "oauth2": { + "access-token-uri": "アクセストークンURI", + "access-token-uri-required": "アクセストークンURIは必須です。", + "activate-user": "ユーザーを有効化", + "add-domain": "ドメインを追加", + "delete-domain": "ドメインを削除", + "add-provider": "プロバイダーを追加", + "delete-provider": "プロバイダーを削除", + "allow-user-creation": "ユーザー作成を許可", + "always-fullscreen": "常に全画面表示", + "authorization-uri": "認可URI", + "authorization-uri-required": "認可URIは必須です。", + "add-client": "OAuth 2.0クライアントを追加", + "client-details": "OAuth 2.0クライアント詳細", + "client": "OAuth 2.0クライアント", + "clients": "OAuth 2.0クライアント", + "no-oauth2-clients": "OAuth 2.0クライアントが見つかりません", + "search-oauth2-clients": "OAuth 2.0クライアントを検索", + "delete-client-title": "OAuth 2.0クライアント'{{clientName}}'を削除してもよろしいですか?", + "delete-client-text": "注意: 確認後、クライアントと関連データはすべて復元できなくなります。", + "delete-mobile-app-title": "モバイルアプリケーション'{{applicationName}}'を削除してもよろしいですか?", + "delete-mobile-app-text": "注意: 確認後、モバイルアプリケーションと関連データはすべて復元できなくなります。", + "title": "タイトル", + "client-title-required": "タイトルは必須です", + "client-title-max-length": "タイトルは100未満である必要があります", + "advanced-settings": "詳細設定", + "domain-details": "ドメイン詳細", + "no-domains": "ドメインが見つかりません", + "search-domains": "ドメインを検索", + "mobile-app-details": "モバイルアプリケーション詳細", + "add-mobile-app": "モバイルアプリケーションを追加", + "no-mobile-apps": "モバイルアプリケーションが見つかりません", + "search-mobile-apps": "モバイルアプリケーションを検索", + "send-token": "トークンを送信", + "create-new": "新規作成", + "client-authentication-method": "クライアント認証方式", + "client-id": "クライアントID", + "client-id-required": "クライアントIDは必須です。", + "client-id-max-length": "クライアントIDは256未満である必要があります", + "client-secret": "クライアントシークレット", + "client-secret-required": "クライアントシークレットは必須です。", + "client-secret-max-length": "クライアントシークレットは2049未満である必要があります", + "custom-setting": "カスタム設定", + "customer-name-pattern": "顧客名パターン", + "customer-name-pattern-max-length": "顧客名パターンは256未満である必要があります", + "default-dashboard-name": "デフォルトダッシュボード名", + "default-dashboard-name-max-length": "デフォルトダッシュボード名は256未満である必要があります", + "delete-domain-text": "注意: 確認後、ドメインおよびすべてのプロバイダーデータは利用できなくなります。", + "delete-domain-title": "ドメイン'{{domainName}}'を削除してもよろしいですか?", + "delete-registration-text": "注意: 確認後、プロバイダーデータは利用できなくなります。", + "delete-registration-title": "プロバイダー'{{name}}'を削除してもよろしいですか?", + "email-attribute-key": "Email属性キー", + "email-attribute-key-required": "Email属性キーは必須です。", + "email-attribute-key-max-length": "Email属性キーは32未満である必要があります", + "first-name-attribute-key": "名属性キー", + "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未満である必要があります", + "login-button-icon": "ログインボタンアイコン", + "login-button-label": "プロバイダーラベル", + "login-button-label-placeholder": "$(Provider label)でログイン", + "login-button-label-required": "ラベルは必須です。", + "login-provider": "ログインプロバイダー", + "mapper": "マッパー", + "new-domain": "新しいドメイン", + "oauth2": "OAuth 2.0", + "password-max-length": "パスワードは256未満である必要があります", + "redirect-uri-template": "リダイレクトURIテンプレート", + "copy-redirect-uri": "リダイレクトURIをコピー", + "registration-id": "登録ID", + "registration-id-required": "登録IDは必須です。", + "registration-id-unique": "登録IDはシステム内で一意である必要があります。", + "scope": "スコープ", + "scope-required": "スコープは必須です。", + "tenant-name-pattern": "テナント名パターン", + "tenant-name-pattern-required": "テナント名パターンは必須です。", + "tenant-name-pattern-max-length": "テナント名パターンは256未満である必要があります", + "tenant-name-strategy": "テナント名戦略", + "type": "マッパータイプ", + "uri-pattern-error": "無効なURI形式です。", + "url": "URL", + "url-pattern": "無効なURL形式です。", + "url-required": "URLは必須です。", + "url-max-length": "URLは256未満である必要があります", + "user-info-uri": "ユーザー情報URI", + "user-info-uri-required": "ユーザー情報URIは必須です。", + "username-max-length": "ユーザー名は256未満である必要があります", + "user-name-attribute-name": "ユーザー名属性キー", + "user-name-attribute-name-required": "ユーザー名属性キーは必須です", + "protocol": "プロトコル", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "OAuth 2.0設定を有効化", + "disable": "OAuth 2.0設定を無効化", + "edge": "Edgeへ伝播", + "edge-enable": "Edgeへの伝播を有効化", + "edge-disable": "Edgeへの伝播を無効化", + "domains": "ドメイン", + "mobile-apps": "モバイルアプリケーション", + "mobile-package": "アプリケーションパッケージ", + "mobile-package-placeholder": "例: my.example.app", + "mobile-package-hint": "Androidの場合: 独自の一意なApplication ID。iOSの場合: Product bundle identifier。", + "mobile-package-unique": "アプリケーションパッケージは一意である必要があります。", + "mobile-package-required": "アプリケーションパッケージは必須です。", + "mobile-package-max-length": "アプリケーションパッケージは256未満である必要があります", + "mobile-package-spaces": "アプリケーションパッケージに空白を含めることはできません", + "mobile-app-secret": "アプリケーションシークレット", + "mobile-app-secret-hint": "少なくとも512ビットのデータを表すBase64エンコード文字列。", + "mobile-app-secret-required": "アプリケーションシークレットは必須です。", + "mobile-app-secret-min-length": "アプリケーションシークレットは少なくとも512ビットのデータである必要があります。", + "mobile-app-secret-base64": "アプリケーションシークレットはBase64形式である必要があります。", + "invalid-mobile-app-secret": "アプリケーションシークレットは英数字のみを含み、16〜2048文字の長さである必要があります。", + "copy-mobile-app-secret": "アプリケーションシークレットをコピー", + "delete-mobile-app": "アプリケーション情報を削除", + "providers": "プロバイダー", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "すべてのプラットフォーム", + "smtp-provider": "SMTPプロバイダー", + "allowed-platforms": "許可されたプラットフォーム", + "authentication": "認証", + "basic": "基本", + "provider": "プロバイダー", + "redirect-url": "リダイレクトURI", + "domain-name": "ドメイン名", + "domain-name-required": "ドメイン名は必須です", + "redirect-url-template": "リダイレクトURIテンプレート", + "microsoft-tenant-id": "ディレクトリ(テナント)ID", + "microsoft-tenant-id-required": "ディレクトリ(テナント)IDは必須です", + "token-uri": "トークンURI", + "token-uri-required": "トークンURIは必須です", + "redirect-uri": "リダイレクトURI", + "google-provider": "Google", + "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-port": "SMPPポート", + "smpp-port-required": "SMPPポートは必須です", + "system-id": "システムID", + "system-id-required": "システムIDは必須です", + "password": "パスワード", + "password-required": "パスワードは必須です", + "type-settings": "タイプ設定", + "source-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 (番号計画識別)", + "address-range": "アドレス範囲", + "coding-scheme": "符号化方式", + "bind-type-tx": "送信", + "bind-type-rx": "受信", + "bind-type-trx": "送受信", + "ton-unknown": "不明", + "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クライアントID (WAP Forumにより定義予定)", + "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 - オクテット未指定 (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 - ピクトグラムエンコーディング", + "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": "名前", + "queue-name-required": "キュー名は必須です!", + "queues": "キュー", + "queue-partitions": "パーティション", + "queue-submit-strategy": "投入戦略", + "queue-processing-strategy": "処理戦略", + "queue-configuration": "キュー設定", + "repository-settings": "リポジトリ設定", + "repository": "リポジトリ", + "repository-url": "リポジトリURL", + "repository-url-required": "リポジトリURLは必須です。", + "default-branch": "デフォルトブランチ名", + "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": "パスワード / アクセストークンを変更", + "private-key": "秘密鍵", + "drop-private-key-file-or": "秘密鍵ファイルをドラッグ&ドロップするか、または", + "passphrase": "パスフレーズ", + "enter-passphrase": "パスフレーズを入力", + "change-passphrase": "パスフレーズを変更", + "check-access": "アクセスを確認", + "check-repository-access-success": "リポジトリへのアクセスが正常に検証されました!", + "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": "注意: 確認後、自動コミット設定は削除され、すべてのエンティティで自動コミットが無効になります。", + "mobile-app": { + "mobile-app": "モバイルアプリ", + "mobile-app-qr-code-widget-settings": "モバイルアプリQRコードウィジェット設定", + "applications": "アプリケーション", + "default": "デフォルト", + "custom": "カスタム", + "android": "Android", + "ios": "iOS", + "appearance": "外観", + "appearance-on-home-page": "ホームページでの表示", + "enabled": "有効", + "disabled": "無効", + "badges": "バッジ", + "label": "ラベル", + "label-required": "ラベルは必須です", + "label-max-length": "ラベルは50文字以下である必要があります", + "right": "右", + "left": "左", + "set": "設定", + "preview": "プレビュー", + "connect-mobile-app": "モバイルアプリを接続", + "use-system-settings": "システム設定を使用" + }, + "2fa": { + "2fa": "二要素認証", + "available-providers": "利用可能なプロバイダー", + "available-providers-required": "少なくとも 1 つの 2FA プロバイダーを設定してください。", + "issuer-name": "発行者名", + "issuer-name-required": "発行者名は必須です。", + "max-verification-failures-before-user-lockout": "ユーザーがロックアウトされるまでの検証失敗回数の上限", + "max-verification-failures-before-user-lockout-pattern": "検証失敗回数の上限は正の整数である必要があります。", + "number-of-checking-attempts": "チェック試行回数", + "number-of-checking-attempts-pattern": "チェック試行回数は正の整数である必要があります。", + "number-of-checking-attempts-required": "チェック試行回数は必須です。", + "number-of-codes": "コード数", + "number-of-codes-pattern": "コード数は正の整数である必要があります。", + "number-of-codes-required": "コード数は必須です。", + "provider": "プロバイダー", + "retry-verification-code-period": "検証コード再試行間隔(秒)", + "retry-verification-code-period-pattern": "最小間隔は5秒です", + "retry-verification-code-period-required": "検証コード再試行間隔は必須です。", + "total-allowed-time-for-verification": "検証の許容合計時間(秒)", + "total-allowed-time-for-verification-pattern": "許容合計時間の最小値は60秒です", + "total-allowed-time-for-verification-required": "許容合計時間は必須です。", + "use-system-two-factor-auth-settings": "システムの二要素認証設定を使用", + "verification-code-check-rate-limit": "検証コードチェックのレート制限", + "verification-code-lifetime": "検証コード有効期限(秒)", + "verification-code-lifetime-pattern": "検証コード有効期限は正の整数である必要があります。", + "verification-code-lifetime-required": "検証コード有効期限は必須です。", + "verification-message-template": "検証メッセージテンプレート", + "verification-limitations": "検証の制限", + "verification-message-template-pattern": "検証メッセージには次のパターンを含める必要があります: ${code}", + "verification-message-template-required": "検証メッセージテンプレートは必須です。", + "within-time": "時間内(秒)", + "within-time-pattern": "時間は正の整数である必要があります。", + "within-time-required": "時間は必須です。", + "force-2fa": "二要素認証を強制", + "enforce-for": "適用対象" + }, + "jwt": { + "security-settings": "JWTセキュリティ設定", + "issuer-name": "発行者名", + "issuer-name-required": "発行者名は必須です。", + "signings-key": "署名キー", + "signings-key-hint": "少なくとも512ビットのデータを表すBase64エンコード文字列。", + "signings-key-required": "署名キーは必須です。", + "signings-key-min-length": "署名キーは少なくとも512ビットのデータである必要があります。", + "signings-key-base64": "署名キーはbase64形式である必要があります。", + "expiration-time": "トークン有効期限 (秒)", + "expiration-time-required": "トークン有効期限は必須です。", + "expiration-time-max": "最大許容時間は2147483647秒(68年)です。", + "expiration-time-min": "最小時間は60秒 (1分)です。", + "refresh-expiration-time": "リフレッシュトークン有効期限 (秒)", + "refresh-expiration-time-required": "リフレッシュトークン有効期限は必須です。", + "refresh-expiration-time-max": "最大許容時間は2147483647秒 (68年)です。", + "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設定", + "mobile-settings": "モバイル設定", + "firebase-service-account-file": "Firebaseサービスアカウント認証情報JSONファイル", + "select-firebase-service-account-file": "Firebaseサービスアカウント認証情報ファイルをドラッグ&ドロップするか、または " + }, + "alarm": { + "alarm": "アラーム", + "alarm-list": "アラーム一覧", + "alarms": "アラーム", + "all-alarms": "すべてのアラーム", + "select-alarm": "アラームを選択", + "no-alarms-matching": "'{{entity}}'に一致するアラームが見つかりません。", + "alarm-required": "アラームは必須です", + "alarm-filter": "アラームフィルター", + "filter": "フィルター", + "alarm-status": "アラームステータス", + "alarm-status-list": "アラームステータス一覧", + "any-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": "詳細", + "originator-label": "発生元ラベル", + "assign": "割り当て", + "assignments": "割り当て", + "assignee": "担当者", + "assignee-id": "担当者ID", + "assignee-first-name": "担当者名", + "assignee-last-name": "担当者姓", + "assignee-email": "担当者Email", + "unassigned": "未割り当て", + "user-deleted": "ユーザーが削除されました", + "assignee-not-set": "すべて", + "status": "ステータス", + "alarm-details": "アラーム詳細", + "start-time": "開始日時", + "assign-time": "割り当て日時", + "end-time": "終了日時", + "ack-time": "確認日時", + "clear-time": "クリア日時", + "duration": "継続時間", + "alarm-severity": "アラーム重要度", + "alarm-severity-list": "アラーム重要度一覧", + "any-severity": "すべての重要度", + "severity-critical": "致命的", + "severity-major": "重大", + "severity-minor": "軽微", + "severity-warning": "警告", + "severity-indeterminate": "未確定", + "acknowledge": "確認", + "clear": "クリア", + "delete": "削除", + "search": "アラームを検索", + "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 {1 件のアラーム} other {# 件のアラーム} } を確認", + "aknowledge-alarms-text": "{ count, plural, =1 {1 件のアラーム} other {# 件のアラーム} } を確認してもよろしいですか?", + "aknowledge-alarm-title": "アラームを確認", + "aknowledge-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-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-error-min": "最小値は10です。", + "alarm-types": "アラームタイプ", + "alarm-type-list": "アラームタイプ一覧", + "any-type": "すべてのタイプ", + "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": "選択されたアラームタイプがありません。", + "system-comments": { + "acked-by-user": "アラームはユーザー {{userName}} により確認されました", + "cleared-by-user": "アラームはユーザー {{userName}} によりクリアされました", + "assigned-to-user": "アラームはユーザー {{userName}} によりユーザー {{assigneeName}} に割り当てられました", + "unassigned-to-user": "アラームの割り当てはユーザー {{userName}} により解除されました", + "unassigned-from-deleted-user": "ユーザー {{userName}} - が削除されたため、アラームの割り当てが解除されました", + "comment-deleted": "ユーザー {{userName}} が自分のコメントを削除しました", + "severity-changed": "アラームの重大度が {{oldSeverity}} から {{newSeverity}} に更新されました" + } + }, + "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": "エイリアスを追加", + "edit": "エイリアスを編集", + "name": "エイリアス名", + "name-required": "エイリアス名は必須です", + "duplicate-alias": "同じ名前のエイリアスはすでに存在します。", + "filter-type-single-entity": "単一エンティティ", + "filter-type-entity-list": "エンティティ一覧", + "filter-type-entity-name": "エンティティ名", + "filter-type-entity-type": "エンティティタイプ", + "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-edge-type": "Edgeタイプ", + "filter-type-edge-type-description": "タイプ '{{edgeTypes}}' のEdges", + "filter-type-edge-type-and-name-description": "タイプ '{{edgeTypes}}' で、名前が'{{prefix}}'で始まるEdges", + "filter-type-relations-query": "関係クエリ", + "filter-type-relations-query-description": "{{rootEntity}} {{direction}} の {{relationType}} 関係を持つ {{entities}}", + "filter-type-edge-search-query": "Edge検索クエリ", + "filter-type-edge-search-query-description": "{{rootEntity}} {{direction}} の {{relationType}} 関係を持つタイプ {{edgeTypes}} のEdges", + "filter-type-asset-search-query": "アセット検索クエリ", + "filter-type-asset-search-query-description": "{{rootEntity}} {{direction}} の {{relationType}} 関係を持つタイプ {{assetTypes}} のアセット", + "filter-type-device-search-query": "デバイス検索クエリ", + "filter-type-device-search-query-description": "{{rootEntity}} {{direction}} の {{relationType}} 関係を持つタイプ {{deviceTypes}} のデバイス", + "filter-type-entity-view-search-query": "エンティティビュー検索クエリ", + "filter-type-entity-view-search-query-description": "{{rootEntity}} {{direction}} の {{relationType}} 関係を持つタイプ {{entityViewTypes}} のエンティティビュー", + "filter-type-apiUsageState": "API使用状況", + "entity-filter": "エンティティフィルター", + "resolve-multiple": "複数エンティティとして解決", + "resolve-multiple-hint": "有効にすると、フィルターされたすべてのエンティティのデータを同時に描画します。\n無効の場合、ウィジェットは選択されたエンティティのデータのみを表示します。", + "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": "デフォルト", + "query-options": "クエリオプション", + "max-relation-level": "最大関係レベル", + "unlimited-level": "無制限レベル", + "state-entity": "ダッシュボード状態エンティティ", + "all-entities": "すべてのエンティティ", + "any-relation": "任意" + }, + "asset": { + "asset": "アセット", + "assets": "アセット", + "management": "アセット管理", + "view-assets": "アセットを表示", + "add": "アセットを追加", + "asset-type-max-length": "アセットタイプは256未満である必要があります", + "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": "名前は必須です。", + "name-max-length": "名前は256未満である必要があります", + "label-max-length": "ラベルは256未満である必要があります", + "description": "説明", + "description-required": "説明は必須です。", + "type": "タイプ", + "type-required": "タイプは必須です。", + "details": "詳細", + "events": "イベント", + "add-asset-text": "新しいアセットを追加", + "asset-details": "アセット詳細", + "assign-assets": "アセットを割り当て", + "assign-assets-text": "顧客に { count, plural, =1 {1 件のアセット} other {# 件のアセット} } を割り当て", + "assign-asset-to-edge-title": "Edgeにアセットを割り当て", + "assign-asset-to-edge-text": "Edgeに割り当てるアセットを選択してください", + "delete-assets": "アセットを削除", + "unassign-assets": "アセットの割り当てを解除", + "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 {1 件のアセット} other {# 件のアセット} } を削除してもよろしいですか?", + "delete-assets-action-title": "{ count, plural, =1 {1 件のアセット} 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 件のアセット} other {# 件のアセット} } の割り当てを解除してもよろしいですか?", + "unassign-assets-text": "確認後、選択したすべてのアセットの割り当てが解除され、顧客はアクセスできなくなります。", + "copyId": "アセットIDをコピー", + "idCopiedMessage": "アセットIDがクリップボードにコピーされました", + "select-asset": "アセットを選択", + "no-assets-matching": "'{{entity}}'に一致するアセットが見つかりません。", + "asset-required": "アセットは必須です", + "name-starts-with": "アセット名の式", + "help-text": "必要に応じて'%'を使用してください: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'。", + "search": "アセットを検索", + "import": "アセットをインポート", + "asset-file": "アセットファイル", + "label": "ラベル", + "assign-asset-to-edge": "Edgeにアセットを割り当て", + "unassign-asset-from-edge": "アセットの割り当てを解除", + "unassign-asset-from-edge-title": "アセット '{{assetName}}' の割り当てを解除してもよろしいですか?", + "unassign-asset-from-edge-text": "確認後、アセットの割り当ては解除され、Edgeはアクセスできなくなります。", + "unassign-assets-from-edge-title": "{ count, plural, =1 {1 件のアセット} other {# 件のアセット} } の割り当てを解除してもよろしいですか?", + "unassign-assets-from-edge-text": "確認後、選択したすべてのアセットの割り当てが解除され、Edgeはアクセスできなくなります。", + "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": "共有属性", + "scope-client-short": "クライアント", + "scope-server-short": "サーバー", + "scope-shared-short": "共有", + "scope-latest-short": "最新", + "scope-any": "任意", + "add": "属性を追加", + "key": "キー", + "key-max-length": "キーは256未満である必要があります", + "last-update-time": "最終更新日時", + "key-required": "属性キーは必須です。", + "value": "値", + "value-required": "属性値は必須です。", + "telemetry-key-required": "テレメトリキーは必須です", + "telemetry-value-required": "テレメトリ値は必須です", + "delete-attributes-title": "{ count, plural, =1 {1 件の属性} 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 {1 件の属性} other {# 件の属性} } を選択", + "selected-telemetry": "{ count, plural, =1 {1 件のテレメトリユニット} other {# 件のテレメトリユニット} } を選択", + "no-attributes-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-features": "API機能", + "api-usage": "API使用状況", + "alarm": "アラーム", + "alarms-created": "作成されたアラーム", + "queue-stats": "キュー統計", + "processing-failures-and-timeouts": "処理失敗とタイムアウト", + "exceptions": "例外", + "alarms-created-daily-activity": "作成されたアラームの日別アクティビティ", + "alarms-created-hourly-activity": "作成されたアラームの時間別アクティビティ", + "alarms-created-monthly-activity": "作成されたアラームの月別アクティビティ", + "data-points": "データポイント", + "data-points-storage-days": "データポイント保存日数", + "data-points-storage-days-hourly-activity": "データポイント保存日数の時間別アクティビティ", + "data-points-storage-days-daily-activity": "データポイント保存日数の日別アクティビティ", + "data-points-storage-days-monthly-activity": "データポイント保存日数の月別アクティビティ", + "device-api": "デバイスAPI", + "email": "Email", + "email-messages": "Emailメッセージ", + "email-messages-daily-activity": "Emailメッセージの日別アクティビティ", + "email-messages-monthly-activity": "Emailメッセージの月別アクティビティ", + "executions": "実行", + "scripts": "スクリプト", + "scripts-hourly-activity": "スクリプトの時間別アクティビティ", + "scripts-daily-activity": "スクリプトの日別アクティビティ", + "scripts-monthly-activity": "スクリプトの月別アクティビティ", + "javascript": "JavaScript", + "javascript-executions": "JavaScript実行", + "tbel": "TBEL", + "tbel-executions": "TBEL実行", + "latest-error": "最新エラー", + "messages": "メッセージ", + "notifications": "通知", + "notifications-email-sms": "通知 (Email/SMS)", + "notifications-hourly-activity": "通知の時間別アクティビティ", + "permanent-failures": "${entityName} 永続的な失敗", + "permanent-timeouts": "${entityName} 永続的なタイムアウト", + "processing-failures": "${entityName} 処理失敗", + "processing-timeouts": "${entityName} 処理タイムアウト", + "rule-chain": "ルールチェーン", + "rule-engine": "ルールエンジン", + "rule-engine-daily-activity": "ルールエンジンの日別アクティビティ", + "rule-engine-executions": "ルールエンジン実行", + "rule-engine-hourly-activity": "ルールエンジンの時間別アクティビティ", + "rule-engine-monthly-activity": "ルールエンジンの月別アクティビティ", + "rule-engine-statistics": "ルールエンジン統計", + "rule-node": "ルールノード", + "sms": "SMS", + "sms-messages": "SMSメッセージ", + "sms-messages-hourly-activity": "SMS メッセージの時間別アクティビティ", + "sms-messages-daily-activity": "SMSメッセージの日別アクティビティ", + "sms-messages-monthly-activity": "SMSメッセージの月別アクティビティ", + "successful": "${entityName} 成功", + "telemetry": "テレメトリ", + "telemetry-persistence": "テレメトリ永続化", + "telemetry-persistence-daily-activity": "テレメトリ永続化の日別アクティビティ", + "telemetry-persistence-hourly-activity": "テレメトリ永続化の時間別アクティビティ", + "telemetry-persistence-monthly-activity": "テレメトリ永続化の月別アクティビティ", + "transport": "トランスポート", + "transport-msg-hourly-activity": "トランスポートメッセージの時間別アクティビティ", + "transport-msg-daily-activity": "トランスポートメッセージの日別アクティビティ", + "transport-msg-monthly-activity": "トランスポートメッセージの月別アクティビティ", + "transport-daily-activity": "トランスポートの日別アクティビティ", + "transport-data-points": "トランスポートデータポイント", + "transport-data-points-hourly-activity": "トランスポートデータポイントの時間別アクティビティ", + "transport-data-points-daily-activity": "トランスポートデータポイントの日別アクティビティ", + "transport-data-points-monthly-activity": "トランスポートデータポイントの月別アクティビティ", + "view-details": "詳細を表示", + "view-statistics": "統計を表示", + "transport-messages": "トランスポートメッセージ", + "transport-messages-hourly-activity": "トランスポートメッセージの時間別アクティビティ", + "transport-data-point-hourly-activity": "トランスポートデータポイントの時間別アクティビティ", + "javascript-function-executions": "JavaScript 関数の実行", + "javascript-function-executions-hourly-activity": "JavaScript 関数実行の時間別アクティビティ", + "javascript-function-executions-daily-activity": "JavaScript 関数実行の日別アクティビティ", + "javascript-function-executions-monthly-activity": "JavaScript 関数実行の月別アクティビティ", + "tbel-function-executions": "TBEL 関数の実行", + "tbel-function-executions-hourly-activity": "TBEL 関数実行の時間別アクティビティ", + "tbel-function-executions-daily-activity": "TBEL 関数実行の日別アクティビティ", + "tbel-function-executions-monthly-activity": "TBEL 関数実行の月別アクティビティ", + "created-reports": "作成済みレポート", + "created-reports-hourly-activity": "作成済みレポートの時間別アクティビティ", + "created-reports-daily-activity": "作成済みレポートの日別アクティビティ", + "created-reports-monthly-activity": "作成済みレポートの月別アクティビティ", + "emails": "Emails", + "emails-hourly-activity": "Emails の時間別アクティビティ", + "emails-daily-activity": "Emails の日別アクティビティ", + "emails-monthly-activity": "Emails の月別アクティビティ", + "status": { + "enabled": "有効", + "disabled": "無効", + "warning": "警告" + } + }, + "api-limit": { + "cassandra-write-queries-core": "Rest API Cassandra 書き込みクエリ", + "cassandra-read-queries-core": "Rest API と WS テレメトリ Cassandra 読み取りクエリ", + "cassandra-write-queries-rule-engine": "ルールエンジン テレメトリ Cassandra 書き込みクエリ", + "cassandra-read-queries-rule-engine": "ルールエンジン テレメトリ Cassandra 読み取りクエリ", + "cassandra-write-queries-monolith": "Monolith テレメトリ Cassandra 書き込みクエリ", + "cassandra-read-queries-monolith": "Monolith テレメトリ 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": "デバイスあたりのトランスポートメッセージ", + "transport-messages-per-gateway": "gatewayあたりのトランスポートメッセージ", + "transport-messages-per-gateway-device": "gatewayデバイスあたりのトランスポートメッセージ", + "ws-updates-per-session": "セッションあたりのWS更新", + "edge-events": "Edgeイベント", + "edge-events-per-edge": "EdgeあたりのEdgeイベント", + "edge-uplink-messages": "Edgeアップリンクメッセージ", + "edge-uplink-messages-per-edge": "EdgeあたりのEdgeアップリンクメッセージ" + }, + "api-key": { + "api-key": "API キー", + "api-keys": "API キー", + "delete-api-key-title": "API キー '{{name}}' を削除してもよろしいですか?", + "delete-api-key-text": "注意: 確認後、このキーは復元できなくなります。", + "delete-api-keys-title": "{ count, plural, =1 {1 API キー} other {# API キー} } を削除してもよろしいですか?", + "delete-api-keys-text": "注意: 確認後、選択したすべてのキーは復元できなくなります。", + "expiration-date": "有効期限", + "date": "日付", + "description": "説明", + "disable": "無効化", + "edit-description": "説明を編集", + "enable": "API キーを有効化", + "expiration-time": "有効期限", + "expiration-time-never": "なし", + "expiration-time-custom": "カスタム", + "generate": "生成", + "generate-title": "API キーを生成", + "generate-text": "注: API キーは、作成対象のユーザーの権限を継承します。", + "generated-api-key-title": "API キーが生成されました。接続を確認しましょう!", + "generated-api-key-copy": "この API キーは再表示できません。今すぐコピーして安全に保存してください。", + "generated-api-key-command": "接続を確認するには次の手順を使用してください。結果として、現在のユーザー情報を受け取るはずです:", + "generated-api-key-insecure-url": "安全でない HTTP 接続でコマンドを実行すると、API キーが暗号化されずに送信され、盗聴される恐れがあります。", + "list": "{ count, plural, =1 {API キー} other {API キー一覧 (# 件)} }", + "manage": "管理", + "manage-api-keys": "API キーを管理", + "no-found": "API キーが見つかりません", + "selected-api-keys": "{ count, plural, =1 {API キー} other {API キー (# 件)} } を選択", + "search": "API キーを検索", + "status": "ステータス", + "status-active": "アクティブ", + "status-inactive": "非アクティブ", + "status-expired": "期限切れ" + }, + "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-assigned-to-edge": "Edgeに割り当て", + "type-unassigned-from-edge": "Edgeから割り当て解除", + "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-alarm-delete": "アラームを削除", + "type-alarm-assign": "アラームを割り当て", + "type-alarm-unassign": "アラームの割り当てを解除", + "type-added-comment": "コメントを追加", + "type-updated-comment": "コメントを更新", + "type-deleted-comment": "コメントを削除", + "type-login": "ログイン", + "type-logout": "ログアウト", + "type-lockout": "ロックアウト", + "status-success": "成功", + "status-failure": "失敗", + "audit-log-details": "監査ログ詳細", + "no-audit-logs-prompt": "ログが見つかりません", + "action-data": "アクションデータ", + "failure-details": "失敗詳細", + "search": "監査ログを検索", + "clear-search": "検索をクリア", + "type-assigned-from-tenant": "テナントから割り当て", + "type-assigned-to-tenant": "テナントに割り当て", + "type-provision-success": "デバイスをプロビジョニング", + "type-provision-failure": "デバイスのプロビジョニングに失敗しました", + "type-timeseries-updated": "テレメトリを更新", + "type-timeseries-deleted": "テレメトリを削除", + "type-sms-sent": "SMSを送信", + "any-type": "すべてのタイプ", + "audit-log-filter-title": "監査ログフィルター", + "filter-title": "フィルター", + "filter-types": "監査ログタイプ" + }, + "debug-settings": { + "label": "デバッグ設定", + "on-failure": "失敗のみ (24/7)", + "all-messages": "すべてのメッセージ ({{time}})", + "failures": "失敗", + "entity": "エンティティ", + "hint": { + "main-limited": "{{time}}あたりに記録される{{entity}}デバッグメッセージは{{msg}}件までです。", + "on-failure": "エラーメッセージのみをログに記録します。", + "all-messages": "すべてのデバッグメッセージをログに記録します。" + } + }, + "calculated-fields": { + "expression": "式", + "no-found": "計算フィールドが見つかりません", + "list": "{ count, plural, =1 {計算フィールド} other {計算フィールド一覧(# 件)} }", + "selected-fields": "{ count, plural, =1 {1 件の計算フィールド} other {# 件の計算フィールド} } を選択", + "type": { + "simple": "シンプル", + "simple-hint": "入力引数に基づくシンプルな算術計算。", + "script": "スクリプト", + "script-hint": "TBEL スクリプトを使用して、定義された引数に対して計算します。", + "geofencing": "ジオフェンシング", + "geofencing-hint": "設定されたジオフェンシングゾーングループに対して、エンティティの GPS 位置と遷移を評価します。", + "propagation": "伝播", + "propagation-hint": "リレーションの方向とタイプに基づき、親または子エンティティへデータを伝播します。", + "related-entities-aggregation": "関連エンティティ集計", + "related-entities-aggregation-hint": "関連エンティティの最新データを集計します。", + "time-series-data-aggregation": "時系列データ集計", + "time-series-data-aggregation-hint": "現在のエンティティの過去データを集計します。" + }, + "preview": "プレビュー", + "arguments": "引数", + "decimals-by-default": "既定で小数を使用", + "debugging": "計算フィールドのデバッグ", + "calculated-field-details": "計算フィールドの詳細", + "argument-name": "引数名", + "name": "名前", + "datasource": "データソース", + "add-argument": "引数を追加", + "test-script-function": "スクリプト関数をテスト", + "no-arguments": "設定された引数がありません", + "argument-settings": "引数設定", + "argument-current": "現在のエンティティ", + "argument-current-tenant": "現在のテナント", + "argument-device": "デバイス", + "argument-asset": "アセット", + "argument-customer": "顧客", + "argument-tenant": "現在のテナント", + "argument-owner": "現在の所有者", + "argument-relation-query": "関連エンティティ", + "argument-type": "引数タイプ", + "attribute": "属性", + "copy-argument-name": "引数名をコピー", + "timeseries-key": "時系列キー", + "device-name": "デバイス名", + "latest-telemetry": "最新テレメトリ", + "rolling": "時系列ローリング", + "attribute-scope": "属性スコープ", + "server-attributes": "サーバー属性", + "client-attributes": "クライアント属性", + "shared-attributes": "共有属性", + "attribute-key": "属性キー", + "default-value": "既定値", + "default-value-required": "既定値は必須です。", + "limit": "最大値数", + "time-window": "時間ウィンドウ", + "customer-name": "顧客名", + "asset-name": "アセット名", + "timeseries": "時系列", + "output": "出力", + "output-hint": "出力の処理方法を定義します。", + "create": "計算フィールドを新規作成", + "file": "計算フィールドファイル", + "invalid-file-error": "無効なファイル形式です。有効な JSON ファイルであることを確認してください。", + "import": "計算フィールドをインポート", + "export": "計算フィールドをエクスポート", + "export-failed-error": "計算フィールドをエクスポートできません: {{error}}", + "output-type": "出力タイプ", + "delete-title": "計算フィールド '{{title}}' を削除してもよろしいですか?", + "delete-text": "注意: 確認後、計算フィールドと関連するすべてのデータは復元できなくなります。", + "delete-multiple-title": "{ count, plural, =1 {計算フィールド 1 件} other {計算フィールド # 件} } を削除してもよろしいですか?", + "delete-multiple-text": "注意: 確認後、選択した計算フィールドはすべて削除され、関連するすべてのデータは復元できなくなります。", + "test-with-this-message": "このメッセージでテスト", + "use-latest-timestamp": "最新タイムスタンプを使用", + "entity-coordinates": "エンティティ座標", + "latitude-time-series-key": "緯度の時系列キー", + "latitude-time-series-key-required": "緯度の時系列キーは必須です。", + "longitude-time-series-key": "経度の時系列キー", + "longitude-time-series-key-required": "経度の時系列キーは必須です。", + "geofencing-zone-groups": "ジオフェンシングゾーングループ", + "geofencing-zone-groups-settings": "ジオフェンシングゾーングループ設定", + "target-zone": "対象ゾーン", + "perimeter-key": "境界キー", + "report-strategy": "レポート戦略", + "no-zone-configured": "少なくとも 1 つのゾーンが必要です。", + "no-zone-configured-required": "少なくとも 1 つのゾーングループを設定する必要があります。", + "add-zone-group": "ゾーングループを追加", + "report-transition-event-only": "遷移イベントのみ", + "report-presence-status-only": "在席ステータスのみ", + "report-transition-event-and-presence": "在席ステータスと遷移イベント", + "perimeter-attribute-key": "境界属性キー", + "perimeter-attribute-key-required": "境界属性キーは必須です。", + "perimeter-attribute-key-pattern": "境界属性キーが無効です。", + "entity-zone-relationship": "エンティティからゾーンへのパス", + "direction": "リレーション方向", + "direction-from": "エンティティ → ゾーン", + "direction-to": "ゾーン → エンティティ", + "relation-type": "リレーションタイプ", + "create-relation-with-matched-zones": "一致したゾーンに対して、ソースエンティティのリレーションを作成", + "relation-level": "リレーションレベル", + "fetch-last-available-level": "利用可能な最終レベルのみ取得", + "zone-group-refresh-interval": "ゾーングループ更新間隔", + "copy-zone-group-name": "ゾーングループ名をコピー", + "open-details-page": "エンティティ詳細ページを開く", + "level": "レベル", + "direction-level": "方向", + "direction-up": "上", + "direction-up-parent": "親へ上方向", + "direction-down": "下", + "direction-down-child": "子へ下方向", + "add-level": "レベルを追加", + "delete-level": "レベルを削除", + "no-level": "レベルが設定されていません", + "levels-required": "少なくとも 1 つのレベルを設定する必要があります。", + "max-allowed-levels-error": "リレーションレベルが許可されている最大値を超えています。", + "propagation-path-related-entities": "関連エンティティへの伝播パス", + "propagate-type": { + "arguments-only": "引数のみ", + "expression-result": "計算結果" + }, + "script": "スクリプト", + "data-propagate": "伝播するデータ", + "output-key": "出力キー", + "copy-output-key": "出力キーをコピー", + "aggregation-path-related-entities": "関連エンティティへの集計パス", + "deduplication-interval": "重複排除間隔", + "deduplication-interval-min": "重複排除間隔は少なくとも {{ sec }} 秒にしてください。", + "deduplication-interval-hint": "テレメトリ集計間の最小時間です。", + "deduplication-interval-required": "重複排除間隔は必須です。", + "calculated-field-filter-title": "計算フィールドフィルター", + "filter-title": "フィルター", + "calculated-field-types": "計算フィールドタイプ", + "events": "イベント", + "any-type": "すべてのタイプ", + "metrics": { + "metrics": "メトリクス", + "metrics-empty": "少なくとも 1 つのメトリクスを設定する必要があります。", + "metric-name": "メトリクス名", + "metric-name-required": "メトリクス名は必須です。", + "metric-name-pattern": "メトリクス名が無効です。", + "metric-name-duplicate": "同じ名前のメトリクスがすでに存在します。", + "metric-name-max-length": "メトリクス名は 256 文字未満にしてください。", + "metric-name-forbidden": "メトリクス名は予約されているため使用できません。", + "copy-metric-name": "メトリクス名をコピー", + "argument-name": "引数名", + "aggregation": "集計", + "aggregation-type": { + "avg": "平均", + "min": "最小", + "max": "最大", + "sum": "合計", + "count": "カウント", + "count-unique": "ユニーク数" + }, + "filtered": "フィルター済み", + "value-source": "値ソース", + "value-source-hint": "集計する値の取得方法を定義します。", + "value-source-type": { + "key": "キー", + "function": "関数" + }, + "no-metrics-configured": "少なくとも 1 つのメトリクスが必要です。", + "add-metric": "メトリクスを追加", + "max-metrics": "メトリクス数が上限に達しました。", + "metric-settings": "メトリクス設定", + "filter": "フィルター", + "filter-hint": "集計中のエンティティをフィルタリングできます。フィルター関数は boolean 値を返す必要があり、設定済みのすべての引数を使用できます。" + }, + "output-strategy": { + "strategy": "戦略", + "process-right-away": "直ちに処理", + "process-rule-chains": "ルールチェーン経由で処理", + "save-time-series": "時系列に保存", + "save-database": "データベースに保存", + "save-latest-values": "最新値に保存", + "send-web-sockets": "WebSockets に送信", + "save-calculated-fields": "計算フィールドに送信", + "update-attribute-only-on-value-change": "値が変化した場合のみ属性を更新", + "send-attributes-updated-notification": "属性更新通知を送信", + "ttl": "カスタム TTL", + "ttl-required": "TTL は必須です", + "ttl-min": "最小 TTL は 0 のみ許可されています", + "processing-parameters": "処理パラメーター", + "hint": { + "strategy": "結果を即時に処理するか、追加処理のためにルールチェーンへ送信するかを制御します。", + "processing-options": "処理オプション", + "update-attribute-only-on-value-change": "値が変化しているかどうかに関係なく、受信したすべてのメッセージで属性を更新します。API 使用量が増え、パフォーマンスが低下します。", + "update-attribute-only-on-value-change-enabled": "値が変化した場合のみ属性を更新します。値が変化しない場合、タイムスタンプは更新されず、通知も送信されません。", + "send-attributes-updated-notification": "デフォルトのルールチェーンに「属性更新」イベントを送信します。", + "save-time-series": "データベースの ts_kv テーブルに時系列データを保存します。", + "save-database": "属性データをデータベースに保存します。", + "save-latest-values": "新しい値がより新しい場合、データベースの ts_kv_latest テーブルの時系列データを更新します。", + "send-web-sockets-attribute": "属性データの更新について WebSocket サブスクリプションに通知します。", + "send-web-sockets-time-series": "時系列データの更新について WebSocket サブスクリプションに通知します。", + "save-calculated-fields-attribute": "属性データの更新について計算フィールドに通知します。", + "save-calculated-fields-time-series": "時系列データの更新について計算フィールドに通知します。", + "ttl": "時系列データの保持期間を定義します。無効の場合は、テナントプロファイルの TTL が使用されます。" + } + }, + "aggregate-interval-type": "集計間隔タイプ", + "aggregate-interval-value": "集計間隔値", + "aggregate-interval-value-required": "集計間隔値は必須です。", + "aggregate-interval-value-min": "集計間隔値は少なくとも { sec, plural, =0 {0 秒} =1 {1 秒} other {# 秒} } にしてください。", + "aggregate-interval-value-step-multiple-of": "集計間隔値は 1 日の約数または倍数である必要があります。", + "aggregate-period": { + "hour": "時間", + "day": "日", + "week": "週 (月 - 日)", + "week-sun-sat": "週 (日 - 土)", + "month": "月", + "quarter": "四半期", + "year": "年", + "custom": "カスタム" + }, + "aggregate-period-hint-offset": "集計間隔は次のとおりです: {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "集計間隔は次のとおりです: {{ interval }} など。", + "entity-aggregation": { + "argument-hint": "データは現在のエンティティから取得されます。", + "argument-title-hint": "集計に使用する入力引数を定義します。", + "argument-setting-hint": "この計算フィールドで利用できる引数タイプは最新テレメトリのみです。", + "aggregation-interval": "集計間隔", + "aggregation-interval-hint": "集計を実行する頻度を定義します。例: 1 時間ごとに 00:00、01:00、02:00... のデータを集計します。集計結果は、集計間隔の開始時刻に対応するタイムスタンプで保存されます。", + "apply-offset": "集計間隔にオフセットを適用", + "apply-offset-hint": "各集計期間の開始時刻をどれだけずらすかを定義します(例: +10 分 - 00:10、01:10)。", + "offset-value": "オフセット値", + "offset-value-required": "オフセット値は必須です。", + "offset-value-min": "オフセット値は正の整数である必要があります。", + "offset-value-max": "オフセット値は集計間隔値より小さくする必要があります。", + "wait-delay": "遅延テレメトリの待機タイムアウトを適用", + "wait-delay-hint": "間隔終了後に遅延テレメトリを待機する時間を定義します。そのようなテレメトリが到着した場合、その間隔の結果は再計算されます。", + "duration": "待機時間", + "duration-required": "待機時間は必須です。", + "duration-min": "待機時間は少なくとも 1 分にしてください。", + "duration-hint": "間隔終了後に遅延データを待機する時間。", + "produce-intermediate-result": "中間結果を生成", + "produce-intermediate-result-hint": "現在の間隔中にメトリクスを計算して中間結果を生成します。更新は {{ time }} に 1 回を超えない頻度で行われます。" + }, + "hint": { + "arguments-simple-with-rolling": "シンプルタイプの計算フィールドには、時系列ローリングタイプのキーを含めることはできません。", + "arguments-propagate-arguments-with-rolling": "「時系列ローリング」タイプは「引数のみ」伝播と互換性がありません。", + "arguments-propagate-argument-entity-type": "エンティティタイプは「引数のみ」伝播と互換性がありません。", + "arguments-propagate-argument-must-current-entity": "少なくとも 1 つの引数は、ソースエンティティタイプとして「現在のエンティティ」を設定する必要があります。", + "arguments-empty": "引数を空にすることはできません。", + "expression-required": "式は必須です。", + "expression-invalid": "式が無効です", + "expression-max-length": "式の長さは255文字未満である必要があります。", + "argument-name-required": "引数名は必須です。", + "argument-name-pattern": "引数名が無効です。", + "argument-name-duplicate": "この名前の引数はすでに存在します。", + "argument-name-max-length": "引数名は256文字未満である必要があります。", + "argument-name-forbidden": "引数名は予約語のため使用できません。", + "output-key-required": "出力キーは必須です。", + "output-key-pattern": "出力キーが無効です。", + "output-key-duplicate": "同じ名前のキーがすでに存在します。", + "output-key-max-length": "出力キーは 256 文字未満にしてください。", + "output-key-forbidden": "出力キーは予約されているため使用できません。", + "entity-type-required": "エンティティタイプは必須です", + "name-required": "名前は必須です。", + "name-pattern": "名前が無効です。", + "name-duplicate": "同じ名前がすでに存在します。", + "name-max-length": "名前は 256 文字未満にしてください。", + "name-forbidden": "名前は予約されているため使用できません。", + "argument-type-required": "引数タイプは必須です。", + "max-args": "引数の最大数に達しました。", + "decimals-range": "デフォルトの小数桁は0〜15の数値である必要があります。", + "expression": "デフォルトの式は、温度を華氏から摂氏に変換する方法を示します。", + "arguments-entity-not-found": "引数の対象エンティティが見つかりません。", + "use-latest-timestamp": "有効にすると、計算値はサーバー時刻ではなく、引数のテレメトリの最新タイムスタンプを使用して永続化されます。", + "entity-coordinates": "エンティティの GPS 座標(緯度・経度)を提供する時系列キーを指定します。", + "geofencing-zone-groups": "チェックするジオフェンシングゾーングループを 1 つ以上定義します(例: 'allowedZones'、'restrictedZones')。各グループには一意の名前が必要で、その名前は計算フィールドの出力テレメトリキーのプレフィックスとして使用されます。", + "perimeter-attribute-key": "ジオフェンシングゾーンの境界定義を含む属性キーを設定します。境界は常にゾーンエンティティのサーバー側属性から取得されます。", + "report-strategy": "在席ステータスは、エンティティが現在ゾーングループの内側(INSIDE)か外側(OUTSIDE)かを報告します。遷移イベントは、エンティティがゾーングループに入った(ENTERED)または出た(LEFT)タイミングを報告します。", + "create-relation-with-matched-zones": "エンティティと、現在その内側にあるゾーンとのリレーションを自動的に作成・維持します。エンティティがゾーンから出るとリレーションは削除され、新しいゾーンに入ると作成されます。", + "relation-type-required": "リレーションタイプは必須です。", + "relation-level-required": "リレーションレベルは必須です。", + "relation-level-min": "リレーションレベルの最小値は 1 です。", + "relation-level-max": "リレーションレベルの最大値は {{max}} です。", + "geofencing-empty": "少なくとも 1 つのゾーングループを設定する必要があります。", + "geofencing-entity-not-found": "ジオフェンシング対象エンティティが見つかりません。", + "max-geofencing-zone": "ジオフェンシングゾーン数が上限に達しました。", + "zone-group-refresh-interval": "関連エンティティ経由で設定されたゾーングループを更新する頻度を定義します。", + "zone-group-refresh-interval-required": "ゾーングループ更新間隔は必須です。", + "zone-group-refresh-interval-min": "ゾーングループ更新間隔は少なくとも {{ min }} 秒にしてください。", + "propagation-path-related-entities": "選択した方向とリレーションタイプに基づき、関連エンティティへの直接の単一レベルのパスを定義します。", + "data-propagate": "下で設定された引数から伝播するデータを定義します。「引数のみ」は取得したデータをそのまま使用し、「計算結果」はそのデータから新しい値を計算します。", + "aggregation-path-related-entities": "方向とリレーションタイプに基づき、親または子エンティティとの直接リレーションを介した単一レベルの集計パスを定義します。デバイス、アセット、顧客、テナントの各エンティティ間のリレーションのみがサポートされます。", + "arguments-aggregation": "フィルタリングと集計に使用する入力引数を定義します。", + "setting-arguments-aggregation": "データは、集計パスで設定された関連エンティティから取得されます。", + "metrics": "設定された引数に基づいて集計されるメトリクスを定義します。", + "entity-aggregation-metrics": "指定した時間間隔にわたり、設定された引数に基づいて集計されるメトリクスを定義します。", + "import-invalid-calculated-field-type": "計算フィールドをインポートできません: 計算フィールド構造が無効です。", + "simple-expression-title": "計算値の算出方法を定義する算術式。", + "script-title": "計算ロジックと出力値を定義する TBEL スクリプト。", + "simple-arguments": "計算値の算出方法を定義する算術式。", + "script-arguments": "スクリプトで利用できる入力引数を定義します。" + } + }, + "alarm-rule": { + "alarm-rules-tab": "アラームルール", + "alarm-rule": "アラームルール", + "alarm-rules": "アラームルール", + "alarm-rules-old": "旧", + "alarm-rules-actual": "現在", + "severities": "重大度", + "cleared": "クリア条件", + "delete-title": "アラームルール '{{title}}' を削除してもよろしいですか?", + "delete-text": "注意: 確認後、アラームルールと関連するすべてのデータは復元できなくなります。", + "delete-multiple-title": "{ count, plural, =1 {アラームルール 1 件} other {アラームルール # 件} } を削除してもよろしいですか?", + "delete-multiple-text": "注意: 確認後、選択したアラームルールはすべて削除され、関連するすべてのデータは復元できなくなります。", + "create": "アラームルールを新規作成", + "add": "アラームルールを追加", + "copy": "アラームルール設定をコピー", + "details": "アラームルールの詳細", + "no-found": "アラームルールが見つかりません", + "list": "{ count, plural, =1 {アラームルール} other {アラームルール一覧 (# 件)} }", + "selected-fields": "{ count, plural, =1 {アラームルール} other {アラームルール (# 件)} } を選択", + "import": "アラームルールをインポート", + "file": "アラームルールファイル", + "export": "アラームルールをエクスポート", + "export-failed-error": "アラームルールをエクスポートできません: {{error}}", + "entity-type": "エンティティタイプ", + "entity-type-required": "エンティティタイプは必須です。", + "alarm-type": "アラームタイプ", + "alarm-type-hint": "競合を防ぐため、アラーム発生元(デバイス、アセットなど)のスコープ内で一意となる識別子(例: HighTempAlarm)。", + "alarm-type-required": "アラームタイプは必須です。", + "alarm-type-pattern": "アラームタイプが無効です。", + "alarm-type-max-length": "アラームタイプは 256 文字未満にしてください。", + "clear-alarm": "アラームをクリア", + "value-argument": "引数", + "value-argument-required": "引数は必須です。", + "static-settings": "固定設定", + "configuration": "設定", + "static-schedule": "固定", + "dynamic-schedule": "動的", + "operation-and": "AND", + "operation-or": "OR", + "condition-during": "{{during}} の間", + "condition-during-dynamic": "\"{{ attribute }}\" の間", + "condition-repeat-times": "{ count, plural, =1 {1 回} other {# 回} }繰り返す", + "condition-repeat-times-dynamic": "\"{{ attribute }}\" 回繰り返す", + "filter-preview": "フィルタープレビュー", + "condition-settings": "条件設定", + "static": "固定", + "dynamic": "動的", + "argument-filters": "引数フィルター", + "argument-name": "引数名", + "value-type": "値タイプ", + "general": "一般", + "filters": "フィルター", + "date-time-hint": "引数はエポックミリ秒である必要があります。例: 1698839340000 は 2023-11-01 12:49:00 UTC に相当します。", + "operation": "演算", + "value-source": "値ソース", + "value": "値", + "ignore-case": "大文字/小文字を区別しない", + "condition": "条件", + "script": "スクリプト", + "add-filter": "引数フィルターを追加", + "edit-filter": "引数フィルター", + "remove-filter": "引数フィルターを削除", + "no-filter": "少なくとも 1 つのフィルターが必要です。", + "conditions": { + "simple": "シンプル", + "duration": "継続時間", + "repeating": "繰り返し" + }, + "schedule-title": "スケジュール", + "edit-schedule": "アラームスケジュールを編集", + "schedule-type": "スケジューラータイプ", + "schedule-type-required": "スケジューラータイプは必須です。", + "schedule": { + "any-time": "常にアクティブ", + "specific-time": "特定の時間にアクティブ", + "custom": "カスタム" + }, + "schedule-day": { + "monday": "月曜日", + "tuesday": "火曜日", + "wednesday": "水曜日", + "thursday": "木曜日", + "friday": "金曜日", + "saturday": "土曜日", + "sunday": "日曜日" + }, + "schedule-days": "日", + "schedule-time": "時間", + "schedule-time-from": "開始", + "schedule-time-to": "終了", + "schedule-days-of-week-required": "少なくとも 1 つの曜日を選択してください。", + "tbel": "TBEL", + "expression-type": { + "simple": "シンプル", + "script": "スクリプト" + }, + "operation-type": { + "and": "AND", + "or": "OR" + }, + "filter-predicate-type": { + "string": "文字列", + "numeric": "数値", + "boolean": "真偽値", + "complex": "複合" + }, + "alarm-rule-additional-info": "追加情報", + "edit-alarm-rule-additional-info": "追加情報を編集", + "alarm-rule-additional-info-placeholder": "ここにコメントや調整内容を入力すると、アラーム詳細の「追加情報」に表示されます", + "alarm-rule-additional-info-hint": "ヒント: ${Argument name} を使用して、アラームルール条件で使用される引数の値を置換できます。", + "alarm-rule-additional-info-icon-hint": "引数名を使用して、アラームルール条件で使用される引数の値を置換します。", + "alarm-rule-mobile-dashboard": "モバイルダッシュボード", + "alarm-rule-mobile-dashboard-hint": "モバイルアプリケーションでアラーム詳細ダッシュボードとして使用されます。", + "alarm-rule-no-mobile-dashboard": "ダッシュボードが選択されていません", + "alarm-rule-condition": "アラームルール条件", + "enter-alarm-rule-condition-prompt": "条件を追加", + "enter-alarm-rule-clear-condition-prompt": "クリア条件を追加", + "edit-alarm-rule-condition": "アラーム条件", + "condition-type": "条件タイプ", + "condition-type-hint": "\"継続時間\" と \"繰り返し\" オプションは、フィルターで \"欠落期間\" の演算を使用している場合は利用できません。", + "select-alarm-severity": "アラーム重大度を選択", + "add-create-alarm-rule-prompt": "少なくとも 1 つのトリガー条件が必要です。", + "add-create-alarm-rule": "トリガー条件を追加", + "add-clear-alarm-rule": "クリア条件を追加", + "condition-duration": "条件の継続時間", + "condition-duration-value": "継続時間の値", + "condition-duration-time-unit": "時間単位", + "condition-duration-value-range": "継続時間の値は 1 ~ 2147483647 の範囲にしてください。", + "condition-duration-value-pattern": "継続時間の値は整数にしてください。", + "condition-duration-value-required": "継続時間の値は必須です。", + "condition-duration-time-unit-required": "時間単位は必須です。", + "condition-repeating-value": "イベント数", + "condition-repeating-value-hint": "アラームルール引数の更新はすべてイベントとしてカウントされます", + "condition-repeating-value-range": "イベント数は 1 ~ 2147483647 の範囲にしてください。", + "condition-repeating-value-pattern": "イベント数は整数にしてください。", + "condition-repeating-value-required": "イベント数は必須です。", + "create-conditions": "トリガー条件", + "clear-condition": "クリア条件", + "no-clear-alarm-rule": "クリア条件が設定されていません。", + "advanced-settings": "詳細設定", + "propagate-alarm": "関連エンティティへアラームを伝播", + "alarm-rule-relation-types-list": "リレーションタイプ", + "alarm-rule-relation-types-list-hint": "関連エンティティをフィルタリングするためのリレーションタイプを定義します。未設定の場合、アラームはすべての関連エンティティに伝播されます。", + "propagate-alarm-to-owner": "エンティティ所有者(顧客またはテナント)へアラームを伝播", + "propagate-alarm-to-tenant": "テナントへアラームを伝播", + "alarm-rule-filter-title": "アラームルールフィルター", + "filter-title": "フィルター", + "debugging": "アラームルールのデバッグ", + "any-type": "すべてのタイプ", + "enter-alarm-rule-type": "アラームタイプを入力", + "no-alarm-rule-types-matching": "'{{entitySubtype}}' に一致するアラームタイプが見つかりません。", + "alarm-rule-type-list-empty": "アラームタイプが選択されていません。", + "alarm-rule-type-list": "アラームタイプ一覧", + "alarm-rule-entity-list": "エンティティ一覧", + "missing-for": "欠落期間", + "time-unit": "単位", + "mode": "モード", + "type": "タイプ", + "value-required": "値は必須です。", + "min-value": "値は 1 以上である必要があります。", + "argument-in-use": "引数は一般引数として使用されています。", + "import-invalid-alarm-rule-type": "アラームルールをインポートできません: アラームルール構造が無効です。", + "no-filter-preview": "フィルターが指定されていません", + "filter-operation": { + "and": "AND", + "or": "OR" + } + }, + "ai-models": { + "ai-models": "AIモデル", + "ai-model": "AIモデル", + "model": "モデル", + "name": "名前", + "ai-provider": "AIプロバイダー", + "no-found": "AIモデルが見つかりません", + "list": "{ count, plural, =1 {AIモデル} other {AIモデル一覧 # 件} }", + "selected-fields": "{ count, plural, =1 {1 件のAIモデル} other {# 件のAIモデル} } を選択", + "add": "モデルを追加", + "delete-model-title": "モデル'{{modelName}}'を削除してもよろしいですか?", + "delete-model-text": "注意: 確認後、モデルと関連データはすべて復元できなくなります。", + "delete-models-title": "{ count, plural, =1 {1 件のモデル} other {# 件のモデル} } を削除してもよろしいですか?", + "delete-models-text": "注意: 確認後、選択したすべてのモデルが削除され、関連データはすべて復元できなくなります。", + "ai-providers": { + "openai": "OpenAI", + "azure-openai": "Azure OpenAI", + "google-ai-gemini": "Google AI Gemini", + "google-vertex-ai-gemini": "Google Vertex AI Gemini", + "mistral-ai": "Mistral AI", + "anthropic": "Anthropic", + "amazon-bedrock": "Amazon Bedrock", + "github-models": "GitHub Models", + "ollama": "Ollama" + }, + "name-required": "名前は必須です。", + "name-max-length": "名前は255文字以内である必要があります。", + "provider": "プロバイダー", + "api-key": "APIキー", + "api-key-required": "APIキーは必須です。", + "api-key-open-ai-required": "公式のOpenAI APIを使用する場合はAPIキーが必須です。", + "project-id": "プロジェクトID", + "project-id-required": "プロジェクトIDは必須です", + "location": "ロケーション", + "location-required": "ロケーションは必須です。", + "service-account-key-file": "サービスアカウントキーファイル", + "service-account-key-file-required": "サービスアカウントキーファイルは必須です。", + "no-file": "ファイルが選択されていません。", + "drop-file": "ファイルをドロップするか、クリックしてアップロードするファイルを選択してください。", + "personal-access-token": "パーソナルアクセストークン", + "personal-access-token-required": "パーソナルアクセストークンは必須です。", + "configuration": "設定", + "model-id": "モデルID", + "model-id-required": "モデルIDは必須です。", + "deployment-name": "デプロイメント名", + "deployment-name-required": "デプロイメント名は必須です", + "set": "設定", + "region": "リージョン", + "region-required": "リージョンは必須です。", + "access-key-id": "アクセスキーID", + "access-key-id-required": "アクセスキーIDは必須です。", + "secret-access-key": "シークレットアクセスキー", + "secret-access-key-required": "シークレットアクセスキーは必須です。", + "temperature": "温度", + "temperature-hint": "モデル出力のランダム性の度合いを調整します。値が高いほどランダム性が増し、低いほど減ります。", + "temperature-min": "0以上である必要があります。", + "top-p": "Top P", + "top-p-hint": "モデルが選択するための確率が高いトークンのプールを作成します。値が高いほどプールは大きく多様になり、低いほど小さくなります。", + "top-p-min-max": "0より大きく、最大1である必要があります。", + "top-k": "Top K", + "top-k-hint": "モデルの選択肢を、最も確からしい \"K\" 個のトークンの固定セットに制限します。", + "top-k-min": "0以上である必要があります。", + "presence-penalty": "出現ペナルティ", + "presence-penalty-hint": "トークンがすでにテキスト内に出現している場合、その確率に固定のペナルティを適用します。", + "frequency-penalty": "頻度ペナルティ", + "frequency-penalty-hint": "テキスト内での出現頻度に応じて増加するペナルティを、トークンの確率に適用します。", + "max-output-tokens": "最大出力トークン数", + "max-output-tokens-hint": "モデルが1回の応答で生成できるトークンの最大数を\n設定します。", + "context-length": "コンテキスト長", + "context-length-hint": "トークン単位でコンテキストウィンドウのサイズを定義します。この値は、ユーザー入力と生成された応答の両方を含む、モデルの総メモリ上限を設定します。", + "endpoint": "エンドポイント", + "endpoint-required": "エンドポイントは必須です。", + "baseurl": "ベースURL", + "baseurl-required": "ベースURLは必須です。", + "service-version": "サービスバージョン", + "check-connectivity": "接続を確認", + "check-connectivity-success": "テストリクエストは成功しました", + "check-connectivity-failed": "テストリクエストに失敗しました", + "no-model-matching": "'{{entity}}'に一致するモデルが見つかりません。", + "model-required": "モデルは必須です。", + "no-model-text": "モデルが見つかりません。", + "authentication": "認証", + "authentication-basic-hint": "標準のHTTP Basic認証を使用します。ユーザー名とパスワードは結合され、Base64エンコードされたうえで、Ollamaサーバーへの各リクエストに\"Authorization\"ヘッダーとして送信されます。", + "authentication-token-hint": "Bearerトークン認証を使用します。指定したトークンは、Ollamaサーバーへの各リクエストで\"Authorization\"ヘッダーにそのまま送信されます。", + "authentication-type": { + "none": "なし", + "basic": "Basic", + "token": "トークン" + }, + "username": "ユーザー名", + "username-required": "ユーザー名は必須です。", + "password": "パスワード", + "password-required": "パスワードは必須です。", + "token": "トークン", + "token-required": "トークンは必須です。" + }, + "confirm-on-exit": { + "message": "未保存の変更があります。このページから移動してもよろしいですか?", + "html-message": "未保存の変更があります。
    このページから移動してもよろしいですか?", + "title": "未保存の変更" + }, + "contact": { + "country": "国", + "country-required": "国は必須です。", + "country-object-required": "一覧から有効な国を選択してください。", + "city": "市区町村", + "state": "州 / 県", + "postal-code": "郵便番号", + "postal-code-invalid": "郵便番号の形式が無効です。", + "address": "住所", + "address2": "住所2", + "phone": "電話番号", + "email": "Email", + "no-address": "住所なし", + "no-country-found": "国が見つかりません。", + "no-country-matching": "'{{country}}'に一致する国が見つかりません。", + "state-max-length": "州は256未満である必要があります", + "phone-max-length": "電話番号は256未満である必要があります", + "city-max-length": "指定された市区町村は256未満である必要があります" + }, + "common": { + "name": "名前", + "type": "タイプ", + "general": "一般", + "username": "ユーザー名", + "password": "パスワード", + "data": "データ", + "timestamp": "タイムスタンプ", + "enter-username": "ユーザー名を入力", + "enter-password": "パスワードを入力", + "enter-search": "検索文字列を入力", + "created-time": "作成日時", + "disabled": "無効", + "loading": "読み込み中...", + "proceed": "続行", + "open-details-page": "詳細ページを開く", + "not-found": "見つかりません", + "value": "値", + "documentation": "ドキュメント", + "time-left": "残り{{time}}", + "output": "出力", + "sort-asc": "昇順", + "sort-desc": "降順", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "名前は必須です。", + "name-pattern": "名前が無効です。", + "name-max-length": "名前は256文字未満である必要があります。", + "title-required": "タイトルは必須です。", + "title-pattern": "タイトルが無効です。", + "title-max-length": "タイトルは256文字未満である必要があります。", + "key-required": "キーは必須です。", + "key-pattern": "キーが無効です。", + "key-max-length": "キーは256文字未満である必要があります。" + }, + "required-fields": "必須項目が未入力です" + }, + "content-type": { + "json": "JSON", + "text": "テキスト", + "binary": "バイナリ (Base64)" + }, + "color": { + "color": "色" + }, + "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-customer-edges": "顧客Edgeを管理", + "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 件の顧客} other {# 件の顧客} } を削除してもよろしいですか?", + "delete-customers-action-title": "{ count, plural, =1 {1 件の顧客} other {# 件の顧客} } を削除", + "delete-customers-text": "注意: 確認後、選択したすべての顧客が削除され、関連データはすべて復元できなくなります。", + "manage-users": "ユーザーを管理", + "manage-assets": "アセットを管理", + "manage-devices": "デバイスを管理", + "manage-dashboards": "ダッシュボードを管理", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "title-max-length": "タイトルは256未満である必要があります", + "description": "説明", + "details": "詳細", + "events": "イベント", + "copyId": "顧客IDをコピー", + "idCopiedMessage": "顧客IDがクリップボードにコピーされました", + "select-customer": "顧客を選択", + "no-customers-matching": "'{{entity}}'に一致する顧客が見つかりません。", + "customer-required": "顧客は必須です", + "select-default-customer": "デフォルト顧客を選択", + "default-customer": "デフォルト顧客", + "default-customer-required": "テナントレベルでダッシュボードをデバッグするにはデフォルト顧客が必要です", + "search": "顧客を検索", + "selected-customers": "{ count, plural, =1 {1 件の顧客} other {# 件の顧客} } を選択", + "edges": "顧客edgeインスタンス", + "manage-edges": "Edgesを管理" + }, + "css-size": { + "size-value-required": "サイズ値は必須です", + "invalid-size-value": "無効なサイズ値" + }, + "date": { + "last-update-n-ago": "最終更新 N 前", + "last-update-n-ago-text": "最終更新 {{ agoText }}", + "custom-date": "カスタム日付", + "format": "形式", + "preview": "プレビュー", + "auto": "自動", + "time-granularity-formats": "時間粒度の形式", + "unit-year": "年", + "unit-month": "月", + "unit-day": "日", + "unit-hour": "時間", + "unit-minute": "分", + "unit-second": "秒", + "unit-millisecond": "ミリ秒" + }, + "datetime": { + "date-from": "開始日", + "time-from": "開始時刻", + "date-to": "終了日", + "time-to": "終了時刻", + "from": "開始", + "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": "新しいウィジェットを追加", + "add-widget-button-text": "ウィジェットを追加", + "title": "タイトル", + "image": "ダッシュボード画像", + "mobile-app-settings": "モバイルアプリケーション設定", + "mobile-order": "モバイルアプリケーションでのダッシュボード順序", + "mobile-hide": "モバイルアプリケーションでダッシュボードを非表示", + "update-image": "ダッシュボード画像を更新", + "update-new-version": "新しいバージョンをアップロード", + "upload-file-to-update": "更新するファイルをアップロード", + "take-screenshot": "スクリーンショットを撮る", + "select-widget-title": "ウィジェットを選択", + "select-widget-value": "{{title}}: ウィジェットを選択", + "select-widget-subtitle": "利用可能なウィジェットタイプ一覧", + "delete": "ダッシュボードを削除", + "title-required": "タイトルは必須です。", + "title-max-length": "タイトルは256未満である必要があります", + "description": "説明", + "details": "詳細", + "dashboard-details": "ダッシュボード詳細", + "add-dashboard-text": "新しいダッシュボードを追加", + "assign-dashboards": "ダッシュボードを割り当て", + "assign-new-dashboard": "新しいダッシュボードを割り当て", + "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": "ダッシュボード'{{dashboardTitle}}'を顧客から割り当て解除してもよろしいですか?", + "delete-dashboard-title": "ダッシュボード'{{dashboardTitle}}'を削除してもよろしいですか?", + "delete-dashboard-text": "注意: 確認後、ダッシュボードと関連データはすべて復元できなくなります。", + "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 {1 件のダッシュボード} 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}}' powered by ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' powered by 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": "画像が選択されていません", + "empty-image": "画像なし", + "drop-image": "画像をドロップするか、クリックしてアップロードするファイルを選択してください。", + "maximum-upload-file-size": "アップロード可能な最大ファイルサイズ: {{ size }}", + "cannot-upload-file": "ファイルをアップロードできません", + "settings": "設定", + "move-all-widgets": "すべてのウィジェットを移動", + "move-by": "移動量", + "cols": "列", + "rows": "行", + "layout": "レイアウト", + "layout-type-default": "デフォルト", + "layout-type-scada": "SCADA", + "layout-type-divider": "区切り", + "layout-settings-type": "レイアウト設定: {{ type }} ブレークポイント", + "columns-count": "列数", + "columns-count-required": "列数は必須です。", + "min-columns-count-message": "列数の最小値として許可されるのは10のみです。", + "max-columns-count-message": "列数の最大値として許可されるのは1000のみです。", + "min-layout-width": "最小レイアウト幅", + "columns-suffix": "列", + "widgets-margins": "ウィジェット間の余白", + "margin-required": "余白の値は必須です。", + "min-margin-message": "余白の最小値として許可されるのは0のみです。", + "max-margin-message": "余白の最大値として許可されるのは50のみです。", + "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のみです。", + "apply-outer-margin": "レイアウトの側面に余白を適用", + "autofill-height": "レイアウト高さを自動で埋める", + "mobile-layout": "モバイルレイアウト設定", + "mobile-row-height": "モバイル行の高さ", + "mobile-row-height-required": "モバイル行の高さの値は必須です。", + "min-mobile-row-height-message": "モバイル行の高さの最小値として許可されるのは5ピクセルのみです。", + "max-mobile-row-height-message": "モバイル行の高さの最大値として許可されるのは200ピクセルのみです。", + "row-height": "行の高さ", + "row-height-required": "行の高さの値は必須です。", + "min-row-height-message": "行の高さの最小値として許可されるのは5ピクセルのみです。", + "max-row-height-message": "行の高さの最大値として許可されるのは200ピクセルのみです。", + "display-first-in-mobile-view": "モバイルビューで先頭に表示", + "title-settings": "タイトル設定", + "display-title": "ダッシュボードタイトルを表示", + "title-color": "タイトル色", + "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": "ダッシュボード画像の更新を表示", + "dashboard-logo-settings": "ダッシュボードロゴ設定", + "display-dashboard-logo": "ダッシュボード全画面モードでロゴを表示", + "dashboard-logo-image": "ダッシュボードロゴ画像", + "advanced-settings": "高度な設定", + "dashboard-css": "ダッシュボードCSS", + "import": "ダッシュボードをインポート", + "export": "ダッシュボードをエクスポート", + "export-failed-error": "ダッシュボードをエクスポートできません: {{error}}", + "export-prompt": "ダッシュボード画像とリソースを埋め込む", + "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": "公開", + "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 {ダッシュボード状態 1 件} other {ダッシュボード状態 # 件} } を選択", + "edit-state": "ダッシュボード状態を編集", + "delete-state": "ダッシュボード状態を削除", + "add-state": "ダッシュボード状態を追加", + "no-states-text": "状態が見つかりません", + "state": "ダッシュボード状態", + "state-name": "名前", + "state-name-required": "ダッシュボード状態名は必須です。", + "state-id": "状態ID", + "state-id-required": "ダッシュボード状態IDは必須です。", + "state-id-exists": "同じIDのダッシュボード状態がすでに存在します。", + "is-root-state": "ルート状態", + "delete-state-title": "ダッシュボード状態を削除", + "delete-state-text": "名前が'{{stateName}}'のダッシュボード状態を削除してもよろしいですか?", + "show-details": "詳細を表示", + "hide-details": "詳細を非表示", + "select-state": "対象状態を選択", + "state-controller": "状態コントローラー", + "state-controller-default": "static (非推奨)", + "search": "ダッシュボードを検索", + "selected-dashboards": "{ count, plural, =1 {ダッシュボード 1 件} other {ダッシュボード # 件} } を選択", + "home-dashboard": "ホームダッシュボード", + "home-dashboard-hide-toolbar": "ホームダッシュボードのツールバーを非表示", + "unassign-dashboard-from-edge-text": "確認後、ダッシュボードの割り当てが解除され、edgeがアクセスできなくなります。", + "unassign-dashboards-from-edge-title": "{ count, plural, =1 {ダッシュボード 1 件} other {ダッシュボード # 件} } の割り当てを解除してもよろしいですか?", + "unassign-dashboards-from-edge-text": "確認後、選択したすべてのダッシュボードの割り当てが解除され、edgeがアクセスできなくなります。", + "assign-dashboard-to-edge": "Edgeにダッシュボードを割り当て", + "assign-dashboard-to-edge-text": "Edgeに割り当てるダッシュボードを選択してください", + "non-existent-dashboard-state-error": "ID \"{{ stateId }}\" のダッシュボード状態が見つかりません", + "edit-mode": "編集モード", + "duplicate-state-action": "状態を複製", + "breakpoint-value": "ブレークポイント ({{ value }})", + "breakpoints-id": { + "default": "デフォルト", + "xs": "モバイル (xs)", + "sm": "タブレット (sm)", + "md": "ラップトップ (md)", + "lg": "デスクトップ (lg)", + "xl": "デスクトップ (xl)" + }, + "view-format-type-grid": "グリッド", + "view-format-type-list": "一覧", + "view-format": "表示形式" + }, + "datakey": { + "settings": "設定", + "general": "一般", + "advanced": "高度な設定", + "key": "キー", + "keys": "キー", + "label": "ラベル", + "color": "色", + "units": "値の横に表示する特殊記号", + "decimals": "小数点以下の桁数", + "data-generation-func": "データ生成関数", + "use-data-post-processing-func": "データ後処理関数を使用", + "configuration": "データキー設定", + "timeseries": "時系列", + "attributes": "属性", + "entity-field": "エンティティフィールド", + "alarm": "アラームフィールド", + "timeseries-required": "エンティティの時系列は必須です。", + "timeseries-or-attributes-required": "エンティティの時系列/属性は必須です。", + "alarm-fields-timeseries-or-attributes-required": "アラームフィールドまたはエンティティの時系列/属性は必須です。", + "maximum-timeseries-or-attributes": "最大 { count, plural, =1 {時系列/属性は1件まで許可されます。} other {時系列/属性は#件まで許可されます} }", + "alarm-fields-required": "アラームフィールドは必須です。", + "function-types": "関数タイプ", + "function-type": "関数タイプ", + "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-key": "時系列データキー", + "timeseries-key-functions": "時系列キー関数", + "timeseries-key-function": "時系列キー関数", + "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": "選択した時間ウィンドウ内のデータポイントの総数です。", + "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": "差分 (パーセント)", + "source": "ソース", + "latest": "最新", + "latest-value": "最新値", + "delta": "差分", + "percent": "パーセント", + "absolute": "絶対値" + }, + "datasource": { + "type": "データソースタイプ", + "name": "名前", + "label": "ラベル", + "add-datasource-prompt": "データソースを追加してください" + }, + "details": { + "details": "詳細", + "edit-mode": "編集モード", + "edit-json": "JSONを編集", + "toggle-edit-mode": "編集モードを切り替え" + }, + "device": { + "device": "デバイス", + "device-required": "デバイスは必須です。", + "devices": "デバイス", + "management": "デバイス管理", + "view-devices": "デバイスを表示", + "device-alias": "デバイスエイリアス", + "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": "新しく作成します!", + "duplicate-alias-error": "重複したエイリアス'{{alias}}'が見つかりました。
    デバイスエイリアスはダッシュボード内で一意である必要があります。", + "configure-alias": "エイリアス'{{alias}}'を設定", + "no-devices-matching": "'{{entity}}'に一致するデバイスが見つかりません。", + "alias": "エイリアス", + "alias-required": "デバイスエイリアスは必須です。", + "remove-alias": "デバイスエイリアスを削除", + "add-alias": "デバイスエイリアスを追加", + "name-starts-with": "デバイス名式", + "help-text": "必要に応じて'%'を使用してください: '%device_name_contains%', '%device_name_ends', 'device_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 件のデバイス} other {# 件のデバイス} } を割り当て", + "delete-devices": "デバイスを削除", + "unassign-from-customer": "顧客から割り当て解除", + "unassign-devices": "デバイスの割り当て解除", + "unassign-devices-action-title": "顧客から { count, plural, =1 {1 件のデバイス} other {# 件のデバイス} } の割り当てを解除", + "unassign-device-from-edge-title": "デバイス'{{deviceName}}'の割り当てを解除してもよろしいですか?", + "unassign-device-from-edge-text": "確認後、デバイスの割り当てが解除され、edgeからアクセスできなくなります。", + "unassign-devices-from-edge": "Edgeからデバイスの割り当てを解除", + "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 件のデバイス} 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 {1 件のデバイス} other {# 件のデバイス} } の割り当てを解除してもよろしいですか?", + "unassign-devices-text": "確認後、選択したすべてのデバイスの割り当てが解除され、顧客がアクセスできなくなります。", + "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": "証明書は必須です。", + "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識別子は、標準[RFC7925]で説明されているとおり、最大128バイトの任意のPSK識別子です。\nPSK識別子はまず文字列に変換し、その後UTF-8を使用してオクテットにエンコードしなければなりません。", + "client-key": "クライアントキー", + "client-key-required": "クライアントキーは必須です。", + "client-key-tooltip-prk": "RPK公開鍵またはIDは標準[RFC7250]に準拠し、Base64形式にエンコードされている必要があります!", + "client-key-tooltip-psk": "PSKキーは標準[RFC4279]に準拠し、HexDec形式である必要があります: 32、64、128文字!", + "endpoint": "エンドポイントクライアント名", + "endpoint-required": "エンドポイントクライアント名は必須です。", + "client-public-key": "クライアント公開鍵", + "client-public-key-hint": "クライアント公開鍵が空の場合、信頼済み証明書が使用されます", + "client-public-key-tooltip": "X509公開鍵はDERエンコードされたX509v3形式で、ECアルゴリズムのみをサポートし、Base64形式にエンコードされている必要があります!", + "mode": "セキュリティ設定モード", + "client-tab": "クライアントセキュリティ設定", + "client-certificate": "クライアント証明書", + "bootstrap-tab": "ブートストラップクライアント", + "bootstrap-server": "ブートストラップサーバー", + "lwm2m-server": "LwM2Mサーバー", + "client-reboot": "登録更新トリガー", + "bootstrap-reboot": "ブートストラップ要求トリガー", + "client-publicKey-or-id": "クライアント公開鍵またはID", + "client-publicKey-or-id-required": "クライアント公開鍵またはIDは必須です。", + "client-publicKey-or-id-tooltip-psk": "PSK識別子は、標準[RFC7925]で説明されているとおり、最大128バイトの任意のPSK識別子です。\nPSK識別子はまず文字列に変換し、その後UTF-8を使用してオクテットにエンコードしなければなりません。", + "client-publicKey-or-id-tooltip-rpk": "RPK公開鍵またはIDは標準[RFC7250]に準拠し、Base64形式にエンコードされている必要があります!", + "client-publicKey-or-id-tooltip-x509": "X509公開鍵はDERエンコードされたX509v3形式で、ECアルゴリズムのみをサポートし、Base64形式にエンコードされている必要があります", + "client-secret-key": "クライアント秘密鍵", + "client-secret-key-required": "クライアント秘密鍵は必須です。", + "client-secret-key-tooltip-psk": "PSKキーは標準[RFC4279]に準拠し、HexDec形式である必要があります: 32、64、128文字!", + "client-secret-key-tooltip-prk": "RPK秘密鍵はPKCS_8形式(DERエンコード、標準[RFC5958])である必要があり、その後Base64形式にエンコードされている必要があります!", + "client-secret-key-tooltip-x509": "X509秘密鍵はPKCS_8形式(DERエンコード、標準[RFC5958])である必要があり、その後Base64形式にエンコードされている必要があります!" + }, + "client-id": "クライアントID", + "client-id-pattern": "無効な文字が含まれています。", + "user-name": "ユーザー名", + "user-name-required": "ユーザー名は必須です。", + "client-id-or-user-name-necessary": "クライアントIDおよび/またはユーザー名が必要です", + "password": "パスワード", + "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-profile-type-list-empty": "少なくとも1つのデバイスプロファイルを選択してください。", + "device-types": "デバイスプロファイル", + "name": "名前", + "name-required": "名前は必須です。", + "name-max-length": "名前は256未満である必要があります", + "label-max-length": "ラベルは256未満である必要があります", + "description": "説明", + "label": "ラベル", + "events": "イベント", + "details": "詳細", + "copyId": "デバイスIDをコピー", + "copyAccessToken": "アクセストークンをコピー", + "copy-mqtt-authentication": "MQTT認証情報をコピー", + "idCopiedMessage": "デバイスIDがクリップボードにコピーされました", + "accessTokenCopiedMessage": "デバイスのアクセストークンがクリップボードにコピーされました", + "mqtt-authentication-copied-message": "デバイスのMQTT認証情報がクリップボードにコピーされました", + "assignedToCustomer": "顧客に割り当て", + "unable-delete-device-alias-title": "デバイスエイリアスを削除できません", + "unable-delete-device-alias-text": "デバイスエイリアス'{{deviceAlias}}'は次のウィジェットで使用されているため削除できません:
    {{widgetsList}}", + "is-gateway": "Gatewayかどうか", + "overwrite-activity-time": "接続されたデバイスのアクティビティ時刻を上書き", + "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 {デバイス 1 件} other {デバイス # 件} } を選択", + "device-configuration": "デバイス設定", + "transport-configuration": "トランスポート設定", + "wizard": { + "device-details": "デバイス詳細" + }, + "unassign-devices-from-edge-title": "{ count, plural, =1 {デバイス 1 件} other {デバイス # 件} } の割り当てを解除してもよろしいですか?", + "unassign-devices-from-edge-text": "確認後、選択したすべてのデバイスの割り当てが解除され、edgeからアクセスできなくなります。", + "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": "次のドキュメントを使用して、X509認証でMQTT経由でデバイスを接続してください", + "coaps-x509-command": "次のドキュメントを使用して、X509認証でDTLS上のCoAP経由でデバイスを接続してください", + "snmp-command": "次のドキュメントを使用して、SNMP経由でデバイスを接続してください。", + "sparkplug-command": "次のドキュメントを使用して、MQTT Sparkplug経由でデバイスを接続してください。", + "lwm2m-command": "次のドキュメントを使用して、LWM2M経由でデバイスを接続してください。" + } + }, + "dynamic-form": { + "property": { + "properties": "プロパティ", + "property": "プロパティ", + "id": "ID", + "name": "名前", + "type": "タイプ", + "type-text": "テキスト", + "type-password": "パスワード", + "type-textarea": "テキストエリア", + "type-number": "数値", + "type-switch": "スイッチ", + "type-select": "選択", + "type-radios": "ラジオボタン", + "type-datetime": "日付/時刻", + "type-image": "画像", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "色", + "type-color-settings": "色設定", + "type-font": "フォント", + "type-units": "単位", + "type-icon": "アイコン", + "type-fieldset": "フィールドセット", + "type-array": "配列", + "type-html-section": "HTMLセクション", + "group-title": "グループタイトル", + "no-properties": "プロパティが設定されていません", + "add-property": "プロパティを追加", + "property-settings": "プロパティ設定", + "remove-property": "プロパティを削除", + "default-value": "デフォルト値", + "value-required": "値は必須です", + "number-settings": "数値設定", + "min": "最小", + "max": "最大", + "step": "ステップ", + "selected-options-limit": "選択オプション数の上限", + "advanced-ui-settings": "高度なUI設定", + "disable-on-property": "プロパティにより無効化", + "disable-on-property-none": "なし(フィールドは常に有効)", + "display-condition-function": "表示条件関数", + "sub-label": "サブラベル", + "vertical-divider-after": "後に縦区切り線", + "input-field-suffix": "入力フィールドのサフィックス", + "property-row-classes": "プロパティ行クラス", + "property-field-classes": "プロパティフィールドクラス", + "not-unique-property-ids-error": "プロパティIDは一意である必要があります!", + "enable-multiple-select": "複数選択を有効化", + "allow-empty-select-option": "空のオプションを許可", + "select-options": "選択オプション", + "not-unique-select-option-value-error": "選択オプションの値は一意である必要があります!", + "value": "値", + "label": "ラベル", + "add-option": "オプションを追加", + "no-options": "オプションが設定されていません", + "remove-option": "オプションを削除", + "textarea-rows": "テキストエリア行数", + "help-id": "ヘルプID", + "buttons-direction": "ボタンの並び方向", + "direction-row": "行", + "direction-column": "列", + "radio-button-options": "ラジオボタンオプション", + "datetime-type": "日付/時刻フィールドタイプ", + "datetime-type-date": "日付", + "datetime-type-time": "時刻", + "datetime-type-datetime": "日付/時刻", + "enable-clear-button": "クリアボタンを有効化", + "html-section-settings": "HTMLセクション設定", + "html-section-classes": "HTMLセクションクラス", + "html-section-content": "HTMLセクション内容", + "array-item": "配列項目", + "item-type": "項目タイプ", + "item-name": "項目名", + "no-items": "項目がありません", + "support-unit-conversion": "単位変換をサポート" + }, + "clear-form": "フォームをクリア", + "clear-form-prompt": "すべてのフォームプロパティを削除してもよろしいですか?", + "import-form": "JSONからフォームをインポート", + "export-form": "フォームをJSONにエクスポート", + "json-file": "JSONファイル", + "json-content": "JSON内容", + "invalid-form-json-file-error": "JSONからフォームをインポートできません: 無効なフォームJSONデータ構造です。" + }, + "asset-profile": { + "asset-profile": "アセットプロファイル", + "asset-profiles": "アセットプロファイル", + "all-asset-profiles": "すべて", + "add": "アセットプロファイルを追加", + "edit": "アセットプロファイルを編集", + "asset-profile-details": "アセットプロファイル詳細", + "no-asset-profiles-text": "アセットプロファイルが見つかりません", + "search": "アセットプロファイルを検索", + "selected-asset-profiles": "{ count, plural, =1 {アセットプロファイル 1 件} other {アセットプロファイル # 件} } を選択", + "no-asset-profiles-matching": "'{{entity}}'に一致するアセットプロファイルが見つかりません。", + "asset-profile-required": "アセットプロファイルは必須です", + "idCopiedMessage": "アセットプロファイルIDがクリップボードにコピーされました", + "set-default": "アセットプロファイルをデフォルトにする", + "delete": "アセットプロファイルを削除", + "copyId": "アセットプロファイルIDをコピー", + "name-max-length": "名前は256未満である必要があります", + "new-device-profile-name": "アセットプロファイル名", + "new-device-profile-name-required": "アセットプロファイル名は必須です。", + "name": "名前", + "name-required": "名前は必須です。", + "image": "アセットプロファイル画像", + "description": "説明", + "default": "デフォルト", + "default-rule-chain": "デフォルトルールチェーン", + "default-edge-rule-chain": "デフォルトedgeルールチェーン", + "default-edge-rule-chain-hint": "Edgeで、このアセットプロファイルのアセットの受信データを処理するルールチェーンとして使用されます", + "mobile-dashboard": "モバイルダッシュボード", + "mobile-dashboard-hint": "モバイルアプリケーションでアセット詳細ダッシュボードとして使用されます", + "select-queue-hint": "ドロップダウンリストから選択してください。", + "delete-asset-profile-title": "アセットプロファイル'{{assetProfileName}}'を削除してもよろしいですか?", + "delete-asset-profile-text": "注意: 確認後、アセットプロファイルと関連データはすべて復元できなくなります。", + "delete-asset-profiles-title": "{ count, plural, =1 {アセットプロファイル 1 件} other {アセットプロファイル # 件} } を削除してもよろしいですか?", + "delete-asset-profiles-text": "注意: 確認後、選択したすべてのアセットプロファイルが削除され、関連データはすべて復元できなくなります。", + "set-default-asset-profile-title": "アセットプロファイル'{{assetProfileName}}'をデフォルトにしてもよろしいですか?", + "set-default-asset-profile-text": "確認後、このアセットプロファイルはデフォルトとしてマークされ、プロファイル未指定の新しいアセットに使用されます。", + "no-asset-profiles-found": "アセットプロファイルが見つかりません。", + "create-new-asset-profile": "新しく作成します!", + "create-asset-profile": "新しいアセットプロファイルを作成", + "import": "アセットプロファイルをインポート", + "export": "アセットプロファイルをエクスポート", + "export-failed-error": "アセットプロファイルをエクスポートできません: {{error}}", + "asset-profile-file": "アセットプロファイルファイル", + "invalid-asset-profile-file-error": "アセットプロファイルをインポートできません: 無効なアセットプロファイルデータ構造です。" + }, + "device-profile": { + "device-profile": "デバイスプロファイル", + "device-profiles": "デバイスプロファイル", + "all-device-profiles": "すべて", + "add": "デバイスプロファイルを追加", + "edit": "デバイスプロファイルを編集", + "device-profile-details": "デバイスプロファイル詳細", + "no-device-profiles-text": "デバイスプロファイルが見つかりません", + "search": "デバイスプロファイルを検索", + "selected-device-profiles": "{ count, plural, =1 {デバイスプロファイル 1 件} other {デバイスプロファイル # 件} } を選択", + "no-device-profiles-matching": "'{{entity}}'に一致するデバイスプロファイルが見つかりません。", + "device-profile-required": "デバイスプロファイルは必須です", + "idCopiedMessage": "デバイスプロファイルIDがクリップボードにコピーされました", + "set-default": "デバイスプロファイルをデフォルトにする", + "delete": "デバイスプロファイルを削除", + "copyId": "デバイスプロファイルIDをコピー", + "name-max-length": "名前は256未満である必要があります", + "name": "名前", + "name-required": "名前は必須です。", + "type": "プロファイルタイプ", + "type-required": "プロファイルタイプは必須です。", + "type-default": "デフォルト", + "image": "デバイスプロファイル画像", + "transport-type": "トランスポートタイプ", + "transport-type-required": "トランスポートタイプは必須です。", + "transport-type-default": "デフォルト", + "transport-type-default-hint": "基本的なMQTT、HTTP、CoAPトランスポートをサポート", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "高度なMQTTトランスポート設定を有効化", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "高度なCoAPトランスポート設定を有効化", + "transport-type-lwm2m": "LWM2M", + "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": "デフォルトedgeルールチェーン", + "default-edge-rule-chain-hint": "Edgeで、このデバイスプロファイルのデバイスの受信データを処理するルールチェーンとして使用されます", + "mobile-dashboard": "モバイルダッシュボード", + "mobile-dashboard-hint": "モバイルアプリケーションでデバイス詳細ダッシュボードとして使用されます", + "select-queue-hint": "ドロップダウンリストから選択してください。", + "delete-device-profile-title": "デバイスプロファイル'{{deviceProfileName}}'を削除してもよろしいですか?", + "delete-device-profile-text": "注意: 確認後、デバイスプロファイルと関連データ(関連するOTAアップデートを含む)はすべて復元できなくなります。", + "delete-device-profiles-title": "{ count, plural, =1 {デバイスプロファイル 1 件} other {デバイスプロファイル # 件} } を削除してもよろしいですか?", + "delete-device-profiles-text": "注意: 確認後、選択したすべてのデバイスプロファイルが削除され、関連データ(関連するOTAアップデートを含む)はすべて復元できなくなります。", + "set-default-device-profile-title": "デバイスプロファイル'{{deviceProfileName}}'をデフォルトにしてもよろしいですか?", + "set-default-device-profile-text": "確認後、このデバイスプロファイルはデフォルトとしてマークされ、プロファイル未指定の新しいデバイスに使用されます。", + "no-device-profiles-found": "デバイスプロファイルが見つかりません。", + "create-new-device-profile": "新しく作成します!", + "mqtt-device-topic-filters": "MQTTデバイストピックフィルター", + "mqtt-device-topic-filters-unique": "MQTTデバイストピックフィルターは一意である必要があります。", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge of Network (EoN) ノード。", + "mqtt-device-topic-filters-spark-plug-hint": "Sparkplug Bペイロードとトピック形式を持つ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デバイスペイロード", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "他のペイロード形式との互換性を有効化。", + "mqtt-enable-compatibility-with-json-payload-format-hint": "有効にすると、プラットフォームはデフォルトでProtobufペイロード形式を使用します。解析に失敗した場合、プラットフォームはJSONペイロード形式の使用を試みます。ファームウェアアップデート中の後方互換性に有用です。たとえば、ファームウェアの初期リリースはJsonを使用し、新しいリリースはProtobufを使用します。デバイス群のファームウェア更新の過程では、ProtobufとJSONの両方を同時にサポートする必要があります。互換モードはわずかな性能低下を招くため、すべてのデバイスが更新されたらこのモードを無効にすることを推奨します。", + "mqtt-use-json-format-for-default-downlink-topics": "デフォルトのダウンリンクトピックにJson形式を使用", + "mqtt-use-json-format-for-default-downlink-topics-hint": "有効にすると、プラットフォームは次のトピックを介して属性とRPCをプッシュするためにJsonペイロード形式を使用します: 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。この設定は、新しい(v2)トピックを使用して送信される属性およびrpcサブスクリプションには影響しません: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id。ここで $request_id は整数のリクエスト識別子です。", + "mqtt-send-ack-on-validation-exception": "PUBLISHメッセージの検証失敗時にPUBACKを送信", + "mqtt-send-ack-on-validation-exception-hint": "デフォルトでは、プラットフォームはメッセージ検証失敗時にMQTTセッションを閉じます。有効にすると、セッションを閉じる代わりにPUBLISH確認応答を送信します。", + "mqtt-protocol-version": "プロトコルバージョン", + "snmp-add-mapping": "SNMPマッピングを追加", + "snmp-mapping-not-configured": "OIDから時系列/テレメトリへのマッピングが設定されていません", + "snmp-timseries-or-attribute-name": "マッピング用の時系列/属性名", + "snmp-timseries-or-attribute-type": "マッピング用の時系列/属性タイプ", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "ペイロードタイプは必須です。", + "coap-device-type": "CoAPデバイスタイプ", + "coap-device-payload-type": "CoAPデバイスペイロード", + "coap-device-type-required": "CoAPデバイスタイプは必須です。", + "coap-device-type-default": "デフォルト", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "単一レベル [+] およびマルチレベル [#] のワイルドカードをサポートします。", + "telemetry-topic-filter": "テレメトリトピックフィルター", + "telemetry-topic-filter-required": "テレメトリトピックフィルターは必須です。", + "attributes-topic-filter": "属性パブリッシュトピックフィルター", + "attributes-subscribe-topic-filter": "属性サブスクライブトピックフィルター", + "attributes-topic-filter-required": "属性パブリッシュトピックフィルターは必須です。", + "attributes-subscribe-topic-filter-required": "属性サブスクライブトピックは必須です", + "telemetry-proto-schema": "テレメトリprotoスキーマ", + "telemetry-proto-schema-required": "テレメトリprotoスキーマは必須です。", + "attributes-proto-schema": "属性protoスキーマ", + "attributes-proto-schema-required": "属性protoスキーマは必須です。", + "rpc-response-proto-schema": "RPCレスポンスprotoスキーマ", + "rpc-response-proto-schema-required": "RPCレスポンスprotoスキーマは必須です。", + "rpc-response-topic-filter": "RPCレスポンストピックフィルター", + "rpc-response-topic-filter-required": "RPCレスポンストピックフィルターは必須です。", + "rpc-request-proto-schema": "RPCリクエストprotoスキーマ", + "rpc-request-proto-schema-required": "RPCリクエストprotoスキーマは必須です。", + "rpc-request-proto-schema-hint": "RPCリクエストメッセージには常にフィールドが必要です: string method = 1; int32 requestId = 2; and params = 3 of any data type.", + "not-valid-pattern-topic-filter": "無効なトピックフィルターパターン", + "not-valid-single-character": "単一レベルのワイルドカード文字の使用が無効です", + "not-valid-multi-character": "マルチレベルのワイルドカード文字の使用が無効です", + "single-level-wildcards-hint": "[+] は任意のトピックフィルターレベルに適しています。例: v1/devices/+/telemetry または +/devices/+/attributes。", + "multi-level-wildcards-hint": "[#] はトピックフィルター自体を置き換えることができ、トピックの最後の記号である必要があります。例: # または v1/devices/me/#。", + "alarm-rules": "アラームルール", + "alarm-rules-with-count": "アラームルール ({{count}})", + "no-alarm-rules": "アラームルールが設定されていません", + "add-alarm-rule": "アラームルールを追加", + "edit-alarm-rule": "アラームルールを編集", + "alarm-type": "アラームタイプ", + "alarm-type-required": "アラームタイプは必須です。", + "alarm-type-unique": "アラームタイプは、デバイスプロファイルのアラームルール内で一意である必要があります。", + "alarm-type-max-length": "アラームタイプは256未満である必要があります", + "create-alarm-pattern": "{{alarmType}} アラームを作成", + "create-alarm-rules": "アラームルールを作成", + "no-create-alarm-rules": "作成条件が設定されていません", + "add-create-alarm-rule-prompt": "作成アラームルールを追加してください", + "clear-alarm-rule": "クリアアラームルール", + "no-clear-alarm-rule": "クリア条件が設定されていません", + "add-create-alarm-rule": "作成条件を追加", + "add-clear-alarm-rule": "クリア条件を追加", + "select-alarm-severity": "アラーム重大度を選択", + "alarm-severity-required": "アラーム重大度は必須です。", + "condition-duration": "条件期間", + "condition-duration-value": "期間値", + "condition-duration-time-unit": "時間単位", + "condition-duration-value-range": "期間値は1〜2147483647の範囲である必要があります。", + "condition-duration-value-pattern": "期間値は整数である必要があります。", + "condition-duration-value-required": "期間値は必須です。", + "condition-duration-time-unit-required": "時間単位は必須です。", + "advanced-settings": "高度な設定", + "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-rule-condition": "アラームルール条件", + "enter-alarm-rule-condition-prompt": "アラームルール条件を追加してください", + "edit-alarm-rule-condition": "アラームルール条件を編集", + "device-provisioning": "デバイスプロビジョニング", + "provision-strategy": "プロビジョニング戦略", + "provision-strategy-required": "プロビジョニング戦略は必須です。", + "provision-strategy-disabled": "無効", + "provision-strategy-created-new": "新しいデバイスの作成を許可", + "provision-strategy-check-pre-provisioned": "事前プロビジョニング済みデバイスを確認", + "provision-device-key": "プロビジョニングデバイスキー", + "provision-device-key-required": "プロビジョニングデバイスキーは必須です。", + "copy-provision-key": "プロビジョニングキーをコピー", + "provision-key-copied-message": "プロビジョニングキーがクリップボードにコピーされました", + "provision-device-secret": "プロビジョニングデバイスシークレット", + "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-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 {1 回} other {# 回} }", + "condition-repeat-times-dynamic": "\"{ attribute }\" を繰り返し ({ count, plural, =1 {1 回} other {# 回} })", + "schedule-type": "スケジューラータイプ", + "schedule-type-required": "スケジューラータイプは必須です。", + "schedule": "スケジュール", + "edit-schedule": "アラームスケジュールを編集", + "schedule-any-time": "常に有効", + "schedule-specific-time": "特定の時間に有効", + "schedule-custom": "カスタム", + "schedule-day": { + "monday": "月曜日", + "tuesday": "火曜日", + "wednesday": "水曜日", + "thursday": "木曜日", + "friday": "金曜日", + "saturday": "土曜日", + "sunday": "日曜日" + }, + "schedule-days": "曜日", + "schedule-time": "時刻", + "schedule-time-from": "開始", + "schedule-time-to": "終了", + "schedule-days-of-week-required": "少なくとも1つの曜日を選択してください。", + "create-device-profile": "新しいデバイスプロファイルを作成", + "import": "デバイスプロファイルをインポート", + "export": "デバイスプロファイルをエクスポート", + "export-failed-error": "デバイスプロファイルをエクスポートできません: {{error}}", + "device-profile-file": "デバイスプロファイルファイル", + "invalid-device-profile-file-error": "デバイスプロファイルをインポートできません: 無効なデバイスプロファイルデータ構造です。", + "power-saving-mode": "省電力モード", + "power-saving-mode-type": { + "default": "デバイスプロファイルの省電力モードを使用", + "psm": "省電力モード (PSM)", + "drx": "断続受信 (DRX)", + "edrx": "拡張断続受信 (eDRX)" + }, + "edrx-cycle": "eDRXサイクル", + "edrx-cycle-required": "eDRXサイクルは必須です。", + "edrx-cycle-pattern": "eDRXサイクルは正の整数である必要があります。", + "edrx-cycle-min": "eDRXサイクルの最小値は {{ min }} 秒です。", + "paging-transmission-window": "ページング送信ウィンドウ", + "paging-transmission-window-required": "ページング送信ウィンドウは必須です。", + "paging-transmission-window-pattern": "ページング送信ウィンドウは正の整数である必要があります。", + "paging-transmission-window-min": "ページング送信ウィンドウの最小値は {{ min }} 秒です。", + "psm-activity-timer": "PSMアクティビティタイマー", + "psm-activity-timer-required": "PSMアクティビティタイマーは必須です。", + "psm-activity-timer-pattern": "PSMアクティビティタイマーは正の整数である必要があります。", + "psm-activity-timer-min": "PSMアクティビティタイマーの最小値は {{ min }} 秒です。", + "lwm2m": { + "object-list": "オブジェクト一覧", + "object-list-empty": "オブジェクトが選択されていません。", + "no-objects-found": "オブジェクトが見つかりません。", + "no-objects-matching": "'{{object}}'に一致するオブジェクトが見つかりません。", + "model-tab": "LWM2Mモデル", + "add-new-instances": "新しいインスタンスを追加", + "instances-list": "インスタンス一覧", + "instances-list-required": "インスタンス一覧は必須です。", + "instance-id-pattern": "インスタンスIDは正の整数である必要があります。", + "instance-id-max": "インスタンスIDの最大値は {{max}} です。", + "instance": "インスタンス", + "resource-label": "#ID リソース名", + "observe-label": "監視", + "attribute-label": "属性", + "telemetry-label": "テレメトリ", + "edit-observe-select": "監視を編集するには、テレメトリまたは属性を選択してください", + "edit-attributes-select": "属性を編集するには、テレメトリまたは属性を選択してください", + "no-attributes-set": "属性が設定されていません", + "key-name": "キー名", + "key-name-required": "キー名は必須です", + "attribute-name": "名前属性", + "attribute-name-required": "名前属性は必須です。", + "attribute-value": "属性値", + "attribute-value-required": "属性値は必須です。", + "attribute-value-pattern": "属性値は正の整数である必要があります。", + "edit-attributes": "属性を編集: {{ name }}", + "view-attributes": "属性を表示: {{ name }}", + "add-attribute": "属性を追加", + "edit-attribute": "属性を編集", + "view-attribute": "属性を表示", + "remove-attribute": "属性を削除", + "delete-server-text": "注意: 確認後、サーバー設定は復元できなくなります。", + "delete-server-title": "サーバーを削除してもよろしいですか?", + "mode": "セキュリティ設定モード", + "bootstrap-tab": "ブートストラップ", + "bootstrap-server-legend": "ブートストラップサーバー (ShortId...)", + "lwm2m-server-legend": "LwM2Mサーバー (ShortId...)", + "server": "サーバー", + "short-id": "短縮サーバーID", + "short-id-tooltip": "サーバー短縮ID。サーバーのオブジェクトインスタンスを関連付けるリンクとして使用されます。\nこの識別子は、LwM2Mクライアントに設定された各LwM2Mサーバーを一意に識別します。\nブートストラップサーバーリソースの値が 'false' の場合、このリソースを設定する必要があります。\nID:0 および ID:65535 の値は、LwM2Mサーバーの識別に使用してはいけません。", + "short-id-tooltip-bootstrap": "サーバー短縮ID。サーバーのオブジェクトインスタンスを関連付けるリンクとして使用されます。\nこの識別子は、LwM2Mクライアントに設定された各LwM2Mサーバーを一意に識別します。\nブートストラップサーバーリソースの値が 'false' の場合、このリソースを設定する必要があります。", + "short-id-required": "短縮サーバーIDは必須です。", + "short-id-range": "短縮サーバーIDは {{ min }} から {{ max }} の範囲である必要があります。", + "short-id-pattern": "短縮サーバーIDは正の整数である必要があります。", + "short-id-pattern-bs": "ショートサーバー ID は null のみ指定できます", + "lifetime": "クライアント登録の有効期間", + "lifetime-required": "クライアント登録の有効期間は必須です。", + "lifetime-pattern": "クライアント登録の有効期間は正の整数である必要があります。", + "default-min-period": "2つの通知間の最小期間 (s)", + "default-min-period-tooltip": "このパラメータがObservationに含まれていない場合に、LwM2MクライアントがObservationのMinimum Periodとして使用すべきデフォルト値。", + "default-min-period-required": "最小期間は必須です。", + "default-min-period-pattern": "最小期間は正の整数である必要があります。", + "notification-storing": "無効またはオフライン時の通知保存", + "binding": "バインディング", + "binding-type": { + "u": "U: クライアントは常にUDPバインディングで到達可能です。", + "m": "M: クライアントは常にMQTTバインディングで到達可能です。", + "h": "H: クライアントは常にHTTPバインディングで到達可能です。", + "t": "T: クライアントは常にTCPバインディングで到達可能です。", + "s": "S: クライアントは常にSMSバインディングで到達可能です。", + "n": "N: クライアントは、そのようなリクエストへの応答をNon-IPバインディング経由で送信する必要があります (LWM2M 1.1 からサポート)。", + "uq": "UQ: キュー・モードのUDP接続 (LWM2M 1.1 以降ではサポートされません)", + "uqs": "UQS: UDPとSMSの両方の接続が有効; UDPはキュー・モード、SMSは標準モード (LWM2M 1.1 以降ではサポートされません)", + "tq": "TQ: キュー・モードのTCP接続 (LWM2M 1.1 以降ではサポートされません)", + "tqs": "TQS: TCPとSMSの両方の接続が有効; TCPはキュー・モード、SMSは標準モード (LWM2M 1.1 以降ではサポートされません)", + "sq": "SQ: キュー・モードのSMS接続 (LWM2M 1.1 以降ではサポートされません)" + }, + "binding-tooltip": "これはLwM2Mサーバーオブジェクト - /1/x/7 の\"binding\"リソースにある一覧です。\nLwM2Mクライアントでサポートされるバインディングモードを示します。\nこの値は、デバイスオブジェクト (/3/0/16) の “Supported Binding and Modes” リソースの値と同じであるべきです。\n複数のトランスポートがサポートされていても、トランスポートセッション全体で使用できるトランスポートバインディングは1つだけです。\n例えばUDPとSMSの両方がサポートされる場合、LwM2MクライアントとLwM2Mサーバーはトランスポートセッション全体でUDPまたはSMSのいずれかで通信することを選択できます。", + "bootstrap-server": "ブートストラップサーバー", + "lwm2m-server": "LwM2Mサーバー", + "include-bootstrap-server": "ブートストラップサーバーの更新を含める", + "bootstrap-update-title": "ブートストラップサーバーはすでに設定されています。更新を除外してもよろしいですか?", + "bootstrap-update-text": "注意: 確認後、ブートストラップサーバーの設定データは復元できなくなります。", + "server-host": "ホスト", + "server-host-required": "ホストは必須です。", + "server-port": "ポート", + "server-port-required": "ポートは必須です。", + "server-port-pattern": "ポートは正の整数である必要があります。", + "server-port-range": "ポートは1〜65535の範囲である必要があります。", + "server-public-key": "サーバー公開鍵", + "server-public-key-required": "サーバー公開鍵は必須です。", + "client-hold-off-time": "ホールドオフ時間", + "client-hold-off-time-required": "ホールドオフ時間は必須です。", + "client-hold-off-time-pattern": "ホールドオフ時間は正の整数である必要があります。", + "client-hold-off-time-tooltip": "ブートストラップサーバーでのみ使用するクライアントホールドオフ時間", + "account-after-timeout": "タイムアウト後のアカウント", + "account-after-timeout-required": "タイムアウト後のアカウントは必須です。", + "account-after-timeout-pattern": "タイムアウト後のアカウントは正の整数である必要があります。", + "account-after-timeout-tooltip": "このリソースで指定されたタイムアウト値後のブートストラップサーバーアカウント。", + "server-type": "サーバータイプ", + "add-new-server-title": "新しいサーバー設定を追加", + "add-server-config": "サーバー設定を追加", + "add-lwm2m-server-config": "LwM2Mサーバーを追加", + "no-config-servers": "サーバーが設定されていません", + "others-tab": "その他の設定", + "ota-update": "OTA更新", + "use-object-19-for-ota-update": "オブジェクト19をOTAファイルメタデータ(チェックサム、サイズ、バージョン、名前)に使用", + "use-object-19-for-ota-update-hint": "OTA更新に Resource ObjectId = 19 を使用: FirmWare → InstanceId = 65534, SoftWare → InstanceId = 65535。データ形式は Base64 でラップされた JSON です。この JSON には OTA ファイルメタデータ(ファイル情報)が含まれます: \"Checksum\" (SHA256)。追加フィールド: \"Title\" (OTA名), \"Version\" (OTAバージョン), \"File Name\" (クライアントでOTAを保存するためのファイル名), \"File Size\" (バイト単位のOTAサイズ)。", + "client-strategy": "接続時のクライアント戦略", + "client-strategy-label": "戦略", + "client-strategy-only-observe": "初回接続後、クライアントへのObserveリクエストのみ", + "client-strategy-read-all": "登録後、すべてのリソースを読み取り & クライアントへのObserveリクエスト", + "fw-update": "ファームウェア更新", + "fw-update-strategy": "ファームウェア更新戦略", + "fw-update-strategy-data": "オブジェクト19とリソース0 (Data) を使用して、バイナリファイルとしてファームウェア更新をプッシュ", + "fw-update-strategy-package": "オブジェクト5とリソース0 (Package) を使用して、バイナリファイルとしてファームウェア更新をプッシュ", + "fw-update-strategy-package-uri": "パッケージをダウンロードする一意のCoAP URLを自動生成し、オブジェクト5とリソース1 (Package URI) としてファームウェア更新をプッシュ", + "sw-update": "ソフトウェア更新", + "sw-update-strategy": "ソフトウェア更新戦略", + "sw-update-strategy-package": "オブジェクト9とリソース2 (Package) を使用してバイナリファイルをプッシュ", + "sw-update-strategy-package-uri": "パッケージをダウンロードする一意のCoAP URLを自動生成し、オブジェクト9とリソース3 (Package URI) を使用してソフトウェア更新をプッシュ", + "fw-update-resource": "ファームウェア更新CoAPリソース", + "fw-update-resource-required": "ファームウェア更新CoAPリソースは必須です。", + "sw-update-resource": "ソフトウェア更新CoAPリソース", + "sw-update-resource-required": "ソフトウェア更新CoAPリソースは必須です。", + "config-json-tab": "Json設定プロファイルデバイス", + "attributes-name": { + "min-period": "最小期間", + "max-period": "最大期間", + "greater-than": "より大きい", + "less-than": "より小さい", + "step": "ステップ", + "min-evaluation-period": "最小評価期間", + "max-evaluation-period": "最大評価期間" + }, + "default-object-id": "デフォルトオブジェクトバージョン(属性)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1", + "v1-2": "1.2" + }, + "observe-strategy": { + "observe-strategy": "観察戦略", + "single": "単一", + "single-description": "リソースごとに1回のObserveリクエスト(精度が高いが、ネットワークトラフィックが多くなる)", + "composite-all": "すべてのリソースを複合的に観察", + "composite-all-description": "すべてのリソースを1回の複合Observeリクエストで観察(効率的だが柔軟性に欠ける)", + "composite-by-object": "オブジェクトごとに複合的に観察", + "composite-by-object-description": "リソースをオブジェクトタイプごとにグループ化し、複数のComposite Observeリクエストで観察(バランスの取れたアプローチ)" + }, + "init-attr-tel-as-obs-strategy": "Observe 戦略を使用して属性とテレメトリを初期化", + "init-attr-tel-as-obs-strategy-hint": "false の場合 - 属性とテレメトリは値を 1 つずつ読み取って初期化されます。\\ntrue の場合 - Observe 戦略を使用して値をサブスクライブすることで、属性とテレメトリが初期化されます。" + }, + "snmp": { + "add-communication-config": "通信設定を追加", + "add-mapping": "マッピングを追加", + "authentication-passphrase": "認証パスフレーズ", + "authentication-passphrase-required": "認証パスフレーズは必須です。", + "authentication-protocol": "認証プロトコル", + "authentication-protocol-required": "認証プロトコルは必須です。", + "communication-configs": "通信設定", + "community": "コミュニティ文字列", + "community-required": "コミュニティ文字列は必須です。", + "context-name": "コンテキスト名", + "data-key": "データキー", + "data-key-required": "データキーは必須です。", + "data-type": "データ型", + "data-type-required": "データ型は必須です。", + "engine-id": "エンジンID", + "host": "ホスト", + "host-required": "ホストは必須です。", + "oid": "OID", + "oid-pattern": "無効なOID形式です。", + "oid-required": "OIDは必須です。", + "please-add-communication-config": "通信設定を追加してください", + "please-add-mapping-config": "マッピング設定を追加してください", + "port": "ポート", + "port-format": "無効なポート形式です。", + "port-required": "ポートは必須です。", + "privacy-passphrase": "プライバシーパスフレーズ", + "privacy-passphrase-required": "プライバシーパスフレーズは必須です。", + "privacy-protocol": "プライバシープロトコル", + "privacy-protocol-required": "プライバシープロトコルは必須です。", + "protocol-version": "プロトコルバージョン", + "protocol-version-required": "プロトコルバージョンは必須です。", + "querying-frequency": "ポーリング頻度, ms", + "querying-frequency-invalid-format": "ポーリング頻度は正の整数である必要があります。", + "querying-frequency-required": "ポーリング頻度は必須です。", + "retries": "再試行回数", + "retries-invalid-format": "再試行回数は正の整数である必要があります。", + "retries-required": "再試行回数は必須です。", + "scope": "スコープ", + "scope-required": "スコープは必須です。", + "security-name": "セキュリティ名", + "security-name-required": "セキュリティ名は必須です。", + "timeout-ms": "タイムアウト, ms", + "timeout-ms-invalid-format": "タイムアウトは正の整数である必要があります。", + "timeout-ms-required": "タイムアウトは必須です。", + "user-name": "ユーザー名", + "user-name-required": "ユーザー名は必須です。" + } + }, + "dialog": { + "close": "ダイアログを閉じる", + "error-message-title": "エラーメッセージ:", + "error-details-title": "エラー詳細" + }, + "direction": { + "column": "列", + "row": "行" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Edge インスタンス", + "instances": "インスタンス", + "edge-file": "Edge ファイル", + "name-max-length": "名前は256未満である必要があります", + "label-max-length": "ラベルは256未満である必要があります", + "type-max-length": "タイプは256未満である必要があります", + "management": "Edge 管理", + "no-edges-matching": "'{{entity}}' に一致する edges が見つかりませんでした。", + "add": "Edge を追加", + "no-edges-text": "Edges が見つかりませんでした", + "edge-details": "Edge 詳細", + "add-edge-text": "新しい Edge を追加", + "delete": "Edge を削除", + "delete-edge-title": "Edge '{{edgeName}}' を削除してもよろしいですか?", + "delete-edge-text": "注意: 確認後、Edge と関連するすべてのデータは復元できなくなります。", + "delete-edges-title": "{ count, plural, =1 {1 edge} other {# edges} } を削除してもよろしいですか?", + "delete-edges-text": "注意: 確認後、選択した edges はすべて削除され、関連するすべてのデータは復元できなくなります。", + "name": "名前", + "name-starts-with": "Edge 名が次で始まる", + "name-required": "名前は必須です。", + "description": "説明", + "details": "詳細", + "events": "イベント", + "copy-id": "Edge Id をコピー", + "id-copied-message": "Edge Id をクリップボードにコピーしました", + "sync": "Edge を同期", + "edge-required": "Edge は必須です", + "edge-type": "Edge タイプ", + "edge-type-required": "Edge タイプは必須です。", + "event-action": "イベントアクション", + "entity-id": "エンティティ ID", + "select-edge-type": "Edge タイプを選択", + "assign-to-customer": "顧客に割り当て", + "assign-to-customer-text": "Edge(s) を割り当てる顧客を選択してください", + "assign-edge-to-customer": "Edge(s) を顧客に割り当て", + "assign-edge-to-customer-text": "顧客に割り当てる edges を選択してください", + "assignedToCustomer": "顧客に割り当て", + "edge-public": "Edge は公開されています", + "assigned-to-customer": "割り当て先: {{customerTitle}}", + "unassign-from-customer": "顧客から割り当て解除", + "unassign-edge-title": "Edge '{{edgeName}}' の割り当てを解除してもよろしいですか?", + "unassign-edge-text": "確認後、edge の割り当ては解除され、顧客からアクセスできなくなります。", + "unassign-edges-title": "{ count, plural, =1 {1 edge} other {# edges} } の割り当てを解除してもよろしいですか?", + "unassign-edges-text": "確認後、選択した edges の割り当てはすべて解除され、顧客からアクセスできなくなります。", + "make-public": "Edge を公開する", + "make-public-edge-title": "Edge '{{edgeName}}' を公開してもよろしいですか?", + "make-public-edge-text": "確認後、edge とそのすべてのデータは公開され、他のユーザーがアクセスできるようになります。", + "make-private": "Edge を非公開にする", + "public": "公開", + "make-private-edge-title": "Edge '{{edgeName}}' を非公開にしてもよろしいですか?", + "make-private-edge-text": "確認後、edge とそのすべてのデータは非公開になり、他のユーザーはアクセスできなくなります。", + "import": "Edge をインポート", + "install-connect-instructions": "インストール & 接続手順", + "install-connect-instructions-edge-created": "Edge を作成しました! インストール & 接続手順を確認してください", + "loading-edge-instructions": "Edge 手順を読み込み中...", + "label": "ラベル", + "load-entity-error": "データの読み込みに失敗しました。エンティティは削除されました。", + "assign-new-edge": "新しい edge を割り当て", + "unassign-from-edge": "Edge から割り当て解除", + "edge-key": "Edge キー", + "copy-edge-key": "Edge キーをコピー", + "edge-key-copied-message": "Edge キーをクリップボードにコピーしました", + "edge-secret": "Edge シークレット", + "copy-edge-secret": "Edge シークレットをコピー", + "edge-secret-copied-message": "Edge シークレットをクリップボードにコピーしました", + "manage-assets": "アセットを管理", + "manage-devices": "デバイスを管理", + "manage-entity-views": "エンティティビューを管理", + "manage-dashboards": "ダッシュボードを管理", + "manage-rulechains": "ルールチェーンを管理", + "assets": "Edge アセット", + "devices": "Edge デバイス", + "entity-views": "Edge エンティティビュー", + "dashboard": "Edge ダッシュボード", + "dashboards": "Edge ダッシュボード", + "rulechain-templates": "ルールチェーンテンプレート", + "edge-rulechain-templates": "Edge ルールチェーンテンプレート", + "rulechains": "Edge ルールチェーン", + "search": "Edge を検索", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edges} } 選択済み", + "any-edge": "任意の edge", + "no-edge-types-matching": "'{{entitySubtype}}' に一致する edge タイプは見つかりませんでした。", + "edge-type-list-empty": "選択した edge タイプはありません。", + "edge-types": "Edge タイプ", + "enter-edge-type": "Edge タイプを入力", + "deployed": "展開済み", + "pending": "保留中", + "downlinks": "ダウンリンク", + "no-downlinks-prompt": "ダウンリンクは見つかりませんでした", + "sync-process-started-successfully": "同期プロセスが正常に開始されました!", + "missing-related-rule-chains-title": "Edge に関連するルールチェーンが不足しています", + "missing-related-rule-chains-text": "Edge に割り当てられたルールチェーンは、メッセージを他のルールチェーンに転送するルールノードを使用していますが、これらのルールチェーンはこの Edge に割り当てられていません。

    不足しているルールチェーンのリスト:
    {{missingRuleChains}}", + "widget-datasource-error": "このウィジェットは EDGE エンティティデータソースのみをサポートしています", + "upgrade-instructions": "アップグレード手順", + "connected": "接続済み", + "disconnected": "接続解除" + }, + "edge-event": { + "type-dashboard": "ダッシュボード", + "type-asset": "アセット", + "type-device": "デバイス", + "type-device-profile": "デバイスプロファイル", + "type-asset-profile": "アセットプロファイル", + "type-entity-view": "エンティティビュー", + "type-alarm": "アラーム", + "type-rule-chain": "ルールチェーン", + "type-rule-chain-metadata": "ルールチェーンメタデータ", + "type-edge": "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": "更新", + "action-type-post-attributes": "属性を送信", + "action-type-attributes-updated": "属性が更新されました", + "action-type-attributes-deleted": "属性が削除されました", + "action-type-timeseries-updated": "時系列データが更新されました", + "action-type-credentials-updated": "認証情報が更新されました", + "action-type-assigned-to-customer": "顧客に割り当て", + "action-type-unassigned-from-customer": "顧客から解除", + "action-type-relation-add-or-update": "リレーションの追加または更新", + "action-type-relation-deleted": "リレーションが削除されました", + "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": "Edgeに割り当て", + "action-type-unassigned-from-edge": "Edgeから解除", + "action-type-credentials-request": "認証情報リクエスト", + "action-type-entity-merge-request": "エンティティ統合リクエスト" + }, + "error": { + "unable-to-connect": "サーバーに接続できません!インターネット接続を確認してください。", + "unhandled-error-code": "処理されていないエラーコード: {{errorCode}}", + "unknown-error": "不明なエラー" + }, + "entity": { + "entity": "エンティティ", + "entities": "エンティティ", + "entities-count": "エンティティの数", + "alarms-count": "アラームの数", + "aliases": "エンティティのエイリアス", + "aliases-short": "エイリアス", + "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": "エンティティエイリアスを追加", + "edit-alias": "エンティティエイリアスを編集", + "entity-list": "エンティティリスト", + "entity-type": "エンティティタイプ", + "entity-types": "エンティティタイプ", + "entity-type-list": "エンティティタイプリスト", + "any-entity": "任意のエンティティ", + "add-entity-type": "エンティティタイプを追加", + "enter-entity-type": "エンティティタイプを入力", + "no-entities-matching": "エンティティ '{{entity}}' に一致するエンティティは見つかりませんでした。", + "no-entities-text": "エンティティは見つかりません", + "no-entity-types-matching": "エンティティタイプ '{{entityType}}' に一致するエンティティタイプは見つかりませんでした。", + "name-starts-with": "名前の表現", + "help-text": "必要に応じて '%' を使用します: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "フィルターを使用", + "entity-list-empty": "エンティティが選択されていません。", + "entity-type-list-required": "少なくとも1つのエンティティタイプを選択する必要があります。", + "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": "新規作成", + "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 {1 台のデバイス} other {# 台のデバイス} }", + "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 {1 つのアセットプロファイル} other {# 個のアセットプロファイル} }", + "asset-profile-name-starts-with": "名前が '{{prefix}}' で始まるアセットプロファイル", + "type-asset": "アセット", + "type-assets": "アセット", + "list-of-assets": "{ count, plural, =1 {1 つのアセット} other {# 個のアセット} }", + "asset-name-starts-with": "名前が '{{prefix}}' で始まるアセット", + "type-entity-view": "エンティティビュー", + "type-entity-views": "エンティティビュー", + "list-of-entity-views": "{ count, plural, =1 {1 つのエンティティビュー} other {# 個のエンティティビュー} }", + "entity-view-name-starts-with": "名前が '{{prefix}}' で始まるエンティティビュー", + "type-rule": "ルール", + "type-rules": "ルール", + "list-of-rules": "{ count, plural, =1 {1 つのルール} other {# 個のルール} }", + "rule-name-starts-with": "名前が '{{prefix}}' で始まるルール", + "type-plugin": "プラグイン", + "type-plugins": "プラグイン", + "list-of-plugins": "{ count, plural, =1 {1 つのプラグイン} other {# 個のプラグイン} }", + "plugin-name-starts-with": "名前が '{{prefix}}' で始まるプラグイン", + "type-tenant": "テナント", + "type-tenants": "テナント", + "list-of-tenants": "{ count, plural, =1 {1 つのテナント} other {# 個のテナント} }", + "tenant-name-starts-with": "名前が '{{prefix}}' で始まるテナント", + "type-tenant-profile": "テナントプロファイル", + "type-tenant-profiles": "テナントプロファイル", + "list-of-tenant-profiles": "{ count, plural, =1 {1 つのテナントプロファイル} other {# 個のテナントプロファイル} }", + "tenant-profile-name-starts-with": "名前が '{{prefix}}' で始まるテナントプロファイル", + "type-customer": "顧客", + "type-customers": "顧客", + "list-of-customers": "{ count, plural, =1 {1 人の顧客} other {# 人の顧客} }", + "customer-name-starts-with": "名前が '{{prefix}}' で始まる顧客", + "type-user": "ユーザー", + "type-users": "ユーザー", + "list-of-users": "{ count, plural, =1 {1 人のユーザー} other {# 人のユーザー} }", + "user-name-starts-with": "名前が '{{prefix}}' で始まるユーザー", + "type-dashboard": "ダッシュボード", + "type-dashboards": "ダッシュボード", + "list-of-dashboards": "{ count, plural, =1 {1 つのダッシュボード} other {# 個のダッシュボード} }", + "dashboard-name-starts-with": "名前が '{{prefix}}' で始まるダッシュボード", + "type-alarm": "アラーム", + "type-alarms": "アラーム", + "list-of-alarms": "{ count, plural, =1 {1 つのアラーム} other {# 個のアラーム} }", + "alarm-name-starts-with": "名前が '{{prefix}}' で始まるアラーム", + "type-rulechain": "ルールチェーン", + "type-rulechains": "ルールチェーン", + "list-of-rulechains": "{ count, plural, =1 {1 つのルールチェーン} other {# 個のルールチェーン} }", + "rulechain-name-starts-with": "名前が '{{prefix}}' で始まるルールチェーン", + "type-rulenode": "ルールノード", + "type-rulenodes": "ルールノード", + "list-of-rulenodes": "{ count, plural, =1 {1 つのルールノード} other {# 個のルールノード} }", + "rulenode-name-starts-with": "名前が '{{prefix}}' で始まるルールノード", + "type-api-key": "API キー", + "type-api-keys": "API キー", + "type-current-customer": "現在の顧客", + "type-current-tenant": "現在のテナント", + "type-current-user": "現在のユーザー", + "type-current-user-owner": "現在のユーザーの所有者", + "type-calculated-field": "計算フィールド", + "type-calculated-fields": "計算フィールド", + "type-ai-model": "AIモデル", + "type-ai-models": "AIモデル", + "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 {1 件のエンティティ} other {# 件のエンティティ} } を選択", + "entity-name": "エンティティ名", + "entity-label": "エンティティラベル", + "details": "エンティティ詳細", + "no-entities-prompt": "エンティティが見つかりません", + "no-data": "表示するデータがありません", + "show-all-columns": "すべて表示", + "columns-to-display": "表示する列", + "type-api-usage-state": "API使用状況", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {edge 1 件} other {edge一覧 # 件} }", + "edge-name-starts-with": "名前が '{{prefix}}' で始まるEdges", + "version-conflict": { + "message": "既存のバージョンを上書きしますか、それとも変更を破棄して最新バージョンを読み込みますか?", + "link": "これを使用して {{entityType}} のあなたのバージョンをダウンロードできます", + "overwrite": "バージョンを上書き", + "discard": "変更を破棄" + }, + "type-tb-resource": "リソース", + "type-tb-resources": "リソース", + "list-of-tb-resources": "{ count, plural, =1 {リソース 1 件} other {リソース一覧 # 件} }", + "type-ota-package": "OTAパッケージ", + "type-ota-packages": "OTAパッケージ", + "list-of-ota-packages": "{ count, plural, =1 {OTAパッケージ 1 件} other {OTAパッケージ一覧 # 件} }", + "type-rpc": "RPC", + "type-queue": "キュー", + "type-queue-stats": "キュー統計", + "type-queues-stats": "キュー統計", + "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 {通知テンプレート一覧 # 件} }", + "link": "リンク", + "type-oauth2-client": "OAuth 2.0 クライアント", + "type-oauth2-clients": "OAuth 2.0 クライアント", + "list-of-oauth2-clients": "{ count, plural, =1 {OAuth 2.0 クライアント 1 件} other {OAuth 2.0 クライアント一覧 # 件} }", + "type-domain": "ドメイン", + "type-domains": "ドメイン", + "list-of-domains": "{ count, plural, =1 {ドメイン 1 件} other {ドメイン一覧 # 件} }", + "type-mobile-app": "モバイルアプリケーション", + "type-mobile-apps": "モバイルアプリケーション", + "list-of-mobile-apps": "{ count, plural, =1 {モバイルアプリケーション 1 件} other {モバイルアプリケーション一覧 # 件} }", + "type-mobile-app-bundle": "モバイルバンドル", + "type-mobile-app-bundles": "モバイルバンドル", + "list-of-mobile-app-bundles": "{ count, plural, =1 {モバイルバンドル 1 件} other {モバイルバンドル一覧 # 件} }", + "limit-reached": "上限に達しました", + "limit-reached-text": "{{ entities }} の上限に達しました。さらに追加するには、システム管理者に {{ entity }} の上限引き上げを依頼してください。", + "request-limit-increase": "上限引き上げを依頼", + "request-sysadmin-text": "システム管理者ですか?", + "login-here": "ここでログイン", + "to-increase-limit": "上限を引き上げるには。", + "increase-limit-request-sent-title": "上限を引き上げるための自動リクエストをシステム管理者に送信しました", + "increase-limit-request-sent-text": "リクエストの確認と設定更新まで少し時間がかかる場合があります。変更を確認するには、このページを更新する必要がある場合があります。" + }, + "entity-field": { + "created-time": "作成日時", + "name": "名前", + "type": "タイプ", + "first-name": "名", + "last-name": "姓", + "email": "Email", + "title": "タイトル", + "country": "国", + "state": "都道府県", + "city": "市区町村", + "address": "住所", + "address2": "住所 2", + "zip": "郵便番号", + "phone": "電話番号", + "label": "ラベル", + "queue-name": "キュー名", + "service-id": "サービスID", + "owner-name": "所有者名", + "owner-type": "所有者タイプ" + }, + "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}}' に一致するエンティティビューが見つかりませんでした。", + "public": "公開", + "alias": "エイリアス", + "alias-required": "エンティティビューのエイリアスは必須です。", + "remove-alias": "エンティティビューのエイリアスを削除", + "add-alias": "エンティティビューのエイリアスを追加", + "name-starts-with": "エンティティビュー名の式", + "help-text": "必要に応じて '%' を使用してください:'%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_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": "エンティティビューを追加", + "entity-view-public": "エンティティビューは公開されています", + "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 件のエンティティビュー} other {# 件のエンティティビュー} } を顧客に割り当て", + "delete-entity-views": "エンティティビューを削除", + "make-public": "エンティティビューを公開", + "make-private": "エンティティビューを非公開", + "unassign-from-customer": "顧客から削除", + "unassign-entity-views": "エンティティビューを削除", + "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 {1 件のエンティティビュー} other {# 件のエンティティビュー} } を削除してもよろしいですか?", + "delete-entity-views-action-title": "{ count, plural, =1 {1 件のエンティティビュー} other {# 件のエンティティビュー} } を削除", + "delete-entity-views-text": "確認後、選択したエンティティビューは削除され、関連データは復元できなくなります。", + "make-public-entity-view-title": "エンティティビュー '{{entityViewName}}' を公開してもよろしいですか?", + "make-public-entity-view-text": "確認後、エンティティビューとそのデータは公開され、他のユーザーがアクセスできるようになります。", + "make-private-entity-view-title": "エンティティビュー '{{entityViewName}}' を非公開にしてもよろしいですか?", + "make-private-entity-view-text": "確認後、エンティティビューとそのデータは非公開になり、他のユーザーはアクセスできなくなります。", + "unassign-entity-view-title": "エンティティビュー '{{entityViewName}}' を削除してもよろしいですか?", + "unassign-entity-view-text": "確認後、エンティティビューは削除され、顧客はアクセスできなくなります。", + "unassign-entity-view": "エンティティビューを削除", + "unassign-entity-views-title": "{ count, plural, =1 {1 件のエンティティビュー} 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": "エンティティビュータイプ", + "created-time": "作成日時", + "name": "名前", + "name-required": "名前は必須です。", + "name-max-length": "名前は 256 文字以内である必要があります。", + "type-max-length": "エンティティビュータイプは 256 文字以内である必要があります。", + "description": "説明", + "events": "イベント", + "details": "詳細", + "copyId": "エンティティビューIDをコピー", + "idCopiedMessage": "エンティティビューIDがクリップボードにコピーされました", + "assignedToCustomer": "顧客に割り当て済み", + "unable-entity-view-device-alias-title": "エンティティビューエイリアスを削除できません", + "unable-entity-view-device-alias-text": "デバイスエイリアス '{{entityViewAlias}}' は以下のウィジェットで使用されているため削除できません:
    {{widgetsList}}", + "select-entity-view": "エンティティビューを選択", + "start-ts": "開始日時", + "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": "エンティティビューは、ターゲットエンティティから指定された属性を保存または更新するたびに自動的にコピーします。パフォーマンス上の理由で、ターゲットエンティティの属性は属性変更ごとにエンティティビューに伝播されません。「ビューへのコピー」ルールノードをルールチェーンに設定し、「属性更新」メッセージを新しいルールノードにリンクすることで自動的な伝播を有効にできます。", + "timeseries-data": "時系列データ", + "timeseries-data-hint": "ターゲットエンティティの時系列データキーを設定し、これらのデータはエンティティビューで読み取り専用としてアクセスされます。", + "search": "エンティティビューを検索", + "selected-entity-views": "{ count, plural, =1 {1 件のエンティティビュー} other {# 件のエンティティビュー} } を選択", + "assign-entity-view-to-edge": "エンティティビューをEdgeに割り当て", + "assign-entity-view-to-edge-text": "Edgeに割り当てるエンティティビューを選択してください", + "unassign-entity-view-from-edge-title": "エンティティビュー '{{entityViewName}}' をEdgeから削除してもよろしいですか?", + "unassign-entity-view-from-edge-text": "確認後、エンティティビューはEdgeから削除され、Edgeではアクセスできなくなります。", + "unassign-entity-views-from-edge-action-title": "Edgeから { count, plural, =1 {1 件のエンティティビュー} other {# 件のエンティティビュー} } を削除", + "unassign-entity-view-from-edge": "エンティティビューをEdgeから削除", + "unassign-entity-views-from-edge-title": "Edgeから { count, plural, =1 {1 件のエンティティビュー} other {# 件のエンティティビュー} } を削除してもよろしいですか?", + "unassign-entity-views-from-edge-text": "確認後、選択したすべてのエンティティビューはEdgeから削除され、Edgeではアクセスできなくなります。" + }, + "event": { + "event-type": "イベントタイプ", + "events-filter": "イベントフィルター", + "clean-events": "イベントをクリア", + "type-error": "エラー", + "type-lc-event": "ライフサイクルイベント", + "type-stats": "統計", + "type-debug-rule-node": "デバッグ", + "type-debug-rule-chain": "デバッグ", + "type-debug-calculated-field": "デバッグ", + "arguments": "引数", + "result": "結果", + "no-events-prompt": "イベントが見つかりません", + "error": "エラー", + "alarm": "アラーム", + "event-time": "イベント時間", + "server": "サーバー", + "body": "ボディ", + "method": "メソッド", + "type": "タイプ", + "metadata": "メタデータ", + "message": "メッセージ", + "message-id": "メッセージID", + "copy-message-id": "メッセージIDをコピー", + "message-type": "メッセージタイプ", + "data-type": "データタイプ", + "relation-type": "関連タイプ", + "data": "データ", + "event": "イベント", + "status": "ステータス", + "success": "成功", + "failed": "失敗", + "messages-processed": "処理されたメッセージ", + "max-messages-processed": "処理された最大メッセージ数", + "min-messages-processed": "処理された最小メッセージ数", + "errors-occurred": "発生したエラー", + "max-errors-occurred": "発生した最大エラー数", + "min-errors-occurred": "発生した最小エラー数", + "min-value": "最小値は0です。", "all-events": "すべて", - "entity-type": "エンティティタイプ" - }, - "extension": { - "extensions": "拡張機能", - "selected-extensions": "{ count, plural, =1 {1 extension} other {# extensions} }選択された", - "type": "タイプ", - "key": "キー", - "value": "値", - "id": "ID", - "extension-id": "内線番号", - "extension-type": "拡張タイプ", - "transformer-json": "JSON *", - "unique-id-required": "現在の拡張IDは既に存在します。", - "delete": "拡張子を削除", - "add": "内線番号を追加", - "edit": "拡張機能を編集する", - "delete-extension-title": "'{{extensionId}}'?", - "delete-extension-text": "確認後、拡張子と関連するすべてのデータが回復不能になることに注意してください。", - "delete-extensions-title": "{ count, plural, =1 {1 extension} other {# extensions} }?", - "delete-extensions-text": "注意してください。確認後、選択したすべての内線番号が削除されます。", - "converters": "コンバーター", - "converter-id": "コンバーターID", - "configuration": "構成", - "converter-configurations": "コンバータ構成", - "token": "セキュリティトークン", - "add-converter": "コンバータを追加する", - "add-config": "コンバータ設定を追加する", - "device-name-expression": "デバイス名式", - "device-type-expression": "デバイスタイプの式", - "custom": "カスタム", - "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": "要求ID式", - "request-id-json-expression": "リクエストID json式", - "request-id-topic-expression": "リクエストIDトピック表現", - "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-device-name-pattern": "デバイス名パターン", - "modbus-server": "サーバー/スレーブ", - "modbus-add-server": "サーバー/スレーブを追加する", - "modbus-add-server-prompt": "サーバー/スレーブを追加してください", - "modbus-transport": "輸送", - "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": "ユニットID", - "modbus-unit-id-range": "ユニットIDは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-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 item} other {# items} }?", - "delete-items-action-title": "{ count, plural, =1 {1 item} other {# items} }", - "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ファイルをドロップするか、アップロードするファイルをクリックして選択します。" - }, - "item": { - "selected": "選択された" - }, - "js-func": { - "no-return-error": "関数は値を返す必要があります!", - "return-type-mismatch": "'{{type}}'タイプ!", - "tidy": "きちんとした" - }, - "key-val": { - "key": "キー", - "value": "値", - "remove-entry": "エントリを削除", - "add-entry": "エントリを追加", - "no-data": "エントリなし" - }, - "layout": { - "layout": "レイアウト", - "manage": "レイアウトの管理", - "settings": "レイアウト設定", - "color": "色", - "main": "メイン", - "right": "右", - "select": "ターゲットレイアウトを選択" - }, - "legend": { - "position": "凡例の位置", - "show-max": "最大値を表示", - "show-min": "最小値を表示", - "show-avg": "平均値を表示", - "show-total": "合計値を表示", - "settings": "凡例の設定", - "min": "最小", - "max": "最大", - "avg": "平均", - "total": "合計" - }, - "login": { - "login": "ログイン", - "request-password-reset": "リクエストパスワードのリセット", - "reset-password": "パスワードを再設定する", - "create-password": "パスワードの作成", - "passwords-mismatch-error": "入力されたパスワードは同じでなければなりません!", - "password-again": "パスワードをもう一度", - "sign-in": "サインインしてください", - "username": "ユーザー名(電子メール)", - "remember-me": "ログイン情報を記憶", - "forgot-password": "パスワードをお忘れですか?", - "password-reset": "パスワードのリセット", - "new-password": "新しいパスワード", - "new-password-again": "新しいパスワードを再入力", - "password-link-sent-message": "パスワードリセットリンクが正常に送信されました!", - "email": "Eメール", - "login-with": "{{name}}でログイン", - "or": "または" - }, - "position": { - "top": "上", - "bottom": "下", - "left": "左", - "right": "右" - }, - "profile": { - "profile": "プロフィール", - "change-password": "パスワードを変更する", - "current-password": "現在のパスワード" - }, - "relation": { - "relations": "関係", - "direction": "方向", - "search-direction": { - "FROM": "から", - "TO": "に" - }, - "direction-type": { - "FROM": "から", - "TO": "に" - }, - "from-relations": "アウトバウンド関係", - "to-relations": "インバウンド関係", - "selected-relations": "{ count, plural, =1 {1 relation} other {# relations} }選択された", - "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 relation} other {# relations} }?", - "delete-to-relations-text": "注意してください。確認後、選択されたリレーションはすべて削除され、対応するエンティティは現在のエンティティとは無関係になります。", - "delete-from-relation-title": "'{{entityName}}'?", - "delete-from-relation-text": "'{{entityName}}'.", - "delete-from-relations-title": "{ count, plural, =1 {1 relation} other {# relations} }?", - "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 rule chain} other {# rule chains} }?", - "delete-rulechains-action-title": "{ count, plural, =1 {1 rule chain} other {# rule chains} }", - "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": "ルール管理", - "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": "助けて" - }, - "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 tenant} other {# tenants} }?", - "delete-tenants-action-title": "{ count, plural, =1 {1 tenant} other {# tenants} }", - "delete-tenants-text": "注意してください。確認後、選択されたすべてのテナントが削除され、関連するすべてのデータは回復不能になります。", - "title": "タイトル", - "title-required": "タイトルは必須です。", - "description": "説明", - "details": "詳細", - "events": "イベント", - "copyId": "テナントIDをコピーする", - "idCopiedMessage": "テナントIDがクリップボードにコピーされました", - "select-tenant": "テナントを選択", - "no-tenants-matching": "'{{entity}}'発見されました。", - "tenant-required": "テナントが必要です" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 second} other {# seconds} }", - "minutes-interval": "{ minutes, plural, =1 {1 minute} other {# minutes} }", - "hours-interval": "{ hours, plural, =1 {1 hour} other {# hours} }", - "days-interval": "{ days, plural, =1 {1 day} other {# days} }", - "days": "日", - "hours": "時", - "minutes": "分", - "seconds": "秒", - "advanced": "カスタム" - }, - "timewindow": { - "days": "{ days, plural, =1 { day } other {# days } }", - "hours": "{ hours, plural, =0 { hour } =1 {1 hour } other {# hours } }", - "minutes": "{ minutes, plural, =0 { minute } =1 {1 minute } other {# minutes } }", - "seconds": "{ seconds, plural, =0 { second } =1 {1 second } other {# seconds } }", - "realtime": "リアルタイム", - "history": "履歴", - "last-prefix": "直近", - "period": "{{ startTime }}{{ endTime }}", - "edit": "タイムウィンドウを編集", - "date-range": "期間", - "last": "直近", - "time-period": "期間" - }, - "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 user} other {# users} }?", - "delete-users-action-title": "{ count, plural, =1 {1 user} other {# users} }", - "delete-users-text": "注意してください。確認後、選択したすべてのユーザーが削除され、関連するすべてのデータは回復不能になります。", - "activation-email-sent-message": "アクティベーション電子メールが正常に送信されました!", - "resend-activation": "アクティブ化を再送", - "email": "Eメール", - "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": "activation link :", - "copy-activation-link": "アクティブ化リンクをコピーする", - "activation-link-copied-message": "ユーザーのアクティベーションリンクがクリップボードにコピーされました", - "details": "詳細" - }, - "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": "ウィジェットの設定を読み込む際に問題が発生しました。
    おそらく関連付けられているウィジェットのタイプが削除されています。", - "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", - "add-widget-type": "新しいウィジェットタイプを追加する", - "widget-template-load-failed-error": "ウィジェットテンプレートを読み込めませんでした!", - "add": "ウィジェットを追加", - "undo": "ウィジェットの変更を元に戻す", - "export": "ウィジェットの書き出し" - }, - "widget-action": { - "header-button": "ウィジェットのヘッダーボタン", - "open-dashboard-state": "新しいダッシュボードの状態に移動する", - "update-dashboard-state": "現在のダッシュボードの状態を更新する", - "open-dashboard": "他のダッシュボードに移動する", - "custom": "カスタムアクション", - "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 widgets bundle} other {# widgets bundles} }?", - "delete-widgets-bundles-action-title": "{ count, plural, =1 {1 widgets bundle} other {# widgets bundles} }", - "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": "ウィジェットをインポートできません。bundle:データ構造が無効です。" - }, - "widget-config": { - "data": "データ", - "settings": "設定", - "advanced": "カスタム", - "title": "タイトル", - "general-settings": "一般設定", - "display-title": "タイトルを表示", - "drop-shadow": "影を落とす", - "enable-fullscreen": "フルスクリーンを有効にする", - "background-color": "背景色", - "text-color": "テキストの色", - "padding": "パディング", - "margin": "マージン", - "widget-style": "ウィジェットスタイル", - "title-style": "タイトルスタイル", - "mobile-mode-settings": "モバイルモードの設定", - "order": "順番", - "height": "高さ", - "units": "単位", - "decimals": "小数点以下の桁数", - "timewindow": "タイムウィンドウ", - "use-dashboard-timewindow": "ダッシュボードのタイムウィンドウを使用する", - "display-legend": "凡例を表示", - "datasources": "データソース", - "maximum-datasources": "{ count, plural, =1 {1 datasource is allowed.} other {# datasources are allowed} }", - "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": "同じ名前の別のアクションがすでに存在します。\nアクション名は、同じアクションソース内で一意である必要があります。", - "action-icon": "アイコン", - "action-type": "タイプ", - "action-type-required": "アクションタイプが必要です。", - "edit-action": "アクションの編集", - "delete-action": "アクションの削除", - "delete-action-title": "ウィジェットアクションを削除する", - "delete-action-text": "'{{actionName}}'?" - }, - "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": "1月", - "Feb": "2月", - "Mar": "3月", - "Apr": "4月", - "May": "5月", - "Jun": "6月", - "Jul": "7月", - "Aug": "8月", - "Sep": "9月", - "Oct": "10月", - "Nov": "11月", - "Dec": "12月", - "January": "1月", - "February": "2月", - "March": "3月", - "April": "4月", - "June": "6月", - "July": "7月", - "August": "8月", - "September": "9月", - "October": "10月", - "November": "11月", - "December": "12月", - "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" - } - } - }, - "icon": { - "icon": "アイコン", - "select-icon": "選択アイコン", - "material-icons": "マテリアルアイコン", - "show-all": "すべてのアイコンを表示する" - }, - "custom": { - "widget-action": { - "action-cell-button": "アクションセルボタン", - "row-click": "行のクリック", - "polygon-click": "ポリゴンクリック", - "marker-click": "マーカークリック", - "tooltip-tag-action": "ツールチップのタグアクション" - } - }, - "language": { - "language": "言語" - } -} + "has-error": "エラーが発生しています", + "entity-id": "エンティティID", + "copy-entity-id": "エンティティIDをコピー", + "entity-type": "エンティティタイプ", + "clear-filter": "フィルターをクリア", + "clear-request-title": "すべてのイベントをクリア", + "clear-request-text": "すべてのイベントをクリアしてもよろしいですか?", + "started": "開始", + "updated": "更新", + "stopped": "停止" + }, + "extension": { + "extensions": "拡張機能", + "selected-extensions": "{ count, plural, =1 {1 件の拡張機能} other {# 件の拡張機能} } が選択されました", + "type": "タイプ", + "key": "キー", + "value": "値", + "id": "ID", + "extension-id": "拡張機能ID", + "extension-type": "拡張機能タイプ", + "transformer-json": "JSON *", + "unique-id-required": "現在の拡張機能IDはすでに存在します。", + "delete": "拡張機能を削除", + "add": "拡張機能を追加", + "edit": "拡張機能を編集", + "delete-extension-title": "拡張機能 '{{extensionId}}' を削除してもよろしいですか?", + "delete-extension-text": "確認後、拡張機能とその関連データは復元できなくなりますのでご注意ください。", + "delete-extensions-title": "{ count, plural, =1 {1 件の拡張機能} other {# 件の拡張機能} } を削除してもよろしいですか?", + "delete-extensions-text": "確認後、選択したすべての拡張機能が削除されますのでご注意ください。", + "converters": "コンバーター", + "converter-id": "コンバーターID", + "configuration": "設定", + "converter-configurations": "コンバーター設定", + "token": "セキュリティトークン", + "add-converter": "コンバーターを追加", + "add-config": "コンバーター設定を追加", + "device-name-expression": "デバイス名式", + "device-type-expression": "デバイスタイプ式", + "custom": "カスタム", + "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": "リクエストID式", + "request-id-json-expression": "リクエストIDJSON式", + "request-id-topic-expression": "リクエストIDトピック式", + "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-device-name-pattern": "デバイス名パターン", + "modbus-server": "サーバー/スレーブ", + "modbus-add-server": "サーバー/スレーブを追加", + "modbus-add-server-prompt": "サーバー/スレーブを追加してください", + "modbus-transport": "トランスポート", + "modbus-tcp-reconnect": "自動再接続", + "modbus-rtu-over-tcp": "RTU over TCP", + "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": "ユニットID", + "modbus-unit-id-range": "ユニットIDは1から247の範囲である必要があります。", + "modbus-device-name": "デバイス名", + "modbus-poll-period": "ポーリング周期(ms)", + "modbus-attributes-poll-period": "属性ポーリング周期(ms)", + "modbus-timeseries-poll-period": "時系列ポーリング周期(ms)", + "modbus-poll-period-range": "ポーリング周期は正の値である必要があります。", + "modbus-tag": "タグ", + "modbus-function": "ファンクション", + "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": "無効な拡張機能ファイル" + }, + "feature": { + "advanced-features": "高度な機能" + }, + "filter": { + "add": "フィルターを追加", + "edit": "フィルターを編集", + "name": "フィルター名", + "name-required": "フィルター名は必須です。", + "duplicate-filter": "同じ名前のフィルターがすでに存在します。", + "filters": "フィルター", + "unable-delete-filter-title": "フィルターを削除できません", + "unable-delete-filter-text": "フィルター '{{filter}}' は、以下のウィジェットで使用されているため削除できません:
    {{widgetsList}}", + "duplicate-filter-error": "重複するフィルター '{{filter}}' が見つかりました。
    フィルターはダッシュボード内で一意である必要があります。", + "missing-key-filters-error": "フィルター '{{filter}}' にキー フィルターがありません。", + "filter": "フィルター", + "editable": "編集可能", + "editable-hint": "ユーザーがダッシュボードでフィルターの値を変更できるようにします。", + "no-filters-found": "フィルターが見つかりません。", + "no-filter-text": "フィルターが指定されていません", + "add-filter-prompt": "フィルターを追加してください", + "no-filter-matching": "'{{filter}}' は見つかりません。", + "create-new-filter": "新しいフィルターを作成!", + "create-new": "新規作成", + "filter-required": "フィルターは必須です。", + "operation": { + "operation": "操作", + "equal": "等しい", + "not-equal": "等しくない", + "starts-with": "で始まる", + "ends-with": "で終わる", + "contains": "を含む", + "not-contains": "を含まない", + "greater": "より大きい", + "less": "より小さい", + "greater-or-equal": "以上", + "less-or-equal": "以下", + "and": "かつ", + "or": "または", + "in": "の中に", + "not-in": "の中にない" + }, + "ignore-case": "大文字小文字を区別しない", + "value": "値", + "remove-filter": "フィルターを削除", + "duplicate-filter-action": "フィルターを重複", + "preview": "フィルタープレビュー", + "no-filters": "フィルターが設定されていません", + "add-filter": "フィルターを追加", + "add-complex-filter": "複雑なフィルターを追加", + "add-complex": "複雑なフィルターを追加", + "complex-filter": "複雑なフィルター", + "edit-complex-filter": "複雑なフィルターを編集", + "edit-filter-user-params": "フィルタープレディケートのユーザーパラメータを編集", + "filter-user-params": "フィルタープレディケートのユーザーパラメータ", + "user-parameters": "ユーザーパラメータ", + "display-label": "表示ラベル", + "custom-label": "カスタムラベル", + "custom-label-hint": "フィルターに独自のラベルを設定できるようにします。無効にすると、ラベルが自動的に生成されます。", + "order-priority": "表示順序", + "key-filter": "キー フィルター", + "key-filters": "キー フィルター", + "key-name": "キー名", + "key-name-required": "キー名は必須です。", + "key-type": { + "key-type": "キータイプ", + "attribute": "属性", + "timeseries": "時系列", + "entity-field": "エンティティフィールド", + "constant": "定数", + "client-attribute": "クライアント属性", + "server-attribute": "サーバー属性", + "shared-attribute": "共有属性" + }, + "value-type": { + "value-type": "値のタイプ", + "string": "文字列", + "numeric": "数値", + "boolean": "ブール値", + "date-time": "日時" + }, + "value-type-required": "キーの値タイプは必須です。", + "key-value-type-change-title": "キーの値タイプを変更してもよろしいですか?", + "key-value-type-change-message": "新しい値タイプを確認すると、入力したすべてのキー フィルターが削除されます。", + "no-key-filters": "キー フィルターは設定されていません", + "add-key-filter": "キー フィルターを追加", + "remove-key-filter": "キー フィルターを削除", + "edit-key-filter": "キー フィルターを編集", + "date": "日付", + "time": "時間", + "current-tenant": "現在のテナント", + "current-customer": "現在の顧客", + "current-user": "現在のユーザー", + "current-device": "現在のデバイス", + "default-value": "デフォルト値", + "default-comma-separated-values": "デフォルトのカンマ区切り値", + "dynamic-source-type": "動的ソースタイプ", + "dynamic-value": "動的値", + "no-dynamic-value": "動的値なし", + "source-attribute": "ソース属性", + "switch-to-dynamic-value": "動的値に切り替え", + "switch-to-default-value": "デフォルト値に切り替え", + "inherit-owner": "所有者から継承", + "source-attribute-not-set": "ソース属性が設定されていない場合", + "unit": "単位" + }, + "fullscreen": { + "expand": "全画面表示に拡大", + "exit": "全画面表示を終了", + "toggle": "全画面モードの切り替え", + "fullscreen": "全画面表示" + }, + "function": { + "function": "関数" + }, + "gateway": { + "gateway-name": "Gateway名", + "gateway-name-required": "Gateway名は必須です。", + "gateways": "Gateways", + "create-new-gateway": "新しいGatewayを作成", + "create-new-gateway-text": "名前 '{{gatewayName}}' で新しいGatewayを作成してもよろしいですか?", + "launch-command": "コマンドを実行", + "no-gateway-found": "Gatewayは見つかりませんでした。", + "no-gateway-matching": " '{{item}}' は見つかりませんでした。" + }, + "grid": { + "delete-item-title": "このアイテムを削除してもよろしいですか?", + "delete-item-text": "確認後、このアイテムと関連するすべてのデータは復元できなくなりますのでご注意ください。", + "delete-items-title": "{ count, plural, =1 {1 件のアイテム} other {# 件のアイテム} } を削除してもよろしいですか?", + "delete-items-action-title": "{ count, plural, =1 {1 件のアイテム} other {# 件のアイテム} } を削除", + "delete-items-text": "確認後、選択したすべてのアイテムが削除され、関連するすべてのデータは復元できなくなります。", + "add-item-text": "新しいアイテムを追加", + "no-items-text": "アイテムが見つかりません", + "item-details": "アイテム詳細", + "delete-item": "アイテムを削除", + "delete-items": "アイテムを削除", + "scroll-to-top": "トップにスクロール" + }, + "help": { + "goto-help-page": "ヘルプページへ移動", + "show-help": "ヘルプを表示" + }, + "home": { + "home": "ホーム", + "profile": "プロフィール", + "logout": "ログアウト", + "menu": "メニュー", + "avatar": "アバター", + "open-user-menu": "ユーザーメニューを開く" + }, + "file-input": { + "browse-file": "ファイルを選択", + "browse-files": "ファイルを選択" + }, + "image": { + "gallery": "画像ギャラリー", + "search": "画像を検索", + "selected-images": "{ count, plural, =1 {1 枚の画像} other {# 枚の画像} } が選択されました", + "created-time": "作成日時", + "name": "名前", + "name-required": "名前は必須です。", + "resolution": "解像度", + "size": "サイズ", + "system": "システム", + "download-image": "画像をダウンロード", + "export-image": "画像をJSONとしてエクスポート", + "import-image": "画像をJSONからインポート", + "upload-image": "画像をアップロード", + "edit-image": "画像を編集", + "image-details": "画像の詳細", + "no-images": "画像が見つかりません", + "delete-image": "画像を削除", + "delete-image-title": "画像 '{{imageTitle}}' を削除してもよろしいですか?", + "delete-image-text": "確認後、この画像は復元できなくなりますのでご注意ください。", + "delete-images-title": "{ count, plural, =1 {1 枚の画像} other {# 枚の画像} } を削除してもよろしいですか?", + "delete-images-text": "確認後、選択したすべての画像が削除され、関連するデータも復元できなくなりますのでご注意ください。", + "list-mode": "リスト表示", + "grid-mode": "グリッド表示", + "image-preview": "画像プレビュー", + "update-image": "画像を更新", + "export-failed-error": "画像をエクスポートできませんでした: {{error}}", + "image-json-file": "画像JSONファイル", + "invalid-image-json-file-error": "画像をJSONからインポートできませんでした: 無効な画像JSONデータ構造です。", + "image-is-in-use": "画像は他のエンティティで使用されています", + "images-are-in-use": "画像は他のエンティティで使用されています", + "image-is-in-use-text": "画像 '{{title}}' は以下のエンティティで使用されているため削除されませんでした。", + "images-are-in-use-text": "すべての画像が削除されなかった理由は、他のエンティティで使用されているためです。
    参照されているエンティティは、対応する画像行の 参照 ボタンをクリックすることで表示できます。
    それでもこれらの画像を削除する場合は、下のテーブルで画像を選択し、選択したものを削除 ボタンをクリックしてください。", + "delete-image-in-use-text": "それでも画像を削除する場合は、とにかく削除 ボタンをクリックしてください。", + "system-entities": "システムエンティティ:", + "entities": "エンティティ:", + "references": "参照", + "include-system-images": "システム画像を含める", + "clear-image": "画像をクリア", + "no-image": "画像なし", + "no-image-selected": "画像が選択されていません", + "browse-from-gallery": "ギャラリーから選択", + "set-link": "リンクを設定", + "image-link": "画像リンク", + "link": "リンク", + "copy-image-link": "画像リンクをコピー", + "embed-image": "画像を埋め込む", + "embed-to-html": "HTMLに埋め込む", + "embed-to-html-hint": "この機能により、リンクが無許可のユーザーにも利用可能になります。", + "embed-to-html-text": "以下のコードスニペットを使用することで、プレーンHTMLベースのコンポーネントに画像を埋め込むことができます。
    そのようなコンポーネントには、HTMLカードウィジェット、セルコンテンツ関数などが含まれます。", + "embed-to-angular-template": "Angular HTMLテンプレートに埋め込む", + "embed-to-angular-template-text": "以下のコードスニペットを使用することで、Angular HTMLテンプレートに画像を埋め込み、コンポーネントで使用できます。
    そのようなコンポーネントには、Markdownウィジェット、ウィジェットエディターのHTMLセクション、カスタムアクションなどが含まれます。" + }, + "image-input": { + "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ファイルをドラッグアンドドロップするか", + "column-value": "値", + "column-title": "タイトル", + "column-example": "例:値データ", + "column-key": "属性/テレメトリキー", + "credentials": "認証情報", + "csv-delimiter": "CSV区切り文字", + "csv-first-line-header": "最初の行に列名が含まれています", + "csv-update-data": "属性/テレメトリの更新", + "details": "詳細", + "import-csv-number-columns-error": "ファイルには少なくとも2列が含まれている必要があります", + "import-csv-invalid-format-error": "無効なファイル形式です。行:'{{line}}'", + "column-type": { + "name": "名前", + "type": "タイプ", + "label": "ラベル", + "column-type": "列タイプ", + "client-attribute": "クライアント属性", + "shared-attribute": "共有属性", + "server-attribute": "サーバー属性", + "timeseries": "時系列", + "entity-field": "エンティティフィールド", + "access-token": "アクセストークン", + "x509": "X.509", + "mqtt": { + "client-id": "MQTTクライアントID", + "user-name": "MQTTユーザー名", + "password": "MQTTパスワード" + }, + "lwm2m": { + "client-endpoint": "LwM2Mエンドポイントクライアント名", + "security-config-mode": "LwM2Mセキュリティ設定モード", + "client-identity": "LwM2MクライアントID", + "client-key": "LwM2Mクライアントキー", + "client-cert": "LwM2Mクライアント公開鍵", + "bootstrap-server-security-mode": "LwM2Mブートストラップサーバーセキュリティモード", + "bootstrap-server-secret-key": "LwM2Mブートストラップサーバー秘密鍵", + "bootstrap-server-public-key-id": "LwM2Mブートストラップサーバー公開鍵またはID", + "lwm2m-server-security-mode": "LwM2Mサーバーセキュリティモード", + "lwm2m-server-secret-key": "LwM2Mサーバー秘密鍵", + "lwm2m-server-public-key-id": "LwM2Mサーバー公開鍵またはID" + }, + "snmp": { + "host": "SNMPホスト", + "port": "SNMPポート", + "version": "SNMPバージョン(v1、v2c、またはv3)", + "community-string": "SNMPコミュニティ文字列" + }, + "isgateway": "Gatewayかどうか", + "activity-time-from-gateway-device": "Gatewayデバイスからの活動時間", + "description": "説明", + "routing-key": "Edgeキー", + "secret": "Edgeシークレット" + }, + "stepper-text": { + "select-file": "ファイルを選択", + "configuration": "設定をインポート", + "column-type": "列タイプを選択", + "creat-entities": "新しいエンティティを作成中" + }, + "message": { + "create-entities": "{{count}} 件の新しいエンティティが正常に作成されました。", + "update-entities": "{{count}} 件のエンティティが正常に更新されました。", + "error-entities": "{{count}} 件のエンティティ作成中にエラーが発生しました。" + } + }, + "scada": { + "symbols": "SCADAシンボル", + "search": "シンボルを検索", + "selected-symbols": "{ count, plural, =1 {1 件のシンボル} other {# 件のシンボル} } が選択されました", + "download-symbol": "SCADAシンボルをダウンロード", + "export-symbol": "SCADAシンボルをJSONとしてエクスポート", + "import-symbol": "SCADAシンボルをJSONからインポート", + "upload-symbol": "SCADAシンボルをアップロード", + "update-symbol": "SCADAシンボルを更新", + "edit-symbol": "SCADAシンボルを編集", + "symbol-details": "SCADAシンボルの詳細", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "シンボルが見つかりません", + "show-hidden-elements": "隠された要素を表示", + "hide-hidden-elements": "隠された要素を非表示", + "delete-symbol": "SCADAシンボルを削除", + "delete-symbol-title": "SCADAシンボル '{{imageTitle}}' を削除してもよろしいですか?", + "delete-symbol-text": "確認後、SCADAシンボルは復元できなくなりますのでご注意ください。", + "delete-symbols-title": "{ count, plural, =1 {1 件のSCADAシンボル} other {# 件のSCADAシンボル} } を削除してもよろしいですか?", + "delete-symbols-text": "確認後、選択したすべてのSCADAシンボルが削除され、関連するデータは復元できなくなりますのでご注意ください。", + "include-system-symbols": "システムシンボルを含める", + "symbol-preview": "シンボルプレビュー", + "general": "一般", + "tags": "タグ", + "properties": "プロパティ", + "title": "タイトル", + "description": "説明", + "search-tags": "タグを検索", + "widget-size": "ウィジェットサイズ", + "cols": "列", + "rows": "行", + "state-render-function": "状態レンダリング関数", + "preview": "プレビュー", + "preview-widget-action-text": "ウィジェットアクション '{{type}}' が正常に呼び出されました!", + "no-symbol": "SCADAシンボルなし", + "no-symbol-selected": "SCADAシンボルが選択されていません", + "clear-symbol": "SCADAシンボルをクリア", + "browse-symbol-from-gallery": "ギャラリーからSCADAシンボルを選択", + "zoom-in": "ズームイン", + "zoom-out": "ズームアウト", + "create-widget": "ウィジェットを作成", + "create-widget-from-symbol": "SCADAシンボルからウィジェットを作成", + "hidden": "隠された", + "tag": { + "tag": "タグ", + "on-click-action": "クリック時のアクション", + "no-tags": "タグが設定されていません", + "delete-tag-text": "{{elementType}} 要素からタグ {{tag}} を削除してもよろしいですか?", + "update-tag": "タグを更新", + "enter-tag": "タグを入力", + "tag-settings": "タグ設定", + "remove-tag": "タグを削除", + "add-tag": "タグを追加" + }, + "behavior": { + "behavior": "動作", + "id": "ID", + "name": "名前", + "type": "タイプ", + "no-behaviors": "動作が設定されていません", + "add-behavior": "動作を追加", + "type-action": "アクション", + "type-value": "値", + "type-widget-action": "ウィジェットアクション", + "behavior-settings": "動作設定", + "remove-behavior": "動作を削除", + "hint": "ヒント", + "group-title": "グループタイトル", + "value-type": "値のタイプ", + "default-value": "デフォルト値", + "true-label": "真ラベル", + "false-label": "偽ラベル", + "state-label": "状態ラベル", + "default-payload": "デフォルトペイロード", + "not-unique-behavior-ids-error": "動作IDは一意でなければなりません!", + "default-settings": "デフォルト設定" + }, + "symbol": { + "symbol": "SCADAシンボル", + "fluid-presence": "流体の存在", + "fluid-presence-hint": "パイプ内に流体が存在するかどうかを示します。", + "fluid-present": "流体が存在", + "present": "存在", + "absent": "不在", + "flow-presence": "流れの存在", + "flow-presence-hint": "パイプ内で流体が流れているかどうかを示します。", + "flow-present": "流れあり", + "flow-direction": "流れの方向", + "flow-direction-hint": "流体の流れる方向を示します。", + "forward": "前方", + "reverse": "逆方向", + "flow-animation-speed": "流れのアニメーション速度", + "flow-animation-speed-hint": "流れのアニメーション速度を示す倍数値。1 - 通常の速度、0 - アニメーションなし、< 1 - より遅いアニメーション、> 1 - より速いアニメーション。", + "leak": "漏れ", + "leak-hint": "パイプ内に漏れがあるかどうかを示します。", + "leak-present": "漏れあり", + "fluid-color": "流体の色", + "pipe-color": "パイプの色", + "horizontal-pipe": "水平パイプ", + "vertical-pipe": "垂直パイプ", + "horizontal-fluid-color": "水平流体の色", + "vertical-fluid-color": "垂直流体の色", + "left-pipe": "左パイプ", + "right-pipe": "右パイプ", + "top-pipe": "上パイプ", + "bottom-pipe": "下パイプ", + "left-fluid-color": "左流体の色", + "right-fluid-color": "右流体の色", + "top-fluid-color": "上流体の色", + "bottom-fluid-color": "下流体の色", + "display": "表示", + "display-format": "表示形式", + "value": "値", + "decimals": "小数点以下の桁数", + "units": "単位", + "flow-meter-value-hint": "流量計表示に表示される倍数値", + "value-hint": "現在の値を示す倍数値", + "running": "実行中", + "running-hint": "コンポーネントが実行中の状態かどうかを示します。", + "warning-state": "警告状態", + "warning": "警告", + "warning-click": "警告クリック", + "warning-state-hint": "コンポーネントが警告状態かどうかを示します。", + "critical-state": "重大状態", + "critical": "重大", + "critical-click": "重大クリック", + "critical-state-hint": "コンポーネントが重大状態かどうかを示します。", + "critical-state-animation": "重大状態アニメーション", + "critical-state-animation-hint": "コンポーネントが重大状態の時に点滅アニメーションを有効にするかどうか。", + "warning-critical-state-animation": "警告/重大状態アニメーション", + "warning-critical-state-animation-hint": "コンポーネントが警告または重大状態の時に点滅アニメーションを有効にするかどうか。", + "animation": "アニメーション", + "broken": "破損", + "broken-hint": "コンポーネントが破損しているかどうかを示します。", + "on-display-click": "表示クリック時", + "on-display-click-hint": "ユーザーが表示をクリックした時に発動するアクション。", + "pipe": "パイプ", + "default-border-color": "デフォルトの境界線色", + "active-border-color": "アクティブ境界線色", + "warning-border-color": "警告境界線色", + "critical-border-color": "重大境界線色", + "background-color": "背景色", + "rotation-animation-speed": "回転アニメーション速度", + "rotation-animation-speed-hint": "回転アニメーションの速度を示す倍数値。1 - 通常の速度、0 - アニメーションなし、< 1 - より遅いアニメーション、> 1 - より速いアニメーション。", + "on-click": "クリック時", + "on-click-hint": "ユーザーがコンポーネントをクリックした時に発動するアクション。", + "connectors-positions": "コネクタの位置", + "right-connector": "右コネクタ", + "right-top-connector": "右上コネクタ", + "right-bottom-connector": "右下コネクタ", + "left-connector": "左コネクタ", + "left-top-connector": "左上コネクタ", + "left-bottom-connector": "左下コネクタ", + "top-left-connector": "左上コネクタ", + "top-right-connector": "右上コネクタ", + "top-connector": "上コネクタ", + "bottom-connector": "下コネクタ", + "running-color": "実行中の色", + "stopped-color": "停止中の色", + "stopped": "停止中", + "warning-color": "警告色", + "critical-color": "重大色", + "opened": "開放", + "opened-hint": "コンポーネントが開放状態かどうかを示します。", + "open": "開く", + "open-hint": "ユーザーがコンポーネントを開くためにクリックしたときに発動するアクション。", + "close": "閉じる", + "close-hint": "ユーザーがコンポーネントを閉じるためにクリックしたときに発動するアクション。", + "close-state-animation": "閉じた状態のアニメーション", + "close-state-animation-hint": "コンポーネントが閉じた状態の時に点滅アニメーションを有効にするかどうか。", + "opened-color": "開放時の色", + "closed-color": "閉じた時の色", + "opened-rotation-angle": "開放時の回転角度", + "closed-rotation-angle": "閉じた時の回転角度", + "tank-capacity": "タンク容量", + "tank-capacity-hint": "タンクの総容量を示す倍数値。", + "current-volume": "現在の体積", + "current-volume-hint": "現在の占有体積を示す倍数値。", + "tank-color": "タンクの色", + "value-box": "値ボックス", + "value-text": "値テキスト", + "scale": "スケール", + "transparent-mode": "透過モード", + "major-ticks": "主目盛り", + "intervals": "間隔", + "major-ticks-color": "主目盛りの色", + "normal": "通常", + "minor-ticks": "副目盛り", + "minor-ticks-color": "副目盛りの色", + "temperature": "温度", + "temperature-hint": "現在の温度を示す倍数値。", + "update-temperature": "温度を更新", + "update-temperature-hint": "ユーザーが現在の温度を変更するためにクリックしたときに発動するアクション。", + "run": "実行", + "run-hint": "ユーザーがコンポーネントを実行するためにクリックしたときに発動するアクション。", + "stop": "停止", + "stop-hint": "ユーザーがコンポーネントを停止するためにクリックしたときに発動するアクション。", + "temperature-step": "温度ステップ増分", + "heat-pump-color": "ヒートポンプの色", + "power-button-background": "電源ボタンの背景", + "value-box-background": "値ボックスの背景", + "value-units": "値の単位", + "enable-units-scale": "スケールに単位を表示", + "filtration-mode": "濾過モード", + "filtration-mode-hint": "現在の濾過モードを示す整数値。", + "filtration-mode-update": "濾過モード更新状態", + "filtration-mode-update-hint": "ユーザーが現在の濾過モードを変更するためにクリックしたときに発動するアクション。", + "filter-mode": "フィルター", + "waste-mode": "廃棄", + "backwash-mode": "バックウォッシュ", + "recirculate-mode": "再循環", + "rinse-mode": "すすぎ", + "closed-mode": "閉じた状態", + "sand-filter-color": "砂フィルターの色", + "mode-box-background": "モードボックスの背景", + "border-color": "境界線の色", + "label-color": "ラベルの色", + "water-leak-hint": "漏れがあるかどうかを示します。", + "default-color": "デフォルトの色", + "leak-color": "漏れの色", + "full-value": "満水値", + "full-value-hint": "満水値を示す倍数値。", + "label": "ラベル", + "icon": "アイコン", + "button-color": "ボタンの色", + "on-label": "'オン' ラベルテキスト", + "off-label": "'オフ' ラベルテキスト", + "arrow-presence": "矢印の存在", + "arrow-presence-hint": "コネクタに矢印があるかどうかを示します。", + "arrow-present": "矢印あり", + "arrow-direction": "流れの方向", + "arrow-direction-hint": "流れの方向を示します。", + "flow-animation": "流れの存在", + "flow-animation-hint": "コネクタ内で流体が流れているかどうかを示します。", + "flow": "流れ", + "flow-line": "ライン", + "flow-line-style": "ラインスタイル", + "flow-style-hint": "完璧なアニメーション同期を実現するため、ダッシュとギャップの値の合計が100で割り切れるように設定してください。", + "flow-dash-cap": "ダッシュキャップ", + "dash-cap-butt": "バット", + "dash-cap-round": "ラウンド", + "dash-cap-square": "スクエア", + "dash": "ダッシュ", + "gap": "ギャップ", + "main-line": "メインライン", + "line": "ライン", + "line-color": "ラインの色", + "arrow-color": "矢印の色", + "target-value": "ターゲット値", + "target-value-hint": "スケール上のターゲットポイントを示します。", + "min-max-value": "最小値と最大値", + "min-value": "最小", + "max-value": "最大", + "progress-bar": "プログレスバー", + "progress-arrow": "プログレスアロー", + "warning-scale-color": "警告スケールの色", + "critical-scale-color": "重大スケールの色", + "scale-color": "スケールの色", + "target": "ターゲット", + "high-warning-state": "高警告状態", + "show-high-warning-scale": "高警告スケールを表示", + "high-warning-scale": "高警告スケール", + "high-warning-state-hint": "倍数値が高警告範囲を示し、最大または高い重大値までです。", + "low-warning-state": "低警告状態", + "show-low-warning-scale": "低警告スケールを表示", + "low-warning-scale": "低警告スケール", + "low-warning-state-hint": "倍数値が低警告範囲を示し、最小または低い重大値までです。", + "high-critical-state": "高重大状態", + "show-high-critical-scale": "高重大スケールを表示", + "high-critical-scale": "高重大スケール", + "high-critical-state-hint": "倍数値が高重大範囲を示し、最大値スケールまでです。", + "low-critical-state": "低重大状態", + "show-low-critical-scale": "低重大状態のスケールを表示", + "low-critical-scale": "低重大状態", + "low-critical-state-hint": "倍数値が低重大範囲を示し、最小値スケールまでです。", + "filter-color": "フィルターの色", + "colors": "色", + "indicator-colors": "インジケーターの色", + "enabled": "有効", + "disabled": "無効", + "on": "ON", + "off": "OFF", + "on-off-state": "オン/オフ状態", + "on-off-state-hint": "コンポーネントがオンまたはオフの状態かどうかを示します。", + "on-update-state": "オン更新状態", + "on-update-state-hint": "ユーザーがオンに状態を更新するためにクリックしたときに発動するアクション。", + "off-update-state": "オフ更新状態", + "off-update-state-hint": "ユーザーがオフに状態を更新するためにクリックしたときに発動するアクション。", + "voltage": "電圧", + "input-voltage": "入力電圧", + "input-voltage-hint": "入力電圧の値を示す倍数値。", + "output-voltage": "出力電圧", + "output-voltage-hint": "出力電圧の値を示す倍数値。", + "first-phase-voltage": "第1相電圧", + "second-phase-voltage": "第2相電圧", + "third-phase-voltage": "第3相電圧", + "phase-voltage-hint": "現在の相の電圧値を示す倍数値。", + "voltage-hint": "現在の電圧を示す倍数値。", + "current-voltage-color": "現在の電圧の色", + "phase-indicator-color": "相インジケーターの色", + "measured": "測定済み", + "measured-hint": "キロワット時でのエネルギー使用量を示す倍数値。", + "day-rate": "昼間料金", + "night-rate": "夜間料金", + "off-peak-rate": "オフピーク料金", + "peak-rate": "ピーク料金", + "export-rate": "輸出料金", + "operating-mode": "運転モード", + "bypass-mode": "バイパス", + "operating-mode-hint": "現在の運転モードを示す整数値(0 - OFF、1 - ON、2 - BYPASS)。", + "connected": "接続済み", + "connected-hint": "コンポーネントが接続状態かどうかを示します。", + "disconnected": "切断", + "indicator": "インジケーター", + "operation-mode": "運転モード", + "operation-mode-hint": "インバーターが電源モードまたはインバーターモードにあるかどうかを示します。", + "operation-mode-indicators-color": "運転モードインジケーターの色", + "mains-on-mode": "電源オンモード", + "inverter-on-mode": "インバーターオンモード", + "charging-mode": "充電モード", + "charging-mode-hint": "現在の充電モードを示す整数値(1 - バルク、2 - 吸収、3 - フロート)。", + "charging-mode-indicators-color": "充電モードインジケーターの色", + "inverter-faults": "故障", + "inverter-fault-indicators-color": "故障インジケーターの色", + "overload-fault": "過負荷", + "overload-fault-hint": "インバーターが過負荷状態かどうかを示します。", + "low-battery-fault": "低バッテリー", + "low-battery-fault-hint": "バッテリーが過度に放電しているかどうかを示します。", + "temperature-fault": "温度", + "temperature-fault-hint": "インバーター内の高温を示します。", + "triangle": "三角形", + "socket": "ソケット", + "left-button": "左ボタン", + "right-button": "右ボタン", + "alarm-colors": "アラームの色", + "hook-color": "フックの色" + } + }, + "item": { + "selected": "選択済み" + }, + "js-func": { + "no-return-error": "関数は値を返さなければなりません!", + "return-type-mismatch": "関数は'{{type}}'タイプの値を返さなければなりません!", + "tidy": "整理", + "mini": "ミニ", + "modules": "モジュール", + "remove-module": "モジュールを削除", + "no-modules": "モジュールが設定されていません", + "add-module": "モジュールを追加", + "module-alias": "エイリアス", + "invalid-module-alias-name": "無効なエイリアス名", + "module-resource": "JSモジュールリソース", + "not-unique-module-aliases-error": "モジュールのエイリアスは一意でなければなりません!", + "show-module-info": "モジュール情報を表示", + "show-module-source-code": "モジュールのソースコードを表示", + "module-members": "モジュールメンバー", + "module-no-members": "モジュールにはエクスポートされたメンバーがありません", + "module-load-error": "モジュールの読み込みエラー", + "source-code": "ソースコード", + "source-code-load-error": "ソースコード読み込みエラー", + "no-js-module-text": "JSモジュールが見つかりません", + "no-js-module-matching": "'{{module}}'に一致するJSモジュールは見つかりませんでした。" + }, + "key-val": { + "key": "キー", + "value": "値", + "remove-entry": "エントリーを削除", + "add-entry": "エントリーを追加", + "no-data": "エントリーがありません" + }, + "layout": { + "layout": "レイアウト", + "layouts": "レイアウト", + "manage": "レイアウトの管理", + "settings": "レイアウト設定", + "color": "色", + "main": "メイン", + "right": "右", + "left": "左", + "select": "ターゲットレイアウトを選択", + "percentage-width": "パーセンテージ幅 (%)", + "fixed-width": "固定幅 (px)", + "left-width": "左カラム (%)", + "right-width": "右カラム (%)", + "pick-fixed-side": "固定側: ", + "layout-fixed-width": "固定幅 (px)", + "value-min-error": "値は {{min}}{{unit}} より大きくなければなりません", + "value-max-error": "値は {{max}}{{unit}} より小さくなければなりません", + "layout-fixed-width-required": "固定幅は必須です", + "right-width-percentage-required": "右側のパーセンテージは必須です", + "left-width-percentage-required": "左側のパーセンテージは必須です", + "divider": "区切り線", + "right-side": "右側レイアウト", + "left-side": "左側レイアウト", + "add-new-breakpoint": "新しいブレークポイントを追加", + "breakpoint": "ブレークポイント", + "breakpoints": "ブレークポイント", + "copy-from": "コピー元", + "size": "サイズ", + "delete-breakpoint-title": "ブレークポイント '{{name}}' を削除してもよろしいですか?", + "delete-breakpoint-text": "確認後、ブレークポイントは復元できなくなり、設定はデフォルトのブレークポイントに戻りますのでご注意ください。" + }, + "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": "最新値を表示", + "settings": "凡例設定", + "min": "最小", + "max": "最大", + "avg": "平均", + "total": "合計", + "latest": "最新", + "Min": "最小", + "Max": "最大", + "Avg": "平均", + "Total": "合計", + "Latest": "最新", + "comparison-time-ago": { + "previousInterval": "(前のインターバル)", + "customInterval": "(カスタムインターバル)", + "days": "(日前)", + "weeks": "(週前)", + "months": "(月前)", + "years": "(年前)" + }, + "column-title": "列タイトル", + "label": "ラベル", + "value": "値" + }, + "login": { + "login": "ログイン", + "request-password-reset": "パスワードリセットをリクエスト", + "reset-password": "パスワードをリセット", + "create-password": "パスワードを作成", + "two-factor-authentication": "二要素認証", + "passwords-mismatch-error": "入力したパスワードは一致していなければなりません!", + "password-again": "パスワード(再入力)", + "sign-in": "サインインしてください", + "username": "ユーザー名(メールアドレス)", + "remember-me": "次回以降のログインを記憶", + "forgot-password": "パスワードを忘れましたか?", + "password-reset": "パスワードリセット", + "expired-password-reset-message": "認証情報の有効期限が切れています!\n新しいパスワードを作成してください。", + "new-password": "新しいパスワード", + "new-password-again": "新しいパスワードを再入力", + "password-link-sent-message": "リセットリンクが送信されました", + "email": "メールアドレス", + "invalid-email-format": "無効なメールアドレス形式です。", + "sign-in-with": "{{name}} でサインイン", + "sign-in-to-your-account": "アカウントにサインイン", + "or": "または", + "error": "ログインエラー", + "verify-your-identity": "本人確認を行ってください", + "select-way-to-verify": "確認方法を選択", + "resend-code": "コードを再送信", + "resend-code-wait": "{ time, plural, =1 {1秒} other {#秒} } 後に再送信", + "try-another-way": "別の方法を試す", + "totp-auth-description": "認証アプリからセキュリティコードを入力してください。", + "totp-auth-placeholder": "コード", + "sms-auth-description": "セキュリティコードが {{contact}} の電話番号に送信されました。", + "sms-auth-placeholder": "SMSコード", + "email-auth-description": "セキュリティコードが {{contact}} のメールアドレスに送信されました。", + "email-auth-placeholder": "メールコード", + "backup-code-auth-description": "バックアップコードの1つを入力してください。", + "backup-code-auth-placeholder": "バックアップコード", + "activation-link-expired": "アクティベーションリンクの有効期限が切れました", + "activation-link-expired-message": "プロファイルをアクティブにするリンクの有効期限が切れました。新しいメールを受け取るためにログインページに戻ってください。", + "reset-password-link-expired": "パスワードリセットリンクの有効期限が切れました", + "reset-password-link-expired-message": "パスワードをリセットするリンクの有効期限が切れました。新しいメールを受け取るためにログインページに戻ってください。", + "two-fa": "二要素認証", + "two-fa-required": "二要素認証が必要です", + "set-up-verification-method": "続行するには認証方法を設定してください", + "set-up-verification-method-login": "認証方法を設定するか、ログインしてください", + "enable-authenticator-app": "認証アプリを有効化", + "enable-authenticator-app-description": "認証アプリのセキュリティコードを入力してください", + "enable-authenticator-sms": "SMS 認証を有効化", + "enable-authenticator-sms-description": "次に送信した 6 桁のコードを入力してください: ", + "enable-authenticator-email": "email 認証を有効化", + "enable-authenticator-email-description": "セキュリティコードを次のメールアドレスに送信しました: ", + "enter-key-manually": "または、この 32 桁のキーを手動で入力してください:", + "continue": "続行", + "confirm": "確認", + "authenticator-app-success": "認証アプリを有効化しました", + "authenticator-app-success-description": "次回ログイン時は、二要素認証コードの入力が必要です", + "authenticator-sms-success": "SMS 認証を有効化しました", + "authenticator-sms-success-description": "次回ログイン時に、電話番号に送信されるセキュリティコードの入力を求められます", + "authenticator-email-success": "Email 認証を有効化しました", + "authenticator-email-success-description": "次回ログイン時に、メールアドレスに送信されるセキュリティコードの入力を求められます", + "authenticator-backup-code-success": "バックアップコードを有効化しました", + "authenticator-backup-code-success-description": "次回ログイン時に、セキュリティコードの入力、またはバックアップコードのいずれか 1 つの使用を求められます。", + "add-verification-method": "認証方法を追加", + "get-backup-code": "バックアップコードを取得", + "copy-key": "キーをコピー", + "send-code": "コードを送信", + "email-label": "Email", + "email-description": "認証に使用するメールアドレスを入力してください。", + "sms-description": "認証に使用する電話番号を入力してください。", + "backup-code-description": "アカウントにログインする際に使用できるよう、コードを印刷して手元に保管してください。各バックアップコードは 1 回のみ使用できます。", + "backup-code-warn": "このページを離れると、これらのコードは再表示できません。以下のオプションを使用して安全に保管してください。", + "download-txt": "ダウンロード(txt)", + "print": "印刷", + "verification-code": "6 桁のコード", + "verification-code-invalid": "認証コード形式が無効です", + "verification-code-incorrect": "認証コードが正しくありません", + "verification-code-many-request": "認証コード確認のリクエストが多すぎます", + "scan-qr-code": "認証アプリでこの QR コードをスキャンしてください", + "phone-input": { + "phone-input-label": "電話番号", + "phone-input-required": "電話番号は必須です", + "phone-input-validation": "電話番号が無効、または使用できません", + "phone-input-pattern": "無効な電話番号です。E.164 形式である必要があります(例: {{phoneNumber}})", + "phone-input-hint": "E.164 形式の電話番号(例: {{phoneNumber}})" + } + }, + "mobile": { + "add-application": "アプリケーションを追加", + "app-id": "アプリサイト関連ID", + "app-id-required": "アプリサイト関連IDは必須です", + "app-id-pattern": "無効なアプリサイト関連IDの形式", + "app-store-link": "App Storeリンク", + "app-store-link-required": "App Storeリンクは必須です", + "application-details": "アプリケーションの詳細", + "application-package": "アプリケーションパッケージ", + "application-secret": "アプリケーションシークレット", + "application-secret-required": "アプリケーションシークレットは必須です", + "application": "アプリケーション", + "applications": "アプリケーション", + "copy-app-id": "アプリIDをコピー", + "copy-app-store-link": "App Storeリンクをコピー", + "copy-application-package": "アプリケーションパッケージをコピー", + "copy-application-secret": "アプリケーションシークレットをコピー", + "copy-google-play-link": "Google Playリンクをコピー", + "copy-sha256-certificate-fingerprints": "SHA256証明書のフィンガープリントをコピー", + "delete-application": "アプリケーションを削除", + "delete-application-button-text": "結果を理解し、アプリケーションを削除", + "delete-application-text": "この操作は元に戻せません。これによりアプリケーションが完全に削除されます。
    永久に削除したくない場合は、アプリケーションを一時停止できます。
    削除する場合は、確認のために\"{{phrase}}\"を入力してください。", + "delete-application-title-short": "アプリケーション '{{name}}' を削除してもよろしいですか?", + "delete-application-text-short": "確認後、アプリケーションと関連データは復元できなくなりますのでご注意ください。", + "delete-application-phrase": "アプリケーションを削除", + "delete-applications-bundle-text": "確認後、モバイルバンドルと関連データは復元できなくなりますのでご注意ください。", + "delete-applications-bundle-title": "モバイルバンドル '{{bundleName}}' を削除してもよろしいですか?", + "generate-application-secret": "アプリケーションシークレットを生成", + "google-play-link": "Google Playリンク", + "google-play-link-required": "Google Playリンクは必須です", + "latest-version": "最新バージョン", + "min-version": "最小バージョン", + "invalid-version-pattern": "無効なバージョン形式です。形式は major.minor.patch(例:1.0.0)を使用してください。", + "mobile-center": "モバイルセンター", + "mobile-package": "アプリケーションパッケージ", + "mobile-package-max-length": "アプリケーションパッケージは256文字以内である必要があります", + "mobile-package-required": "アプリケーションパッケージは必須です。", + "mobile-package-pattern": "アプリケーションパッケージの形式が無効です", + "mobile-package-title": "アプリケーションタイトル", + "mobile-package-title-max-length": "アプリケーションタイトルは256文字以内である必要があります", + "no-application": "アプリケーションが見つかりません", + "no-bundles": "バンドルが見つかりません", + "platform-type": "プラットフォームタイプ", + "search-application": "アプリケーションを検索", + "search-bundles": "バンドルを検索", + "set": "設定", + "sha256-certificate-fingerprints": "SHA256証明書フィンガープリント", + "sha256-certificate-fingerprints-required": "SHA256証明書フィンガープリントは必須です", + "sha256-certificate-fingerprints-pattern": "無効なSHA256証明書フィンガープリント形式", + "show-hidden-pages": "隠されたページを表示", + "status": "ステータス", + "status-type": { + "deprecated": "非推奨", + "draft": "下書き", + "published": "公開済み", + "suspended": "一時停止" + }, + "store-information": "ストア情報", + "version-information": "バージョン情報", + "min-version-release-notes": "最小バージョンのリリースノート", + "latest-version-release-notes": "最新バージョンのリリースノート", + "bundle": "バンドル", + "bundles": "バンドル", + "add-bundle": "バンドルを追加", + "title": "タイトル", + "title-required": "タイトルは必須です", + "title-cannot-contain-only-spaces": "タイトルはスペースのみで構成できません", + "title-max-length": "タイトルは256文字以内である必要があります", + "oauth-clients": "OAuth 2.0クライアント", + "android-app": "Androidアプリ", + "android-application": "Androidアプリケーション", + "ios-app": "iOSアプリ", + "ios-application": "iOSアプリケーション", + "invalid-store-link": "無効なストアリンク", + "enable-oauth": "OAuth 2.0を有効化", + "enable-self-registration": "セルフ登録を有効化", + "edit-bundle": "バンドルを編集", + "description": "説明", + "basic-settings": "基本設定", + "no-application-matching": "'{{entity}}' に一致するアプリケーションは見つかりませんでした。", + "no-bundle-matching": "'{{entity}}' に一致するバンドルは見つかりませんでした。", + "application-required": "アプリケーションは必須です。", + "bundle-required": "バンドルは必須です。", + "no-application-text": "アプリケーションが見つかりません", + "no-bundle-text": "バンドルが見つかりません", + "layout": "レイアウト", + "pages": "ページ", + "hide-all-pages": "すべてのページを非表示", + "reset-to-default-pages": "デフォルトのページにリセット", + "add-specific-page": "特定のページを追加", + "visible": "表示", + "hidden": "非表示", + "reset-to-page-default": "ページをデフォルトにリセット", + "mobile-599": "モバイル(最大599px)", + "tablet-959": "タブレット(最大959px)", + "max-element-number": "最大要素数", + "page-name": "ページ名", + "page-name-required": "ページ名は必須です。", + "page-name-cannot-contain-only-spaces": "ページ名はスペースのみで構成できません。", + "page-name-max-length": "ページ名は256文字以内である必要があります", + "page-type": "ページタイプ", + "pages-types": { + "dashboard": "ダッシュボード", + "web-view": "ウェブビュー", + "custom": "カスタム" + }, + "url": "URL", + "invalid-url-format": "無効なURL形式", + "path": "パス", + "invalid-path-format": "無効なパス形式", + "custom-page": "カスタムページ", + "edit-page": "ページを編集", + "edit-custom-page": "カスタムページを編集", + "delete-page": "ページを削除", + "qr-code-widget": "QRコードウィジェット", + "type-here": "ここに入力", + "configuration-dialog": "設定ダイアログ", + "configuration-app": "設定アプリ", + "configuration-step": { + "prepare-environment-title": "開発環境の準備", + "prepare-environment-text": "Flutter ThingsBoardモバイルアプリケーションにはFlutter SDKが必要です。Flutter SDKのセットアップ手順に従ってください。", + "get-source-code-title": "アプリソースコードの取得", + "get-source-code-text": "Flutter ThingsBoardモバイルアプリケーションのソースコードは、GitHubリポジトリからクローンして取得できます:", + "configure-app-settings-title": "アプリ設定の構成", + "configure-app-settings-text": "設定ファイルをダウンロードし、前の手順でクローンしたプロジェクトのルートディレクトリに配置してください。", + "download-file": "ファイルをダウンロード", + "run-app-title": "アプリを実行", + "run-app-text": "IDEに記載された方法でアプリを実行してください。\nターミナルを使用する場合、次のコマンドでアプリを実行します:", + "more-information": "詳細情報は、はじめにのドキュメントに記載されています。", + "getting-started": "はじめにガイド" + } + }, + "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": "制限超過トリガー設定", + "task-processing-failure-trigger-settings": "タスク処理失敗トリガー設定", + "resources-shortage-trigger-settings": "リソース不足トリガー設定", + "at-least-one-should-be-selected": "少なくとも1つは選択する必要があります", + "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": "作成済み", + "customize-messages": "メッセージをカスタマイズ", + "cpu-threshold": "CPUの閾値", + "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-preview": "メール通知プレビュー", + "slack": "Slack", + "slack-preview": "Slack通知プレビュー", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Microsoft Teams通知プレビュー", + "sms": "SMS", + "sms-preview": "SMS通知プレビュー", + "web": "Web", + "web-preview": "Web通知プレビュー", + "mobile-app": "モバイルアプリ", + "mobile-app-preview": "モバイルアプリ通知プレビュー" + }, + "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": "無効", + "edge-trigger-settings": "Edgeトリガー設定", + "edge-list-rule-hint": "フィールドが空の場合、トリガーはすべてのEdgeインスタンスに適用されます。", + "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-max-length": "リンクは{{ length }}文字以内である必要があります", + "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-recipients-matching": "'{{entity}}' に一致する受信者は見つかりませんでした。", + "no-recipients-text": "受信者が見つかりません", + "no-rule": "設定されたルールがありません", + "no-rules-notification": "ルールの通知がありません", + "no-severity-found": "重大度が見つかりません", + "no-severity-matching": "'{{severity}}' が見つかりません。", + "no-template-matching": "'{{template}}' に一致するリソースが見つかりませんでした。", + "create-new-template": "新しいテンプレートを作成!", + "not-found-slack-recipient": "Slackの受信者が見つかりません", + "notification": "通知", + "notification-center": "通知センター", + "notification-tap-action": "通知タップアクション", + "notification-tap-action-hint": "無効の場合、デフォルトのアラームダッシュボードが使用されます", + "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": "プラットフォームユーザー", + "ram-threshold": "RAM閾値", + "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-recipient": "通知受信者", + "notification-recipient-required": "通知受信者は必須です。", + "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": "送信済み", + "setup": "設定", + "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": "アラームステータスが次に変更された場合、エスカレーションを停止:", + "storage-threshold": "ストレージ閾値", + "subject": "件名", + "subject-required": "件名は必須です", + "subject-max-length": "件名は{{ length }}文字以内である必要があります", + "template": "テンプレート", + "template-name": "テンプレート名", + "template-required": "テンプレートは必須です", + "template-type": { + "alarm": "アラーム", + "alarm-assignment": "アラーム割り当て", + "alarm-comment": "アラームコメント", + "api-usage-limit": "API 使用量上限", + "device-activity": "デバイスアクティビティ", + "entities-limit": "エンティティ上限", + "entities-limit-increase-request": "エンティティ上限引き上げリクエスト", + "entity-action": "エンティティアクション", + "general": "一般", + "rule-engine-lifecycle-event": "ルールエンジンライフサイクルイベント", + "rule-node": "ルールノード", + "new-platform-version": "プラットフォームの新バージョン", + "rate-limits": "レート制限超過", + "edge-communication-failure": "Edge 通信障害", + "edge-connection": "Edge 接続", + "task-processing-failure": "タスク処理失敗", + "resources-shortage": "リソース不足" + }, + "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": "レート制限超過", + "edge-connection": "Edge接続", + "edge-communication-failure": "Edge通信障害", + "task-processing-failure": "タスク処理失敗", + "resources-shortage": "リソース不足", + "trigger": "トリガー", + "trigger-required": "トリガーは必須です" + }, + "type": "タイプ", + "unread": "未読", + "updated": "更新済み", + "use-deprecated-webhook-connectors": "廃止予定のWebhookコネクタを使用", + "use-old-api": "古いAPIを使用", + "use-template": "テンプレートを使用", + "view-all": "すべて表示", + "warning": "警告", + "webhook-url": "Webhook URL", + "webhook-url-required": "Webhook URLは必須です", + "workflow-url": "ワークフローURL", + "workflow-url-required": "ワークフロー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": "パッケージを追加", + "assign-firmware": "割り当てられたファームウェア", + "assign-firmware-required": "割り当てられたファームウェアは必須です", + "assign-software": "割り当てられたソフトウェア", + "assign-software-required": "割り当てられたソフトウェアは必須です", + "auto-generate-checksum": "チェックサムを自動生成", + "checksum": "チェックサム", + "checksum-hint": "チェックサムが空の場合、自動的に生成されます", + "checksum-algorithm": "チェックサムアルゴリズム", + "checksum-copied-message": "パッケージのチェックサムがクリップボードにコピーされました", + "change-firmware": "ファームウェアの変更により、{ count, plural, =1 {1 デバイス} other {# デバイス} } の更新が行われる可能性があります。", + "change-software": "ソフトウェアの変更により、{ count, plural, =1 {1 デバイス} other {# デバイス} } の更新が行われる可能性があります。", + "change-ota-setting-title": "OTA設定を変更してもよろしいですか?", + "chose-compatible-device-profile": "アップロードされたパッケージは、選択されたプロファイルを持つデバイスでのみ使用可能です。", + "chose-firmware-distributed-device": "デバイスに配布されるファームウェアを選択", + "chose-software-distributed-device": "デバイスに配布されるソフトウェアを選択", + "content-type": "コンテンツタイプ", + "copy-checksum": "チェックサムをコピー", + "copy-direct-url": "直接URLをコピー", + "copyId": "パッケージIDをコピー", + "copied": "コピー完了!", + "delete": "パッケージを削除", + "delete-ota-update-text": "確認後、OTAアップデートは復元できなくなりますのでご注意ください。", + "delete-ota-update-title": "OTAアップデート '{{title}}' を削除してもよろしいですか?", + "delete-ota-updates-text": "確認後、すべての選択されたOTAアップデートは削除されます。", + "delete-ota-updates-title": "{ count, plural, =1 {1 OTAアップデート} other {# OTAアップデート} } を削除してもよろしいですか?", + "description": "説明", + "direct-url": "直接URL", + "direct-url-copied-message": "パッケージの直接URLがクリップボードにコピーされました", + "direct-url-required": "直接URLは必須です", + "download": "パッケージをダウンロード", + "drop-file": "パッケージファイルをドラッグ&ドロップするか、クリックして選択してください。", + "drop-package-file-or": "パッケージファイルをドラッグ&ドロップするか", + "file-name": "ファイル名", + "file-size": "ファイルサイズ", + "file-size-bytes": "ファイルサイズ(バイト)", + "idCopiedMessage": "パッケージIDがクリップボードにコピーされました", + "no-firmware-matching": "'{{entity}}' に一致する互換性のあるファームウェアOTAアップデートパッケージは見つかりませんでした。", + "no-firmware-text": "互換性のあるファームウェアOTAアップデートパッケージはプロビジョニングされていません。", + "no-packages-text": "パッケージが見つかりません", + "no-software-matching": "'{{entity}}' に一致する互換性のあるソフトウェアOTAアップデートパッケージは見つかりませんでした。", + "no-software-text": "互換性のあるソフトウェアOTAアップデートパッケージはプロビジョニングされていません。", + "ota-update": "OTAアップデート", + "ota-update-details": "OTAアップデートの詳細", + "ota-updates": "OTAアップデート", + "package-file": "パッケージファイル", + "package-type": "パッケージタイプ", + "packages-repository": "パッケージリポジトリ", + "search": "パッケージを検索", + "selected-package": "{ count, plural, =1 {1 パッケージ} other {# パッケージ} } 選択済み", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "title-max-length": "タイトルは256文字以内である必要があります", + "types": { + "firmware": "ファームウェア", + "software": "ソフトウェア" + }, + "upload-binary-file": "バイナリファイルをアップロード", + "use-external-url": "外部URLを使用", + "version": "バージョン", + "version-required": "バージョンは必須です。", + "version-tag": "バージョンタグ", + "version-tag-hint": "カスタムタグは、デバイスから報告されるパッケージバージョンと一致する必要があります。", + "version-max-length": "バージョンは256文字以内である必要があります", + "warning-after-save-no-edit": "パッケージがアップロードされると、タイトル、バージョン、デバイスプロファイル、およびパッケージタイプを変更できなくなります。" + }, + "position": { + "top": "上", + "bottom": "下", + "left": "左", + "right": "右" + }, + "profile": { + "profile": "プロフィール", + "last-login-time": "最終ログイン", + "change-password": "パスワードを変更", + "current-password": "現在のパスワード", + "copy-jwt-token": "JWTトークンをコピー", + "jwt-token": "JWTトークン", + "token-valid-till": "トークンの有効期限", + "tokenCopiedSuccessMessage": "JWTトークンがクリップボードにコピーされました", + "tokenCopiedWarnMessage": "JWTトークンは期限切れです!ページを更新してください。" + }, + "profiles": { + "profiles": "プロフィール" + }, + "security": { + "security": "セキュリティ", + "general-settings": "一般的なセキュリティ設定", + "access-token": "アクセストークン", + "access-token-required": "アクセストークンは必須です", + "clientId": "クライアントID", + "clientId-required": "クライアントIDは必須です", + "username": "ユーザー名", + "username-required": "ユーザー名は必須です", + "ca-cert": "CA証明書", + "2fa": { + "2fa": "二段階認証", + "2fa-description": "二段階認証は、アカウントへの不正アクセスから保護します。ログイン時にセキュリティコードを入力するだけです。", + "authenticate-with": "以下の方法で認証できます:", + "disable-2fa-provider-text": "{{name}}を無効にすると、アカウントのセキュリティが低下します", + "disable-2fa-provider-title": "{{name}}を無効にしてもよろしいですか?", + "get-new-code": "新しいコードを取得", + "main-2fa-method": "主要な二段階認証方法として使用", + "dialog": { + "activation-step-description-email": "次回ログイン時に、あなたのメールアドレスに送信されたセキュリティコードを入力するよう求められます。", + "activation-step-description-sms": "次回ログイン時に、あなたの電話番号に送信されたセキュリティコードを入力するよう求められます。", + "activation-step-description-totp": "次回ログイン時に、二段階認証コードを入力する必要があります。", + "activation-step-label": "アクティベーション", + "backup-code-description": "コードを印刷しておくと、アカウントにログインする際に便利です。各バックアップコードは1回のみ使用できます。", + "backup-code-warn": "このページを離れると、これらのコードは再度表示できなくなります。以下のオプションで安全に保存してください。", + "download-txt": "ダウンロード (txt)", + "email-step-description": "認証に使用するメールアドレスを入力してください。", + "email-step-label": "メールアドレス", + "enable-email-title": "メール認証を有効にする", + "enable-sms-title": "SMS認証を有効にする", + "enable-totp-title": "認証アプリを有効にする", + "enter-verification-code": "ここに6桁のコードを入力", + "get-backup-code-title": "バックアップコードを取得", + "next": "次へ", + "scan-qr-code": "このQRコードを認証アプリでスキャン", + "send-code": "コードを送信", + "sms-step-description": "認証に使用する電話番号を入力してください。", + "sms-step-label": "電話番号", + "success": "成功!", + "totp-step-description-install": "Google Authenticator、Authy、またはDuoなどのアプリをインストールできます。", + "totp-step-description-open": "携帯電話の認証アプリを開いてください。", + "totp-step-label": "アプリを取得", + "verification-code": "6桁のコード", + "verification-code-invalid": "無効な認証コード形式", + "verification-code-incorrect": "認証コードが正しくありません", + "verification-code-many-request": "認証コードのリクエストが多すぎます", + "verification-step-description": "{{address}}に送信された6桁のコードを入力してください", + "verification-step-label": "認証" + }, + "provider": { + "email": "メール", + "email-description": "セキュリティコードをメールアドレスに送信して認証します。", + "email-hint": "認証コードは{{ info }}に送信されます。", + "sms": "SMS", + "sms-description": "電話を使用して認証します。ログイン時にSMSメッセージでセキュリティコードを送信します。", + "sms-hint": "認証コードは{{ info }}にテキストメッセージで送信されます。", + "totp": "認証アプリ", + "totp-description": "Google Authenticator、Authy、またはDuoなどのアプリを使用して認証します。ログイン用のセキュリティコードを生成します。", + "totp-hint": "認証アプリがアカウントに設定されています。", + "backup_code": "バックアップコード", + "backup-code-description": "これらの印刷可能な一回限りのパスコードを使用すると、旅行中などに電話が使えないときにサインインできます。", + "backup-code-hint": "{{ info }} の単一使用コードが現在有効です" + } + }, + "password-requirement": { + "at-least": "少なくとも:", + "character": "{ count, plural, =1 {1 文字} other {# 文字} }", + "digit": "{ count, plural, =1 {1 桁} other {# 桁} }", + "password-tooltip-min-length": "長さは少なくとも {{minimumLength}} 文字", + "password-tooltip-max-length": "長さは最大 {{maximumLength}} 文字", + "password-tooltip-uppercase": "大文字 {{minimumUppercaseLetters}} 文字", + "password-tooltip-lowercase": "小文字 {{minimumLowercaseLetters}} 文字", + "password-tooltip-digit": "数字 {{minimumDigits}} 個", + "password-tooltip-special-characters": "特殊文字 {{minimumSpecialCharacters}} 文字", + "incorrect-password-try-again": "パスワードが正しくありません。もう一度お試しください", + "lowercase-letter": "{ count, plural, =1 {小文字 1 文字} other {小文字 # 文字} }", + "new-passwords-not-match": "新しいパスワードが一致しません", + "password-should-not-contain-spaces": "パスワードにスペースを含めないでください", + "password-not-meet-requirements": "パスワードが要件を満たしていません", + "password-requirements": "パスワード要件", + "password-should-difference": "新しいパスワードは現在のパスワードと異なる必要があります", + "special-character": "{ count, plural, =1 {特殊文字 1 文字} other {特殊文字 # 文字} }", + "uppercase-letter": "{ count, plural, =1 {大文字 1 文字} other {大文字 # 文字} }", + "at-most": "最大:" + } + }, + "relation": { + "relations": "リレーション", + "direction": "方向", + "clear-relation-type": "リレーションタイプをクリア", + "search-direction": { + "FROM": "送信元", + "TO": "宛先" + }, + "direction-type": { + "FROM": "送信元", + "TO": "宛先" + }, + "from-relations": "アウトバウンドリレーション", + "to-relations": "インバウンドリレーション", + "selected-relations": "{ count, plural, =1 {1 リレーション} other {# リレーション} } 選択済み", + "type": "タイプ", + "to-entity-type": "宛先エンティティタイプ", + "to-entity-name": "宛先エンティティ名", + "from-entity-type": "送信元エンティティタイプ", + "from-entity-name": "送信元エンティティ名", + "to-entity": "宛先エンティティ", + "from-entity": "送信元エンティティ", + "delete": "リレーションを削除", + "relation-type": "リレーションタイプ", + "relation-type-required": "リレーションタイプは必須です。", + "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 {1 リレーション} other {# リレーション} } を削除してもよろしいですか?", + "delete-to-relations-text": "確認後、すべての選択されたリレーションが削除され、対応するエンティティは現在のエンティティから関連付けが解除されます。", + "delete-from-relation-title": "エンティティ '{{entityName}}' からのリレーションを削除してもよろしいですか?", + "delete-from-relation-text": "確認後、現在のエンティティはエンティティ '{{entityName}}' から関連付けが解除されます。", + "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": "リレーションフィルター", + "relation-filter": "リレーションフィルター", + "additional-info": "追加情報(JSON)", + "invalid-additional-info": "追加情報のJSONの解析に失敗しました。", + "no-relations-text": "リレーションが見つかりません", + "not": "ない", + "copy-type": "タイプをコピー" + }, + "resource": { + "add": "リソースを追加", + "all-types": "すべて", + "copyId": "リソースIDをコピー", + "delete": "リソースを削除", + "delete-resource-text": "確認後、リソースは復元できなくなりますのでご注意ください。", + "delete-resource-title": "リソース '{{resourceTitle}}' を削除してもよろしいですか?", + "delete-resources-action-title": "{ count, plural, =1 {1 リソース} other {# リソース} } を削除", + "delete-resources-text": "選択されたリソースは、デバイスプロファイルで使用されている場合でも削除されることに注意してください。", + "delete-resources-title": "{ count, plural, =1 {1 リソース} other {# リソース} } を削除してもよろしいですか?", + "download": "リソースをダウンロード", + "drop-file": "リソースファイルをドラッグ&ドロップするか、クリックして選択してください。", + "drop-resource-file-or": "リソースファイルをドラッグ&ドロップするか", + "empty": "リソースは空です", + "file-name": "ファイル名", + "idCopiedMessage": "リソースIDがクリップボードにコピーされました", + "no-resource-matching": "'{{widgetsBundle}}' に一致するリソースは見つかりませんでした。", + "no-resource-text": "リソースが見つかりません", + "open-widgets-bundle": "ウィジェットバンドルを開く", + "resource": "リソース", + "resource-file": "リソースファイル", + "resource-files": "リソースファイル", + "resource-library-details": "リソースの詳細", + "resource-type": "リソースタイプ", + "resources-library": "リソースライブラリ", + "search": "リソースを検索", + "selected-resources": "{ count, plural, =1 {1 リソース} other {# リソース} } 選択済み", + "system": "システム", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "title-max-length": "タイトルは256文字以内である必要があります", + "type": { + "jks": "JKS", + "js-module": "JSモジュール", + "lwm2m-model": "LWM2Mモデル", + "pkcs-12": "PKCS #12", + "general": "一般" + }, + "resource-sub-type": "サブタイプ", + "sub-type": { + "image": "画像", + "scada-symbol": "SCADAシンボル", + "extension": "拡張機能", + "module": "モジュール" + }, + "resource-is-in-use": "リソースは他のエンティティによって使用されています", + "resources-are-in-use": "リソースは他のエンティティによって使用されています", + "resource-is-in-use-text": "リソース '{{title}}' は以下のエンティティによって使用されているため、削除されませんでした。", + "resources-are-in-use-text": "すべてのリソースが削除されなかったのは、他のエンティティによって使用されているためです。
    参照されているエンティティは、対応するリソース行の 参照 ボタンをクリックすることで確認できます。
    それでもこれらのリソースを削除したい場合は、テーブルから選択し、選択したリソースを削除 ボタンをクリックしてください。", + "delete-resource-in-use-text": "それでもリソースを削除したい場合は、削除してもよい ボタンをクリックしてください。" + }, + "javascript": { + "add": "JavaScriptリソースを追加", + "delete": "JavaScriptリソースを削除", + "delete-javascript-resource-text": "確認後、JavaScriptリソースは復元できなくなりますのでご注意ください。", + "delete-javascript-resource-title": "JavaScriptリソース '{{resourceTitle}}' を削除してもよろしいですか?", + "delete-javascript-resources-action-title": "JavaScript { count, plural, =1 {1 リソース} other {# リソース} } を削除", + "delete-javascript-resources-text": "選択されたJavaScriptリソースは、JavaScript関数で使用されている場合でも削除されることに注意してください。", + "delete-javascript-resources-title": "JavaScript { count, plural, =1 {1 リソース} other {# リソース} } を削除してもよろしいですか?", + "delete-javascript-resource-in-use-text": "それでもJavaScriptリソースを削除したい場合は、削除してもよい ボタンをクリックしてください。", + "download": "JavaScriptリソースをダウンロード", + "upload-from-file": "ファイルからJavaScriptをアップロード", + "resource-file": "JavaScriptリソースファイル", + "drop-file": "JavaScriptファイルをドラッグ&ドロップするか、クリックして選択してください。", + "drop-resource-file-or": "JavaScriptファイルをドラッグ&ドロップするか", + "javascript-library": "JavaScriptライブラリ", + "javascript-type": "JavaScriptタイプ", + "javascript-resource-details": "JavaScriptリソースの詳細", + "javascript-resource-is-in-use": "JavaScriptリソースは他のエンティティによって使用されています", + "javascript-resources-are-in-use": "JavaScriptリソースは他のエンティティによって使用されています", + "javascript-resource-is-in-use-text": "JavaScriptリソース '{{title}}' は以下のエンティティによって使用されているため、削除されませんでした。", + "javascript-resources-are-in-use-text": "すべてのJavaScriptリソースが削除されなかったのは、他のエンティティによって使用されているためです。
    参照されているエンティティは、対応するリソース行の 参照 ボタンをクリックすることで確認できます。
    それでもこれらのJavaScriptリソースを削除したい場合は、テーブルから選択し、選択したJavaScriptリソースを削除 ボタンをクリックしてください。", + "search": "JavaScriptリソースを検索", + "selected-javascript-resources": "{ count, plural, =1 {1 JavaScriptリソース} other {# JavaScriptリソース} } 選択済み", + "no-javascript-resource-text": "JavaScriptリソースが見つかりません", + "all-types": "すべて", + "module-script": "モジュールスクリプト" + }, + "rpc": { + "error": { + "target-device-is-not-set": "ターゲットデバイスが設定されていません!", + "invalid-target-entity": "RPCコマンドは{{entityType}}エンティティではサポートされていません。", + "failed-to-resolve-target-device": "ターゲットデバイスの解決に失敗しました!", + "request-timeout": "リクエストタイムアウト", + "rpc-http-error": "エラー: {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "ルールチェーン", + "rulechain-events": "ルールチェーンイベント", + "rulechains": "ルールチェーン", + "root": "ルート", + "delete": "ルールチェーンを削除", + "name": "名前", + "name-required": "名前は必須です。", + "name-max-length": "名前は256文字以内である必要があります", + "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 ルールチェーン} other {# ルールチェーン} } を削除してもよろしいですか?", + "delete-rulechains-action-title": "{ count, plural, =1 {1 ルールチェーン} 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": "ルールチェーンIDをコピー", + "idCopiedMessage": "ルールチェーンIDがクリップボードにコピーされました", + "select-rulechain": "ルールチェーンを選択", + "no-rulechains-matching": "'{{entity}}' に一致するルールチェーンは見つかりませんでした。", + "rulechain-required": "ルールチェーンは必須です", + "management": "ルール管理", + "debug-mode": "デバッグモード", + "search": "ルールチェーンを検索", + "selected-rulechains": "{ count, plural, =1 {1 ルールチェーン} other {# ルールチェーン} } 選択済み", + "open-rulechain": "ルールチェーンを開く", + "edge-template-root": "テンプレートルート", + "assign-to-edge": "Edgeに割り当て", + "edge-rulechain": "Edgeルールチェーン", + "unassign-rulechain-from-edge-text": "確認後、このルールチェーンはEdgeから割り当て解除され、Edgeからアクセスできなくなります。", + "unassign-rulechains-from-edge-title": "{ count, plural, =1 {1 ルールチェーン} other {# ルールチェーン} } をEdgeから割り当て解除してもよろしいですか?", + "unassign-rulechains-from-edge-text": "確認後、選択したすべてのルールチェーンはEdgeから割り当て解除され、Edgeからアクセスできなくなります。", + "assign-rulechain-to-edge-title": "ルールチェーンをEdgeに割り当て", + "assign-rulechain-to-edge-text": "Edgeに割り当てるルールチェーンを選択してください", + "set-edge-template-root-rulechain": "ルールチェーンをEdgeテンプレートルートとして設定", + "set-edge-template-root-rulechain-title": "ルールチェーン '{{ruleChainName}}' をEdgeテンプレートルートとして設定してもよろしいですか?", + "set-edge-template-root-rulechain-text": "確認後、このルールチェーンはEdgeテンプレートルートとなり、新しく作成されたEdgeのルートルールチェーンとなります。", + "invalid-rulechain-type-error": "ルールチェーンをインポートできません: 無効なルールチェーンタイプ。期待されるタイプは {{expectedRuleChainType}} です。", + "set-auto-assign-to-edge": "ルールチェーンを作成時にEdgeに自動的に割り当て", + "set-auto-assign-to-edge-title": "作成時にEdgeルールチェーン '{{ruleChainName}}' をEdgeに自動的に割り当ててもよろしいですか?", + "set-auto-assign-to-edge-text": "確認後、このEdgeルールチェーンは作成時に自動的にEdgeに割り当てられます。", + "unset-auto-assign-to-edge": "作成時にルールチェーンをEdgeに自動的に割り当てない", + "unset-auto-assign-to-edge-title": "作成時にEdgeルールチェーン '{{ruleChainName}}' をEdgeに自動的に割り当てないことを確認してもよろしいですか?", + "unset-auto-assign-to-edge-text": "確認後、このEdgeルールチェーンは作成時にEdgeに自動的に割り当てられなくなります。", + "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文字以内である必要があります", + "type": "タイプ", + "rule-node-description": "ルールノードの説明", + "delete": "ルールノードを削除", + "select-all-objects": "すべてのノードと接続を選択", + "deselect-all-objects": "すべてのノードと接続を選択解除", + "delete-selected-objects": "選択されたノードと接続を削除", + "delete-selected": "選択されたものを削除", + "create-nested-rulechain": "ネストされたルールチェーンを作成", + "select-all": "すべてを選択", + "copy-selected": "選択したものをコピー", + "deselect-all": "すべてを選択解除", + "rulenode-details": "ルールノードの詳細", + "debug-mode": "デバッグモード", + "singleton": "シングルトン", + "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-flow": "フロー", + "type-flow-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": "スクリプト関数をテスト", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "メッセージ", + "message-type": "メッセージタイプ", + "select-message-type": "メッセージタイプを選択", + "message-type-required": "メッセージタイプは必須です", + "metadata": "メタデータ", + "metadata-required": "メタデータのエントリは空にできません。", + "output": "出力", + "test": "テスト", + "help": "ヘルプ", + "reset-debug-settings": "すべてのノードのデバッグ設定をリセット", + "test-with-this-message": "このメッセージで{{test}}", + "queue-hint": "メッセージを別キューへ転送するためのキューを選択します。既定では 'Main' キューが使用されます。", + "queue-singleton-hint": "マルチインスタンス環境でメッセージを転送するためのキューを選択します。既定では 'Main' キューが使用されます。" + }, + "rule-node-config": { + "id": "ID", + "additional-info": "追加情報", + "advanced-settings": "詳細設定", + "create-entity-if-not-exists": "エンティティが存在しない場合は新規作成", + "create-entity-if-not-exists-hint": "有効にすると、指定したパラメータの新しいエンティティが、まだ存在しない場合に作成されます。既存のエンティティは、そのままリレーションに使用されます。", + "select-device-connectivity-event": "デバイス接続イベントを選択", + "entity-name-pattern": "名前パターン", + "device-name-pattern": "デバイス名", + "asset-name-pattern": "アセット名", + "entity-view-name-pattern": "エンティティビュー名", + "customer-title-pattern": "顧客タイトル", + "dashboard-name-pattern": "ダッシュボードタイトル", + "user-name-pattern": "ユーザーEmail", + "edge-name-pattern": "Edge名", + "entity-name-pattern-required": "名前パターンは必須です", + "entity-name-pattern-hint": "名前パターンフィールドはテンプレート化をサポートします。$[messageKey] を使用してメッセージから値を抽出し、${metadataKey} を使用してメタデータから値を抽出します。", + "copy-message-type": "メッセージタイプをコピー", + "entity-type-pattern": "タイプパターン", + "entity-type-pattern-required": "タイプパターンは必須です", + "message-type-value": "メッセージタイプ値", + "message-type-value-required": "メッセージタイプ値は必須です", + "message-type-value-max-length": "メッセージタイプ値は256文字以内である必要があります", + "output-message-type": "出力メッセージタイプ", + "entity-cache-expiration": "エンティティキャッシュ有効期限(秒)", + "entity-cache-expiration-hint": "見つかったエンティティレコードを保存できる最大時間間隔を指定します。0 は、レコードが期限切れにならないことを意味します。", + "entity-cache-expiration-required": "エンティティキャッシュ有効期限は必須です。", + "entity-cache-expiration-range": "エンティティキャッシュ有効期限は0以上である必要があります。", + "customer-name-pattern": "顧客タイトル", + "customer-name-pattern-required": "顧客タイトルは必須です", + "customer-name-pattern-hint": "$[messageKey] を使用してメッセージから値を抽出し、${metadataKey} を使用してメタデータから値を抽出します。", + "create-customer-if-not-exists": "顧客が存在しない場合は新規作成", + "unassign-from-customer": "オリジネーターがダッシュボードの場合、特定の顧客から割り当て解除", + "unassign-from-customer-tooltip": "複数の顧客に同時に割り当て可能なのはダッシュボードのみです。\nメッセージのオリジネーターがダッシュボードの場合、割り当て解除する顧客のタイトルを明示的に指定する必要があります。", + "customer-cache-expiration": "顧客キャッシュ有効期限(秒)", + "customer-cache-expiration-hint": "見つかった顧客レコードを保存できる最大時間間隔を指定します。0 は、レコードが期限切れにならないことを意味します。", + "customer-cache-expiration-required": "顧客キャッシュ有効期限は必須です。", + "customer-cache-expiration-range": "顧客キャッシュ有効期限は0以上である必要があります。", + "interval-start": "間隔開始", + "interval-end": "間隔終了", + "time-unit": "時間単位", + "fetch-mode": "取得モード", + "order-by-timestamp": "タイムスタンプで並べ替え", + "limit": "制限", + "limit-hint": "最小値は2、最大値は1000です。単一のエントリを取得する場合は、取得モード 'First' または 'Last' を選択してください。", + "limit-required": "制限は必須です。", + "limit-range": "制限は2〜1000の範囲である必要があります。", + "time-unit-milliseconds": "ミリ秒", + "time-unit-seconds": "秒", + "time-unit-minutes": "分", + "time-unit-hours": "時間", + "time-unit-days": "日", + "time-value-range": "1〜2147483647 の範囲で指定できます。", + "start-interval-value-required": "間隔開始は必須です。", + "end-interval-value-required": "間隔終了は必須です。", + "filter": "フィルター", + "switch": "スイッチ", + "math-templatization-tooltip": "このフィールドはテンプレート化をサポートします。$[messageKey] を使用してメッセージから値を抽出し、${metadataKey} を使用してメタデータから値を抽出します。", + "add-message-type": "メッセージタイプを追加", + "select-message-types-required": "少なくとも1つのメッセージタイプを選択してください。", + "select-message-types": "メッセージタイプを選択", + "no-message-types-found": "メッセージタイプが見つかりません", + "no-message-type-matching": "'{{messageType}}' は見つかりませんでした。", + "create-new-message-type": "新しいメッセージタイプを作成します。", + "message-types-required": "メッセージタイプは必須です。", + "client-attributes": "クライアント属性", + "shared-attributes": "共有属性", + "server-attributes": "サーバー属性", + "attributes-keys": "属性キー", + "attributes-keys-required": "属性キーは必須です", + "attributes-scope": "属性スコープ", + "attributes-scope-value": "属性スコープ値", + "attributes-scope-value-copy": "属性スコープ値をコピー", + "attributes-scope-hint": "メタデータキー 'scope' を使用して、メッセージごとに属性スコープを動的に設定できます。指定した場合、構成で設定したスコープより優先されます。", + "notify-device": "デバイスへの通知を強制", + "send-attributes-updated-notification": "属性更新通知を送信", + "send-attributes-updated-notification-hint": "更新された属性に関する通知を、ルールエンジンキューへ別メッセージとして送信します。", + "send-attributes-deleted-notification": "属性削除通知を送信", + "send-attributes-deleted-notification-hint": "削除された属性に関する通知を、ルールエンジンキューへ別メッセージとして送信します。", + "update-attributes-only-on-value-change": "値が変化した場合のみ属性を保存", + "update-attributes-only-on-value-change-hint": "値が変化したかどうかに関係なく、受信メッセージごとに属性を更新します。APIの使用量が増え、パフォーマンスが低下します。", + "update-attributes-only-on-value-change-hint-enabled": "値が変化した場合にのみ属性を更新します。値が変化していない場合、属性のタイムスタンプ更新も属性変更通知の送信も行われません。", + "fetch-credentials-to-metadata": "認証情報をメタデータに取得", + "notify-device-on-update-hint": "有効にすると、共有属性の更新についてデバイスへの通知を強制します。無効の場合、通知動作は受信メッセージのメタデータに含まれる 'notifyDevice' パラメータで制御されます。通知をオフにするには、メタデータに 'notifyDevice' パラメータを含め、値を 'false' に設定する必要があります。それ以外の場合は、デバイスへの通知がトリガーされます。", + "notify-device-on-delete-hint": "有効にすると、共有属性の削除についてデバイスへの通知を強制します。無効の場合、通知動作は受信メッセージのメタデータに含まれる 'notifyDevice' パラメータで制御されます。通知をオンにするには、メタデータに 'notifyDevice' パラメータを含め、値を 'true' に設定する必要があります。それ以外の場合は、デバイスへの通知はトリガーされません。", + "latest-timeseries": "最新の時系列データキー", + "timeseries-keys": "時系列キー", + "timeseries-keys-required": "少なくとも1つの時系列キーを選択してください。", + "add-timeseries-key": "時系列キーを追加", + "add-message-field": "メッセージフィールドを追加", + "relation-search-parameters": "リレーション検索パラメータ", + "relation-parameters": "リレーションパラメータ", + "add-metadata-field": "メタデータフィールドを追加", + "data-keys": "メッセージフィールド名", + "copy-from": "コピー元", + "data-to-metadata": "データ→メタデータ", + "metadata-to-data": "メタデータ→データ", + "use-regular-expression-hint": "正規表現を使用して、パターンでキーをコピーします。\n\nヒントとコツ:\n'Enter' を押してフィールド名の入力を完了します。\n'Backspace' を押してフィールド名を削除します。複数のフィールド名をサポートします。", + "interval": "間隔", + "interval-required": "間隔は必須です", + "interval-hint": "秒単位の重複排除間隔。", + "interval-min-error": "許可される最小値は1です", + "max-pending-msgs": "保留中メッセージの最大数", + "max-pending-msgs-hint": "一意の重複排除IDごとにメモリに保持されるメッセージの最大数。", + "max-pending-msgs-required": "保留中メッセージの最大数は必須です", + "max-pending-msgs-max-error": "許可される最大値は1000です", + "max-pending-msgs-min-error": "許可される最小値は1です", + "max-retries": "最大再試行回数", + "max-retries-required": "最大再試行回数は必須です", + "max-retries-hint": "重複排除されたメッセージをキューに投入する最大再試行回数。再試行の間に10秒の遅延が使用されます", + "max-retries-max-error": "許可される最大値は100です", + "max-retries-min-error": "許可される最小値は0です", + "strategy": "戦略", + "strategy-required": "戦略は必須です", + "strategy-all-hint": "重複排除期間中に到着したすべてのメッセージを、単一のJSON配列メッセージとして返します。各要素は msg と metadata の内部プロパティを持つオブジェクトを表します。", + "strategy-first-hint": "重複排除期間中に到着した最初のメッセージを返します。", + "strategy-last-hint": "重複排除期間中に到着した最後のメッセージを返します。", + "first": "最初", + "last": "最後", + "all": "すべて", + "output-msg-type-hint": "重複排除結果のメッセージタイプ。", + "queue-name-hint": "重複排除結果を発行するキュー名。", + "keys": "キー", + "keys-required": "キーは必須です", + "rename-keys-in": "キー名の変更対象", + "data": "データ", + "message": "メッセージ", + "metadata": "メタデータ", + "current-key-name": "現在のキー名", + "key-name-required": "キー名は必須です", + "new-key-name": "新しいキー名", + "new-key-name-required": "新しいキー名は必須です", + "metadata-keys": "メタデータフィールド名", + "json-path-expression": "JSONパス式", + "json-path-expression-required": "JSONパス式は必須です", + "json-path-expression-hint": "JSONPath は、JSON構造内の要素または要素集合へのパスを指定します。'$' はルートオブジェクトまたは配列を表します。", + "relations-query": "リレーションクエリ", + "device-relations-query": "デバイスリレーションクエリ", + "max-relation-level": "最大リレーションレベル", + "max-relation-level-error": "値は0より大きいか、未指定である必要があります。", + "max-relation-level-invalid": "値は整数である必要があります。", + "relation-type": "リレーションタイプ", + "relation-type-pattern": "リレーションタイプパターン", + "relation-type-pattern-required": "リレーションタイプパターンは必須です", + "relation-types-list": "伝播するリレーションタイプ", + "relation-types-list-hint": "「伝播するリレーションタイプ」を選択しない場合、アラームはリレーションタイプによるフィルタリングなしで伝播されます。", + "unlimited-level": "無制限レベル", + "latest-telemetry": "最新のテレメトリ", + "add-telemetry-key": "テレメトリキーを追加", + "delete-from": "削除元", + "use-regular-expression-delete-hint": "正規表現を使用して、パターンでキーを削除します。\n\nヒントとコツ:\n'Enter' を押してフィールド名の入力を完了します。\n'Backspace' を押してフィールド名を削除します。\n複数のフィールド名をサポートします。", + "fetch-into": "取得先", + "attr-mapping": "属性マッピング:", + "source-attribute": "ソース属性キー", + "source-attribute-required": "ソース属性キーは必須です。", + "source-telemetry": "ソーステレメトリキー", + "source-telemetry-required": "ソーステレメトリキーは必須です。", + "target-key": "ターゲットキー", + "target-key-required": "ターゲットキーは必須です。", + "attr-mapping-required": "少なくとも1つのマッピングエントリを指定してください。", + "fields-mapping": "フィールドマッピング", + "fields-mapping-hint": "メッセージフィールドを $entityId に設定すると、メッセージのオリジネーターIDが指定したテーブル列に保存されます。", + "relations-query-config-direction-suffix": "オリジネーター", + "profile-name": "プロファイル名", + "fetch-circle-parameter-info-from-metadata-hint": "メタデータフィールド '{{perimeterKeyName}}' は次の形式で定義する必要があります: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "メタデータフィールド '{{perimeterKeyName}}' は次の形式で定義する必要があります: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "メッセージから値を抽出するには $[messageKey] を使用し、メタデータから値を抽出するには ${metadataKey} を使用します。", + "fields-mapping-required": "少なくとも1つのフィールドマッピングを指定してください。", + "at-least-one-field-required": "少なくとも1つの入力フィールドに値が指定されている必要があります。", + "originator-fields-sv-map-hint": "ターゲットキーのフィールドはテンプレート化をサポートします。メッセージから値を抽出するには $[messageKey] を使用し、メタデータから値を抽出するには ${metadataKey} を使用します。", + "sv-map-hint": "テンプレート化をサポートするのはターゲットキーのフィールドのみです。メッセージから値を抽出するには $[messageKey] を使用し、メタデータから値を抽出するには ${metadataKey} を使用します。", + "source-field": "ソースフィールド", + "source-field-required": "ソースフィールドは必須です。", + "originator-source": "オリジネーターソース", + "new-originator": "新しいオリジネーター", + "originator-customer": "顧客", + "originator-tenant": "テナント", + "originator-related": "関連エンティティ", + "originator-alarm-originator": "アラームオリジネーター", + "originator-entity": "名前パターンによるエンティティ", + "clone-message": "メッセージを複製", + "transform": "変換", + "default-ttl": "デフォルトTTL", + "default-ttl-required": "デフォルトTTLは必須です。", + "default-ttl-hint": "ルールノードはメッセージメタデータから Time-to-Live(TTL)値を取得します。値が存在しない場合は、構成で指定されたTTLがデフォルトになります。値が0に設定されている場合は、テナントプロファイル構成のTTLが適用されます。", + "default-ttl-zero-hint": "値が0に設定されている場合、TTLは適用されません。", + "min-default-ttl-message": "最小TTLは0のみ許可されています。", + "generation-parameters": "生成パラメータ", + "message-count": "生成メッセージ数の上限(0 - 無制限)", + "message-count-required": "生成メッセージ数の上限は必須です。", + "min-message-count-message": "最小メッセージ数は0のみ許可されています。", + "period-seconds": "期間(秒)", + "period-seconds-required": "期間は必須です。", + "generation-frequency-seconds": "生成頻度(秒)", + "generation-frequency-required": "生成頻度は必須です。", + "min-generation-frequency-message": "最小は60秒のみ許可されています。", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "秒単位の期間パターンを使用", + "use-metadata-period-in-seconds-patterns-hint": "選択すると、ルールノードはメッセージのメタデータまたはデータから、間隔が秒であるものとして秒単位の期間パターンを使用します。", + "period-in-seconds-pattern": "秒単位の期間パターン", + "period-in-seconds-pattern-required": "秒単位の期間パターンは必須です", + "min-period-seconds-message": "期間の最小値は60秒のみ許可されています。", + "originator": "オリジネーター", + "message-body": "メッセージ本文", + "message-metadata": "メッセージメタデータ", + "generate": "生成", + "current-rule-node": "現在のルールノード", + "current-tenant": "現在のテナント", + "generator-function": "ジェネレーター関数", + "test-generator-function": "ジェネレーター関数をテスト", + "generator": "ジェネレーター", + "test-filter-function": "フィルター関数をテスト", + "test-switch-function": "スイッチ関数をテスト", + "test-transformer-function": "トランスフォーマー関数をテスト", + "transformer": "トランスフォーマー", + "alarm-create-condition": "アラーム作成条件", + "test-condition-function": "条件関数をテスト", + "alarm-clear-condition": "アラームクリア条件", + "alarm-details-builder": "アラーム詳細ビルダー", + "test-details-function": "詳細関数をテスト", + "alarm-type": "アラームタイプ", + "select-entity-types": "エンティティタイプを選択", + "alarm-type-required": "アラームタイプは必須です。", + "alarm-severity": "アラーム重大度", + "alarm-severity-required": "アラーム重大度は必須です", + "alarm-severity-pattern": "アラーム重大度パターン", + "alarm-status-filter": "アラームステータスフィルター", + "alarm-status-list-empty": "アラームステータス一覧が空です", + "no-alarm-status-matching": "一致するアラームステータスが見つかりませんでした。", + "propagate": "関連エンティティへアラームを伝播", + "propagate-to-owner": "エンティティ所有者(顧客またはテナント)へアラームを伝播", + "propagate-to-tenant": "テナントへアラームを伝播", + "condition": "条件", + "details": "詳細", + "to-string": "文字列へ変換", + "test-to-string-function": "文字列変換関数をテスト", + "from-template": "送信元", + "from-template-required": "送信元は必須です", + "message-to-metadata": "メッセージからメタデータへ", + "metadata-to-message": "メタデータからメッセージへ", + "from-message": "メッセージから", + "from-metadata": "メタデータから", + "to-template": "宛先", + "to-template-required": "宛先テンプレートは必須です", + "mail-address-list-template-hint": "カンマ区切りのアドレス一覧。メタデータの値には ${metadataKey} を使用し、メッセージ本文の値には $[messageKey] を使用します", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "件名", + "subject-template-required": "件名テンプレートは必須です", + "body-template": "本文", + "body-template-required": "本文テンプレートは必須です", + "dynamic-mail-body-type": "動的メール本文タイプ", + "mail-body-type": "メール本文タイプ", + "body-type-template": "本文タイプテンプレート", + "reply-routing-configuration": "返信ルーティング設定", + "rpc-reply-routing-configuration-hint": "これらの構成パラメータは、返信を返送するために使用するサービス、セッション、リクエストを識別するメタデータキー名を指定します。", + "reply-routing-configuration-hint": "これらの構成パラメータは、返信を返送するために使用するサービスとリクエストを識別するメタデータキー名を指定します。", + "request-id-metadata-attribute": "リクエストID", + "service-id-metadata-attribute": "サービスID", + "session-id-metadata-attribute": "セッションID", + "timeout-sec": "秒単位のタイムアウト", + "timeout-required": "タイムアウトは必須です", + "min-timeout-message": "最小タイムアウト値は0のみ許可されています。", + "endpoint-url-pattern": "エンドポイントURLパターン", + "endpoint-url-pattern-required": "エンドポイントURLパターンは必須です", + "request-method": "リクエストメソッド", + "use-simple-client-http-factory": "シンプルクライアントHTTPファクトリーを使用", + "ignore-request-body": "リクエスト本文なし", + "parse-to-plain-text": "プレーンテキストに解析", + "parse-to-plain-text-hint": "選択すると、リクエスト本文のメッセージペイロードはJSON文字列からプレーンテキストに変換されます。例: msg = \"Hello,\\t\"world\"\" は Hello, \"world\" に解析されます", + "read-timeout": "ミリ秒単位の読み取りタイムアウト", + "read-timeout-hint": "値が0の場合、タイムアウトは無制限になります", + "max-parallel-requests-count": "並列リクエスト最大数", + "max-parallel-requests-count-hint": "値が0の場合、並列処理の制限なしを指定します", + "max-response-size": "最大レスポンスサイズ (in KB)", + "max-response-size-hint": "JSONやXMLペイロードなどのHTTPメッセージをデコードまたはエンコードする際に、データをバッファリングするために割り当てられる最大メモリ量", + "headers": "ヘッダー", + "headers-hint": "ヘッダー/値フィールドで、メタデータの値には ${metadataKey} を使用し、メッセージ本文の値には $[messageKey] を使用します", + "header": "ヘッダー", + "header-required": "ヘッダーは必須です", + "value": "値", + "value-required": "値は必須です", + "topic-pattern": "トピックパターン", + "key-pattern": "キーパターン", + "key-pattern-hint": "任意。有効なパーティション番号が指定されている場合、レコード送信時に使用されます。パーティションが指定されていない場合は、代わりにキーが使用されます。どちらも指定されていない場合は、ラウンドロビン方式でパーティションが割り当てられます。", + "topic-pattern-required": "トピックパターンは必須です", + "topic": "トピック", + "topic-required": "トピックは必須です", + "bootstrap-servers": "ブートストラップサーバー", + "bootstrap-servers-required": "ブートストラップサーバーの値は必須です", + "other-properties": "その他のプロパティ", + "key": "キー", + "key-required": "キーは必須です", + "retries": "失敗時の自動再試行回数", + "min-retries-message": "最小再試行回数は0のみ許可されています。", + "batch-size-bytes": "生成するバッチサイズ(バイト)", + "min-batch-size-bytes-message": "最小バッチサイズは0のみ許可されています。", + "linger-ms": "ローカルでバッファリングする時間(ms)", + "min-linger-ms-message": "最小値は0 msのみ許可されています。", + "buffer-memory-bytes": "クライアントバッファの最大サイズ(バイト)", + "min-buffer-memory-message": "最小バッファサイズは0のみ許可されています。", + "memory-buffer-size-range": "メモリバッファサイズは0〜{{max}} KBの範囲である必要があります", + "acks": "確認応答数", + "topic-arn-pattern": "トピックARNパターン", + "topic-arn-pattern-required": "トピックARNパターンは必須です", + "aws-access-key-id": "AWSアクセスキーID", + "aws-access-key-id-required": "AWSアクセスキーIDは必須です", + "aws-secret-access-key": "AWSシークレットアクセスキー", + "aws-secret-access-key-required": "AWSシークレットアクセスキーは必須です", + "aws-region": "AWSリージョン", + "aws-region-required": "AWSリージョンは必須です", + "exchange-name-pattern": "エクスチェンジ名パターン", + "routing-key-pattern": "ルーティングキーパターン", + "message-properties": "メッセージプロパティ", + "host": "ホスト", + "host-required": "ホストは必須です", + "port": "ポート", + "port-required": "ポートは必須です", + "port-range": "ポートは1〜65535の範囲である必要があります。", + "virtual-host": "仮想ホスト", + "username": "ユーザー名", + "password": "パスワード", + "automatic-recovery": "自動リカバリー", + "connection-timeout-ms": "接続タイムアウト(ms)", + "min-connection-timeout-ms-message": "最小値は0 msのみ許可されています。", + "handshake-timeout-ms": "ハンドシェイクタイムアウト(ms)", + "min-handshake-timeout-ms-message": "最小値は0 msのみ許可されています。", + "client-properties": "クライアントプロパティ", + "queue-url-pattern": "キューURLパターン", + "queue-url-pattern-required": "キューURLパターンは必須です", + "delay-seconds": "遅延(秒)", + "min-delay-seconds-message": "最小値は0秒のみ許可されています。", + "max-delay-seconds-message": "最大値は900秒のみ許可されています。", + "name": "名前", + "name-required": "名前は必須です。", + "queue-type": "キュータイプ", + "sqs-queue-standard": "標準", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCPプロジェクトID", + "gcp-project-id-required": "GCPプロジェクトIDは必須です", + "gcp-service-account-key": "GCPサービスアカウントキー ファイル", + "gcp-service-account-key-required": "GCPサービスアカウントキー ファイルは必須です", + "pubsub-topic-name": "トピック名", + "pubsub-topic-name-required": "トピック名は必須です", + "message-attributes": "メッセージ属性", + "message-attributes-hint": "名前/値フィールドで、メタデータから値を取得するには ${metadataKey} を使用し、メッセージ本文から値を取得するには $[messageKey] を使用します", + "connect-timeout": "接続タイムアウト(秒単位)", + "connect-timeout-required": "接続タイムアウトは必須です。", + "connect-timeout-range": "接続タイムアウトは1から200の範囲内である必要があります。", + "client-id": "クライアントID", + "client-id-hint": "ヒント:オプションです。空白のままにすると自動生成されます。クライアントIDを指定する場合は注意してください。ほとんどのMQTTブローカーは同じクライアントIDで複数の接続を許可しません。そのようなブローカーに接続するには、MQTTクライアントIDが一意である必要があります。プラットフォームがマイクロサービスモードで動作している場合、ルールノードのコピーが各マイクロサービスで起動されます。これにより同じIDを持つMQTTクライアントが複数作成され、ルールノードの失敗を引き起こす可能性があります。このような失敗を回避するには、以下の\"クライアントIDにサービスIDをサフィックスとして追加\"オプションを有効にしてください。", + "append-client-id-suffix": "クライアントIDにサービスIDをサフィックスとして追加", + "client-id-suffix-hint": "ヒント:オプションです。\"クライアントID\"を明示的に指定した場合に適用されます。選択すると、サービスIDがサフィックスとしてクライアントIDに追加されます。プラットフォームがマイクロサービスモードで動作している場合の失敗を回避するのに役立ちます。", + "device-id": "デバイスID", + "device-id-required": "デバイスIDは必須です。", + "clean-session": "クリーンセッション", + "enable-ssl": "SSLを有効化", + "credentials": "認証情報", + "credentials-type": "認証情報タイプ", + "credentials-type-required": "認証情報タイプは必須です。", + "credentials-anonymous": "匿名", + "credentials-basic": "ベーシック", + "credentials-pem": "PEM", + "credentials-pem-hint": "少なくともサーバーCA証明書ファイル、またはクライアント証明書ファイルとクライアント秘密鍵ファイルのペアが必要です", + "credentials-sas": "共有アクセス署名", + "sas-key": "SASキー", + "sas-key-required": "SASキーは必須です。", + "hostname": "ホスト名", + "hostname-required": "ホスト名は必須です。", + "azure-ca-cert": "CA証明書ファイル", + "username-required": "ユーザー名は必須です。", + "password-required": "パスワードは必須です。", + "ca-cert": "CA証明書ファイル", + "private-key": "秘密鍵ファイル", + "cert": "証明書ファイル", + "no-file": "ファイルが選択されていません。", + "drop-file": "ファイルをドロップするか、クリックしてファイルを選択してアップロードしてください。", + "private-key-password": "秘密鍵のパスワード", + "use-system-smtp-settings": "システムのSMTP設定を使用", + "use-metadata-dynamic-interval": "動的間隔を使用", + "metadata-dynamic-interval-hint": "間隔開始と間隔終了の入力フィールドはテンプレート化をサポートします。置換されるテンプレート値はミリ秒で設定する必要があります。メッセージから値を抽出するには $[messageKey] を使用し、メタデータから値を抽出するには ${metadataKey} を使用します。", + "use-metadata-interval-patterns-hint": "選択すると、ルールノードは間隔がミリ秒であるものとして、メッセージのメタデータまたはデータから開始/終了間隔パターンを使用します。", + "use-message-alarm-data": "メッセージのアラームデータを使用", + "overwrite-alarm-details": "アラーム詳細を上書き", + "use-alarm-severity-pattern": "アラーム重大度パターンを使用", + "check-all-keys": "指定したすべてのフィールドが存在することを確認", + "check-all-keys-hint": "選択すると、指定したすべてのキーがメッセージデータとメタデータに存在することを確認します。", + "check-relation-to-specific-entity": "特定エンティティとのリレーションを確認", + "check-relation-to-specific-entity-tooltip": "有効にすると、特定のエンティティとのリレーションの存在を確認します。それ以外の場合は、任意のエンティティとのリレーションの存在を確認します。どちらの場合も、リレーション検索は設定された方向とタイプに基づきます。", + "check-relation-hint": "方向とリレーションタイプに基づき、特定エンティティまたは任意のエンティティへのリレーションの存在を確認します。", + "delete-relation-with-specific-entity": "特定エンティティとのリレーションを削除", + "delete-relation-with-specific-entity-hint": "有効にすると、1つの特定エンティティとのリレーションのみを削除します。それ以外の場合は、一致するすべてのエンティティとのリレーションが削除されます。", + "delete-relation-hint": "受信メッセージのオリジネーターから、方向とタイプに基づいて、指定したエンティティまたはエンティティ一覧へのリレーションを削除します。", + "remove-current-relations": "現在のリレーションを削除", + "remove-current-relations-hint": "方向とタイプに基づいて、受信メッセージのオリジネーターから現在のリレーションを削除します。", + "change-originator-to-related-entity": "オリジネーターを関連エンティティに変更", + "change-originator-to-related-entity-hint": "送信されたメッセージを別のエンティティからのメッセージとして処理するために使用します。", + "start-interval": "間隔開始", + "end-interval": "間隔終了", + "start-interval-required": "間隔開始は必須です。", + "end-interval-required": "間隔終了は必須です。", + "smtp-protocol": "SMTPプロトコル", + "smtp-host": "SMTPホスト", + "smtp-host-required": "SMTPホストは必須です。", + "smtp-port": "SMTPポート", + "smtp-port-required": "SMTPポートを指定する必要があります。", + "smtp-port-range": "SMTPポートは1〜65535の範囲である必要があります。", + "timeout-msec": "タイムアウト (msec)", + "min-timeout-msec-message": "最小値は0 msのみ許可されています。", + "enter-username": "ユーザー名を入力", + "enter-password": "パスワードを入力", + "enable-tls": "TLSを有効化", + "tls-version": "TLSバージョン", + "enable-proxy": "プロキシを有効化", + "use-system-proxy-properties": "システムのプロキシプロパティを使用", + "proxy-host": "プロキシホスト", + "proxy-host-required": "プロキシホストは必須です。", + "proxy-port": "プロキシポート", + "proxy-port-required": "プロキシポートは必須です。", + "proxy-port-range": "プロキシポートは1〜65535の範囲である必要があります。", + "proxy-user": "プロキシユーザー", + "proxy-password": "プロキシパスワード", + "proxy-scheme": "プロキシスキーム", + "numbers-to-template": "送信先電話番号テンプレート", + "numbers-to-template-required": "送信先電話番号テンプレートは必須です", + "numbers-to-template-hint": "カンマ区切りの電話番号。メタデータの値には ${metadataKey} を使用し、メッセージ本文の値には $[messageKey] を使用します", + "sms-message-template": "SMSメッセージテンプレート", + "sms-message-template-required": "SMSメッセージテンプレートは必須です", + "use-system-sms-settings": "システムのSMSプロバイダー設定を使用", + "min-period-0-seconds-message": "最小期間は0秒のみ許可されます。", + "max-pending-messages": "保留中メッセージの最大数", + "max-pending-messages-required": "保留中メッセージの最大数は必須です。", + "max-pending-messages-range": "保留中メッセージの最大数は1〜100000の範囲である必要があります。", + "originator-types-filter": "発生元タイプフィルター", + "interval-seconds": "秒単位の間隔", + "interval-seconds-required": "間隔は必須です。", + "int-range": "値は最大整数制限 (2147483648) を超えないようにしてください", + "min-interval-seconds-message": "最小間隔は1秒です。", + "output-timeseries-key-prefix": "出力時系列キープレフィックス", + "output-timeseries-key-prefix-required": "出力時系列キープレフィックスは必須です。", + "separator-hint": "フィールド入力を完了するには \"Enter\" を押してください。", + "select-details": "詳細を選択", + "entity-details-id": "ID", + "entity-details-title": "タイトル", + "entity-details-country": "国", + "entity-details-state": "都道府県", + "entity-details-city": "市区町村", + "entity-details-zip": "郵便番号", + "entity-details-address": "住所", + "entity-details-address2": "住所2", + "entity-details-additional_info": "追加情報", + "entity-details-phone": "電話番号", + "entity-details-email": "Email", + "email-sender": "Email送信者", + "fields-to-check": "チェックするフィールド", + "add-detail": "詳細を追加", + "check-all-keys-tooltip": "有効にすると、受信メッセージとそのメタデータ内の、メッセージおよびメタデータのフィールド名に列挙されたすべてのフィールドの存在を確認します。", + "fields-to-check-hint": "\"Enter\" を押してフィールド名の入力を完了します。複数のフィールド名に対応しています。", + "entity-details-list-empty": "少なくとも1つの詳細を選択してください。", + "alarm-status": "アラームステータス", + "alarm-required": "少なくとも1つのアラームステータスを選択してください。", + "no-entity-details-matching": "一致するエンティティ詳細が見つかりませんでした。", + "custom-table-name": "カスタムテーブル名", + "custom-table-name-required": "テーブル名は必須です", + "custom-table-hint": "テーブルはCassandraクラスターに作成されている必要があり、共通のTBテーブルへのデータ挿入を避けるために、名前はプレフィックス 'cs_tb_' で始まる必要があります。ここには 'cs_tb_' プレフィックスなしでテーブル名を入力してください。", + "message-field": "メッセージフィールド", + "message-field-required": "メッセージフィールドは必須です。", + "table-col": "テーブル列", + "table-col-required": "テーブル列は必須です。", + "latitude-field-name": "緯度フィールド名", + "longitude-field-name": "経度フィールド名", + "latitude-field-name-required": "緯度フィールド名は必須です。", + "longitude-field-name-required": "経度フィールド名は必須です。", + "fetch-perimeter-info-from-metadata": "メタデータからペリメータ情報を取得", + "fetch-perimeter-info-from-metadata-tooltip": "ペリメータタイプが 'Polygon' に設定されている場合、メタデータフィールド '{{perimeterKeyName}}' の値は、追加の解析を行わずにペリメータ定義として設定されます。それ以外の場合、ペリメータタイプが 'Circle' に設定されていると、'{{perimeterKeyName}}' メタデータフィールドの値が解析され、円のペリメータ定義に使用する 'latitude'、'longitude'、'radius'、'radiusUnit' フィールドが抽出されます。", + "perimeter-key-name": "ペリメータキー名", + "perimeter-key-name-hint": "ペリメータ情報を含むメタデータフィールド名。", + "perimeter-key-name-required": "ペリメータキー名は必須です。", + "perimeter-circle": "円", + "perimeter-polygon": "ポリゴン", + "perimeter-type": "ペリメータタイプ", + "circle-center-latitude": "中心緯度", + "circle-center-latitude-required": "中心緯度は必須です。", + "circle-center-longitude": "中心経度", + "circle-center-longitude-required": "中心経度は必須です。", + "range-unit-meter": "メートル", + "range-unit-kilometer": "キロメートル", + "range-unit-foot": "フィート", + "range-unit-mile": "マイル", + "range-unit-nautical-mile": "海里", + "range-units": "範囲単位", + "range-units-required": "範囲単位は必須です。", + "range": "範囲", + "range-required": "範囲は必須です。", + "polygon-definition": "ポリゴン定義", + "polygon-definition-required": "ポリゴン定義は必須です。", + "polygon-definition-hint": "ポリゴンを手動で定義するには、次の形式を使用します: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]]。", + "min-inside-duration": "最小内側滞在時間", + "min-inside-duration-value-required": "最小内側滞在時間は必須です", + "min-inside-duration-time-unit": "最小内側滞在時間の時間単位", + "min-outside-duration": "最小外側滞在時間", + "min-outside-duration-value-required": "最小外側滞在時間は必須です", + "min-outside-duration-time-unit": "最小外側滞在時間の時間単位", + "tell-failure-if-absent": "失敗を通知", + "tell-failure-if-absent-hint": "選択したキーのうち少なくとも1つが存在しない場合、送信メッセージは \"Failure\" を報告します。", + "get-latest-value-with-ts": "最新テレメトリ値のタイムスタンプを取得", + "get-latest-value-with-ts-hint": "選択した場合、最新テレメトリ値にはタイムスタンプも含まれます。例: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "null文字列を無視", + "ignore-null-strings-hint": "選択した場合、ルールノードは値が空のエンティティフィールドを無視します。", + "add-metadata-key-values-as-kafka-headers": "メッセージメタデータのキー値ペアをKafkaレコードヘッダーに追加", + "add-metadata-key-values-as-kafka-headers-hint": "選択した場合、メッセージメタデータのキー値ペアが、事前定義された文字セットエンコーディングを使用するバイト配列として、送信レコードのヘッダーに追加されます。", + "charset-encoding": "文字セットエンコーディング", + "charset-encoding-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": "キュー名はドロップダウンリストから選択するか、カスタム名を追加できます。", + "device-profile-node-hint": "アラーム状態評価の継続性を確保するために、持続時間や繰り返し条件がある場合に有用です。", + "persist-alarm-rules": "アラームルールの状態を永続化", + "persist-alarm-rules-hint": "有効にすると、ルールノードは処理状態をデータベースに保存します。", + "fetch-alarm-rules": "アラームルールの状態を取得", + "fetch-alarm-rules-hint": "有効にすると、ルールノードは初期化時に処理状態を復元し、サーバー再起動後でもアラームが発生するようにします。無効の場合、デバイスから最初のメッセージが到着したときに状態が復元されます。", + "input-value-key": "入力値キー", + "input-value-key-required": "入力値キーは必須です。", + "output-value-key": "出力値キー", + "output-value-key-required": "出力値キーは必須です。", + "number-of-digits-after-floating-point": "小数点以下の桁数", + "number-of-digits-after-floating-point-range": "小数点以下の桁数は0〜15の範囲である必要があります。", + "failure-if-delta-negative": "デルタが負の場合は失敗を通知", + "failure-if-delta-negative-tooltip": "デルタ値が負の場合、ルールノードはメッセージ処理を失敗にします。", + "use-caching": "キャッシュを使用", + "use-caching-tooltip": "ルールノードはパフォーマンス向上のため、受信メッセージから届く \"{{inputValueKey}}\" の値をキャッシュします。注意: 別の場所で \"{{inputValueKey}}\" の値を変更しても、キャッシュは更新されません。", + "add-time-difference-between-readings": "\"{{inputValueKey}}\" の読み取り間の時間差を追加", + "add-time-difference-between-readings-tooltip": "有効にすると、ルールノードは送信メッセージに \"{{periodValueKey}}\" を追加します。", + "period-value-key": "期間値キー", + "period-value-key-required": "期間値キーは必須です。", + "general-pattern-hint": "メタデータの値には ${metadataKey}、メッセージ本文の値には $[messageKey] を使用します。", + "alarm-severity-pattern-hint": "メタデータの値には ${metadataKey}、メッセージ本文の値には $[messageKey] を使用します。アラーム重大度はシステム(CRITICAL、MAJOR など)である必要があります。", + "output-node-name-hint": "ルールノード名 は、出力メッセージの リレーションタイプ に対応し、呼び出し元ルールチェーン内で他のルールノードへメッセージを転送するために使用されます。", + "use-server-ts": "サーバータイムスタンプを使用", + "use-server-ts-hint": "明示的なタイムスタンプがない時系列データに対して、サーバーの現在のタイムスタンプを使用します。これは、複数のソースからのメッセージを処理する場合や、メッセージが順不同で到着した場合でも、適切な順序を維持するのに役立ちます。", + "kv-map-pattern-hint": "すべての入力フィールドはテンプレート化をサポートします。$[messageKey] を使用してメッセージから値を抽出し、${metadataKey} を使用してメタデータから値を抽出します。", + "kv-map-single-pattern-hint": "入力フィールドはテンプレート化をサポートします。$[messageKey] を使用してメッセージから値を抽出し、${metadataKey} を使用してメタデータから値を抽出します。", + "shared-scope": "共有スコープ", + "server-scope": "サーバースコープ", + "client-scope": "クライアントスコープ", + "attribute-type": "属性", + "attribute-type-description": "データベースから属性値を取得", + "attribute-type-result-description": "結果をデータベース内のエンティティ属性として保存", + "constant-type": "定数", + "constant-type-description": "定数値を定義", + "time-series-type": "時系列", + "time-series-type-description": "データベースから最新の時系列値を取得", + "time-series-type-result-description": "結果をエンティティの時系列としてデータベースに保存", + "message-body-type": "メッセージ", + "message-body-type-description": "受信メッセージから引数値を取得", + "message-body-type-result-description": "結果を送信メッセージに追加", + "message-metadata-type": "メタデータ", + "message-metadata-type-description": "受信メッセージのメタデータから引数値を取得", + "message-metadata-result-description": "結果を送信メッセージのメタデータに追加", + "argument-tile": "引数", + "no-arguments-prompt": "引数が設定されていません", + "result-title": "結果", + "functions-field-input": "関数", + "no-option-found": "オプションが見つかりません", + "argument-source-field-input": "ソース", + "argument-source-field-input-required": "引数ソースは必須です。", + "argument-key-field-input": "キー", + "argument-key-field-input-required": "引数キーは必須です。", + "constant-value-field-input": "定数値", + "constant-value-field-input-required": "定数値は必須です。", + "attribute-scope-field-input": "属性スコープ", + "attribute-scope-field-input-required": "属性スコープは必須です。", + "default-value-field-input": "デフォルト値", + "type-field-input": "タイプ", + "type-field-input-required": "タイプは必須です。", + "key-field-input": "キー", + "add-entity-type": "エンティティタイプを追加", + "add-device-profile": "デバイスプロファイルを追加", + "key-field-input-required": "キーは必須です。", + "number-floating-point-field-input": "小数点以下の桁数", + "number-floating-point-field-input-hint": "0 を指定すると結果を整数に変換します", + "add-to-message-field-input": "メッセージに追加", + "add-to-metadata-field-input": "メタデータに追加", + "custom-expression-field-input": "数式", + "custom-expression-field-input-required": "数式は必須です", + "custom-expression-field-input-hint": "評価する数式を指定します。デフォルトの式は華氏を摂氏に変換する方法を示します", + "retained-message": "保持メッセージ", + "attributes-mapping": "属性マッピング", + "latest-telemetry-mapping": "最新テレメトリマッピング", + "add-mapped-attribute-to": "マッピングされた属性の追加先", + "add-mapped-latest-telemetry-to": "マッピングされた最新テレメトリの追加先", + "add-mapped-fields-to": "マッピングされたフィールドの追加先", + "add-selected-details-to": "選択した詳細の追加先", + "clear-selected-types": "選択したタイプをクリア", + "clear-selected-details": "選択した詳細をクリア", + "clear-selected-fields": "選択したフィールドをクリア", + "clear-selected-keys": "選択したキーをクリア", + "geofence-configuration": "ジオフェンス設定", + "coordinate-field-names": "座標フィールド名", + "coordinate-field-hint": "ルールノードはメッセージから指定されたフィールドの取得を試みます。存在しない場合はメタデータから参照します。", + "presence-monitoring-strategy": "在圏監視戦略", + "presence-monitoring-strategy-on-first-message": "最初のメッセージ時", + "presence-monitoring-strategy-on-each-message": "メッセージごと", + "presence-monitoring-strategy-on-first-message-hint": "前回の在圏ステータス 'Entered' または 'Left' の更新から、設定された最小時間が経過した後の最初のメッセージで、在圏ステータス 'Inside' または 'Outside' を報告します。", + "presence-monitoring-strategy-on-each-message-hint": "在圏ステータス 'Entered' または 'Left' の更新後、各メッセージで在圏ステータス 'Inside' または 'Outside' を報告します。", + "fetch-credentials-to": "資格情報の取得先", + "add-originator-attributes-to": "発生元属性の追加先", + "originator-attributes": "発生元属性", + "fetch-latest-telemetry-with-timestamp": "最新テレメトリをタイムスタンプ付きで取得", + "fetch-latest-telemetry-with-timestamp-tooltip": "選択した場合、最新テレメトリ値はタイムスタンプ付きで送信メタデータに追加されます。例: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "属性が不足している場合は失敗を通知", + "tell-failure-tooltip": "選択したキーのうち少なくとも1つが存在しない場合、送信メッセージは \"Failure\" を報告します。", + "created-time": "作成時刻", + "chip-help": "{{inputName}} の入力を完了するには 'Enter' を押してください。 \n{{inputName}} を削除するには 'Backspace' を押してください。 \n複数の値に対応しています。", + "detail": "詳細", + "field-name": "フィールド名", + "device-profile": "デバイスプロファイル", + "entity-type": "エンティティタイプ", + "message-type": "メッセージタイプ", + "timeseries-key": "時系列キー", + "type": "タイプ", + "first-name": "名", + "last-name": "姓", + "label": "ラベル", + "originator-fields-mapping": "発生元フィールドマッピング", + "add-mapped-originator-fields-to": "マッピングされた発生元フィールドの追加先", + "fields": "フィールド", + "skip-empty-fields": "空のフィールドをスキップ", + "skip-empty-fields-tooltip": "値が空のフィールドは、出力メッセージ/出力メタデータに追加されません。", + "fetch-interval": "取得間隔", + "fetch-strategy": "取得戦略", + "fetch-timeseries-from-to": "{{startInterval}} {{startIntervalTimeUnit}} 前から {{endInterval}} {{endIntervalTimeUnit}} 前までの時系列を取得。", + "fetch-timeseries-from-to-invalid": "時系列の取得が無効です(\"間隔開始\" は \"間隔終了\" より小さい必要があります)。", + "use-metadata-dynamic-interval-tooltip": "選択すると、ルールノードはメッセージおよびメタデータのパターンに基づいて動的なインターバルの開始と終了を使用します。", + "all-mode-hint": "取得モード \"All\" を選択すると、ルールノードは設定可能なクエリパラメータを使用して取得間隔からテレメトリを取得します。", + "first-mode-hint": "取得モード \"First\" を選択すると、ルールノードは取得間隔の開始に最も近いテレメトリを取得します。", + "last-mode-hint": "取得モード \"Last\" を選択すると、ルールノードは取得間隔の終了に最も近いテレメトリを取得します。", + "ascending": "昇順", + "descending": "降順", + "min": "最小", + "max": "最大", + "average": "平均", + "sum": "合計", + "count": "件数", + "none": "なし", + "last-level-relation-tooltip": "選択すると、ルールノードは最大関係レベルで設定されたレベルでのみ関連エンティティを検索します。", + "last-level-device-relation-tooltip": "選択すると、ルールノードは最大関係レベルで設定されたレベルでのみ関連デバイスを検索します。", + "data-to-fetch": "取得するデータ", + "mapping-of-customers": "顧客のマッピング", + "map-fields-required": "すべてのマッピングフィールドは必須です。", + "attributes": "属性", + "related-device-attributes": "関連デバイス属性", + "add-selected-attributes-to": "選択した属性の追加先", + "device-profiles": "デバイスプロファイル", + "mapping-of-tenant": "テナントのマッピング", + "add-attribute-key": "属性キーを追加", + "message-template": "メッセージテンプレート", + "message-template-required": "メッセージテンプレートは必須です", + "use-system-slack-settings": "システムのSlack設定を使用", + "slack-api-token": "Slack APIトークン", + "slack-api-token-required": "Slack APIトークンは必須です", + "keys-mapping": "キーのマッピング", + "add-key": "キーを追加", + "recipients": "受信者", + "message-subject-and-content": "メッセージ件名と内容", + "template-rules-hint": "両方の入力フィールドはテンプレート化をサポートします。メッセージから値を抽出するには $[messageKey] を使用し、メッセージメタデータから値を抽出するには ${metadataKey} を使用します。", + "originator-customer-desc": "受信メッセージのオリジネーターの顧客を新しいオリジネーターとして使用します。", + "originator-tenant-desc": "現在のテナントを新しいオリジネーターとして使用します。", + "originator-related-entity-desc": "関連エンティティを新しいオリジネーターとして使用します。設定された関係タイプと方向に基づいて検索します。", + "originator-alarm-originator-desc": "アラームオリジネーターを新しいオリジネーターとして使用します。受信メッセージのオリジネーターがアラームエンティティの場合のみ。", + "originator-entity-by-name-pattern-desc": "DBから取得したエンティティを新しいオリジネーターとして使用します。エンティティタイプと指定された名前パターンに基づいて検索します。", + "email-from-template-hint": "メッセージから値を抽出するには $[messageKey] を使用し、メタデータから値を抽出するには ${metadataKey} を使用します。", + "recipients-block-main-hint": "カンマ区切りのアドレスリスト。すべての入力フィールドはテンプレート化をサポートします。メッセージから値を抽出するには $[messageKey] を使用し、メタデータから値を抽出するには ${metadataKey} を使用します。", + "forward-msg-default-rule-chain": "メッセージをオリジネーターのデフォルトルールチェーンへ転送", + "forward-msg-default-rule-chain-tooltip": "有効にすると、メッセージはオリジネーターのデフォルトルールチェーン(または、オリジネーターのエンティティプロファイルにデフォルトルールチェーンが定義されていない場合は設定のルールチェーン)に転送されます。", + "exclude-zero-deltas": "出力メッセージからゼロデルタを除外", + "exclude-zero-deltas-hint": "有効にすると、\"{{outputValueKey}}\" の出力キーは値が 0 ではない場合にのみ出力メッセージに追加されます。", + "exclude-zero-deltas-time-difference-hint": "有効にすると、\"{{outputValueKey}}\" と \"{{periodValueKey}}\" の出力キーは、\"{{outputValueKey}}\" の値が 0 ではない場合にのみ出力メッセージに追加されます。", + "search-direction-from": "発生元からターゲットエンティティへ", + "search-direction-to": "ターゲットエンティティから発生元へ", + "del-relation-direction-from": "発生元から", + "del-relation-direction-to": "発生元へ", + "target-entity": "ターゲットエンティティ", + "function-configuration": "関数設定", + "function-name": "関数名", + "function-name-required": "関数名は必須です。", + "qualifier": "修飾子", + "qualifier-hint": "修飾子が指定されていない場合、デフォルトの修飾子 \"$LATEST\" が使用されます。", + "aws-credentials": "AWS認証情報", + "connection-timeout": "接続タイムアウト", + "connection-timeout-required": "接続タイムアウトは必須です。", + "connection-timeout-min": "最小接続タイムアウトは0です。", + "connection-timeout-hint": "最初に接続を確立する際に、諦めてタイムアウトするまで待機する秒数。値が0の場合は無限を意味し、推奨されません。", + "request-timeout": "リクエストタイムアウト", + "request-timeout-required": "リクエストタイムアウトは必須です", + "request-timeout-min": "最小リクエストタイムアウトは0です", + "request-timeout-hint": "リクエストが完了するのを、諦めてタイムアウトするまで待機する秒数。値が0の場合は無限を意味し、推奨されません。", + "units": "単位", + "tell-failure-aws-lambda": "AWS Lambda関数の実行で例外が発生した場合は失敗を通知", + "tell-failure-aws-lambda-hint": "AWS Lambda関数の実行で例外が発生した場合、ルールノードはメッセージ処理を失敗にします。", + "basic-mode": "基本", + "advanced-mode": "詳細", + "save-time-series": { + "processing-settings": "処理設定", + "processing-settings-hint": "受信メッセージの処理方法を定義します。基本処理設定では事前設定された戦略を選択でき、高度な設定では各アクションごとに個別の処理戦略を選択できます。", + "advanced-settings-hint": "処理戦略を設定する際は注意してください。特定の組み合わせにより予期しない動作が発生する場合があります。", + "strategy": "戦略", + "deduplication-interval": "重複排除間隔", + "deduplication-interval-required": "重複排除間隔は必須です", + "deduplication-interval-min-max-range": "重複排除間隔は最小1秒、最大1日である必要があります", + "strategy-type": { + "every-message": "メッセージごとに", + "skip": "スキップ", + "deduplicate": "重複排除", + "web-sockets-only": "WebSocketsのみ" + }, + "time-series": "時系列", + "latest": "最新値", + "web-sockets": "WebSockets", + "calculated-fields-and-alarm-rules": "計算フィールドとアラームルール" + }, + "save-attribute": { + "processing-settings": "処理設定", + "processing-settings-hint": "受信メッセージの処理方法を定義します。基本処理設定では事前設定された戦略を選択でき、高度な設定では各アクションごとに個別の処理戦略を選択できます。", + "advanced-settings-hint": "処理戦略を設定する際は注意してください。特定の組み合わせにより予期しない動作が発生する場合があります。", + "strategy": "戦略", + "deduplication-interval": "重複排除間隔", + "deduplication-interval-required": "重複排除間隔は必須です", + "deduplication-interval-min-max-range": "重複排除間隔は最小1秒、最大1日である必要があります", + "scope": "スコープ", + "strategy-type": { + "every-message": "メッセージごとに", + "skip": "スキップ", + "deduplicate": "重複排除", + "web-sockets-only": "WebSocketsのみ" + }, + "attributes": "属性" + }, + "key-val": { + "key": "キー", + "value": "値", + "see-examples": "例を見る。", + "remove-entry": "エントリを削除", + "remove-mapping-entry": "マッピングエントリを削除", + "add-mapping-entry": "マッピングを追加", + "add-entry": "エントリを追加", + "copy-key-values-from": "キー値をコピー元", + "delete-key-values": "キー値を削除", + "delete-key-values-from": "キー値を削除元", + "at-least-one-key-error": "少なくとも1つのキーを選択してください。", + "unique-key-value-pair-error": "'{{keyText}}' は '{{valText}}' と異なる必要があります!" + }, + "mail-body-types": { + "plain-text": "プレーンテキスト", + "html": "HTML", + "dynamic": "動的", + "use-body-type-template": "本文タイプテンプレートを使用", + "plain-text-description": "特別なスタイルや書式設定のない、シンプルな非装飾テキストです。", + "html-text-description": "メール本文で書式設定、リンク、画像にHTMLタグを使用できます。", + "dynamic-text-description": "テンプレート化機能に基づいて、プレーンテキストまたはHTMLの本文タイプを動的に使用できます。", + "after-template-evaluation-hint": "テンプレート評価後の値は、HTMLの場合は true、プレーンテキストの場合は false である必要があります。" + }, + "ai": { + "ai-model": "AIモデル", + "model": "モデル", + "ai-model-hint": "このルールノードが送信するリクエストを処理する事前設定済みのAIモデルを選択するか、\"新規作成\"を使用して新しいモデルを設定します。", + "prompt-settings": "プロンプト設定", + "prompt-settings-hint": "オプションのシステムプロンプトはAIの一般的な役割と制約を設定し、ユーザープロンプトは実行する具体的なタスクを定義します。両方のフィールドはテンプレート化もサポートします。", + "system-prompt": "システムプロンプト", + "system-prompt-max-length": "システムプロンプトは500000文字以下である必要があります。", + "system-prompt-blank": "システムプロンプトを空欄にできません。", + "user-prompt": "ユーザープロンプト", + "user-prompt-required": "ユーザープロンプトは必須です。", + "user-prompt-max-length": "ユーザープロンプトは500000文字以下である必要があります。", + "user-prompt-blank": "ユーザープロンプトを空欄にできません。", + "response-format": "レスポンス形式", + "response-text": "テキスト", + "response-json": "JSON", + "response-json-schema": "JSON Schema", + "response-format-hint-TEXT": "モデルが任意のテキストを生成できるようにします。生成結果は有効なJSONオブジェクトである場合も、そうでない場合もあります。出力が有効なJSONオブジェクトでない場合、自動的に \"response\" キー配下のJSONオブジェクトとしてラップされます。", + "response-format-hint-JSON": "モデルは有効なJSONを生成する必要があります。出力が有効なJSONオブジェクトでない場合、自動的に \"response\" キー配下のJSONオブジェクトとしてラップされます。", + "response-format-hint-JSON_SCHEMA": "モデルは、提供されたスキーマで定義された特定の構造とデータ型に一致するJSONを生成する必要があります。出力が有効なJSONオブジェクトでない場合、自動的に \"response\" キー配下のJSONオブジェクトとしてラップされます。", + "response-json-schema-hint": "有効なJSON Schemaは任意に入力できますが、このルールノードはその機能の限定されたサブセットのみをサポートします。詳細はノードドキュメントを参照してください。", + "response-json-schema-required": "JSON Schemaは必須です", + "advanced-settings": "詳細設定", + "timeout": "タイムアウト", + "timeout-hint": "リクエストが終了する前にAIモデルからの応答を待機する最大時間 \nを指定します。", + "timeout-required": "タイムアウトは必須です", + "timeout-validation": "1秒から10分の範囲である必要があります。", + "force-acknowledgement": "確認応答を強制", + "force-acknowledgement-hint": "有効にすると、受信メッセージは直ちに確認応答されます。その後、モデルの応答は別の新しいメッセージとしてキューに追加されます。", + "ai-resources": "AIリソース" + } + }, + "timezone": { + "timezone": "タイムゾーン", + "select-timezone": "タイムゾーンを選択", + "no-timezones-matching": "'{{timezone}}' に一致するタイムゾーンが見つかりませんでした。", + "timezone-required": "タイムゾーンは必須です。", + "browser-time": "ブラウザー時刻" + }, + "queue": { + "queue-name": "キュー", + "no-queues-found": "キューが見つかりません。", + "no-queues-matching": "'{{queue}}' に一致するキューが見つかりませんでした。", + "select-name": "キュー名を選択", + "name": "名前", + "name-required": "キュー名は必須です!", + "name-unique": "キュー名が一意ではありません!", + "name-pattern": "キュー名に ASCII 英数字、'.'、'_'、'-' 以外の文字が含まれています!", + "queue-required": "キューは必須です!", + "topic-required": "キュートピックは必須です!", + "poll-interval-required": "ポーリング間隔は必須です!", + "poll-interval-min-value": "ポーリング間隔の値は 1 未満にできません", + "partitions-required": "パーティションは必須です!", + "partitions-min-value": "パーティション数は 1 未満にできません", + "pack-processing-timeout-required": "処理タイムアウトは必須です", + "pack-processing-timeout-min-value": "処理タイムアウトの値は 1 未満にできません", + "batch-size-required": "バッチサイズは必須です!", + "batch-size-min-value": "バッチサイズの値は 1 未満にできません", + "retries-required": "リトライ回数は必須です!", + "retries-min-value": "リトライ回数は負にできません", + "failure-percentage-required": "失敗率は必須です!", + "failure-percentage-min-value": "失敗率の値は 0 未満にできません", + "failure-percentage-max-value": "失敗率の値は 100 を超えられません", + "pause-between-retries-required": "リトライ間の待機時間は必須です!", + "pause-between-retries-min-value": "リトライ間の待機時間は 1 未満にできません", + "max-pause-between-retries-required": "リトライ間の最大待機時間は必須です!", + "max-pause-between-retries-min-value": "リトライ間の最大待機時間は 1 未満にできません", + "submit-strategy-type-required": "送信戦略タイプは必須です!", + "processing-strategy-type-required": "処理戦略タイプは必須です!", + "queues": "キュー", + "selected-queues": "{ count, plural, =1 {キュー 1 件} other {キュー # 件} } を選択", + "delete-queue-title": "キュー '{{queueName}}' を削除してもよろしいですか?", + "delete-queues-title": "{ count, plural, =1 {キュー 1 件} other {キュー # 件} } を削除してもよろしいですか?", + "delete-queue-text": "注意: 確認後、キューと関連データはすべて復元できなくなります。", + "delete-queues-text": "確認後、選択したすべてのキューが削除され、アクセスできなくなります。", + "search": "キューを検索", + "add": "キューを追加", + "details": "キュー詳細", + "topic": "トピック", + "submit-settings": "送信設定", + "submit-strategy": "戦略タイプ *", + "grouping-parameter": "グルーピングパラメータ", + "processing-settings": "リトライ処理設定", + "processing-strategy": "処理タイプ *", + "retries-settings": "リトライ設定", + "polling-settings": "ポーリング設定", + "batch-processing": "バッチ処理", + "poll-interval": "ポーリング間隔", + "partitions": "パーティション", + "immediate-processing": "即時処理", + "consumer-per-partition": "各コンシューマーごとにメッセージをポーリング", + "consumer-per-partition-hint": "各パーティションごとに個別のコンシューマーを有効化します", + "duplicate-msg-to-all-partitions": "すべてのパーティションにメッセージを複製", + "processing-timeout": "処理時間(ms)", + "batch-size": "バッチサイズ", + "retries": "リトライ回数(0 – 無制限)", + "failure-percentage": "リトライをスキップする失敗メッセージ率(%)", + "pause-between-retries": "リトライ間隔(秒)", + "max-pause-between-retries": "追加リトライ間隔(秒)", + "delete": "キューを削除", + "copyId": "キューIDをコピー", + "idCopiedMessage": "キューIDがクリップボードにコピーされました", + "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の前のメッセージがACKされるまで送信されません", + "sequential-by-tenant-label": "テナント別に順次処理", + "sequential-by-tenant-hint": "例: テナントAの新しいメッセージは、テナントAの前のメッセージがACKされるまで送信されません", + "sequential-label": "順次処理", + "sequential-hint": "新しいメッセージは、前のメッセージがACKされるまで送信されません", + "burst-label": "バースト", + "burst-hint": "すべてのメッセージは到着した順にルールチェーンへ送信されます", + "batch-label": "バッチ", + "batch-hint": "新しいバッチは、前のバッチがACKされるまで送信されません", + "skip-all-failures-label": "すべての失敗をスキップ", + "skip-all-failures-hint": "すべての失敗を無視します", + "skip-all-failures-and-timeouts-label": "すべての失敗とタイムアウトをスキップ", + "skip-all-failures-and-timeouts-hint": "すべての失敗とタイムアウトを無視します", + "retry-all-label": "すべて再試行", + "retry-all-hint": "処理パック内のすべてのメッセージを再試行します", + "retry-failed-label": "失敗を再試行", + "retry-failed-hint": "処理パック内の失敗したメッセージをすべて再試行します", + "retry-timeout-label": "タイムアウトを再試行", + "retry-timeout-hint": "処理パック内のタイムアウトしたメッセージをすべて再試行します", + "retry-failed-and-timeout-label": "失敗とタイムアウトを再試行", + "retry-failed-and-timeout-hint": "処理パック内の失敗したメッセージとタイムアウトしたメッセージをすべて再試行します" + } + }, + "queue-statistics": { + "queue-statistics": "キュー統計", + "no-queue-statistics-matching": "'{{entity}}' に一致するキュー統計が見つかりませんでした。", + "queue-statistics-required": "キュー統計は必須です。", + "list-of-queue-statistics": "{ count, plural, =1 {キュー統計 1 件} other {キュー統計 # 件の一覧} }", + "selected-queue-statistics": "{ count, plural, =1 {キュー統計 1 件} other {キュー統計 # 件} } を選択", + "no-queue-statistics-text": "キュー統計が見つかりません", + "queue-statistics-starts-with": "名前が '{{prefix}}' で始まるキュー統計" + }, + "server-error": { + "general": "一般サーバーエラー", + "authentication": "認証エラー", + "jwt-token-expired": "JWTトークンの有効期限切れ", + "tenant-trial-expired": "テナントのトライアル期限切れ", + "credentials-expired": "認証情報の有効期限切れ", + "permission-denied": "権限がありません", + "invalid-arguments": "引数が無効です", + "bad-request-params": "リクエストパラメータが不正です", + "item-not-found": "項目が見つかりません", + "too-many-requests": "リクエストが多すぎます", + "too-many-updates": "更新が多すぎます", + "entities-limit-exceeded": "エンティティ上限を超えました" + }, + "tenant": { + "tenant": "テナント", + "tenants": "テナント", + "management": "テナント管理", + "add": "テナントを追加", + "admins": "管理者", + "manage-tenant-admins": "テナント管理者を管理", + "delete": "テナントを削除", + "add-tenant-text": "新しいテナントを追加", + "no-tenants-text": "テナントが見つかりません", + "tenant-details": "テナント詳細", + "title-max-length": "タイトルは 256 未満にしてください", + "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": "テナントIDをコピー", + "idCopiedMessage": "テナントIDがクリップボードにコピーされました", + "select-tenant": "テナントを選択", + "no-tenants-matching": "'{{entity}}' に一致するテナントが見つかりませんでした。", + "tenant-required": "テナントは必須です", + "search": "テナントを検索", + "selected-tenants": "{ count, plural, =1 {テナント 1 件} other {テナント # 件} } を選択", + "isolated-tb-rule-engine": "分離された ThingsBoard ルールエンジン キューを使用", + "isolated-tb-rule-engine-details": "各テナントに専用のルールエンジンキューが割り当てられます" + }, + "tenant-profile": { + "tenant-profile": "テナントプロファイル", + "tenant-profiles": "テナントプロファイル", + "add": "テナントプロファイルを追加", + "add-profile": "プロファイルを追加", + "debug": "デバッグ", + "edit": "テナントプロファイルを編集", + "tenant-profile-details": "テナントプロファイル詳細", + "no-tenant-profiles-text": "テナントプロファイルが見つかりません", + "name-max-length": "名前は 256 未満にしてください", + "search": "テナントプロファイルを検索", + "selected-tenant-profiles": "{ count, plural, =1 {テナントプロファイル 1 件} other {テナントプロファイル # 件} } を選択", + "no-tenant-profiles-matching": "'{{entity}}' に一致するテナントプロファイルが見つかりませんでした。", + "tenant-profile-required": "テナントプロファイルは必須です", + "idCopiedMessage": "テナントプロファイルIDがクリップボードにコピーされました", + "set-default": "テナントプロファイルをデフォルトに設定", + "delete": "テナントプロファイルを削除", + "copyId": "テナントプロファイルIDをコピー", + "name": "名前", + "name-required": "名前は必須です。", + "data": "プロファイルデータ", + "profile-configuration": "プロファイル設定", + "description": "説明", + "default": "デフォルト", + "delete-tenant-profile-title": "テナントプロファイル '{{tenantProfileName}}' を削除してもよろしいですか?", + "delete-tenant-profile-text": "注意: 確認後、テナントプロファイルと関連データはすべて復元できなくなります。", + "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": "新しいテナントプロファイルを作成", + "import": "テナントプロファイルをインポート", + "export": "テナントプロファイルをエクスポート", + "export-failed-error": "テナントプロファイルをエクスポートできません: {{error}}", + "tenant-profile-file": "テナントプロファイルファイル", + "invalid-tenant-profile-file-error": "テナントプロファイルをインポートできません: テナントプロファイルのデータ構造が無効です。", + "advanced-settings": "詳細設定", + "entities": "エンティティ", + "rule-engine": "ルールエンジン", + "time-to-live": "TTL", + "calculated-fields": "計算フィールド", + "alarms-and-notifications": "アラームと通知", + "ota-files-in-bytes": "ファイル", + "ws-title": "WS", + "unlimited": "(0 - 無制限)", + "maximum-devices": "デバイス最大数", + "maximum-devices-required": "デバイス最大数は必須です。", + "maximum-devices-range": "デバイス最大数は負にできません", + "maximum-assets": "アセット最大数", + "maximum-assets-required": "アセット最大数は必須です。", + "maximum-assets-range": "アセット最大数は負にできません", + "maximum-customers": "顧客最大数", + "maximum-customers-required": "顧客最大数は必須です。", + "maximum-customers-range": "顧客最大数は負にできません", + "maximum-users": "ユーザー最大数", + "maximum-users-required": "ユーザー最大数は必須です。", + "maximum-users-range": "ユーザー最大数は負にできません", + "maximum-dashboards": "ダッシュボード最大数", + "maximum-dashboards-required": "ダッシュボード最大数は必須です。", + "maximum-dashboards-range": "ダッシュボード最大数は負にできません", + "maximum-edges": "Edge 最大数", + "maximum-edges-required": "Edge 最大数は必須です。", + "maximum-edges-range": "Edge 最大数は負にできません", + "maximum-rule-chains": "ルールチェーン最大数", + "maximum-rule-chains-required": "ルールチェーン最大数は必須です。", + "maximum-rule-chains-range": "ルールチェーン最大数は負にできません", + "maximum-resources-sum-data-size": "リソースファイルの合計最大サイズ(バイト)", + "maximum-resources-sum-data-size-required": "リソースファイルの合計最大サイズは必須です。", + "maximum-resources-sum-data-size-range": "リソースファイルの合計最大サイズは負にできません", + "maximum-resource-size": "リソースファイルの最大サイズ(バイト)", + "maximum-resource-size-required": "リソースファイルの最大サイズは必須です", + "maximum-resource-size-range": "リソースファイルの最大サイズは負にできません", + "maximum-ota-packages-sum-data-size": "OTAパッケージファイルの合計最大サイズ(バイト)", + "maximum-ota-package-sum-data-size-required": "OTAパッケージファイルの合計最大サイズは必須です。", + "maximum-ota-package-sum-data-size-range": "OTAパッケージファイルの合計最大サイズは負にできません", + "maximum-debug-duration-min": "最大デバッグ時間(分)", + "maximum-debug-duration-min-range": "最大デバッグ時間は負にできません", + "rest-requests-for-tenant": "テナントのRESTリクエスト", + "transport-tenant-telemetry-msg-rate-limit": "トランスポート:テナントのテレメトリメッセージ", + "transport-tenant-telemetry-data-points-rate-limit": "トランスポート:テナントのテレメトリデータポイント", + "transport-device-msg-rate-limit": "トランスポート:デバイスメッセージ", + "transport-device-telemetry-msg-rate-limit": "トランスポート:デバイスのテレメトリメッセージ", + "transport-device-telemetry-data-points-rate-limit": "トランスポート:デバイスのテレメトリデータポイント", + "transport-gateway-msg-rate-limit": "トランスポート:Gatewayメッセージ", + "transport-gateway-telemetry-msg-rate-limit": "トランスポート:Gatewayのテレメトリメッセージ", + "transport-gateway-telemetry-data-points-rate-limit": "トランスポート:Gatewayのテレメトリデータポイント", + "transport-gateway-device-msg-rate-limit": "トランスポート:Gatewayデバイスメッセージ", + "transport-gateway-device-telemetry-msg-rate-limit": "トランスポート:Gatewayデバイスのテレメトリメッセージ", + "transport-gateway-device-telemetry-data-points-rate-limit": "トランスポート:Gatewayデバイスのテレメトリデータポイント", + "tenant-entity-export-rate-limit": "エンティティバージョン作成", + "tenant-entity-import-rate-limit": "エンティティバージョン読み込み", + "tenant-notification-request-rate-limit": "通知リクエスト", + "tenant-notification-requests-per-rule-rate-limit": "通知ルールごとの通知リクエスト", + "max-calculated-fields": "エンティティあたりの計算フィールド最大数", + "max-calculated-fields-range": "エンティティあたりの計算フィールド最大数は負にできません", + "max-calculated-fields-required": "エンティティあたりの計算フィールド最大数は必須です", + "max-data-points-per-rolling-arg": "ローリング引数の最大データポイント数", + "max-data-points-per-rolling-arg-range": "ローリング引数の最大データポイント数は負にできません", + "max-data-points-per-rolling-arg-required": "ローリング引数の最大データポイント数は必須です", + "max-arguments-per-cf": "計算フィールドあたりの引数最大数", + "max-arguments-per-cf-range": "計算フィールドあたりの引数最大数は負にできません", + "max-arguments-per-cf-required": "計算フィールドあたりの引数最大数は必須です", + "max-related-level-per-argument": "「関連エンティティ」引数あたりの最大リレーションレベル", + "max-related-level-per-argument-range": "「関連エンティティ」引数あたりのリレーションレベルの最大値は '1' 未満にできません", + "max-related-level-per-argument-required": "「関連エンティティ」引数あたりのリレーションレベルの最大値は必須です", + "min-allowed-scheduled-update-interval": "「関連エンティティ」引数の許可される最小更新間隔(秒)", + "min-allowed-scheduled-update-interval-range": "許可される最小更新間隔の最小値は負の値にできません", + "min-allowed-deduplication-interval": "許可される最小重複排除間隔(秒)", + "min-allowed-deduplication-interval-range": "許可される最小重複排除間隔の値は負の値にできません", + "min-allowed-deduplication-interval-required": "許可される最小重複排除間隔は必須です", + "intermediate-aggregation-interval": "中間集計間隔(秒)", + "intermediate-aggregation-interval-range": "中間集計間隔の値は '1' 未満にできません", + "intermediate-aggregation-interval-required": "中間集計間隔は必須です", + "reevaluation-check-interval": "再評価チェック間隔(秒)", + "reevaluation-check-interval-range": "再評価チェック間隔の値は '1' 未満にできません", + "reevaluation-check-interval-required": "再評価チェック間隔は必須です", + "alarms-reevaluation-interval": "アラーム再評価間隔(秒)", + "alarms-reevaluation-interval-range": "アラーム再評価間隔の値は '1' 未満にできません", + "alarms-reevaluation-interval-required": "アラーム再評価間隔は必須です", + "min-allowed-aggregation-interval": "許可される最小集計間隔(秒)", + "min-allowed-aggregation-interval-range": "許可される最小集計間隔の値は負の値にできません", + "min-allowed-aggregation-interval-required": "許可される最小集計間隔は必須です", + "min-allowed-scheduled-update-interval-required": "許可される最小更新間隔の最小値は必須です", + "max-state-size": "状態の最大サイズ(KB)", + "max-state-size-range": "状態の最大サイズ(KB)は負にできません", + "max-state-size-required": "状態の最大サイズ(KB)は必須です", + "max-value-argument-size": "単一値引数の最大サイズ(KB)", + "max-value-argument-size-range": "単一値引数の最大サイズ(KB)は負にできません", + "max-value-argument-size-required": "単一値引数の最大サイズ(KB)は必須です", + "max-transport-messages": "トランスポートメッセージ最大数", + "max-transport-messages-required": "トランスポートメッセージ最大数は必須です。", + "max-transport-messages-range": "トランスポートメッセージ最大数は負にできません", + "max-transport-data-points": "トランスポートデータポイント最大数 ", + "max-transport-data-points-required": "トランスポートデータポイント最大数 は必須です。", + "max-transport-data-points-range": "トランスポートデータポイント最大数 は負にできません", + "max-r-e-executions": "ルールエンジン実行最大数", + "max-r-e-executions-required": "ルールエンジン実行最大数は必須です。", + "max-r-e-executions-range": "ルールエンジン実行最大数は負にできません", + "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": "データポイント保存日数最大数は負にできません", + "default-storage-ttl-days": "デフォルトのストレージ TTL 日数", + "default-storage-ttl-days-required": "デフォルトのストレージ TTL 日数は必須です。", + "default-storage-ttl-days-range": "デフォルトのストレージ TTL 日数は負にできません", + "alarms-ttl-days": "アラーム TTL 日数", + "alarms-ttl-days-required": "アラーム TTL 日数は必須です", + "alarms-ttl-days-days-range": "アラーム TTL 日数は負にできません", + "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": "送信 Emails 最大数", + "max-emails-required": "送信 Emails 最大数は必須です。", + "max-emails-range": "送信 Emails 最大数は負にできません", + "sms-enabled": "SMS を有効化", + "max-sms": "送信 SMS 最大数", + "max-sms-required": "送信 SMS 最大数は必須です。", + "max-sms-range": "送信 SMS 最大数は負にできません", + "max-created-alarms": "作成アラーム最大数", + "max-created-alarms-required": "作成アラーム最大数は必須です。", + "max-created-alarms-range": "作成アラーム最大数は負にできません", + "no-queue": "キューが設定されていません", + "add-queue": "キューを追加", + "queues-with-count": "キュー({{count}})", + "tenant-rest-limits": "テナントのRESTリクエスト", + "customer-rest-limits": "顧客のRESTリクエスト", + "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-write-tenant-core-limits-configuration": "Rest API Cassandra 書き込みクエリ", + "cassandra-read-tenant-core-limits-configuration": "Rest API および WS テレメトリ Cassandra 読み取りクエリ", + "cassandra-write-tenant-rule-engine-limits-configuration": "ルールエンジンテレメトリ Cassandra 書き込みクエリ", + "cassandra-read-tenant-rule-engine-limits-configuration": "ルールエンジンテレメトリ Cassandra 読み取りクエリ", + "ws-limit-max-sessions-per-tenant": "テナントあたりのセッション最大数", + "ws-limit-max-sessions-per-customer": "顧客あたりのセッション最大数", + "ws-limit-max-sessions-per-regular-user": "通常ユーザーあたりのセッション最大数", + "ws-limit-max-sessions-per-public-user": "パブリックユーザーあたりのセッション最大数", + "ws-limit-queue-per-session": "セッションあたりのメッセージキュー最大サイズ", + "ws-limit-max-subscriptions-per-tenant": "テナントあたりのサブスクリプション最大数", + "ws-limit-max-subscriptions-per-customer": "顧客あたりのサブスクリプション最大数", + "ws-limit-max-subscriptions-per-regular-user": "通常ユーザーあたりのサブスクリプション最大数", + "ws-limit-max-subscriptions-per-public-user": "パブリックユーザーあたりのサブスクリプション最大数", + "ws-limit-updates-per-session": "セッションあたりの WS 更新", + "relation-search-entity-limit": "リレーション検索エンティティ上限", + "relation-search-entity-limit-hint": "リレーションパスの最終レベルで解決されるエンティティ数を制限します。「関連エンティティ」引数および伝播フィールドに適用されます。", + "relation-search-entity-limit-required": "リレーション検索エンティティ上限は必須です", + "relation-search-entity-limit-range": "リレーション検索エンティティ上限は '1' 未満にできません", + "rate-limits": { + "add-limit": "制限を追加", + "and-also-less-than": "かつ次より小さい", + "advanced-settings": "詳細設定", + "edit-limit": "制限を編集", + "calculated-field-debug-event-rate-limit": "計算フィールドのデバッグイベント", + "edit-calculated-field-debug-event-rate-limit": "計算フィールドのデバッグイベントのレート制限を編集", + "edit-transport-tenant-msg-title": "トランスポート:テナントメッセージのレート制限を編集", + "edit-transport-tenant-telemetry-msg-title": "トランスポート:テナントのテレメトリメッセージのレート制限を編集", + "edit-transport-tenant-telemetry-data-points-title": "トランスポート:テナントのテレメトリデータポイントのレート制限を編集", + "edit-transport-device-msg-title": "トランスポート:デバイスメッセージのレート制限を編集", + "edit-transport-device-telemetry-msg-title": "トランスポート:デバイスのテレメトリメッセージのレート制限を編集", + "edit-transport-device-telemetry-data-points-title": "トランスポート:デバイスのテレメトリデータポイントのレート制限を編集", + "edit-transport-gateway-msg-title": "トランスポート:Gatewayメッセージのレート制限を編集", + "edit-transport-gateway-telemetry-msg-title": "トランスポート:Gatewayのテレメトリメッセージのレート制限を編集", + "edit-transport-gateway-telemetry-data-points-title": "トランスポート:Gatewayのテレメトリデータポイントのレート制限を編集", + "edit-transport-gateway-device-msg-title": "トランスポート:Gatewayデバイスメッセージのレート制限を編集", + "edit-transport-gateway-device-telemetry-msg-title": "トランスポート:Gatewayデバイスのテレメトリメッセージのレート制限を編集", + "edit-transport-gateway-device-telemetry-data-points-title": "トランスポート:Gatewayデバイスのテレメトリデータポイントのレート制限を編集", + "edit-tenant-rest-limits-title": "テナントのRESTリクエストのレート制限を編集", + "edit-customer-rest-limits-title": "顧客のRESTリクエストのレート制限を編集", + "edit-ws-limit-updates-per-session-title": "セッションあたりの WS 更新のレート制限を編集", + "edit-cassandra-write-tenant-core-limits-configuration": "Rest API Cassandra 書き込みクエリを編集", + "edit-cassandra-read-tenant-core-limits-configuration": "Rest API および WS テレメトリ Cassandra 読み取りクエリを編集", + "edit-cassandra-write-tenant-rule-engine-limits-configuration": "ルールエンジンテレメトリ Cassandra 書き込みクエリを編集", + "edit-cassandra-read-tenant-rule-engine-limits-configuration": "ルールエンジンテレメトリ 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": "通知ルールごとの通知リクエストのレート制限を編集", + "edit-edge-events-rate-limit": "Edge イベントのレート制限を編集", + "edit-edge-events-per-edge-rate-limit": "Edge あたりの Edge イベントのレート制限を編集", + "edge-events-rate-limit": "Edge イベント", + "edge-events-per-edge-rate-limit": "Edge あたりの Edge イベント", + "edit-edge-uplink-messages-rate-limit": "Edge アップリンクメッセージのレート制限を編集", + "edit-edge-uplink-messages-per-edge-rate-limit": "Edge あたりの Edge アップリンクメッセージのレート制限を編集", + "edge-uplink-messages-rate-limit": "Edge アップリンクメッセージ", + "edge-uplink-messages-per-edge-rate-limit": "Edge あたりの Edge アップリンクメッセージ", + "messages-per": "次あたりのメッセージ数", + "not-set": "未設定", + "number-of-messages": "メッセージ数", + "number-of-messages-required": "メッセージ数は必須です。", + "number-of-messages-min": "最小値は 1 です。", + "preview": "プレビュー", + "per-seconds": "秒あたり", + "per-seconds-required": "時間レートは必須です。", + "per-seconds-min": "最小値は 1 です。", + "per-seconds-duplicate": "時間レートが重複しています。各時間間隔は一意である必要があります。", + "rate-limits": "レート制限", + "remove-limit": "制限を削除", + "transport-tenant-msg": "トランスポート:テナントメッセージ", + "transport-tenant-telemetry-msg": "トランスポート:テナントのテレメトリメッセージ", + "transport-tenant-telemetry-data-points": "トランスポート:テナントのテレメトリデータポイント", + "transport-device-msg": "トランスポート:デバイスメッセージ", + "transport-device-telemetry-msg": "トランスポート:デバイスのテレメトリメッセージ", + "transport-device-telemetry-data-points": "トランスポート:デバイスのテレメトリデータポイント", + "transport-gateway-msg": "トランスポート:Gatewayメッセージ", + "transport-gateway-telemetry-msg": "トランスポート:Gatewayのテレメトリメッセージ", + "transport-gateway-telemetry-data-points": "トランスポート:Gatewayのテレメトリデータポイント", + "transport-gateway-device-msg": "トランスポート:Gatewayデバイスメッセージ", + "transport-gateway-device-telemetry-msg": "トランスポート:Gatewayデバイスのテレメトリメッセージ", + "transport-gateway-device-telemetry-data-points": "トランスポート:Gatewayデバイスのテレメトリデータポイント", + "sec": "秒" + } + }, + "timeinterval": { + "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": "分", + "seconds": "秒", + "advanced": "高度な設定", + "custom": "カスタム", + "predefined": { + "yesterday": "昨日", + "day-before-yesterday": "一昨日", + "this-day-last-week": "先週の同じ日", + "previous-week": "前週(日 - 土)", + "previous-week-iso": "前週(月 - 日)", + "previous-month": "前月", + "previous-quarter": "前四半期", + "previous-half-year": "前半期", + "previous-year": "前年", + "current-hour": "現在の時間", + "current-day": "今日", + "current-day-so-far": "今日(現在まで)", + "current-week": "今週(日 - 土)", + "current-week-iso": "今週(月 - 日)", + "current-week-so-far": "今週(現在まで)(日 - 土)", + "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": "今年(現在まで)" + }, + "type": { + "week": "週(日 - 土)", + "week-iso": "週(月 - 日)", + "month": "月", + "quarter": "四半期" + } + }, + "timeunit": { + "milliseconds": "ミリ秒", + "seconds": "秒", + "minutes": "分", + "hours": "時間", + "days": "日" + }, + "timewindow": { + "timewindow": "時間範囲", + "timewindow-settings": "時間範囲設定", + "years": "{ years, plural, =1 {1年 } other {#年 } }", + "years-short": "{{ years }}年", + "months": "{ months, plural, =1 {1か月 } other {#か月 } }", + "months-short": "{{ months }}か月", + "weeks": "{ weeks, plural, =1 {1週 } other {#週 } }", + "weeks-short": "{{ weeks }}週", + "days": "{ days, plural, =1 {1日 } other {#日 } }", + "days-short": "{{ days }}日", + "hours": "{ hours, plural, =0 {時間 } =1 {1時間 } other {#時間 } }", + "hr": "{{ hr }} 時間", + "hr-short": "{{ hr }}時間", + "minutes": "{ minutes, plural, =0 {分 } =1 {1分 } other {#分 } }", + "min": "{{ min }} 分", + "min-short": "{{ min }}分", + "seconds": "{ seconds, plural, =0 {秒 } =1 {1秒 } other {#秒 } }", + "sec": "{{ sec }} 秒", + "sec-short": "{{ sec }}秒", + "short": { + "years": "{ years, plural, =1 {1年 } other {#年 } }", + "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": "間隔", + "just-now": "たった今", + "just-now-lower": "たった今", + "ago": "前", + "style": "時間範囲スタイル", + "icon": "アイコン", + "icon-position": "アイコン位置", + "icon-position-left": "左", + "icon-position-right": "右", + "font": "フォント", + "color": "色", + "displayTypePrefix": "リアルタイム/履歴のプレフィックスを表示", + "preview": "プレビュー", + "relative": "相対", + "range": "範囲", + "hide-timewindow-section": "エンドユーザーに時間範囲セクションを表示しない", + "hide-last-interval": "エンドユーザーに「過去」間隔を表示しない", + "hide-relative-interval": "エンドユーザーに相対間隔を表示しない", + "hide-fixed-interval": "エンドユーザーに固定間隔を表示しない", + "hide-aggregation": "エンドユーザーに集計を表示しない", + "hide-group-interval": "エンドユーザーにグルーピング間隔を表示しない", + "hide-max-values": "エンドユーザーに最大値を表示しない", + "hide-timezone": "エンドユーザーにタイムゾーンを表示しない", + "disable-custom-interval": "カスタム間隔の選択を無効化", + "edit-aggregation-functions-list": "集計関数リストを編集", + "edit-aggregation-functions-list-hint": "利用可能なオプションのリストを指定できます。", + "allowed-aggregation-functions": "許可された集計関数", + "edit-intervals-list": "間隔リストを編集", + "allowed-agg-intervals": "グルーピング間隔", + "default-agg-interval": "デフォルトのグルーピング間隔", + "edit-intervals-list-hint": "利用可能な間隔オプションのリストを指定できます。", + "edit-grouping-intervals-list-hint": "グルーピング間隔リストとデフォルトのグルーピング間隔を設定できます。", + "all": "すべて", + "save-current-settings-as-default": "現在の設定を既定の時間ウィンドウとして保存", + "hide-option-from-end-users": "エンドユーザーにオプションを表示しない" + }, + "tooltip": { + "trigger": "トリガー", + "trigger-point": "ポイント", + "trigger-axis": "軸", + "label": "ラベル", + "value": "値", + "date": "日付", + "show-date-time-interval": "日時間隔を表示", + "show-date-time-interval-hint": "データ集計に従って日時間隔を表示します。", + "hide-zero-tooltip-values": "0 の値を非表示", + "show-stack-total": "積み上げモードで合計値を表示", + "background-color": "背景色", + "background-blur": "背景のぼかし" + }, + "unit": { + "set-unit-conversion": "単位変換を設定", + "unit-settings": { + "unit-settings": "単位設定", + "source-unit": "元の単位", + "source-unit-hint": "これは保存された値の単位です。変換元の単位です。元データで使用されている記号を入力してください(例: m, km, ft, in)。", + "target-metric-unit": "変換先のメートル法単位", + "target-metric-unit-hint": "元の値を変換するメートル法(SI)の単位を選択します(例: cm, mm, km)。", + "target-imperial-unit": "変換先のヤード・ポンド法単位", + "target-imperial-unit-hint": "元の値を変換するヤード・ポンド法の単位を選択します(例: in, ft, yd)。", + "target-hybrid-unit": "変換先のハイブリッド単位", + "target-hybrid-unit-hint": "元の値を変換するハイブリッド単位を選択します(例: cm, in, km)。ハイブリッド単位はメートル法またはヤード・ポンド法の単位を組み合わせます。", + "enable-unit-conversion": "単位変換を有効化", + "enable-unit-conversion-hint": "オンにすると変換が有効になります。オフの場合、元の値は変更されずにそのまま渡されます。対応する測定グループに単位が 1 つしかない場合(例: Luminous flux, AQI)は無効になります。" + }, + "unit-system": "単位系", + "unit-system-type": { + "AUTO": "自動", + "METRIC": "メートル法", + "IMPERIAL": "ヤード・ポンド法", + "HYBRID": "ハイブリッド" + }, + "measures": { + "absorbed-dose-rate": "吸収線量率", + "acceleration": "加速度", + "acidity": "酸性度", + "air-quality-index": "大気質指数", + "amount-of-substance": "物質量", + "angle": "角度", + "angular-acceleration": "角加速度", + "area": "面積", + "area-density": "面密度", + "capacitance": "静電容量", + "catalytic-activity": "触媒活性", + "catalytic-concentration": "触媒濃度", + "charge": "電荷", + "current-density": "電流密度", + "data-transfer-rate": "データ転送速度", + "density": "密度", + "digital": "デジタル", + "dimension-ratio": "寸法比", + "dynamic-viscosity": "粘度", + "earthquake-magnitude": "地震マグニチュード", + "electric-charge-density": "電荷密度", + "electric-current": "電流", + "electric-dipole-moment": "電気双極子モーメント", + "electric-field-strength": "電界強度", + "electric-flux": "電束", + "electric-permittivity": "誘電率", + "electric-polarizability": "電気分極率", + "electrical-conductance": "電気コンダクタンス", + "electrical-conductivity": "電気伝導率", + "energy": "エネルギー", + "energy-density": "エネルギー密度", + "force": "力", + "frequency": "周波数", + "fuel-efficiency": "燃費", + "heat-capacity": "熱容量", + "illuminance": "照度", + "inductance": "インダクタンス", + "kinematic-viscosity": "動粘度", + "length": "長さ", + "light-exposure": "露光量", + "linear-charge-density": "線電荷密度", + "logarithmic-ratio": "対数比", + "luminous-efficacy": "発光効率", + "luminous-flux": "光束", + "luminous-intensity": "光度", + "magnetic-field-gradient": "磁場勾配", + "magnetic-flux": "磁束", + "magnetic-flux-density": "磁束密度", + "magnetic-moment": "磁気モーメント", + "magnetic-permeability": "透磁率", + "mass": "質量", + "mass-fraction": "質量分率", + "molar-concentration": "モル濃度", + "molar-energy": "モルエネルギー", + "molar-heat-capacity": "モル熱容量", + "molar-mass": "モル質量", + "number-concentration": "数濃度", + "parts-per-million": "ppm", + "power": "電力", + "power-density": "電力密度", + "pressure": "圧力", + "radiance": "放射輝度", + "radiant-intensity": "放射強度", + "radiation-dose": "放射線量", + "radioactive-decay": "放射性崩壊", + "radioactivity": "放射能", + "radioactivity-concentration": "放射能濃度", + "reciprocal-length": "逆長さ", + "resistance": "抵抗", + "reynolds-number": "レイノルズ数", + "signal-level": "信号レベル", + "solid-angle": "立体角", + "specific-energy": "比エネルギー", + "specific-heat-capacity": "比熱容量", + "specific-humidity": "比湿", + "specific-volume": "比容積", + "speed": "速度", + "surface-charge-density": "表面電荷密度", + "surface-tension": "表面張力", + "temperature": "温度", + "thermal-conductivity": "熱伝導率", + "time": "時間", + "torque": "トルク", + "turbidity": "濁度", + "voltage": "電圧", + "volume": "体積", + "volume-flow": "体積流量" + }, + "millimeter": "ミリメートル", + "centimeter": "センチメートル", + "decimeter": "デシメートル", + "angstrom": "オングストローム", + "nanometer": "ナノメートル", + "micrometer": "マイクロメートル", + "meter": "メートル", + "kilometer": "キロメートル", + "inch": "インチ", + "foot": "フィート", + "foot-us": "フィート(米国測量)", + "yard": "ヤード", + "mile": "マイル", + "nautical-mile": "海里", + "astronomical-unit": "天文単位", + "reciprocal-metre": "逆メートル", + "meter-per-meter": "メートル毎メートル", + "steradian": "ステラジアン", + "thou": "サウ", + "barleycorn": "バーレイコーン", + "hand": "ハンド", + "chain": "チェーン", + "furlong": "ファーロング", + "league": "リーグ", + "fathom": "ファゾム", + "cable": "ケーブル", + "link": "リンク", + "rod": "ロッド", + "nanogram": "ナノグラム", + "microgram": "マイクログラム", + "milligram": "ミリグラム", + "gram": "グラム", + "kilogram": "キログラム", + "tonne": "トン", + "ounce": "オンス", + "pound": "ポンド", + "stone": "ストーン", + "hundredweight-count": "ハンドレッドウェイト(カウント)", + "short-tons": "ショートトン", + "dalton": "ダルトン", + "grain": "グレーン", + "drachm": "ドラクム", + "quarter": "クォーター", + "slug": "スラグ", + "carat": "カラット", + "cubic-millimeter": "立方ミリメートル", + "cubic-centimeter": "立方センチメートル", + "cubic-meter": "立方メートル", + "cubic-kilometer": "立方キロメートル", + "microliter": "マイクロリットル", + "milliliter": "ミリリットル", + "liter": "リットル", + "hectoliter": "ヘクトリットル", + "cubic-inch": "立方インチ", + "cubic-foot": "立方フィート", + "cubic-yard": "立方ヤード", + "fluid-ounce": "液量オンス", + "fluid-ounce-per-second": "液量オンス毎秒", + "pint": "パイント", + "quart": "クォート", + "gallon": "ガロン", + "oil-barrels": "バレル(石油)", + "cubic-meter-per-kilogram": "立方メートル毎キログラム", + "gill": "ジル", + "hogshead": "ホッグスヘッド", + "teaspoon": "ティースプーン", + "tablespoon": "テーブルスプーン", + "cup": "カップ", + "celsius": "摂氏", + "kelvin": "ケルビン", + "rankine": "ランキン", + "fahrenheit": "華氏", + "percent": "パーセント", + "meter-per-second": "メートル毎秒", + "kilometer-per-hour": "キロメートル毎時", + "foot-per-second": "フィート毎秒", + "foot-per-minute": "フィート毎分", + "mile-per-hour": "マイル毎時", + "knot": "ノット", + "inch-per-second": "インチ毎秒", + "inch-per-hour": "インチ毎時", + "millimeters-per-minute": "ミリメートル毎分", + "meter-per-minute": "メートル毎分", + "kilometer-per-hour-squared": "キロメートル毎時毎時", + "foot-per-second-squared": "フィート毎秒毎秒", + "pascal": "パスカル", + "kilopascal": "キロパスカル", + "megapascal": "メガパスカル", + "gigapascal": "ギガパスカル", + "millibar": "ミリバール", + "bar": "バール", + "kilobar": "キロバール", + "newton": "ニュートン", + "newton-meter": "ニュートンメートル", + "foot-pounds": "フィートポンド", + "inch-pounds": "インチポンド", + "newton-per-meter": "ニュートン毎メートル", + "atmospheres": "気圧", + "pounds-per-square-inch": "psi", + "kilopound-per-square-inch": "キロpsi", + "torr": "トル", + "inches-of-mercury": "水銀柱インチ", + "pascal-per-square-meter": "パスカル毎平方メートル", + "pound-per-square-inch": "psi", + "newton-per-square-meter": "ニュートン毎平方メートル", + "kilogram-force-per-square-meter": "キログラム重毎平方メートル", + "pascal-per-square-centimeter": "パスカル毎平方センチメートル", + "ton-force-per-square-inch": "トン重毎平方インチ", + "kilonewton-per-square-meter": "キロニュートン毎平方メートル", + "newton-per-square-millimeter": "ニュートン毎平方ミリメートル", + "microjoule": "マイクロジュール", + "millijoule": "ミリジュール", + "joule": "ジュール", + "kilojoule": "キロジュール", + "megajoule": "メガジュール", + "gigajoule": "ギガジュール", + "watt-hour": "ワット時", + "watt-minute": "ワット分", + "kilowatt-hour": "キロワット時", + "milliwatt-hour": "ミリワット時", + "megawatt-hour": "メガワット時", + "gigawatt-hour": "ギガワット時", + "electron-volts": "電子ボルト", + "joules-per-coulomb": "ジュール毎クーロン", + "british-thermal-unit": "英熱量単位(BTU)", + "thousand-british-thermal-unit": "千BTU", + "million-british-thermal-unit": "百万BTU", + "foot-pound": "フィートポンド", + "calorie": "カロリー", + "small-calorie": "小カロリー", + "kilocalorie": "キロカロリー", + "joule-per-kelvin": "ジュール毎ケルビン", + "joule-per-kilogram-kelvin": "ジュール毎キログラム・ケルビン", + "joule-per-kilogram": "ジュール毎キログラム", + "watt-per-meter-kelvin": "ワット毎メートル・ケルビン", + "joule-per-cubic-meter": "ジュール毎立方メートル", + "therm": "サーム", + "electric-dipole-moment": "電気双極子モーメント", + "magnetic-dipole-moment": "磁気双極子モーメント", + "debye": "デバイ", + "coulomb-per-square-meter-per-volt": "クーロン毎平方メートル毎ボルト", + "milliwatt": "ミリワット", + "microwatt": "マイクロワット", + "watt": "ワット", + "kilowatt": "キロワット", + "megawatt": "メガワット", + "gigawatt": "ギガワット", + "metric-horsepower": "仏馬力", + "milliwatt-per-square-centimeter": "ミリワット毎平方センチメートル", + "watt-per-square-centimeter": "ワット毎平方センチメートル", + "kilowatt-per-square-centimeter": "キロワット毎平方センチメートル", + "milliwatt-per-square-meter": "ミリワット毎平方メートル", + "watt-per-square-meter": "ワット毎平方メートル", + "kilowatt-per-square-meter": "キロワット毎平方メートル", + "watt-per-square-inch": "ワット毎平方インチ", + "kilowatt-per-square-inch": "キロワット毎平方インチ", + "horsepower": "馬力", + "btu-per-hour": "BTU毎時", + "btu-per-second": "BTU毎秒", + "btu-per-day": "BTU毎日", + "mbtu-per-hour": "千BTU毎時", + "mbtu-per-second": "千BTU毎秒", + "mbtu-per-day": "千BTU毎日", + "mmbtu-per-hour": "百万BTU毎時", + "mmbtu-per-second": "百万BTU毎秒", + "mmbtu-per-day": "百万BTU毎日", + "foot-pound-per-second": "フィートポンド毎秒", + "coulomb": "クーロン", + "millicoulomb": "ミリクーロン", + "microcoulomb": "マイクロクーロン", + "nanocoulomb": "ナノクーロン", + "picocoulomb": "ピコクーロン", + "coulomb-per-meter": "クーロン毎メートル", + "coulomb-per-cubic-meter": "クーロン毎立方メートル", + "coulomb-per-square-meter": "クーロン毎平方メートル", + "square-millimeter": "平方ミリメートル", + "square-centimeter": "平方センチメートル", + "square-meter": "平方メートル", + "hectare": "ヘクタール", + "square-kilometer": "平方キロメートル", + "square-inch": "平方インチ", + "square-foot": "平方フィート", + "square-yard": "平方ヤード", + "acre": "エーカー", + "square-mile": "平方マイル", + "are": "アール", + "barn": "バーン", + "circular-inch": "サーキュラーインチ", + "milliampere-hour": "ミリアンペア時", + "ampere-hours": "アンペア時", + "kiloampere-hours": "キロアンペア時", + "nanoampere": "ナノアンペア", + "picoampere": "ピコアンペア", + "microampere": "マイクロアンペア", + "milliampere": "ミリアンペア", + "ampere": "アンペア", + "kiloampere": "キロアンペア", + "megaampere": "メガアンペア", + "gigaampere": "ギガアンペア", + "microampere-per-square-centimeter": "マイクロアンペア毎平方センチメートル", + "ampere-per-square-meter": "アンペア毎平方メートル", + "ampere-per-meter": "アンペア毎メートル", + "oersted": "エルステッド", + "bohr-magneton": "ボーア磁子", + "ampere-meter-squared": "アンペア・平方メートル", + "nanovolt": "ナノボルト", + "picovolt": "ピコボルト", + "millivolt": "ミリボルト", + "microvolt": "マイクロボルト", + "volt": "ボルト", + "kilovolt": "キロボルト", + "megavolt": "メガボルト", + "dbmV": "デシベルボルト", + "dbm": "デシベルミリワット", + "volt-meter": "ボルトメートル", + "kilovolt-meter": "キロボルトメートル", + "megavolt-meter": "メガボルトメートル", + "microvolt-meter": "マイクロボルトメートル", + "millivolt-meter": "ミリボルトメートル", + "nanovolt-meter": "ナノボルトメートル", + "ohm": "オーム", + "microohm": "マイクロオーム", + "milliohm": "ミリオーム", + "kilohm": "キロオーム", + "megohm": "メガオーム", + "gigohm": "ギガオーム", + "millihertz": "ミリヘルツ", + "hertz": "ヘルツ", + "kilohertz": "キロヘルツ", + "megahertz": "メガヘルツ", + "gigahertz": "ギガヘルツ", + "terahertz": "テラヘルツ", + "rpm": "回転数/分", + "candela-per-square-meter": "カンデラ毎平方メートル", + "candela": "カンデラ", + "lumen": "ルーメン", + "lux": "ルクス", + "foot-candle": "フートキャンドル", + "lumen-per-square-meter": "ルーメン毎平方メートル", + "lux-second": "ルクス秒", + "lumen-second": "ルーメン秒", + "lumens-per-watt": "ルーメン毎ワット", + "mole": "モル", + "nanomole": "ナノモル", + "micromole": "マイクロモル", + "millimole": "ミリモル", + "kilomole": "キロモル", + "mole-per-cubic-meter": "モル毎立方メートル", + "rssi": "受信信号強度インジケーター", + "ppm": "ppm", + "ppb": "ppb", + "micrograms-per-cubic-meter": "マイクログラム毎立方メートル", + "aqi": "AQI", + "gram-per-cubic-meter": "グラム毎立方メートル", + "gram-per-kilogram": "比湿", + "millimeters-per-second": "ミリメートル毎秒", + "neper": "ネーパ", + "bel": "ベル", + "decibel": "デシベル", + "meters-per-second-squared": "メートル毎秒毎秒", + "becquerel": "ベクレル", + "curie": "キュリー", + "gray": "グレイ", + "sievert": "シーベルト", + "roentgen": "レントゲン", + "cps": "カウント/秒", + "rad": "ラド", + "rem": "レム", + "dps": "崩壊/秒", + "rutherford": "ラザフォード", + "coulombs-per-kilogram": "クーロン毎キログラム", + "becquerels-per-cubic-meter": "ベクレル毎立方メートル", + "curies-per-liter": "キュリー毎リットル", + "becquerels-per-second": "ベクレル毎秒", + "curies-per-second": "キュリー毎秒", + "gy-per-second": "グレイ毎秒", + "watt-per-steradian": "ワット毎ステラジアン", + "watt-per-square-metre-steradian": "ワット毎平方メートル・ステラジアン", + "ph-level": "pH", + "turbidity": "濁度", + "mg-per-liter": "ミリグラム毎リットル", + "microsiemens-per-centimeter": "マイクロジーメンス毎センチメートル", + "millisiemens-per-meter": "ミリジーメンス毎メートル", + "siemens-per-meter": "ジーメンス毎メートル", + "kilogram-per-cubic-meter": "キログラム毎立方メートル", + "gram-per-cubic-centimeter": "グラム毎立方センチメートル", + "kilogram-per-square-meter": "キログラム毎平方メートル", + "milligram-per-milliliter": "ミリグラム毎ミリリットル", + "milligram-per-cubic-meter": "ミリグラム毎立方メートル", + "pound-per-cubic-foot": "ポンド毎立方フィート", + "ounces-per-cubic-inch": "オンス毎立方インチ", + "tons-per-cubic-yard": "トン毎立方ヤード", + "particle-density": "粒子密度", + "kilometers-per-liter": "キロメートル毎リットル", + "miles-per-gallon": "マイル毎ガロン", + "liters-per-100-km": "100 km あたりリットル", + "gallons-per-mile": "マイルあたりガロン", + "liters-per-hour": "リットル毎時", + "gallons-per-hour": "ガロン毎時", + "beats-per-minute": "拍/分", + "millimeters-of-mercury": "水銀柱ミリメートル", + "milligrams-per-deciliter": "ミリグラム毎デシリットル", + "g-force": "G(加速度)", + "kilonewton": "キロニュートン", + "kilogram-force": "キログラム重", + "pound-force": "ポンド重", + "kilopound-force": "キロポンド重", + "dyne": "ダイン", + "poundal": "パウンダル", + "kip": "キップ", + "gal": "ガル", + "gravity": "重力", + "hectopascal": "ヘクトパスカル", + "atmosphere": "気圧", + "millibars": "ミリバール", + "inch-of-mercury": "水銀柱インチ", + "richter-scale": "リヒター尺度", + "nanosecond": "ナノ秒", + "microsecond": "マイクロ秒", + "millisecond": "ミリ秒", + "second": "秒", + "minute": "分", + "hour": "時間", + "day": "日", + "week": "週", + "month": "月", + "year": "年", + "cubic-foot-per-minute": "立方フィート毎分", + "cubic-meters-per-hour": "立方メートル毎時", + "cubic-meters-per-second": "立方メートル毎秒", + "liter-per-second": "リットル毎秒", + "liter-per-minute": "リットル毎分", + "gallons-per-minute": "ガロン毎分", + "cubic-foot-per-second": "立方フィート毎秒", + "milliliters-per-minute": "ミリリットル毎分", + "cubic-decimeter-per-second": "立方デシメートル毎秒", + "bit": "ビット", + "byte": "バイト", + "kilobyte": "キロバイト", + "megabyte": "メガバイト", + "gigabyte": "ギガバイト", + "terabyte": "テラバイト", + "petabyte": "ペタバイト", + "exabyte": "エクサバイト", + "zettabyte": "ゼタバイト", + "yottabyte": "ヨタバイト", + "bit-per-second": "ビット毎秒", + "kilobit-per-second": "キロビット毎秒", + "megabit-per-second": "メガビット毎秒", + "gigabit-per-second": "ギガビット毎秒", + "terabit-per-second": "テラビット毎秒", + "byte-per-second": "バイト毎秒", + "kilobyte-per-second": "キロバイト毎秒", + "megabyte-per-second": "メガバイト毎秒", + "gigabyte-per-second": "ギガバイト毎秒", + "degree": "度", + "radian": "ラジアン", + "gradian": "グラード", + "arcminute": "分(角分)", + "arcsecond": "秒(角秒)", + "milliradian": "ミリラジアン", + "revolution": "回転", + "siemens": "ジーメンス", + "millisiemens": "ミリジーメンス", + "microsiemens": "マイクロジーメンス", + "kilosiemens": "キロジーメンス", + "megasiemens": "メガジーメンス", + "gigasiemens": "ギガジーメンス", + "farad": "ファラド", + "millifarad": "ミリファラド", + "microfarad": "マイクロファラド", + "nanofarad": "ナノファラド", + "picofarad": "ピコファラド", + "kilofarad": "キロファラド", + "megafarad": "メガファラド", + "gigafarad": "ギガファラド", + "terfarad": "テラファラド", + "farad-per-meter": "ファラド毎メートル", + "tesla": "テスラ", + "gauss": "ガウス", + "kilogauss": "キロガウス", + "millitesla": "ミリテスラ", + "microtesla": "マイクロテスラ", + "nanotesla": "ナノテスラ", + "kilotesla": "キロテスラ", + "megatesla": "メガテスラ", + "millitesla-square-meters": "ミリテスラ平方メートル", + "gamma": "ガンマ", + "lambda": "ラムダ", + "square-meter-per-second": "平方メートル毎秒", + "square-centimeter-per-second": "平方センチメートル毎秒", + "stoke": "ストークス", + "centistokes": "センチストークス", + "square-foot-per-second": "平方フィート毎秒", + "square-inch-per-second": "平方インチ毎秒", + "pascal-second": "パスカル秒", + "centipoise": "センチポアズ", + "poise": "ポアズ", + "reynolds": "レイノルズ", + "pound-per-foot-hour": "ポンド毎フィート時", + "newton-second-per-square-meter": "ニュートン秒毎平方メートル", + "dyne-second-per-square-centimeter": "ダイン秒毎平方センチメートル", + "kilogram-per-meter-second": "キログラム毎メートル秒", + "tesla-square-meters": "テスラ平方メートル", + "maxwell": "マクスウェル", + "tesla-per-meter": "テスラ毎メートル", + "gauss-per-centimeter": "ガウス毎センチメートル", + "weber": "ウェーバ", + "microweber": "マイクロウェーバ", + "milliweber": "ミリウェーバ", + "gauss-square-centimeter": "ガウス平方センチメートル", + "kilogauss-square-centimeter": "キロガウス平方センチメートル", + "henry": "ヘンリー", + "millihenry": "ミリヘンリー", + "microhenry": "マイクロヘンリー", + "nanohenry": "ナノヘンリー", + "henry-per-meter": "ヘンリー毎メートル", + "tesla-meter-per-ampere": "テスラメートル毎アンペア", + "gauss-per-oersted": "ガウス毎エルステッド", + "kilogram-per-mole": "キログラム毎モル", + "gram-per-mole": "グラム毎モル", + "milligram-per-mole": "ミリグラム毎モル", + "joule-per-mole": "ジュール毎モル", + "joule-per-mole-kelvin": "ジュール毎モル・ケルビン", + "millivolts-per-meter": "ミリボルト毎メートル", + "volts-per-meter": "ボルト毎メートル", + "kilovolts-per-meter": "キロボルト毎メートル", + "radian-per-second": "ラジアン毎秒", + "radian-per-second-squared": "ラジアン毎秒毎秒", + "revolutions-per-minute-per-second": "角加速度", + "deg-per-second": "度毎秒", + "rotation-per-minute": "回転/分", + "degrees-brix": "ブリックス度", + "katal": "カタル", + "katal-per-cubic-metre": "カタル毎立方メートル", + "paris-inch": "パリインチ" + }, + "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": "ユーザーをアクティベートするには、次のアクティベーションリンクを使用してください(有効期限:{{activationLinkTtl}}):", + "copy-activation-link": "アクティベーションリンクをコピー", + "activation-link-copied-message": "ユーザーアクティベーションリンクがクリップボードにコピーされました", + "details": "詳細", + "login-as-tenant-admin": "テナント管理者としてログイン", + "login-as-customer-user": "顧客ユーザーとしてログイン", + "search": "ユーザーを検索", + "selected-users": "{ count, plural, =1 {1人のユーザー} other {#人のユーザー} } を選択", + "disable-account": "ユーザーアカウントを無効化", + "enable-account": "ユーザーアカウントを有効化", + "enable-account-message": "ユーザーアカウントが正常に有効化されました!", + "disable-account-message": "ユーザーアカウントが正常に無効化されました!", + "copyId": "ユーザーIDをコピー", + "idCopiedMessage": "ユーザーIDがクリップボードにコピーされました", + "user-list": "ユーザーリスト", + "user-list-required": "ユーザーリストは必須です" + }, + "value": { + "type": "値の型", + "string": "文字列", + "string-value": "文字列の値", + "string-value-required": "文字列の値は必須です", + "integer": "整数", + "integer-value": "整数の値", + "integer-value-required": "整数の値は必須です", + "invalid-integer-value": "無効な整数の値です", + "double": "倍精度浮動小数点", + "double-value": "倍精度浮動小数点の値", + "double-value-required": "倍精度浮動小数点の値は必須です", + "boolean": "ブール値", + "boolean-value": "ブール値", + "false": "偽", + "true": "真", + "long": "長整数", + "json": "JSON", + "json-value": "JSONの値", + "json-value-invalid": "JSONの値は無効な形式です", + "json-value-required": "JSONの値は必須です" + }, + "version-control": { + "version-control": "バージョン管理", + "management": "バージョン管理", + "search": "バージョンを検索", + "branch": "ブランチ", + "default": "デフォルト", + "select-branch": "ブランチを選択", + "branch-required": "ブランチは必須です", + "create-entity-version": "エンティティバージョンを作成", + "version-name": "バージョン名", + "version-name-required": "バージョン名は必須です", + "author": "作成者", + "export-relations": "関連をエクスポート", + "export-attributes": "属性をエクスポート", + "export-credentials": "認証情報をエクスポート", + "export-calculated-fields": "計算フィールドをエクスポート", + "export-alarm-rules": "アラームルールをエクスポート", + "entity-versions": "エンティティバージョン", + "versions": "バージョン", + "created-time": "作成時間", + "version-id": "バージョンID", + "no-entity-versions-text": "エンティティバージョンが見つかりません", + "no-versions-text": "バージョンが見つかりません", + "copy-full-version-id": "完全なバージョンIDをコピー", + "create-version": "バージョンを作成", + "creating-version": "バージョン作成中... しばらくお待ちください", + "nothing-to-commit": "コミットする変更はありません", + "restore-version": "バージョンを復元", + "restore-entity-from-version": "バージョン '{{versionName}}' からエンティティを復元", + "restoring-entity-version": "エンティティバージョンを復元中... しばらくお待ちください", + "load-relations": "関連を読み込む", + "load-attributes": "属性を読み込む", + "load-credentials": "認証情報を読み込む", + "load-calculated-fields": "計算フィールドを読み込む", + "load-alarm-rules": "アラームルールを読み込み", + "compare-with-current": "現在のバージョンと比較", + "diff-entity-with-version": "エンティティバージョン '{{versionName}}' と差分を表示", + "previous-difference": "前の差分", + "next-difference": "次の差分", + "current": "現在のバージョン", + "differences": "{ count, plural, =1 {1つの差分} other {#個の差分} }", + "create-entities-version": "エンティティバージョンを作成", + "default-sync-strategy": "デフォルトの同期戦略", + "sync-strategy-merge": "マージ", + "sync-strategy-overwrite": "上書き", + "entities-to-export": "エクスポートするエンティティ", + "entities-to-restore": "復元するエンティティ", + "sync-strategy": "同期戦略", + "all-entities": "すべてのエンティティ", + "no-entities-to-export-prompt": "エクスポートするエンティティを指定してください", + "no-entities-to-restore-prompt": "復元するエンティティを指定してください", + "add-entity-type": "エンティティタイプを追加", + "remove-all": "すべて削除", + "version-create-result": "{ added, plural, =0 {エンティティは追加されませんでした} =1 {1エンティティが追加されました} other {#エンティティが追加されました} }.
    { modified, plural, =0 {エンティティは変更されませんでした} =1 {1エンティティが変更されました} other {#エンティティが変更されました} }.
    { removed, plural, =0 {エンティティは削除されませんでした} =1 {1エンティティが削除されました} other {#エンティティが削除されました} }。", + "remove-other-entities": "他のエンティティを削除", + "find-existing-entity-by-name": "名前で既存のエンティティを検索", + "restore-entities-from-version": "バージョン '{{versionName}}' からエンティティを復元", + "restoring-entities-from-version": "エンティティを復元中... しばらくお待ちください", + "no-entities-restored": "復元されたエンティティはありません", + "created": "{{created}} 作成済み", + "updated": "{{updated}} 更新済み", + "deleted": "{{deleted}} 削除済み", + "remove-other-entities-confirm-text": "ご注意ください!これにより現在のすべてのエンティティが
    削除され、復元したいバージョンには存在しません。

    確認のため、\"remove other entities\" と入力してください。", + "auto-commit-to-branch": "{{ branch }}ブランチに自動コミット", + "default-create-entity-version-name": "{{entityName}} の更新", + "sync-strategy-merge-hint": "選択したエンティティをリポジトリに作成または更新します。他のリポジトリエンティティは変更されません。", + "sync-strategy-overwrite-hint": "選択したエンティティをリポジトリに作成または更新します。他のリポジトリエンティティは削除されます。", + "device-credentials-conflict": "外部ID {{entityId}} のデバイスの読み込みに失敗しました
    別のデバイスにすでに同じ認証情報がデータベースに存在しています。
    復元フォームで認証情報の読み込み設定を無効にすることを検討してください。", + "missing-referenced-entity": "外部ID {{sourceEntityId}}{{sourceEntityTypeName}}の読み込みに失敗しました
    欠落している{{targetEntityTypeName}}のID {{targetEntityId}} を参照しています。", + "runtime-failed": "失敗: {{message}}", + "auto-commit-settings-read-only-hint": "自動コミット機能はリポジトリ設定で読み取り専用オプションが有効な場合は機能しません。", + "rollback-on-error": "エラー時にロールバック", + "rollback-on-error-hint": "復元するエンティティが大量にある場合、パフォーマンス向上のためこのオプションを無効にすることを検討してください。\n注意:バージョンの読み込み中にエラーが発生した場合、すでに永続化されたエンティティ(関連、属性など)はそのまま残ります。" + }, + "widget": { + "widget-library": "ウィジェットライブラリ", + "widget-bundle": "ウィジェットバンドル", + "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": "未保存のウィジェット設定があります。
    このページを離れますか?", + "widget-type-not-found": "ウィジェットの設定の読み込みに問題があります。
    おそらく関連するウィジェットタイプが削除されました。", + "widget-type-load-error": "ウィジェットが以下のエラーにより読み込まれませんでした:", + "remove": "ウィジェットを削除", + "delete": "ウィジェットを削除", + "edit": "ウィジェットを編集", + "remove-widget-title": "ウィジェット '{{widgetTitle}}' を削除してもよろしいですか?", + "remove-widget-text": "確認後、ウィジェットと関連するすべてのデータは回復できなくなります。", + "replace-reference-with-widget-copy": "参照をウィジェットのコピーに置き換える", + "timeseries": "タイムシリーズ", + "search-data": "データを検索", + "no-data-found": "データが見つかりません", + "latest": "最新の値", + "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": "ウィジェットを名前を付けて保存", + "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-extension": "拡張機能ですか", + "remove-resource": "リソースを削除", + "add-resource": "リソースを追加", + "html": "HTML", + "tidy": "整理", + "css": "CSS", + "settings-form": "設定フォーム", + "data-key-settings-form": "データキー設定フォーム", + "latest-data-key-settings-form": "最新のデータキー設定フォーム", + "widget-settings": "ウィジェットの設定", + "description": "説明", + "tags": "タグ", + "image-preview": "画像プレビュー", + "settings-form-selector": "設定フォームの選択", + "data-key-settings-form-selector": "データキー設定フォームの選択", + "latest-data-key-settings-form-selector": "最新のデータキー設定フォームの選択", + "all": "すべて", + "actual": "実際", + "scada": "SCADAシンボル", + "deprecated": "非推奨", + "has-basic-mode": "基本モードがあります", + "basic-mode-form-selector": "基本モードフォームの選択", + "basic-mode": "基本", + "advanced-mode": "高度", + "javascript": "JavaScript", + "js": "JS", + "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": "ウィジェットテンプレートの読み込みに失敗しました!", + "details": "詳細", + "widget-details": "ウィジェットの詳細", + "add": "ウィジェットを追加", + "add-existing-widget": "既存のウィジェットを追加", + "add-new-widget": "新しいウィジェットを追加", + "search-widgets": "ウィジェットを検索", + "selected-widgets": "{ count, plural, =1 {1 ウィジェット} other {# ウィジェット} } が選択されました", + "undo": "ウィジェットの変更を元に戻す", + "export": "ウィジェットをエクスポート", + "export-prompt": "ウィジェットの画像とリソースを埋め込む", + "export-widgets": "ウィジェットをエクスポート", + "export-widgets-prompt": "ウィジェットの画像とリソースを埋め込む", + "import": "ウィジェットをインポート", + "no-data": "ウィジェットに表示するデータがありません", + "data-overflow": "ウィジェットは {{total}} 件のエンティティのうち {{count}} 件を表示しています", + "alarm-data-overflow": "ウィジェットは {{totalEntities}} 件のエンティティのうち {{allowedEntities}} 件(最大許可) のアラームを表示しています", + "search": "ウィジェットを検索", + "filter": "ウィジェットフィルタータイプ", + "loading-widgets": "ウィジェットを読み込み中...", + "widget-template-error": "無効なウィジェットHTMLテンプレートです。", + "reference": "参照" + }, + "widget-action": { + "header-button": "ウィジェットヘッダーボタン", + "do-nothing": "何もしない", + "open-dashboard-state": "新しいダッシュボードステートに移動", + "update-dashboard-state": "現在のダッシュボードステートを更新", + "open-dashboard": "別のダッシュボードに移動", + "custom": "カスタムアクション", + "custom-pretty": "カスタムアクション(HTMLテンプレート付き)", + "custom-pretty-error-title": "カスタムダイアログエラー", + "custom-pretty-template-error": "無効なカスタムダイアログテンプレートです。", + "custom-pretty-controller-error": "カスタムダイアログ機能の評価中にエラーが発生しました。", + "mobile-action": "モバイルアクション", + "target-dashboard-state": "ターゲットダッシュボードステート", + "target-dashboard-state-required": "ターゲットダッシュボードステートは必須です", + "set-entity-from-widget": "ウィジェットからエンティティを設定", + "target-dashboard": "ターゲットダッシュボード", + "select-target-dashboard": "ターゲットダッシュボードを選択", + "target-dashboard-required": "ターゲットダッシュボードは必須です。", + "open-right-layout": "右側ダッシュボードレイアウトを開く(モバイルビュー)", + "state-display-type": "ダッシュボードステート表示オプション", + "open-normal": "通常", + "open-in-separate-dialog": "別のダイアログで開く", + "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": "右", + "popover-placement-rightTop": "右上", + "popover-placement-rightBottom": "右下", + "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": "ポップオーバーの幅", + "popover-height": "ポップオーバーの高さ", + "popover-style": "ポップオーバーのスタイル", + "open-new-browser-tab": "新しいブラウザタブで開く", + "open-URL": "URLを開く", + "URL": "URL", + "url-required": "URLは必須です。", + "mobile": { + "device-provision": "デバイスのプロビジョニング", + "action-type": "モバイルアクションの種類", + "select-action-type": "モバイルアクションの種類を選択", + "action-type-required": "モバイルアクションの種類は必須です", + "take-picture-from-gallery": "ギャラリーから写真を撮る", + "take-photo": "写真を撮る", + "map-direction": "地図の方向を開く", + "map-location": "地図の位置を開く", + "scan-qr-code": "QRコードをスキャン", + "make-phone-call": "電話をかける", + "get-location": "電話の位置を取得", + "take-screenshot": "スクリーンショットを撮る", + "handle-provision-success-function": "プロビジョニング成功処理関数", + "get-location-function": "位置情報取得関数", + "process-launch-result-function": "起動結果処理関数", + "get-phone-number-function": "電話番号取得関数", + "process-image-function": "画像処理関数", + "process-qr-code-function": "QR コード処理関数", + "process-location-function": "位置情報処理関数", + "handle-empty-result-function": "空結果処理関数", + "handle-error-function": "エラー処理関数", + "handle-non-mobile-fallback-function": "非モバイルフォールバック処理関数", + "save-to-gallery": "ギャラリーに保存", + "provision-type": "プロビジョニングタイプ", + "auto": "自動", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" + }, + "custom-action-function": "カスタムアクション機能", + "custom-pretty-function": "カスタムアクション(HTMLテンプレート付き)機能", + "map-item-type": "マップアイテムの種類", + "map-item": { + "marker": "マーカー", + "polygon": "ポリゴン", + "rectangle": "長方形", + "circle": "円", + "polyline": "ポリライン" + }, + "place-map-item": "マップアイテムを配置", + "map-item-tooltip": { + "customize-map-item-tooltips": "マップアイテムのツールチップをカスタマイズ", + "place-marker": "マーカーを配置", + "start-draw-rectangle": "長方形を描き始める", + "finish-draw-rectangle": "長方形を描き終わる", + "start-draw-polygon": "ポリゴンを描き始める", + "continue-draw-polygon": "ポリゴンの描画を続ける", + "finish-draw-polygon": "ポリゴンを描き終わる", + "start-draw-circle": "円を描き始める", + "finish-draw-circle": "円を描き終わる", + "start-draw-polyline": "ポリラインの描画を開始", + "finish-draw-polyline": "ポリラインの描画を完了" + } + }, + "widgets-bundle": { + "current": "現在のバンドル", + "widgets-bundles": "ウィジェットバンドル", + "widgets-bundle-widgets": "ウィジェットバンドルのウィジェット", + "add": "ウィジェットバンドルを追加", + "delete": "ウィジェットバンドルを削除", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "title-max-length": "タイトルは256文字以下である必要があります。", + "description": "説明", + "image-preview": "画像プレビュー", + "scada": "SCADAウィジェットバンドル", + "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 {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": "エクスポートするデータにバンドルのウィジェットを含めます(そうでない場合は、参照されているウィジェットFQNのみがエクスポートされます)", + "export-failed-error": "ウィジェットバンドルのエクスポートに失敗しました: {{error}}", + "create-new-widgets-bundle": "新しいウィジェットバンドルを作成", + "widgets-bundle-file": "ウィジェットバンドルファイル", + "invalid-widgets-bundle-file-error": "ウィジェットバンドルのインポートに失敗しました: 無効なウィジェットバンドルのデータ構造。", + "search": "ウィジェットバンドルを検索", + "selected-widgets-bundles": "{ count, plural, =1 {1 ウィジェットバンドル} other {# ウィジェットバンドル} } が選択されました", + "open-widgets-bundle": "ウィジェットバンドルを開く", + "loading-widgets-bundles": "ウィジェットバンドルを読み込み中...", + "create-new": "新しいウィジェットバンドルを作成" + }, + "widget-config": { + "data": "データ", + "settings": "設定", + "advanced": "高度な設定", + "appearance": "外観", + "widget-card": "ウィジェットカード", + "mobile": "モバイル", + "title": "タイトル", + "title-tooltip": "タイトルツールチップ", + "general-settings": "一般設定", + "display-title": "ウィジェットタイトルの表示", + "card-title": "カードタイトル", + "drop-shadow": "ドロップシャドウ", + "enable-fullscreen": "全画面表示を有効にする", + "background-color": "背景色", + "text-color": "テキスト色", + "border-radius": "ボーダー半径", + "padding": "パディング", + "margin": "マージン", + "widget-style": "ウィジェットスタイル", + "widget-css": "ウィジェットCSS", + "title-style": "タイトルスタイル", + "mobile-mode-settings": "モバイルモード", + "order": "順序", + "height": "高さ", + "mobile-hide": "モバイルモードでウィジェットを非表示", + "desktop-hide": "デスクトップモードでウィジェットを非表示", + "units": "値の隣に表示する特殊記号", + "units-by-default": "デフォルトの単位", + "decimals": "小数点以下の桁数", + "decimals-by-default": "デフォルトの小数点", + "default-data-key-parameter-hint": "このパラメーターは、データキー設定によって上書きされない限り、すべてのウィジェット値に適用されます", + "units-short": "単位", + "decimals-short": "小数点", + "decimals-suffix": "小数点", + "digits-suffix": "桁", + "timewindow": "時間ウィンドウ", + "use-dashboard-timewindow": "ダッシュボードの時間ウィンドウを使用", + "use-widget-timewindow": "ウィジェットの時間ウィンドウを使用", + "display-timewindow": "時間ウィンドウを表示", + "legend": "凡例", + "display-legend": "凡例を表示", + "datasources": "データソース", + "datasource": "データソース", + "maximum-datasources": "最大{ count, plural, =1 {1つのデータソースのみ許可されています。} other {#件のデータソースが許可されています。} }", + "timeseries-key-error": "少なくとも1つの時系列データキーを指定する必要があります", + "datasource-type": "タイプ", + "datasource-parameters": "パラメータ", + "remove-datasource": "データソースを削除", + "add-datasource": "データソースを追加", + "target-device": "ターゲットデバイス", + "alarm-source": "アラームソース", + "actions": "アクション", + "action": "アクション", + "add-action": "アクションを追加", + "search-actions": "アクションを検索", + "no-actions-text": "アクションが見つかりません", + "action-source": "アクションソース", + "select-action-source": "アクションソースを選択", + "action-source-required": "アクションソースは必須です。", + "column-index": "列インデックス", + "select-column-index": "列インデックスを選択", + "column-index-required": "列インデックスは必須です。", + "not-set": "設定されていません", + "action-name": "名前", + "action-name-required": "アクション名は必須です。", + "action-name-not-unique": "同じ名前のアクションがすでに存在します。\nアクション名は同一のアクションソース内で一意である必要があります。", + "action-icon": "アイコン", + "header-button": { + "button-settings": "ボタン設定", + "button-type": "ボタンタイプ", + "button-type-basic": "基本", + "button-type-raised": " raised", + "button-type-stroked": "ストローク", + "button-type-flat": "フラット", + "button-type-icon": "アイコン", + "button-type-mini-fab": "FAB", + "colors": "色", + "color": "色", + "background": "背景", + "border": "枠線", + "advanced-button-style": "高度なボタンスタイル", + "button-style": "ボタンスタイル" + }, + "show-hide-action-using-function": "関数を使ってアクションの表示/非表示", + "show-action-function": "アクション表示関数", + "action-type": "タイプ", + "action-type-required": "アクションタイプは必須です。", + "edit-action": "アクションを編集", + "delete-action": "アクションを削除", + "delete-action-title": "ウィジェットアクションの削除", + "delete-action-text": "アクション名 '{{actionName}}' のウィジェットアクションを削除してもよろしいですか?", + "title-icon": "タイトルアイコン", + "display-icon": "タイトルアイコンを表示", + "card-icon": "カードアイコン", + "icon": "アイコン", + "icon-color": "アイコンの色", + "icon-size": "アイコンのサイズ", + "advanced-settings": "高度な設定", + "data-settings": "データ設定", + "limits": "制限", + "no-data-display-message": "\"表示するデータがありません\" の代替メッセージ", + "data-page-size": "データソースごとの最大エンティティ数", + "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-padding": "カードのパディング", + "card-appearance": "カードの外観", + "color": "色", + "tooltip": "ツールチップ", + "units-required": "単位は必須です。", + "list-layout": "リストレイアウト", + "layout": "レイアウト", + "resize-options": "リサイズオプション", + "resizable": "リサイズ可能", + "preserve-aspect-ratio": "アスペクト比を維持" + }, + "widget-type": { + "import": "ウィジェットタイプをインポート", + "export": "ウィジェットタイプをエクスポート", + "export-failed-error": "ウィジェットのエクスポートに失敗しました: {{error}}", + "widget-file": "ウィジェットファイル", + "invalid-widget-file-error": "ウィジェットのインポートに失敗しました: 無効なウィジェットデータ構造です。" + }, + "markdown": { + "edit": "編集", + "preview": "プレビュー", + "copy-code": "コピーするにはクリック", + "copied": "コピーしました!" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "設定はプラットフォームのメイン設定でのモバイルアプリQRコードウィジェットに依存します", + "get-it-on-google-play": "Google Playで入手", + "download-on-the-app-store": "App Storeでダウンロード" + }, + "action-button": { + "behavior": "動作", + "on-click": "クリック時", + "on-click-hint": "ボタンがクリックされたときにトリガーされるアクション", + "first-button-click": "最初のボタンクリック", + "first-button-click-hint": "最初のボタンを押している間のアクション。", + "second-button-click": "2番目のボタンクリック", + "second-button-click-hint": "2番目のボタンを押している間のアクション。", + "button-click-hint": "ウィジェットを押している間のアクション" + }, + "command-button": { + "behavior": "動作", + "on-click": "クリック時", + "on-click-hint": "ボタンがクリックされたときに実行されるアクション" + }, + "power-button": { + "behavior": "動作", + "power-on": "電源 'オン'", + "power-on-hint": "コンポーネントの電源をオンにするアクション。", + "power-off": "電源 'オフ'", + "power-off-hint": "コンポーネントの電源をオフにするアクション。", + "on-label": "オン", + "off-label": "オフ", + "layout": "レイアウト", + "layout-default": "デフォルト", + "layout-simplified": "簡易版", + "layout-outlined": "アウトライン", + "layout-default-volume": "デフォルト.ボリューム", + "layout-simplified-volume": "簡易版.ボリューム", + "layout-outlined-volume": "アウトライン.ボリューム", + "layout-default-icon": "デフォルト.アイコン", + "layout-simplified-icon": "簡易版.アイコン", + "layout-outlined-icon": "アウトライン.アイコン", + "main": "メイン", + "background": "背景", + "button-icon-on": "ボタンアイコン 'オン'", + "button-icon-off": "ボタンアイコン 'オフ'", + "power-on-colors": "電源 'オン' の色", + "power-off-colors": "電源 'オフ' の色", + "disabled-colors": "無効の色", + "button": "ボタン" + }, + "toggle-button": { + "behavior": "動作", + "checked": "チェック済み", + "unchecked": "未チェック", + "check": "チェック", + "check-hint": "コンポーネントをチェックするためのアクション。", + "uncheck": "チェック解除", + "uncheck-hint": "コンポーネントのチェックを解除するためのアクション。", + "auto-scale": "自動スケール", + "horizontal-fill": "横方向にフィル", + "vertical-fill": "縦方向にフィル", + "button-appearance": "ボタンの外観" + }, + "segmented-button": { + "layout": "レイアウト", + "layout-squared": "角型", + "layout-rounded": "丸型", + "card-border": "カードの枠線", + "button-appearance": "ボタンの外観", + "first": "最初", + "second": "次", + "color-styles": "カラースタイル", + "selected": "選択済み", + "unselected": "未選択" + }, + "button": { + "layout": "レイアウト", + "outlined": "アウトライン", + "filled": "塗りつぶし", + "underlined": "下線付き", + "basic": "基本", + "auto-scale": "自動スケール", + "label": "ラベル", + "icon": "アイコン", + "border-radius": "枠線の角丸", + "color-palette": "カラーパレット", + "main": "メイン", + "background": "背景", + "border": "枠線", + "custom-styles": "カスタムスタイル", + "clear-style": "スタイルをクリア", + "shadow": "影", + "enabled": "有効", + "disabled": "無効", + "preview": "プレビュー", + "copy-style-from": "スタイルをコピー元から" + }, + "value-stepper": { + "behavior": "動作", + "simplified": "簡易版", + "filled": "塗りつぶし", + "outlined": "アウトライン", + "volume": "ボリューム", + "initial-state": "初期状態", + "initial-state-hint": "初期値を取得するためのアクション。", + "disabled-state": "無効状態", + "disabled-state-hint": "コンポーネントが無効になる条件を設定。", + "right-button-click": "右ボタンクリック", + "right-button-click-hint": "右ボタンを押している間のアクション。", + "left-button-click": "左ボタンクリック", + "left-button-click-hint": "左ボタンを押している間のアクション。", + "auto-scale": "自動スケール", + "value-range": "範囲", + "min-range": "最小", + "max-range": "最大", + "value-increment-decrement-step": "値の増減ステップ", + "value": "値", + "value-box-background": "値ボックスの背景", + "border": "枠線", + "button-appearance": "ボタンの外観", + "left": "左", + "right": "右", + "left-button": "左ボタン", + "right-button": "右ボタン", + "icon": "アイコン", + "color-palette": "カラーパレット", + "main": "メイン", + "background": "背景", + "button-icon-on": "ボタンアイコン 'オン'", + "button-on-colors": "電源 'オン' の色", + "disabled-colors": "無効の色" + }, + "button-state": { + "activated-state": "アクティブ状態", + "activated-state-hint": "ボタンがアクティブである条件を設定。", + "disabled-state": "無効状態", + "disabled-state-hint": "ボタンが無効である条件を設定。", + "selected-state": "選択されたボタン", + "selected-state-hint": "ボタンが選択される条件を設定。", + "enabled": "有効", + "hovered": "ホバー", + "pressed": "押された", + "activated": "アクティブ", + "disabled": "無効", + "initial": "最初のボタン", + "first": "最初", + "second": "次" + }, + "background": { + "background": "背景", + "background-settings": "背景設定", + "background-type-image": "画像", + "background-type-color": "色", + "image-url": "画像URL", + "overlay": "オーバーレイ", + "enable-overlay": "オーバーレイを有効にする", + "blur": "ぼかし", + "preview": "プレビュー" + }, + "bar-chart": { + "bar-appearance": "バーの外観", + "label-on-bar": "バー上のラベル", + "value-on-bar": "バー上の値", + "bar-chart-style": "バーグラフのスタイル", + "bar-axis": "バー軸" + }, + "polar-area-chart": { + "polar-axis": "極軸", + "start-angle": "開始角度", + "polar-area-chart-style": "極エリアチャートスタイル" + }, + "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": "非アクティブな信号バーの色", + "signal-strength-card-style": "信号強度カードスタイル", + "no-signal-rssi-value": "\"信号なし\" RSSI値" + }, + "status-widget": { + "behavior": "動作", + "layout": "レイアウト", + "layout-default": "デフォルト", + "layout-center": "中央", + "layout-icon": "アイコン", + "on": "オン", + "off": "オフ", + "label": "ラベル", + "status": "ステータス", + "icon": "アイコン", + "color-palette": "カラーパレット", + "disabled-color-palette": "無効なカラーパレット", + "primary": "プライマリ", + "primary-color-hint": "アイコンとラベルの色", + "secondary": "セカンダリ", + "secondary-color-hint": "ステータスの色", + "background": "背景" + }, + "chart": { + "common-settings": "共通設定", + "enable-stacking-mode": "スタッキングモードを有効にする", + "selection": "時間範囲の選択", + "enable-selection-mode": "選択モードを有効にする", + "line-shadow-size": "ラインシャドウのサイズ", + "display-smooth-lines": "スムーズ(曲線)ラインを表示", + "default-bar-width": "非集計データのデフォルトのバー幅(ミリ秒)", + "bar-alignment": "バーの配置", + "bar-alignment-left": "左", + "bar-alignment-right": "右", + "bar-alignment-center": "中央", + "default-font": "デフォルトフォント", + "default-font-size": "デフォルトフォントサイズ", + "default-font-color": "デフォルトフォントカラー", + "thresholds-line-width": "すべてのしきい値のデフォルトのライン幅", + "tooltip-settings": "ツールチップ設定", + "tooltip": "ツールチップ", + "show-tooltip": "ツールチップを表示", + "hover-individual-points": "個々のポイントをホバー", + "show-cumulative-values": "スタッキングモードで累積値を表示", + "hide-zero-false-values": "ツールチップからゼロ/偽の値を非表示", + "tooltip-value-format-function": "ツールチップの値のフォーマット関数", + "grid-settings": "グリッド設定", + "show-vertical-lines": "縦線を表示", + "show-horizontal-lines": "横線を表示", + "grid-outline-border-width": "グリッドアウトライン/枠線の幅(px)", + "primary-color": "プライマリカラー", + "background-color": "背景色", + "ticks-color": "目盛りの色", + "xaxis-settings": "X軸設定", + "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-days": "1日前", + "time-for-comparison-weeks": "1週間前", + "time-for-comparison-months": "1ヶ月前", + "time-for-comparison-years": "1年前", + "time-for-comparison-custom-interval": "カスタムインターバル", + "custom-interval-value": "カスタムインターバル値(ms)", + "comparison-x-axis-settings": "比較X軸設定", + "axis-position": "軸の位置", + "axis-position-top": "上(デフォルト)", + "axis-position-bottom": "下", + "custom-legend-settings": "カスタム凡例設定", + "enable-custom-legend": "カスタム凡例を有効にする(これにより、キーラベルに属性/時系列の値を使用できるようになります)", + "key-name": "キー名", + "key-name-required": "キー名は必須です", + "key-type": "キータイプ", + "key-type-attribute": "属性", + "key-type-timeseries": "時系列", + "label-keys-list": "ラベルで使用するキーリスト", + "no-label-keys": "設定されたキーはありません", + "add-label-key": "新しいキーを追加", + "line-width": "ライン幅", + "color": "色", + "data-is-hidden-by-default": "データはデフォルトで非表示です", + "disable-data-hiding": "データの非表示を無効にする", + "remove-from-legend": "凡例からデータキーを削除", + "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": "十字", + "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": "右", + "thresholds": "しきい値", + "no-thresholds": "設定されたしきい値はありません", + "add-threshold": "しきい値を追加", + "show-values-for-comparison": "比較のために履歴値を表示", + "comparison-values-label": "履歴値ラベル", + "comparison-line-color": "比較線の色", + "threshold-settings": "しきい値設定", + "use-as-threshold": "キー値をしきい値として使用", + "threshold-line-width": "しきい値のライン幅", + "threshold-color": "しきい値の色", + "common-pie-settings": "共通の円グラフ設定", + "radius": "半径", + "inner-radius": "内半径", + "tilt": "傾き", + "common-pie-settings-range-error": "値は0から1の範囲である必要があります", + "stroke-settings": "線設定", + "width-pixels": "幅(ピクセル)", + "show-labels": "ラベルを表示", + "animation-settings": "アニメーション設定", + "animated-pie": "円グラフのアニメーションを有効にする(実験的)", + "border-settings": "枠線設定", + "border-width": "枠線の幅", + "border-color": "枠線の色", + "legend-settings": "凡例設定", + "display-legend": "凡例を表示", + "labels-font-color": "ラベルのフォント色", + "series": "シリーズ", + "add-series": "シリーズを追加", + "series-settings": "シリーズ設定", + "remove-series": "シリーズを削除", + "no-series": "設定されたシリーズはありません", + "no-series-error": "少なくとも1つのシリーズを指定する必要があります", + "chart-appearance": "グラフの外観", + "vertical-grid-lines": "縦のグリッドライン", + "horizontal-grid-lines": "横のグリッドライン", + "chart-background": "グラフ背景", + "grid-lines-color": "グリッドラインの色", + "border": "枠線", + "axis": "軸", + "vertical-axis": "縦軸", + "ticks": "目盛り", + "horizontal-axis": "横軸", + "shape-empty-circle": "空の円", + "shape-circle": "円", + "shape-rect": "長方形", + "shape-round-rect": "角丸長方形", + "shape-triangle": "三角形", + "shape-diamond": "ひし形", + "shape-pin": "ピン", + "shape-arrow": "矢印", + "shape-none": "なし", + "line-type-solid": "実線", + "line-type-dashed": "破線", + "line-type-dotted": "点線", + "label-position-top": "上", + "label-position-bottom": "下", + "label-position-outside": "外側", + "label-position-inside": "内側", + "fill": "塗りつぶし", + "fill-type-none": "なし", + "fill-type-solid": "実線", + "fill-type-opacity": "不透明度", + "fill-type-gradient": "グラデーション", + "background": "背景", + "opacity": "不透明度", + "gradient-stops": "グラデーションストップ", + "gradient-start": "開始", + "gradient-end": "終了", + "animation": { + "animation": "アニメーション", + "animation-threshold": "アニメーションしきい値", + "animation-duration": "アニメーションの期間", + "animation-easing": "アニメーションのイージング", + "animation-delay": "アニメーションの遅延", + "update-animation-duration": "アニメーション期間を更新", + "update-animation-easing": "アニメーションのイージングを更新", + "update-animation-delay": "アニメーションの遅延を更新" + }, + "chart-axis": { + "limit": "上限", + "source": "ソース", + "key-value": "キー / 値", + "value-required": "値は必須です。", + "entity-key-required": "エンティティキーは必須です。", + "key-required": "キーは必須です。", + "scale-limits": "スケール上限/下限", + "scale-appearance": "スケール表示", + "scale": "スケール", + "scale-min": "最小", + "scale-max": "最大", + "scale-auto": "自動" + }, + "bar": { + "show-border": "枠線を表示", + "border-width": "枠線の幅", + "border-radius": "枠線の角丸", + "bar-width": "バーの幅", + "label": "ラベル", + "label-hint": "バーの上にラベルを表示。", + "series-label-hint": "バーの上に値とともにラベルを表示。", + "label-background": "ラベルの背景" + } + }, + "color": { + "color-settings": "カラー設定", + "color-type-constant": "定数", + "color-type-gradient": "グラデーション", + "color-type-range": "範囲", + "color-type-function": "関数", + "color": "色", + "value-range": "値の範囲", + "from": "開始", + "to": "終了", + "color-function": "色関数", + "copy-color-settings-from": "カラー設定をコピー元からコピー", + "copy-from": "コピー元", + "settings-type": "設定タイプ", + "basic-mode": "基本", + "advanced-mode": "詳細", + "entity-alias": "エンティティエイリアス", + "entity-attribute": "エンティティ属性", + "gradient-color": "グラデーションカラー", + "gradient-color-min": "色", + "gradient-start": "グラデーション開始色", + "gradient-start-min": "開始", + "gradient-end": "グラデーション終了色", + "gradient-end-min": "終了", + "start-value": "開始値", + "end-value": "終了値", + "gradient-type": "グラデーションタイプ" + }, + "dashboard-state": { + "dashboard-state-settings": "ダッシュボード状態設定", + "dashboard-state": "ダッシュボード状態ID", + "autofill-state-layout": "デフォルトで状態レイアウトの高さを自動入力", + "default-margin": "デフォルトのウィジェットマージン", + "default-background-color": "デフォルトの背景色", + "sync-parent-state-params": "親ダッシュボードと状態パラメータを同期" + }, + "date-range-navigator": { + "date-range-picker-settings": "日付範囲ピッカー設定", + "hide-date-range-picker": "日付範囲ピッカーを非表示", + "picker-one-panel": "日付範囲ピッカー1パネル", + "picker-auto-confirm": "日付範囲ピッカー自動確認", + "picker-show-template": "日付範囲ピッカーのテンプレートを表示", + "first-day-of-week": "週の初めの日", + "interval-settings": "インターバル設定", + "hide-interval": "インターバルを非表示", + "initial-interval": "初期インターバル", + "interval-hour": "時間", + "interval-day": "日", + "interval-week": "週", + "interval-two-weeks": "2週間", + "interval-month": "月", + "interval-three-months": "3ヶ月", + "interval-six-months": "6ヶ月", + "step-settings": "ステップ設定", + "hide-step-size": "ステップサイズを非表示", + "initial-step-size": "初期ステップサイズ", + "hide-labels": "ラベルを非表示", + "use-session-storage": "セッションストレージを使用", + "localizationMap": { + "Sun": "日", + "Mon": "月", + "Tue": "火", + "Wed": "水", + "Thu": "木", + "Fri": "金", + "Sat": "土", + "Jan": "1月", + "Feb": "2月", + "Mar": "3月", + "Apr": "4月", + "May": "5月", + "Jun": "6月", + "Jul": "7月", + "Aug": "8月", + "Sep": "9月", + "Oct": "10月", + "Nov": "11月", + "Dec": "12月", + "January": "1月", + "February": "2月", + "March": "3月", + "April": "4月", + "June": "6月", + "July": "7月", + "August": "8月", + "September": "9月", + "October": "10月", + "November": "11月", + "December": "12月", + "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" + } + }, + "doughnut": { + "doughnut-appearance": "ドーナツの外観", + "layout": "レイアウト", + "layout-default": "デフォルト", + "layout-with-total": "合計あり", + "central-total-value": "中央の合計値", + "doughnut-card-style": "ドーナツカードスタイル" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "階層データ設定", + "relations-query-function": "ノード関係クエリ関数", + "has-children-function": "ノードに子がいる関数", + "node-state-settings": "ノード状態設定", + "node-opened-function": "デフォルトのノード開閉関数", + "node-disabled-function": "ノード無効関数", + "display-settings": "表示設定", + "node-icon-function": "ノードアイコン関数", + "node-text-function": "ノードテキスト関数", + "sort-settings": "並べ替え設定", + "nodes-sort-function": "ノード並べ替え関数" + }, + "edge": { + "display-default-title": "デフォルトタイトルを表示" + }, + "gateway": { + "general-settings": "一般設定", + "widget-title": "ウィジェットのタイトル", + "default-archive-file-name": "デフォルトのアーカイブファイル名", + "device-type-for-new-gateway": "新しいgatewayのデバイスタイプ", + "messages-settings": "メッセージ設定", + "save-config-success-message": "正常に保存されたgateway構成についてのテキストメッセージ", + "device-name-exists-message": "入力した名前のデバイスがすでに存在する場合のテキストメッセージ", + "gateway-title": "Gatewayフォーム", + "read-only": "読み取り専用", + "events-title": "Gatewayイベントフォームタイトル", + "events-filter": "イベントフィルター", + "event-key-contains": "イベントキーに含まれる...", + "show-connector": "コネクタ用に表示", + "connector-state-param-key": "コネクタ状態パラメーターキー", + "message": "メッセージ", + "level": "レベル", + "created-time": "作成時間" + }, + "gauge": { + "default-color": "デフォルトの色", + "radial-gauge-settings": "放射状ゲージ設定", + "ticks-settings": "目盛り設定", + "min-value": "最小値", + "max-value": "最大値", + "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": "値ボックス設定", + "show-value-box": "値ボックスを表示", + "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": "プレートの色", + "needle-settings": "針設定", + "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": "ハイライトを追加", + "animation-settings": "アニメーション設定", + "enable-animation": "アニメーションを有効にする", + "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": "進行状況バー", + "progress-bar-color-start": "進行状況バーの色 - 開始グラデーション", + "progress-bar-color-end": "進行状況バーの色 - 終了グラデーション", + "major-ticks-names": "主要目盛り名", + "show-stroke-ticks": "目盛り枠線を表示", + "major-ticks-font": "主要目盛りフォント", + "border-color": "枠線の色", + "border-width": "枠線の幅", + "needle-circle": "針の円", + "needle-circle-color": "針の円の色", + "animation-target": "アニメーションターゲット", + "animation-target-needle": "針", + "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)", + "neon-glow-brightness-hint": "0 - 効果を無効にする", + "stripes-thickness": "ストライプの厚さ", + "stripes-thickness-hint": "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": "ゲージタイトルフォント", + "unit-title-and-timestamp-settings": "単位タイトルとタイムスタンプ設定", + "show-timestamp": "タイムスタンプ", + "timestamp-format": "タイムスタンプ形式", + "label-font": "値の下に表示されるラベルのフォント", + "value-settings": "値設定", + "show-value": "値テキストを表示", + "min-max-settings": "最小/最大ラベル設定", + "show-min-max": "最小および最大値を表示", + "min-max-font": "最小および最大ラベルのフォント", + "show-ticks": "目盛りを表示", + "tick-width": "目盛りの幅", + "tick-color": "目盛りの色", + "tick-values": "目盛り値", + "no-tick-values": "設定された目盛り値はありません", + "add-tick-value": "目盛り値を追加", + "gauge-appearance": "ゲージの外観", + "units-title": "単位タイトル", + "value": "値", + "ticks": "目盛り", + "arrow-and-scale-color": "矢印とスケールのデフォルト色", + "scale-settings": "スケール設定", + "scale": "スケール", + "scale-color": "スケールの色", + "compass-appearance": "コンパスの外観", + "label": "ラベル", + "labels": "ラベル", + "label-style": "ラベルスタイル", + "simple-gauge-type": "タイプ", + "gauge-bar-background": "ゲージバーの背景", + "bar-color": "バーの色", + "min-and-max-value": "最小および最大値", + "min-and-max-label": "最小および最大ラベル", + "font": "フォント", + "tick-width-and-color": "目盛りの幅と色", + "min-max-validation-text": "最大値は最小値より大きくなければなりません" + }, + "gpio": { + "pin": "ピン", + "label": "ラベル", + "row": "行", + "column": "列", + "color": "色", + "panel-settings": "パネル設定", + "background-color": "背景色", + "gpio-switches": "GPIOスイッチ", + "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": "GPIO LEDs", + "no-gpio-leds": "設定されたGPIO LEDsはありません", + "add-gpio-led": "GPIO LEDを追加" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "このウィジェットでは属性パラメーターは使用できません", + "blocked-location": "ブラウザで位置情報がブロックされています", + "claim-device": "デバイスをクレーム", + "claim-failed": "デバイスのクレームに失敗しました!", + "claim-not-found": "デバイスが見つかりません!", + "claim-successful": "デバイスは正常にクレームされました!", + "date": "日付", + "device-name": "デバイス名", + "device-name-required": "デバイス名は必須です", + "discard-changes": "変更を破棄", + "entity-attribute-required": "エンティティ属性は必須です", + "entity-coordinate-required": "緯度と経度の両方のフィールドが必要です", + "entity-timeseries-required": "エンティティの時系列は必須です", + "get-location": "現在位置を取得", + "invalid-date": "無効な日付", + "latitude": "緯度", + "longitude": "経度", + "min-value-error": "最小値は{{value}}です", + "max-value-error": "最大値は{{value}}です", + "not-allowed-entity": "選択したエンティティには共有属性を持つことができません", + "no-attribute-selected": "属性が選択されていません", + "no-datakey-selected": "データキーが選択されていません", + "no-coordinate-specified": "緯度/経度のデータキーが指定されていません", + "no-entity-selected": "エンティティが選択されていません", + "no-image": "画像がありません", + "no-support-geolocation": "ブラウザは位置情報をサポートしていません", + "no-support-web-camera": "ブラウザはカメラをサポートしていません", + "enable-https-use-widget": "このウィジェットを使用するにはHTTPSを有効にしてください", + "no-found-your-camera": "カメラが見つかりません", + "no-permission-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": "値", + "general-settings": "一般設定", + "widget-title": "ウィジェットタイトル", + "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": "'シークレットキーは必須' エラーメッセージ", + "show-label": "ラベルを表示", + "label": "ラベル", + "required": "必須", + "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": "チェックボックス設定", + "true-label": "チェック済みラベル", + "false-label": "未チェックラベル", + "image-input-settings": "画像入力設定", + "display-preview": "プレビューを表示", + "display-clear-button": "クリアボタンを表示", + "display-apply-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": "緯度のラベル", + "longitude-label": "経度のラベル", + "input-fields-alignment": "入力フィールドの配置", + "input-fields-alignment-column": "列(デフォルト)", + "input-fields-alignment-row": "行", + "layout": "レイアウト", + "row-gap": "行間のギャップ(ピクセル)", + "column-gap": "列間のギャップ(ピクセル)", + "latitude-field-required": "緯度フィールドは必須です", + "longitude-field-required": "経度フィールドは必須です", + "attribute-settings": "属性設定", + "widget-mode": "ウィジェットモード", + "widget-mode-update-attribute": "属性を更新", + "widget-mode-update-timeseries": "時系列を更新", + "attribute-scope": "属性範囲", + "attribute-scope-server": "サーバー属性", + "attribute-scope-shared": "共有属性", + "value-required": "値は必須です", + "image-settings": "画像設定", + "image-format": "画像形式", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "損失圧縮を使用する画像品質(JPEGやWEBPなど)", + "max-image-width": "最大画像幅", + "max-image-height": "最大画像高さ", + "action-buttons": "アクションボタン", + "show-action-buttons": "アクションボタンを表示", + "update-all-values": "変更された値だけでなく、すべての値を更新", + "save-button-label": "'SAVE' ボタンラベル", + "reset-button-label": "'UNDO' ボタンラベル", + "group-settings": "グループ設定", + "show-group-title": "異なるエンティティに関連するフィールドのグループタイトルを表示", + "group-title": "グループタイトル", + "fields-alignment": "フィールドの配置", + "fields-alignment-row": "行(デフォルト)", + "fields-alignment-column": "列", + "fields-in-row": "行のフィールド数", + "option-value": "値(空のオプションを作成するために 'null' を入力)", + "option-label": "ラベル", + "hide-input-field": "入力フィールドを非表示", + "datakey-type": "データキータイプ", + "datakey-type-server": "サーバー属性(デフォルト)", + "datakey-type-shared": "共有属性", + "datakey-type-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-radio": "ラジオ", + "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": "他のデータキーの偽の値で無効にする(データキー名を指定)", + "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": "アイコン設定", + "dialog-editor-settings": "ダイアログエディター設定", + "use-custom-icon": "カスタムアイコンを使用", + "input-cell-icon": "入力セル前に表示するアイコン", + "value-conversion-settings": "値変換設定", + "get-value-settings": "値取得設定", + "use-get-value-function": "getValue関数を使用", + "get-value-function": "getValue関数", + "set-value-settings": "値設定設定", + "use-set-value-function": "setValue関数を使用", + "set-value-function": "setValue関数", + "json-invalid": "JSON値の形式が無効です", + "title": "タイトル", + "cancel-button-label": "'キャンセル' ボタンラベル", + "radio-button-settings": "ラジオボタン設定", + "color": "色", + "columns": "列", + "radio-options": "ラジオオプション", + "no-radio-options": "設定されたラジオオプションはありません", + "add-radio-option": "ラジオオプションを追加", + "radio-label-position": "ラベル位置", + "radio-label-position-before": "前", + "radio-label-position-after": "後", + "save-image": "画像を保存", + "save-to-gallery": "撮影した画像を画像ギャラリーに自動保存", + "public-image": "画像を未認証のユーザーにも公開します" + }, + "invalid-qr-code-text": "QRコードの入力テキストが無効です。入力は文字列タイプである必要があります", + "qr-code": { + "use-qr-code-text-function": "QRコードテキスト関数を使用", + "qr-code-text-pattern": "QRコードテキストパターン(例: '${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-hint": "QRコードテキストパターンは、エンティティのエイリアス内で最初に見つかったキーの値を使用します。", + "qr-code-text-pattern-required": "QRコードテキストパターンは必須です。", + "qr-code-text-function": "QRコードテキスト関数" + }, + "label-widget": { + "label-pattern": "パターン", + "label-pattern-hint": "ヒント: 例: 'Text ${keyName} units.' または ${#<key index>} units'", + "label-pattern-required": "パターンは必須です", + "label-position": "位置(背景に対するパーセンテージ)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "背景色", + "font-settings": "フォント設定", + "background-image": "背景画像", + "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を入力..." + }, + "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": "リクエストを削除してもよろしいですか?", + "details-title": "詳細 RPC ID: ", + "additional-info": "追加情報", + "response": "レスポンス", + "any-status": "任意のステータス", + "rpc-status-list": "RPCステータスリスト", + "no-request-prompt": "表示するリクエストはありません", + "send-request": "リクエストを送信", + "add-title": "永続的なRPCリクエストを作成", + "method-error": "メソッドは必須です。", + "timeout-error": "最小タイムアウト値は5000(5秒)です。", + "white-space-error": "空白は許可されていません。", + "rpc-status": { + "QUEUED": "待機中", + "SENT": "送信済み", + "DELIVERED": "配信済み", + "SUCCESSFUL": "成功", + "TIMEOUT": "タイムアウト", + "EXPIRED": "期限切れ", + "FAILED": "失敗" + }, + "rpc-search-status-all": "すべて", + "message-types": { + "false": "双方向", + "true": "一方向" + }, + "general-settings": "一般設定", + "enable-filter": "フィルターを有効にする", + "enable-sticky-header": "スクロール中にヘッダーを表示", + "enable-sticky-action": "スクロール中にアクション列を表示", + "display-request-details": "リクエスト詳細を表示", + "allow-send-request": "RPCリクエストの送信を許可", + "allow-delete-request": "リクエストの削除を許可", + "columns-settings": "列設定", + "display-columns": "表示する列", + "column": "列", + "no-columns-found": "列が見つかりません", + "no-columns-matching": "'{{column}}' が見つかりません。" + }, + "range-chart": { + "chart": "チャート", + "data-zoom": "データズーム", + "range-chart-appearance": "範囲チャートの外観", + "range-colors": "範囲の色", + "out-of-range-color": "範囲外の色", + "show-range-thresholds": "範囲しきい値を表示", + "range-thresholds-settings": "範囲しきい値設定", + "fill-area": "塗りつぶし領域", + "fill-area-opacity": "塗りつぶし領域の不透明度", + "range-chart-style": "範囲チャートスタイル" + }, + "knob": { + "behavior": "動作", + "initial-value": "初期値", + "initial-value-hint": "ノブの初期値を取得するためのアクション。", + "on-value-change": "値の変更時", + "on-value-change-hint": "ノブの値が変更されたときにトリガーされるアクション。", + "range": "範囲", + "min": "最小", + "max": "最大", + "value": "値", + "fallback-initial-value": "フォールバック初期値" + }, + "rpc": { + "value-settings": "値設定", + "initial-value": "初期値", + "retrieve-value-settings": "値の取得設定(オン/オフ)", + "retrieve-value-method": "値を取得する方法", + "retrieve-value-method-none": "取得しない", + "retrieve-value-method-rpc": "RPCのget値メソッドを呼び出す", + "retrieve-value-method-attribute": "属性をサブスクライブする", + "retrieve-value-method-timeseries": "時系列をサブスクライブする", + "attribute-value-key": "属性キー", + "timeseries-value-key": "時系列キー", + "get-value-method": "RPCのget値メソッド", + "parse-value-function": "値解析関数", + "update-value-settings": "値設定の更新", + "set-value-method": "RPCのset値メソッド", + "convert-value-function": "値変換関数", + "rpc-settings": "RPC設定", + "request-timeout": "RPCリクエストタイムアウト(ms)", + "persistent-rpc-settings": "永続的なRPC設定", + "request-persistent": "RPCリクエスト永続化", + "persistent-polling-interval": "永続的なRPCコマンドレスポンスを取得するポーリング間隔(ms)", + "common-settings": "共通設定", + "switch-title": "スイッチタイトル", + "show-on-off-labels": "オン/オフラベルを表示", + "slide-toggle-label": "スライドトグルラベル", + "label-position": "ラベル位置", + "label-position-before": "前", + "label-position-after": "後", + "slider-color": "スライダーの色", + "slider-color-primary": "プライマリ", + "slider-color-accent": "アクセント", + "slider-color-warn": "警告", + "button-style": "ボタンスタイル", + "button-raised": "浮き上がったボタン", + "button-primary": "プライマリカラー", + "button-background-color": "ボタンの背景色", + "button-text-color": "ボタンのテキストカラー", + "widget-title": "ウィジェットタイトル", + "button-label": "ボタンラベル", + "device-attribute-scope": "デバイス属性範囲", + "server-attribute": "サーバー属性", + "shared-attribute": "共有属性", + "device-attribute-parameters": "デバイス属性パラメーター", + "is-one-way-command": "一方向コマンドかどうか", + "rpc-method": "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": "LEDステータス値解析関数", + "knob-title": "ノブタイトル" + }, + "maps": { + "map-type": { + "type": "地図タイプ", + "map": "地図", + "image": "画像" + }, + "image": { + "image-source": "画像ソース", + "image-source-image": "画像", + "image-source-entity-key": "エンティティキー", + "source-entity-alias": "ソースエンティティエイリアス", + "image-url-key": "画像URLキー", + "image-url-key-required": "画像URLキーは必須です" + }, + "control": { + "map-controls": "地図コントロール", + "position": "位置", + "position-topleft": "左上", + "position-topright": "右上", + "position-bottomleft": "左下", + "position-bottomright": "右下", + "zoom-actions": "ズームアクション", + "zoom-scroll": "スクロール", + "zoom-double-click": "ダブルクリック", + "zoom-control-buttons": "コントロールボタン", + "scale": "スケール", + "scale-metric": "メートル法", + "scale-imperial": "インペリアル単位", + "switch-to-drag-mode-using-button": "ボタンを使用してドラッグモードに切り替え" + }, + "timeline": { + "control-panel": "タイムラインコントロールパネル", + "time-step": "時間ステップ", + "speed-options": "速度オプション", + "timestamp": "タイムスタンプ", + "snap-to-real-location": "実際の位置にスナップ", + "location-snap-filter-function": "位置スナップフィルター関数", + "no-trips-data-available": "利用可能な旅行データはありません" + }, + "map-action": { + "map-action-buttons": "地図アクションボタン", + "label": "ラベル", + "icon": "アイコン", + "color": "色", + "action": "アクション", + "add-button": "ボタンを追加", + "no-action-buttons-configured": "設定されたアクションボタンはありません", + "remove-action-button": "アクションボタンを削除", + "map-action-button": "地図アクションボタン", + "button-requires": "ボタンにはラベルまたはアイコンが必要です" + }, + "common": { + "common-map-settings": "共通の地図設定", + "fit-map-bounds": "地図の境界を調整してすべてのマーカーをカバー", + "default-map-center-position": "デフォルトの地図中央位置", + "default-map-zoom-level": "デフォルトの地図ズームレベル", + "entities-limit": "読み込むエンティティの制限" + }, + "layer": { + "label": "ラベル", + "layer": "レイヤー", + "layers": "レイヤー", + "map-layers": "地図レイヤー", + "add-layer": "レイヤーを追加", + "layer-settings": "レイヤー設定", + "remove-layer": "レイヤーを削除", + "no-layers": "設定されたレイヤーはありません", + "roadmap": "道路地図", + "satellite": "衛星", + "hybrid": "ハイブリッド", + "reference": { + "reference-layer": "参照レイヤー", + "no-layer": "レイヤーなし", + "openstreetmap-hybrid": "OpenStreetMap ハイブリッド", + "world-edition-hybrid": "ワールドエディション ハイブリッド", + "enhanced-contrast-hybrid": "強調コントラスト ハイブリッド" + }, + "provider": { + "provider": "プロバイダー", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "道路地図", + "satellite": "衛星", + "hybrid": "ハイブリッド", + "terrain": "地形" + }, + "here": { + "title": "HERE", + "normal-day": "通常の日", + "normal-night": "通常の夜", + "hybrid-day": "ハイブリッドの日", + "terrain-day": "地形の日" + }, + "tencent": { + "title": "Tencent", + "normal": "通常", + "satellite": "衛星", + "terrain": "地形" + }, + "custom": { + "title": "カスタム", + "tile-url": "タイルURL" + } + }, + "credentials": { + "credentials": "認証情報", + "api-key": "APIキー" + } + }, + "overlays": { + "overlays": "オーバーレイ", + "overlays-hint": "地図エンティティのデータソース、外観、動作、編集オプション、およびグループ化を設定", + "trips": "旅行", + "markers": "マーカー", + "polygons": "ポリゴン", + "circles": "円", + "polylines": "ポリライン" + }, + "data-layer": { + "source": "ソース", + "filter": "フィルター", + "additional-data-keys": "追加データキー", + "additional-datasources": "追加データソース", + "additional-datasources-hint": "地図に表示されていないエンティティから属性またはテレメトリデータにアクセスするためのデータソース、地図オーバーレイ関数で使用可能。", + "more-datasources": "さらにデータソース", + "data-keys": "データキー", + "add-datasource": "データソースを追加", + "no-datasources": "設定されたデータソースはありません", + "remove-datasource": "データソースを削除", + "behavior": "動作", + "on-click": "クリック時", + "on-click-hint": "ユーザーが地図アイテムをクリックしたときに呼び出されるアクション。", + "groups": "グループ", + "groups-hint": "オーバーレイに割り当てられたグループ名のリスト、地図上での可視性の切り替えに使用されます。", + "color": "色", + "color-settings": "色設定", + "color-type-constant": "定数", + "color-type-range": "範囲", + "color-type-function": "関数", + "color-range-source-key": "色範囲ソースキー", + "color-range-source-key-required": "色範囲ソースキーは必須です", + "color-range": "色範囲", + "color-function": "色関数", + "label": "ラベル", + "tooltip": "ツールチップ", + "pattern-type-pattern": "パターン", + "pattern-type-function": "関数", + "label-pattern": "ラベル(パターン例: '${entityName}', '${entityName}: (テキスト ${keyName} 単位.)')", + "label-function": "ラベル関数", + "tooltip-pattern": "ツールチップ(例: 'テキスト ${keyName} 単位.' または リンクテキスト)", + "tooltip-function": "ツールチップ関数", + "tooltip-trigger": "ツールチップトリガー", + "tooltip-trigger-click": "クリック時にツールチップを表示", + "tooltip-trigger-hover": "ホバー時にツールチップを表示", + "auto-close-tooltips": "ツールチップを自動的に閉じる", + "tooltip-offset": "ツールチップオフセット", + "tooltip-offset-horizontal": "水平方向", + "tooltip-offset-vertical": "垂直方向", + "tooltip-tag-actions": "タグアクション", + "add-tooltip-tag-action": "タグアクションを追加", + "edit-tooltip-tag-action": "タグアクションを編集", + "remove-tooltip-tag-action": "タグアクションを削除", + "action-add": "追加", + "action-edit": "編集", + "action-move": "移動", + "action-remove": "削除", + "edit-instruments": "インスツルメント", + "persist-location-attribute-scope": "位置を永続化する属性の範囲", + "enable-snapping": "精密描画のために他の頂点にスナッピングを有効にする", + "enable-snapping-hint": "新しいポイントを既存の形状と自動的に整列させ、描画を簡単かつ正確にします。", + "drag-drop-mode": "ドラッグ&ドロップモード", + "trip": { + "no-trips": "設定された旅行はありません", + "add-trip": "旅行を追加", + "trip-configuration": "旅行設定", + "remove-trip": "旅行を削除" + }, + "marker": { + "marker": "マーカー", + "latitude-key": "緯度キー", + "longitude-key": "経度キー", + "x-pos-key": "X位置キー", + "y-pos-key": "Y位置キー", + "latitude-key-required": "緯度キーは必須です", + "longitude-key-required": "経度キーは必須です", + "x-pos-key-required": "X位置キーは必須です", + "y-pos-key-required": "Y位置キーは必須です", + "no-markers": "設定されたマーカーはありません", + "add-marker": "マーカーを追加", + "marker-configuration": "マーカー設定", + "remove-marker": "マーカーを削除", + "marker-type": "マーカータイプ", + "marker-type-shape": "形状", + "marker-type-icon": "アイコン", + "marker-type-image": "画像", + "shape": "形状", + "icon": "アイコン", + "image": "画像", + "marker-shapes": "マーカー形状", + "marker-icon": "マーカーアイコン", + "marker-appearance": "マーカーの外観", + "marker-image": "マーカー画像", + "marker-image-type-image": "画像", + "marker-image-type-function": "関数", + "custom-marker-image-size": "カスタムマーカー画像サイズ", + "marker-image-function": "マーカー画像関数", + "marker-images": "マーカー画像", + "marker-offset": "マーカーオフセット", + "offset-horizontal": "水平方向", + "offset-vertical": "垂直方向", + "rotate-marker": "マーカーを回転", + "offset-angle": "オフセット角度", + "position-conversion": "位置変換", + "position-conversion-function": "位置変換関数、x,y 座標を0から1の範囲で返す必要があります", + "clustering": { + "use-map-markers-clustering": "地図マーカーのクラスタリングを使用", + "zoom-on-cluster-click": "クラスタをクリックしたときにズーム", + "max-zoom": "マーカーがクラスタの一部として含まれる最大ズームレベル(0 - 18)", + "max-radius": "クラスタがカバーする最大半径", + "zoom-animation": "ズーム時のマーカーのアニメーション", + "bounds-on-cluster-mouse-over": "クラスタにマウスオーバーしたときのマーカーの境界", + "spiderfy-max-zoom-level": "最大ズームレベルでスパイダーファイ(すべてのクラスターマーカーを見るため)", + "load-optimization": "読み込み最適化", + "chunked-load": "ページがフリーズしないようにマーカーをチャンクで追加", + "lazy-load": "マーカーを遅延読み込みで追加", + "use-cluster-marker-color-function": "クラスターマーカーの色関数を使用", + "marker-color-function": "マーカー色関数" + }, + "edit": "マーカーを編集", + "remove-marker-for": "'{{entityName}}'のマーカーを削除", + "place-marker": "マーカーを配置", + "place-marker-hint": "クリックしてマーカーを配置", + "place-marker-hint-with-entity": "'{{entityName}}'エンティティのマーカーを配置するにはクリック" + }, + "path": { + "path": "パス", + "path-decorator": "パスデコレーター", + "decorator-symbol": "デコレーターシンボル", + "decorator-symbol-arrow-head": "矢印", + "decorator-symbol-dash": "ダッシュ", + "decorator-arrangement": "デコレーター配置", + "decorator-offset": "開始", + "decorator-end-offset": "終了", + "decorator-repeat": "繰り返し" + }, + "points": { + "points": "ポイント", + "point-tooltip": "ポイントツールチップ" + }, + "shape": { + "fill": "塗りつぶし", + "fill-type-color": "色", + "fill-type-stripe": "ストライプ", + "fill-type-image": "画像", + "color": "色", + "stripe": "ストライプ", + "image": "画像", + "stroke": "枠線", + "fill-image": "塗りつぶし画像", + "fill-image-type-image": "画像", + "fill-image-type-function": "関数", + "preserve-aspect-ratio": "アスペクト比を保持", + "opacity": "不透明度", + "angle": "回転角度", + "scale": "スケール", + "fill-image-function": "形状塗りつぶし画像関数", + "fill-images": "形状塗りつぶし画像", + "stripe-pattern": "ストライプパターン", + "first-stripe": "最初のストライプ", + "second-stripe": "2番目のストライプ" + }, + "polygon": { + "polygon-key": "ポリゴンキー", + "polygon-key-required": "ポリゴンキーは必須です", + "no-polygons": "設定されたポリゴンはありません", + "add-polygon": "ポリゴンを追加", + "polygon-configuration": "ポリゴン設定", + "remove-polygon": "ポリゴンを削除", + "edit": "ポリゴンを編集", + "remove-polygon-for": "'{{entityName}}'のポリゴンを削除", + "cut": "ポリゴン領域を切り取る", + "rotate": "ポリゴンを回転", + "draw-rectangle": "矩形を描画", + "draw-polygon": "ポリゴンを描画", + "polygon-place-first-point-cut-hint": "最初のポイントを配置するにはクリック", + "continue-polygon-cut-hint": "描画を続けるにはクリック", + "finish-polygon-cut-hint": "最初のマーカーをクリックして描画を終了し、保存", + "polygon-place-first-point-hint": "ポリゴン: 最初のポイントを配置するにはクリック", + "polygon-place-first-point-hint-with-entity": "'{{entityName}}'のポリゴン: 最初のポイントを配置するにはクリック", + "continue-polygon-hint": "ポリゴン: 描画を続けるにはクリック", + "continue-polygon-hint-with-entity": "'{{entityName}}'のポリゴン: 描画を続けるにはクリック", + "finish-polygon-hint": "ポリゴン: 最初のマーカーをクリックして描画を終了", + "finish-polygon-hint-with-entity": "'{{entityName}}'のポリゴン: 最初のマーカーをクリックして描画を終了し、保存", + "rectangle-place-first-point-hint": "矩形: 最初のポイントを配置するにはクリック", + "rectangle-place-first-point-hint-with-entity": "'{{entityName}}'の矩形: 最初のポイントを配置するにはクリック", + "finish-rectangle-hint": "矩形: 描画を終了するにはクリック", + "finish-rectangle-hint-with-entity": "'{{entityName}}'の矩形: 描画を終了して保存するにはクリック" + }, + "circle": { + "circle-key": "円キー", + "circle-key-required": "円キーは必須です", + "no-circles": "設定された円はありません", + "add-circle": "円を追加", + "circle-configuration": "円設定", + "remove-circle": "円を削除", + "edit": "円を編集", + "remove-circle-for": "'{{entityName}}'の円を削除", + "draw-circle": "円を描画", + "place-circle-center-hint-with-entity": "'{{entityName}}'の円: 円の中心を配置するにはクリック", + "place-circle-center-hint": "円: 円の中心を配置するにはクリック", + "finish-circle-hint-with-entity": "'{{entityName}}'の円: 描画を終了して保存するにはクリック", + "finish-circle-hint": "円: 描画を終了するにはクリック" + }, + "polyline": { + "polyline-key": "ポリラインキー", + "polyline-key-required": "ポリラインキーは必須です", + "no-polylines": "ポリラインが設定されていません", + "add-polylines": "ポリラインを追加", + "polyline-configuration": "ポリライン設定", + "remove-polyline": "ポリラインを削除", + "edit": "ポリラインを編集", + "cut": "ポリライン領域を切り取り", + "rotate": "ポリラインを回転", + "remove-polyline-for": "'{{entityName}}' のポリラインを削除", + "draw-polyline": "ポリラインを描画", + "polyline-place-first-point-hint-with-entity": "'{{entityName}}' のポリライン: クリックして最初のポイントを配置", + "polyline-place-first-point-hint": "ポリライン: クリックして最初のポイントを配置", + "finish-polyline-hint-with-entity": "'{{entityName}}' のポリライン: クリックして描画を完了", + "finish-polyline-hint": "ポリライン: クリックして描画を完了", + "polyline-place-first-point-cut-hint": "クリックして最初のポイントを配置", + "finish-polyline-cut-hint": "最初のマーカーをクリックして完了し保存" + }, + "select-entity": "エンティティを選択", + "select-entity-hint": "ヒント: 選択後、マップをクリックして位置を設定します" + }, + "select-entity": "エンティティを選択", + "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": "円マーカーを配置するにはクリック" + }, + "actions": { + "finish": "終了", + "cancel": "キャンセル", + "removeLastVertex": "最後のポイントを削除" + }, + "buttonTitles": { + "drawMarkerButton": "エンティティを配置", + "drawPolyButton": "ポリゴンを作成", + "drawLineButton": "ポリラインを作成", + "drawCircleButton": "円を作成", + "drawRectButton": "矩形を作成", + "editButton": "編集モード", + "dragButton": "ドラッグ&ドロップモード", + "cutButton": "ポリゴン領域を切り取る", + "deleteButton": "削除", + "drawCircleMarkerButton": "円マーカーを作成", + "rotateButton": "ポリゴンを回転" + }, + "map-provider-settings": "地図プロバイダー設定", + "map-provider": "地図プロバイダー", + "map-provider-google": "Google マップ", + "map-provider-openstreet": "OpenStreet マップ", + "map-provider-here": "HERE マップ", + "map-provider-image": "画像地図", + "map-provider-tencent": "Tencent マップ", + "openstreet-provider": "OpenStreet マッププロバイダー", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik(デフォルト)", + "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": "カスタムプロバイダーを使用", + "custom-provider-tile-url": "カスタムプロバイダータイルURL", + "google-maps-api-key": "Google Maps APIキー", + "default-map-type": "デフォルトの地図タイプ", + "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アプリコード", + "here-api-key": "HERE APIキー", + "here-use-new-version-api-3": "APIバージョン3を使用", + "tencent-maps-api-key": "Tencent Maps 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": "共通の地図設定", + "x-pos-key-name": "X位置キー名", + "y-pos-key-name": "Y位置キー名", + "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": "読み込むエンティティの制限", + "markers-settings": "マーカー設定", + "marker-offset-x": "位置に対するマーカーXオフセット(マーカー幅で乗算)", + "marker-offset-y": "位置に対するマーカーYオフセット(マーカー高さで乗算)", + "position-function": "位置変換関数、x,y 座標を0から1の範囲で返す必要があります", + "draggable-marker": "ドラッグ可能なマーカー", + "label": "ラベル", + "show-label": "ラベルを表示", + "use-label-function": "ラベル関数を使用", + "label-pattern": "ラベル(パターン例: '${entityName}', '${entityName}: (テキスト ${keyName} 単位.)')", + "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} 単位.' または リンクテキスト)", + "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": "カスタムマーカー画像サイズ(px)", + "marker-image-function": "マーカー画像関数", + "marker-images": "マーカー画像", + "polygon-settings": "ポリゴン設定", + "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} 単位.' または リンクテキスト)", + "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} 単位.' または リンクテキスト)", + "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": "すべての編集コントロールボタンを非表示", + "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": "ツールチップを自動的に閉じる", + "rotation-angle": "マーカーの追加回転角度を設定(度)", + "path-settings": "パス設定", + "path-color": "パス色", + "use-path-color-function": "パス色関数を使用", + "path-color-function": "パス色関数", + "path-decorator": "パスデコレーター", + "use-path-decorator": "パスデコレーターを使用", + "decorator-symbol": "デコレーターシンボル", + "decorator-symbol-arrow-head": "矢印", + "decorator-symbol-dash": "ダッシュ", + "decorator-symbol-size": "デコレーターシンボルのサイズ(px)", + "use-path-decorator-custom-color": "パスデコレーターのカスタムカラーを使用", + "decorator-custom-color": "デコレーターのカスタムカラー", + "decorator-offset": "デコレーターオフセット", + "end-decorator-offset": "終了デコレーターオフセット", + "decorator-repeat": "デコレーターの繰り返し", + "points-settings": "ポイント設定", + "show-points": "ポイントを表示", + "point-color": "ポイントの色", + "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": "マーカーの色関数を使用", + "marker-color-function": "マーカー色関数" + }, + "markdown": { + "use-markdown-text-function": "Markdown/HTML値関数を使用", + "markdown-text-function": "Markdown/HTML値関数", + "markdown-text-pattern": "Markdown/HTMLパターン(変数を使用したmarkdownまたはHTML、例: '${entityName} または ${keyName} - 一部のテキスト.')", + "apply-default-markdown-style": "デフォルトのMarkdownスタイルを適用", + "markdown-css": "Markdown/HTML CSS" + }, + "simple-card": { + "label": "ラベル", + "label-position": "ラベル位置", + "label-position-left": "左", + "label-position-top": "上" + }, + "single-switch": { + "behavior": "動作", + "layout": "レイアウト", + "layout-right": "右", + "layout-left": "左", + "layout-centered": "中央", + "auto-scale": "自動スケール", + "label": "ラベル", + "icon": "アイコン", + "switch-color": "スイッチの色", + "on": "オン", + "off": "オフ", + "disabled": "無効", + "tumbler-color": "タンブラーの色", + "on-label": "オンラベル", + "off-label": "オフラベル", + "switch": "スイッチ" + }, + "slider": { + "behavior": "動作", + "initial-value": "初期値", + "initial-value-hint": "スライダーの初期値を取得するためのアクション。", + "on-value-change": "値変更時", + "on-value-change-hint": "スライダーの値が変更されたときにトリガーされるアクション。", + "layout": "レイアウト", + "layout-default": "デフォルト", + "layout-extended": "拡張", + "layout-simplified": "簡易", + "auto-scale": "自動スケール", + "icon": "アイコン", + "value": "値", + "range": "範囲", + "min": "最小", + "max": "最大", + "range-ticks": "範囲目盛り", + "tick-marks": "目盛り", + "colors": "色", + "main": "メイン", + "background": "背景", + "left-icon": "左アイコン", + "right-icon": "右アイコン", + "slider": "スライダー" + }, + "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": "自動スケール" + }, + "label-card": { + "auto-scale": "自動スケール", + "label": "ラベル", + "icon": "アイコン", + "label-card-style": "ラベルカードスタイル" + }, + "label-value-card": { + "value": "値", + "label-value-card-style": "ラベルと値カードのスタイル" + }, + "liquid-level-card": { + "layout-simple": "シンプル", + "layout-percentage": "パーセンテージ", + "layout-absolute": "絶対", + "layout": "レイアウト", + "background-overlay": "値の背景オーバーレイ", + "total-volume": "総容量", + "total-volume-units": "総容量単位", + "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": "プログレスバーカードスタイル" + }, + "notification": { + "max-notification-display": "表示する最大通知数", + "counter": "カウンター", + "counter-hint": "\"ウィジェットタイトル\"が有効な場合にカウンターが表示されます", + "icon": "アイコン", + "counter-value": "値", + "counter-color": "色", + "notification-button": "通知ボタン", + "button-view-all": "すべて表示", + "button-filter": "フィルター", + "type-filter": "タイプフィルター", + "button-mark-read": "すべてを既読にマーク", + "notification-types": "通知タイプ", + "notification-type": "通知タイプ", + "search-type": "タイプ検索", + "any-type": "任意のタイプ" + }, + "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": "共通テーブル設定", + "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": "タイムスタンプ", + "timestamp-column-name": "タイムスタンプ", + "display-pagination": "ページネーションを表示", + "default-page-size": "デフォルトのページサイズ", + "page-step-settings": "ページステップ設定", + "page-step-count": "ステップ数", + "page-step-increment": "ステップ増分", + "page-step-count-format-message": "1から100の範囲の整数値である必要があります。", + "page-step-increment-format-message": "1以上の整数値である必要があります。", + "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": "セルコンテンツ関数", + "show-latest-data-column": "最新データ列を表示", + "latest-data-column-order": "最新データ列の順序", + "entities-table-title": "エンティティテーブルタイトル", + "enable-select-column-display": "表示する列を選択を有効にする", + "display-entity-name": "エンティティ名列を表示", + "entity-name-column-title": "エンティティ名列タイトル", + "display-entity-label": "エンティティラベル列を表示", + "entity-label-column-title": "エンティティラベル列タイトル", + "display-entity-type": "エンティティタイプ列を表示", + "default-sort-order": "デフォルトの並べ替え順序", + "custom-title": "カスタムヘッダータイトル", + "column-width": "列の幅(pxまたは%)", + "default-column-visibility": "デフォルトの列表示設定", + "column-visibility-visible": "表示", + "column-visibility-hidden": "非表示", + "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": "アラームクリアを許可", + "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": "少なくとも1つの時系列列を指定する必要があります", + "alarm-column-error": "少なくとも1つのアラーム列を指定する必要があります", + "table-tabs": "テーブルタブ", + "show-cell-actions-menu-mobile": "モバイルモードでセルアクションのドロップダウンメニューを表示", + "disable-sorting": "ソートを無効にする", + "sort-by": "タブの並べ替え基準", + "sort-timestamp-option": "作成日時" + }, + "latest-chart": { + "total": "合計", + "auto-scale": "自動スケール", + "clockwise-layout": "時計回りのレイアウト", + "sort-series": "ラベルで系列をソート", + "tooltip-value-type-absolute": "絶対", + "tooltip-value-type-percentage": "パーセンテージ" + }, + "pie-chart": { + "pie-chart-appearance": "円グラフの外観", + "label": "ラベル", + "border": "枠線", + "radius": "半径", + "pie-chart-card-style": "円グラフカードスタイル" + }, + "radar-chart": { + "radar-appearance": "レーダーの外観", + "shape": "形状", + "shape-polygon": "ポリゴン", + "shape-circle": "円形", + "color": "色", + "line": "線", + "points": "ポイント", + "points-label": "ポイントラベル", + "radar-axis": "レーダー軸", + "axis-label": "軸ラベル", + "ticks-label": "目盛りラベル", + "radar-chart-style": "レーダーチャートスタイル", + "max-axes-scaling": "最大軸スケーリング", + "max-axes-scaling-hint": "各レーダー軸に個別の最大値を設定するか(別々に)、またはウィジェットデータセットに基づいてすべての軸で最高値を共有するか(共通)を選択します。", + "separate": "別々に", + "common": "共通" + }, + "time-series-chart": { + "chart": "チャート", + "chart-style": "チャートスタイル", + "data-zoom": "データズーム", + "stack-mode": "スタックモード", + "stack-mode-hint": "チャート上で系列を積み重ねます。同じ単位の系列は互いに重ねて表示されます。", + "axes": "軸", + "y-axes": "Y軸", + "line-type": "線の種類", + "line-width": "線の幅", + "type-line": "線", + "type-bar": "バー", + "type-point": "ポイント", + "no-aggregation-bar-width-strategy": "非集計データのバー幅戦略", + "no-aggregation-bar-width-strategy-group": "グループ", + "no-aggregation-bar-width-strategy-separate": "別々に", + "bar-group-width": "バーグループ幅", + "bar-width": "バー幅", + "bar-width-relative": "時間ウィンドウの割合", + "bar-width-absolute": "絶対値(ms)", + "comparison": { + "comparison": "比較", + "comparison-hint": "比較は過去のデータにのみ機能します!", + "show": "表示", + "settings": "比較設定", + "show-values-for-comparison": "比較のために過去のデータを表示", + "comparison-values-label": "比較キーラベル", + "comparison-values-label-auto": "自動", + "comparison-data-color": "比較データの色" + }, + "threshold": { + "thresholds": "閾値", + "source": "ソース", + "key-value": "キー / 値", + "no-thresholds": "設定された閾値はありません", + "add-threshold": "閾値を追加", + "type-constant": "定数", + "type-latest-key": "キー", + "type-entity": "エンティティ", + "threshold-settings": "閾値設定", + "remove-threshold": "閾値を削除", + "threshold-value-required": "閾値の値は必須です。", + "key-required": "キーは必須です。", + "entity-key-required": "エンティティキーは必須です。", + "line-appearance": "線の外観", + "line-color": "線の色", + "start-symbol": "開始シンボル", + "end-symbol": "終了シンボル", + "symbol-size": "サイズ", + "label": "ラベル", + "label-position-start": "開始", + "label-position-middle": "中央", + "label-position-end": "終了", + "label-position-inside-start": "内側開始", + "label-position-inside-start-top": "内側開始上", + "label-position-inside-start-bottom": "内側開始下", + "label-position-inside-middle": "内側中央", + "label-position-inside-middle-top": "内側中央上", + "label-position-inside-middle-bottom": "内側中央下", + "label-position-inside-end": "内側終了", + "label-position-inside-end-top": "内側終了上", + "label-position-inside-end-bottom": "内側終了下", + "label-background": "ラベルの背景" + }, + "state": { + "states": "状態", + "label": "ラベル", + "ticks-value": "目盛りの値", + "source": "ソース", + "value-range": "値 / 範囲", + "no-states": "設定された状態はありません", + "add-state": "状態を追加", + "type-constant": "定数", + "type-range": "範囲", + "from": "から", + "to": "まで", + "remove-state": "状態を削除" + }, + "grid": { + "grid": "グリッド", + "background-color": "背景色", + "border": "枠線" + }, + "axis": { + "axes": "軸", + "x-axis": "X軸", + "y-axis": "Y軸", + "y-axis-settings": "Y軸設定", + "comparison-x-axis-settings": "比較X軸設定", + "remove-y-axis": "Y軸を削除", + "id": "ID", + "label": "ラベル", + "position": "位置", + "position-left": "左", + "position-right": "右", + "position-top": "上", + "position-bottom": "下", + "tick-labels": "目盛りラベル", + "ticks-formatter-function": "目盛り書式関数", + "ticks-generator-function": "目盛り生成関数", + "show-ticks": "目盛りを表示", + "show-line": "線を表示", + "show-split-lines": "分割線を表示", + "show-split-lines-x-axis-hint": "有効にすると、チャート上の縦線が表示されます。", + "show-split-lines-y-axis-hint": "有効にすると、チャート上の横線が表示されます。", + "ticks-interval": "目盛り間隔", + "ticks-interval-hint": "軸の分割間隔を強制的に設定します。", + "split-number": "分割数", + "split-number-hint": "軸が分割されるセグメント数。", + "min": "最小", + "max": "最大", + "show": "表示", + "add-y-axis": "Y軸を追加" + }, + "series": { + "legend-settings": "凡例設定", + "show-in-legend": "凡例に表示", + "show-in-legend-hint": "凡例に系列名とデータを表示します。", + "hidden-by-default": "デフォルトで非表示", + "hidden-by-default-hint": "デフォルトで凡例に系列を非表示にします。", + "series-type": "系列タイプ", + "type": "タイプ", + "type-line": "線", + "type-bar": "バー", + "line": { + "line": "線", + "show-line": "線を表示", + "step-line": "ステップライン", + "step-type-start": "開始", + "step-type-middle": "中央", + "step-type-end": "終了", + "smooth-line": "スムーズライン" + }, + "point": { + "points": "ポイント", + "show-points": "ポイントを表示", + "point-label": "ポイントラベル", + "point-label-hint": "系列ポイント上に値を表示するラベルを表示。", + "point-label-background": "ポイントラベルの背景", + "point-shape": "ポイントの形状", + "point-size": "ポイントのサイズ" + } + } + }, + "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": "値のソース", + "predefined-value": "定数", + "entity-attribute": "エンティティ属性", + "value": "値", + "value-required": "値は必須です。", + "key-required": "キーは必須です。", + "entity-key-required": "エンティティキーは必須です。", + "source-entity-alias": "ソースエンティティエイリアス", + "source-entity-attribute": "ソースエンティティ属性", + "type-constant": "定数", + "type-latest-key": "キー", + "type-entity": "エンティティ" + }, + "rpc-state": { + "initial-state": "初期状態", + "initial-state-hint": "コンポーネントの初期状態(オン/オフ)を取得するアクション。", + "disabled-state": "無効状態", + "disabled-state-hint": "コンポーネントが無効になる条件を設定します。", + "turn-on": "'オン'にする", + "turn-on-hint": "'オン'に切り替えたときにトリガーされるアクション", + "turn-off": "'オフ'にする", + "turn-off-hint": "'オフ'に切り替えたときにトリガーされるアクション", + "on": "オン", + "off": "オフ", + "disabled": "無効" + }, + "value-action": { + "do-nothing": "何もしない", + "execute-rpc": "RPCを実行", + "get-attribute": "属性を取得", + "set-attribute": "属性を設定", + "get-time-series": "時系列データを取得", + "get-alarm-status": "アラーム状態を取得", + "get-dashboard-state": "ダッシュボード状態IDを取得", + "get-dashboard-state-object": "ダッシュボード状態オブジェクトを取得", + "add-time-series": "時系列データを追加", + "execute-rpc-text": "RPCメソッド '{{methodName}}' を実行", + "get-time-series-text": "時系列 '{{key}}' を使用", + "get-attribute-text": "属性 '{{key}}' を使用", + "get-alarm-status-text": "アラーム状態を使用", + "get-dashboard-state-text": "ダッシュボード状態を使用", + "get-dashboard-state-object-text": "ダッシュボード状態オブジェクトを使用", + "when-dashboard-state-is-text": "ダッシュボード状態IDが '{{state}}' の場合", + "when-dashboard-state-function-is-text": "f(ダッシュボード状態ID)が '{{state}}' の場合", + "when-dashboard-state-object-function-is-text": "f(ダッシュボード状態オブジェクト)が '{{state}}' の場合", + "set-attribute-to-value-text": "'{{key}}' 属性を {{value}} に設定", + "add-time-series-value-text": "'{{key}}' 時系列値を {{value}} に追加", + "set-attribute-text": "'{{key}}' 属性を設定", + "add-time-series-text": "'{{key}}' 時系列データを追加", + "action": "アクション", + "value": "値", + "init-value-hint": "デバイスがデータを送信するまで設定される値。", + "method": "メソッド", + "method-name-required": "メソッド名は必須です。", + "request-timeout-ms": "RPCリクエストタイムアウト(ms)", + "request-timeout-required": "リクエストタイムアウトは必須です。", + "min-request-timeout-error": "リクエストタイムアウト値は5000ms(5秒)以上である必要があります。", + "request-persistent": "RPCリクエストの永続性", + "persistent-polling-interval": "永続的なポーリング間隔(ms)", + "persistent-polling-interval-hint": "永続的なRPCコマンドレスポンスを取得するためのポーリング間隔(ms)", + "persistent-polling-interval-required": "永続的なポーリング間隔は必須です。", + "min-persistent-polling-interval-error": "永続的なポーリング間隔値は1000ms(1秒)以上である必要があります。", + "attribute-scope": "属性スコープ", + "attribute-key": "属性キー", + "attribute-key-required": "属性キーは必須です。", + "time-series-key": "時系列キー", + "time-series-key-required": "時系列キーは必須です。", + "action-result-converter": "アクション結果コンバーター", + "converter-none": "なし", + "converter-function": "関数", + "converter-constant": "定数", + "converter-value": "値", + "parse-value-function": "値解析関数", + "state-when-result-is": "結果が'{{state}}'のとき", + "parameters": "パラメータ", + "convert-value-function": "値変換関数", + "error": { + "target-entity-is-not-set": "ターゲットエンティティが設定されていません!", + "failed-to-perform-action": "{{ actionLabel }}アクションの実行に失敗しました。", + "invalid-attribute-scope": "{{scope}} 属性スコープは{{entityType}}エンティティではサポートされていません。" + } + }, + "widget-font": { + "font-settings": "フォント設定", + "font-family": "フォントファミリ", + "size": "サイズ", + "relative-font-size": "相対フォントサイズ(パーセント)", + "font-style": "スタイル", + "font-style-normal": "標準", + "font-style-italic": "斜体", + "font-style-oblique": "傾斜体", + "font-weight": "太さ", + "font-weight-normal": "標準", + "font-weight-bold": "太字", + "font-weight-bolder": "より太字", + "font-weight-lighter": "より細字", + "color": "色", + "shadow-color": "影の色", + "preview": "プレビュー", + "line-height": "行の高さ", + "auto": "自動" + }, + "home": { + "no-data-available": "データがありません" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "ディスク", + "cpu-warning-text": "CPU使用率が高い状態です。システム障害を避けるため、システムパフォーマンスを最適化してください。", + "cpu-critical-text": "CPU使用率が非常に高い状態です。システム障害を避けるため、システムパフォーマンスを最適化してください。", + "ram-warning-text": "RAMの空き容量が少なくなっています。システム障害を避けるため、システムパフォーマンスを最適化するか、RAMのサイズを増加させてください。", + "ram-critical-text": "RAMの空き容量が非常に少ない状態です。システム障害を避けるため、システムパフォーマンスを最適化するか、RAMのサイズを増加させてください。", + "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": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "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\nProfessional Edition のみ", + "switch-to-pe": "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": "機能の設定: SMSプロバイダー", + "content": "

    SMSプロバイダーを設定して、顧客にアラームをSMSで通知します。

    設定方法については、以下のドキュメントを参照してください:

    ", + "how-to-configure-sms-provider": "SMSプロバイダーを設定する方法" + }, + "step4": { + "title": "機能の設定: ホワイトラベリング", + "content": "

    コードを記述せず、サービスを再起動せずに、会社や製品のロゴやカラースキームを簡単にカスタマイズできます。

    設定方法については、以下のドキュメントを参照してください:

    " + }, + "step5": { + "title": "機能の設定: 2FA", + "content": "

    二要素認証でプラットフォームアカウントのセキュリティを強化します。

    設定方法については、以下のドキュメントを参照してください:

    " + }, + "step6": { + "title": "機能の設定: OAuth 2", + "content": "

    OAuth 2.0を使用したシングルサインオン機能で、テナントと顧客ユーザーのログインを簡素化します。

    設定方法については、以下のドキュメントを参照してください:

    " + } + }, + "tenant-admin": { + "step1": { + "title": "デバイスの作成", + "content": "

    UIを通じて最初のデバイスをプラットフォームにプロビジョニングします。手順については、以下のドキュメントを参照してください:

    ", + "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°C以上を送信してください。

    ", + "replace-access-token": "$ACCESS_TOKEN をデバイスのトークンに置き換える:", + "content-after": "

    設定方法については、以下のドキュメントを参照してください:

    ", + "how-to-create-alarm": "アラームの作成方法" + }, + "step6": { + "title": "顧客の作成とダッシュボードの共有", + "content": "

    エンドユーザー用のダッシュボードを作成することにより、顧客ユーザーは自分のデバイスのみを表示でき、他の顧客のデータは非表示にされます。

    作成方法については、以下のドキュメントを参照してください:

    " + } + } + }, + "api-usage": { + "api-usage": "API 使用状況", + "label": "ラベル", + "state-name": "状態名", + "status": "ステータス", + "status-required": "ステータスは必須です。", + "limit": "最大上限", + "limit-required": "最大上限は必須です。", + "current-number": "現在の数", + "current-number-required": "現在の数は必須です。", + "add-key": "キーを追加", + "no-key": "キーがありません", + "delete-key": "キーを削除", + "target-dashboard-state": "対象ダッシュボード状態", + "go-to-main-state": "既定ビューに移動" + } + }, + "icon": { + "icon": "アイコン", + "icons": "アイコン", + "custom": "カスタム", + "select-icon": "アイコンを選択", + "material-icons": "マテリアルアイコン", + "show-all": "すべてのアイコンを表示", + "search-icon": "アイコンを検索", + "no-icons-found": "'{{iconSearch}}' のアイコンは見つかりませんでした" + }, + "phone-input": { + "phone-input-label": "電話番号", + "phone-input-required": "電話番号は必須です", + "phone-input-validation": "電話番号が無効か、利用不可能です", + "phone-input-pattern": "無効な電話番号です。E.164形式でなければなりません。例: {{phoneNumber}}", + "phone-input-hint": "E.164形式の電話番号。例: {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "アクションセルボタン", + "row-click": "行をクリックしたとき", + "cell-click": "セルをクリックしたとき", + "polygon-click": "ポリゴンをクリックしたとき", + "marker-click": "マーカーをクリックしたとき", + "circle-click": "円をクリックしたとき", + "tooltip-tag-action": "ツールチップタグアクション", + "node-selected": "ノードが選択されたとき", + "element-click": "HTML要素をクリックしたとき", + "pie-slice-click": "スライスをクリックしたとき", + "row-double-click": "行をダブルクリックしたとき", + "cell-double-click": "セルをダブルクリックしたとき", + "card-click": "カードをクリックしたとき", + "click": "クリックしたとき" + } + }, + "paginator": { + "items-per-page": "ページあたりのアイテム数:", + "first-page-label": "最初のページ", + "last-page-label": "最後のページ", + "next-page-label": "次のページ", + "previous-page-label": "前のページ", + "items-per-page-separator": "の" + }, + "language": { + "auto": "自動", + "language": "言語" + } +} \ No newline at end of file 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 ae8ce2b943..996e5b86c5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json @@ -1250,7 +1250,6 @@ "create-password": "პაროლის შექმნა", "passwords-mismatch-error": "პაროლები არ ემთხვევა", "password-again": "შეიყვანეთ პაროლი თავიდა", - "sign-in": "სისტემაში შესვლა", "username": "მომხმარებლის სახელი", "remember-me": "დამახსოვრება", "forgot-password": " დაგავიწყდა პაროლი?", 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 a122a04ee7..787d26ee2e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -11,7 +11,7 @@ "permission-denied-text": "이 작업을 수행할 권한이 없습니다!" }, "action": { - "activate": "활설화", + "activate": "활성화", "suspend": "비활성화", "save": "저장", "saveAs": "다른 이름으로 저장", @@ -1708,7 +1708,6 @@ "create-password": "비밀번호 생성", "passwords-mismatch-error": "입력된 비밀번호는 같아야 합니다!", "password-again": "비밀번호 확인", - "sign-in": "로그인", "username": "사용자명 (이메일)", "remember-me": "아이디 저장", "forgot-password": "비밀번호찾기", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index c3ddb5af74..893453031b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -3767,7 +3767,6 @@ "two-factor-authentication": "Dviejų veiksnių autentifikavimas", "passwords-mismatch-error": "Įvesti slaptažodžiai turi sutapti!", "password-again": "Pakartokite slaptažodį", - "sign-in": "Prisijungti", "username": "Vartotojo vardas (el. paštas)", "remember-me": "Prisiminti mane", "forgot-password": "Pamiršote slaptažodį?", 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 bba71609ec..1eed936bf0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json +++ b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json @@ -1178,7 +1178,6 @@ "create-password": "Radīt paroli", "passwords-mismatch-error": "Ievadītajai parolei ir jāsakrīt!", "password-again": "Atkārtot paroli", - "sign-in": "Lūdzu pierakstīties", "username": "Lietotājvārds (email)", "remember-me": "Atcerēties mani", "forgot-password": "Aizmirsu paroli?", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index 3c8bfbde60..965cc9a3aa 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -3847,7 +3847,6 @@ "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?", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_NL.json b/ui-ngx/src/assets/locale/locale.constant-nl_NL.json index 810f49ff00..01d828b1be 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_NL.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_NL.json @@ -78,6 +78,7 @@ "show-more": "Meer weergeven", "dont-show-again": "Niet opnieuw weergeven", "see-documentation": "Bekijk documentatie", + "see-debug-events": "Debuggebeurtenissen bekijken", "clear": "Wissen", "upload": "Uploaden", "delete-anyway": "Toch verwijderen", @@ -485,6 +486,7 @@ "2fa": { "2fa": "Twee-factor-authenticatie", "available-providers": "Beschikbare providers", + "available-providers-required": "Er moet ten minste één 2FA-provider zijn geconfigureerd.", "issuer-name": "Naam uitgever", "issuer-name-required": "Naam uitgever is verplicht.", "max-verification-failures-before-user-lockout": "Maximaal aantal verificatiefouten vóór accountvergrendeling", @@ -513,7 +515,9 @@ "verification-message-template-required": "Sjabloon verificatiebericht is verplicht.", "within-time": "Binnen tijd (sec)", "within-time-pattern": "Tijd moet een positief geheel getal zijn.", - "within-time-required": "Tijd is verplicht." + "within-time-required": "Tijd is verplicht.", + "force-2fa": "Twee-factor-authenticatie afdwingen", + "enforce-for": "Afdwingen voor" }, "jwt": { "security-settings": "JWT-beveiligingsinstellingen", @@ -545,16 +549,11 @@ "slack-settings": "Slack-instellingen", "mobile-settings": "Mobiele instellingen", "firebase-service-account-file": "Firebase serviceaccountreferenties JSON-bestand", - "select-firebase-service-account-file": "Sleep je Firebase serviceaccountreferentiesbestand hierheen of ", - "trendz": "Trendz", - "trendz-settings": "Trendz-instellingen", - "trendz-url": "Trendz-URL", - "trendz-url-required": "Trendz-URL is vereist", - "trendz-api-key": "Trendz API-sleutel", - "trendz-enable": "Trendz inschakelen" + "select-firebase-service-account-file": "Sleep je Firebase serviceaccountreferentiesbestand hierheen of " }, "alarm": { "alarm": "Alarm", + "alarm-list": "Alarmlijst", "alarms": "Alarmen", "all-alarms": "Alle alarmen", "select-alarm": "Selecteer alarm", @@ -655,7 +654,16 @@ "alarm-type": "Alarmtype", "enter-alarm-type": "Voer alarmtype in", "no-alarm-types-matching": "Geen alarmtypen gevonden die overeenkomen met '{{entitySubtype}}'.", - "alarm-type-list-empty": "Geen alarmtypen geselecteerd." + "alarm-type-list-empty": "Geen alarmtypen geselecteerd.", + "system-comments": { + "acked-by-user": "Alarm is bevestigd door gebruiker {{userName}}", + "cleared-by-user": "Alarm is beëindigd door gebruiker {{userName}}", + "assigned-to-user": "Alarm is toegewezen door gebruiker {{userName}} aan gebruiker {{assigneeName}}", + "unassigned-to-user": "Alarmtoewijzing is verwijderd door gebruiker {{userName}}", + "unassigned-from-deleted-user": "Alarmtoewijzing is verwijderd omdat gebruiker {{userName}} - is verwijderd", + "comment-deleted": "Gebruiker {{userName}} heeft de opmerking verwijderd", + "severity-changed": "Alarmernst is bijgewerkt van {{oldSeverity}} naar {{newSeverity}}" + } }, "alarm-activity": { "add": "Voeg een opmerking toe...", @@ -760,6 +768,7 @@ "name-max-length": "Naam mag maximaal 256 tekens bevatten", "label-max-length": "Label mag maximaal 256 tekens bevatten", "description": "Beschrijving", + "description-required": "Beschrijving is vereist.", "type": "Type", "type-required": "Type is verplicht.", "details": "Details", @@ -873,6 +882,9 @@ "alarms-created-monthly-activity": "Maandelijkse activiteit gegenereerde alarmen", "data-points": "Datapunten", "data-points-storage-days": "Opslagduur van datapunten (dagen)", + "data-points-storage-days-hourly-activity": "Datapuntenopslagdagen: uurlijkse activiteit", + "data-points-storage-days-daily-activity": "Datapuntenopslagdagen: dagelijkse activiteit", + "data-points-storage-days-monthly-activity": "Datapuntenopslagdagen: maandelijkse activiteit", "device-api": "Apparaat-API", "email": "E-mail", "email-messages": "E-mailberichten", @@ -906,6 +918,7 @@ "rule-node": "Rule node", "sms": "SMS", "sms-messages": "SMS-berichten", + "sms-messages-hourly-activity": "SMS-berichten: uurlijkse activiteit", "sms-messages-daily-activity": "Dagelijkse activiteit SMS-berichten", "sms-messages-monthly-activity": "Maandelijkse activiteit SMS-berichten", "successful": "${entityName} Succesvol", @@ -915,13 +928,38 @@ "telemetry-persistence-hourly-activity": "Uurlijkse activiteit telemetrie-opslag", "telemetry-persistence-monthly-activity": "Maandelijkse activiteit telemetrie-opslag", "transport": "Transport", + "transport-msg-hourly-activity": "Transportberichten: uurlijkse activiteit", + "transport-msg-daily-activity": "Transportberichten: dagelijkse activiteit", + "transport-msg-monthly-activity": "Transportberichten: maandelijkse activiteit", "transport-daily-activity": "Dagelijkse activiteit transport", "transport-data-points": "Transport datapunten", - "transport-hourly-activity": "Uurlijkse activiteit transport", + "transport-data-points-hourly-activity": "Transportdatapunten: uurlijkse activiteit", + "transport-data-points-daily-activity": "Transportdatapunten: dagelijkse activiteit", + "transport-data-points-monthly-activity": "Transportdatapunten: maandelijkse activiteit", "transport-messages": "Transportberichten", - "transport-monthly-activity": "Maandelijkse activiteit transport", - "view-details": "Details bekijken", - "view-statistics": "Statistieken bekijken" + "transport-messages-hourly-activity": "Transportberichten: uurlijkse activiteit", + "transport-data-point-hourly-activity": "Transportdatapunt: uurlijkse activiteit", + "javascript-function-executions": "JavaScript-functie-uitvoeringen", + "javascript-function-executions-hourly-activity": "JavaScript-functie-uitvoeringen: uurlijkse activiteit", + "javascript-function-executions-daily-activity": "JavaScript-functie-uitvoeringen: dagelijkse activiteit", + "javascript-function-executions-monthly-activity": "JavaScript-functie-uitvoeringen: maandelijkse activiteit", + "tbel-function-executions": "TBEL-functie-uitvoeringen", + "tbel-function-executions-hourly-activity": "TBEL-functie-uitvoeringen: uurlijkse activiteit", + "tbel-function-executions-daily-activity": "TBEL-functie-uitvoeringen: dagelijkse activiteit", + "tbel-function-executions-monthly-activity": "TBEL-functie-uitvoeringen: maandelijkse activiteit", + "created-reports": "Aangemaakte rapporten", + "created-reports-hourly-activity": "Aangemaakte rapporten: uurlijkse activiteit", + "created-reports-daily-activity": "Aangemaakte rapporten: dagelijkse activiteit", + "created-reports-monthly-activity": "Aangemaakte rapporten: maandelijkse activiteit", + "emails": "E-mails", + "emails-hourly-activity": "E-mails: uurlijkse activiteit", + "emails-daily-activity": "E-mails: dagelijkse activiteit", + "emails-monthly-activity": "E-mails: maandelijkse activiteit", + "status": { + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "warning": "Waarschuwing" + } }, "api-limit": { "cassandra-write-queries-core": "Rest API Cassandra schrijfbewerkingen", @@ -946,6 +984,40 @@ "edge-uplink-messages": "Edge-uplinkberichten", "edge-uplink-messages-per-edge": "Edge-uplinkberichten per edge" }, + "api-key": { + "api-key": "API-sleutel", + "api-keys": "API-sleutels", + "delete-api-key-title": "Weet u zeker dat u de API-sleutel '{{name}}' wilt verwijderen?", + "delete-api-key-text": "Let op: na bevestiging kan de sleutel niet meer worden hersteld.", + "delete-api-keys-title": "Weet u zeker dat u { count, plural, =1 {1 API-sleutel} other {# API-sleutels} } wilt verwijderen?", + "delete-api-keys-text": "Let op: na bevestiging kunnen alle geselecteerde sleutels niet meer worden hersteld.", + "expiration-date": "Vervaldatum", + "date": "datum", + "description": "Beschrijving", + "disable": "Uitschakelen", + "edit-description": "Beschrijving bewerken", + "enable": "API-sleutel inschakelen ", + "expiration-time": "Vervaltijd", + "expiration-time-never": "Nooit", + "expiration-time-custom": "Aangepast", + "generate": "Genereren", + "generate-title": "API-sleutel genereren", + "generate-text": "Opmerking: de API-sleutel erft de machtigingen van de gebruiker waarvoor deze is aangemaakt.", + "generated-api-key-title": "API-sleutel gegenereerd. Laten we de connectiviteit controleren!", + "generated-api-key-copy": "Zorg ervoor dat u uw API-sleutel nu kopieert en opslaat, want u kunt deze niet opnieuw bekijken.", + "generated-api-key-command": "Gebruik de volgende instructies om de connectiviteit te controleren. Als resultaat zou u de huidige gebruikersinformatie moeten ontvangen:", + "generated-api-key-insecure-url": "Het uitvoeren van opdrachten via een onveilige HTTP-verbinding verzendt uw API-sleutel onversleuteld, waardoor deze kwetsbaar is voor onderschepping.", + "list": "{ count, plural, =1 {Eén API-sleutel} other {Lijst met # API-sleutels} }", + "manage": "Beheren", + "manage-api-keys": "API-sleutels beheren", + "no-found": "Geen API-sleutels gevonden", + "selected-api-keys": "{ count, plural, =1 {1 API-sleutel} other {# API-sleutels} } geselecteerd", + "search": "API-sleutels zoeken", + "status": "Status", + "status-active": "Actief", + "status-inactive": "Inactief", + "status-expired": "Verlopen" + }, "audit-log": { "audit": "Audit", "audit-logs": "Auditlogs", @@ -999,7 +1071,11 @@ "type-provision-failure": "Provisioning apparaat mislukt", "type-timeseries-updated": "Telemetrie bijgewerkt", "type-timeseries-deleted": "Telemetrie verwijderd", - "type-sms-sent": "SMS verzonden" + "type-sms-sent": "SMS verzonden", + "any-type": "Alle typen", + "audit-log-filter-title": "Auditlogfilter", + "filter-title": "Filter", + "filter-types": "Auditlogtypen" }, "debug-settings": { "label": "Debugconfiguratie", @@ -1020,12 +1096,25 @@ "selected-fields": "{ count, plural, =1 {1 berekend veld} other {# berekende velden} } geselecteerd", "type": { "simple": "Eenvoudig", - "script": "Script" + "simple-hint": "Eenvoudige rekenkundige berekening op basis van invoerargumenten.", + "script": "Script", + "script-hint": "Berekening over gedefinieerde argumenten met behulp van een TBEL-script.", + "geofencing": "Geofencing", + "geofencing-hint": "Evaluatie van de GPS-positie van de entiteit en overgangen ten opzichte van geconfigureerde geofencingzonegroepen.", + "propagation": "Propagatie", + "propagation-hint": "Propagatie van gegevens naar bovenliggende of onderliggende entiteiten op basis van relatierichting en relatietype.", + "related-entities-aggregation": "Aggregatie van gerelateerde entiteiten", + "related-entities-aggregation-hint": "Aggregatie van de nieuwste gegevens van gerelateerde entiteiten.", + "time-series-data-aggregation": "Aggregatie van tijdreeksgegevens", + "time-series-data-aggregation-hint": "Aggregatie van historische gegevens van een huidige entiteit." }, + "preview": "Voorbeeld", "arguments": "Argumenten", "decimals-by-default": "Standaard aantal decimalen", "debugging": "Debuggen berekend veld", + "calculated-field-details": "Details van berekend veld", "argument-name": "Argumentnaam", + "name": "Naam", "datasource": "Databron", "add-argument": "Argument toevoegen", "test-script-function": "Scriptfunctie testen", @@ -1037,8 +1126,9 @@ "argument-asset": "Asset", "argument-customer": "Klant", "argument-tenant": "Huidige tenant", + "argument-owner": "Huidige eigenaar", + "argument-relation-query": "Gerelateerde entiteiten", "argument-type": "Argumenttype", - "see-debug-events": "Debuggebeurtenissen bekijken", "attribute": "Attribuut", "copy-argument-name": "Argumentnaam kopiëren", "timeseries-key": "Tijdreeks-sleutel", @@ -1051,12 +1141,14 @@ "shared-attributes": "Gedeelde attributen", "attribute-key": "Attribuutsleutel", "default-value": "Standaardwaarde", + "default-value-required": "Standaardwaarde is vereist.", "limit": "Maximale waarden", "time-window": "Tijdsvenster", "customer-name": "Klantnaam", "asset-name": "Assetnaam", "timeseries": "Tijdreeks", "output": "Uitvoer", + "output-hint": "Bepaalt hoe de uitvoer wordt verwerkt.", "create": "Nieuw berekend veld aanmaken", "file": "Berekend veld-bestand", "invalid-file-error": "Ongeldig bestandsformaat. Zorg ervoor dat het een geldig JSON-bestand is.", @@ -1070,9 +1162,175 @@ "delete-multiple-text": "Let op: na bevestiging worden alle geselecteerde berekende velden verwijderd en zijn de gegevens onherstelbaar.", "test-with-this-message": "Test met dit bericht", "use-latest-timestamp": "Gebruik laatste tijdstempel", + "entity-coordinates": "Entiteitscoördinaten", + "latitude-time-series-key": "Tijdreeksleutel voor breedtegraad", + "latitude-time-series-key-required": "Tijdreeksleutel voor breedtegraad is vereist.", + "longitude-time-series-key": "Tijdreeksleutel voor lengtegraad", + "longitude-time-series-key-required": "Tijdreeksleutel voor lengtegraad is vereist.", + "geofencing-zone-groups": "Geofencingzonegroepen", + "geofencing-zone-groups-settings": "Instellingen voor geofencingzonegroepen", + "target-zone": "Doelzone", + "perimeter-key": "Perimetersleutel", + "report-strategy": "Rapportagestrategie", + "no-zone-configured": "Er is ten minste één zone vereist.", + "no-zone-configured-required": "Er moet ten minste één zonegroep zijn geconfigureerd.", + "add-zone-group": "Zonegroep toevoegen", + "report-transition-event-only": "Alleen overgangsgebeurtenissen", + "report-presence-status-only": "Alleen aanwezigheidsstatus", + "report-transition-event-and-presence": "Aanwezigheidsstatus en overgangsgebeurtenissen", + "perimeter-attribute-key": "Perimeterattribuutsleutel", + "perimeter-attribute-key-required": "Perimeterattribuutsleutel is vereist.", + "perimeter-attribute-key-pattern": "Perimeterattribuutsleutel is ongeldig.", + "entity-zone-relationship": "Pad van entiteit naar zones", + "direction": "Relatierichting", + "direction-from": "Van entiteit naar zone", + "direction-to": "Van zone naar entiteit", + "relation-type": "Relatietype", + "create-relation-with-matched-zones": "Relaties aanmaken voor bronentiteit met overeenkomende zones", + "relation-level": "Relatieniveau", + "fetch-last-available-level": "Alleen laatst beschikbare niveau ophalen", + "zone-group-refresh-interval": "Vernieuwingsinterval voor zonegroepen", + "copy-zone-group-name": "Zonegroepnaam kopiëren", + "open-details-page": "Detailspagina van entiteit openen", + "level": "Niveau", + "direction-level": "Richting", + "direction-up": "Omhoog", + "direction-up-parent": "Omhoog naar bovenliggend", + "direction-down": "Omlaag", + "direction-down-child": "Omlaag naar onderliggend", + "add-level": "Niveau toevoegen", + "delete-level": "Niveau verwijderen", + "no-level": "Geen niveau geconfigureerd", + "levels-required": "Er moet ten minste één niveau zijn geconfigureerd.", + "max-allowed-levels-error": "Relatieniveau overschrijdt het maximaal toegestane.", + "propagation-path-related-entities": "Propagatiepad naar gerelateerde entiteiten", + "propagate-type": { + "arguments-only": "Alleen argumenten", + "expression-result": "Berekeningsresultaat" + }, + "script": "Script", + "data-propagate": "Gegevens om te propageren", + "output-key": "Uitvoersleutel", + "copy-output-key": "Uitvoersleutel kopiëren", + "aggregation-path-related-entities": "Aggregatiepad naar gerelateerde entiteiten", + "deduplication-interval": "Deduplicatie-interval", + "deduplication-interval-min": "Deduplicatie-interval moet ten minste {{ sec }} seconden zijn.", + "deduplication-interval-hint": "Minimale tijd tussen telemetrie-aggregaties.", + "deduplication-interval-required": "Deduplicatie-interval is vereist.", + "calculated-field-filter-title": "Filter voor berekend veld", + "filter-title": "Filter", + "calculated-field-types": "Typen berekend veld", + "events": "Gebeurtenissen", + "any-type": "Alle typen", + "metrics": { + "metrics": "Metrieken", + "metrics-empty": "Er moet ten minste één metriek zijn geconfigureerd.", + "metric-name": "Metrieknaam", + "metric-name-required": "Metrieknaam is vereist.", + "metric-name-pattern": "Metrieknaam is ongeldig.", + "metric-name-duplicate": "Er bestaat al een metriek met deze naam.", + "metric-name-max-length": "Metrieknaam moet korter zijn dan 256 tekens.", + "metric-name-forbidden": "Metrieknaam is gereserveerd en kan niet worden gebruikt.", + "copy-metric-name": "Metrieknaam kopiëren", + "argument-name": "Argumentnaam", + "aggregation": "Aggregatie", + "aggregation-type": { + "avg": "Gemiddelde", + "min": "Minimum", + "max": "Maximum", + "sum": "Som", + "count": "Aantal", + "count-unique": "Uniek aantal" + }, + "filtered": "Gefilterd", + "value-source": "Waardebron", + "value-source-hint": "Bepaalt hoe de waarde voor aggregatie wordt verkregen.", + "value-source-type": { + "key": "Sleutel", + "function": "Functie" + }, + "no-metrics-configured": "Er is ten minste één metriek vereist.", + "add-metric": "Metriek toevoegen", + "max-metrics": "Maximumaantal metrieken bereikt.", + "metric-settings": "Metriekinstellingen", + "filter": "Filter", + "filter-hint": "Schakelt het filteren van entiteiten tijdens aggregatie in. De filterfunctie moet een booleaanse waarde retourneren en kan alle geconfigureerde argumenten gebruiken." + }, + "output-strategy": { + "strategy": "Strategie", + "process-right-away": "Onmiddellijk verwerken", + "process-rule-chains": "Verwerken via Regelketens", + "save-time-series": "Opslaan in tijdreeksgegevens", + "save-database": "Opslaan in database", + "save-latest-values": "Opslaan in nieuwste waarden", + "send-web-sockets": "Verzenden naar WebSockets", + "save-calculated-fields": "Verzenden naar Berekende velden", + "update-attribute-only-on-value-change": "Attribuut alleen bij waardewijziging bijwerken", + "send-attributes-updated-notification": "'Attributes Updated'-notificatie verzenden", + "ttl": "Aangepaste TTL", + "ttl-required": "TTL is vereist", + "ttl-min": "Alleen een minimale TTL van 0 is toegestaan", + "processing-parameters": "Verwerkingsparameters", + "hint": { + "strategy": "Bepaalt of het resultaat onmiddellijk wordt verwerkt of naar een regelketen wordt verzonden voor aanvullende verwerking.", + "processing-options": "Verwerkingsopties", + "update-attribute-only-on-value-change": "Werkt het attribuut bij bij elk binnenkomend bericht, ongeacht of de waarde is gewijzigd. Dit verhoogt het API-gebruik en verlaagt de prestaties.", + "update-attribute-only-on-value-change-enabled": "Werkt het attribuut alleen bij wanneer de waarde wijzigt. Als de waarde ongewijzigd is, worden tijdstempels niet bijgewerkt en worden er geen notificaties verzonden.", + "send-attributes-updated-notification": "Verzendt een 'Attributes Updated'-gebeurtenis naar de standaardregelketen.", + "save-time-series": "Slaat tijdreeksgegevens op in de ts_kv-tabel in de database.", + "save-database": "Slaat attribuutgegevens op in de database.", + "save-latest-values": "Werkt tijdreeksgegevens bij in de ts_kv_latest-tabel in de database als de nieuwe waarde recenter is.", + "send-web-sockets-attribute": "Stelt WebSocket-abonnementen op de hoogte van updates van de attribuutgegevens.", + "send-web-sockets-time-series": "Stelt WebSocket-abonnementen op de hoogte van updates van de tijdreeksgegevens.", + "save-calculated-fields-attribute": "Stelt berekende velden op de hoogte van updates van de attribuutgegevens.", + "save-calculated-fields-time-series": "Stelt berekende velden op de hoogte van updates van de tijdreeksgegevens.", + "ttl": "Bepaalt de bewaarperiode voor tijdreeksgegevens. Als dit is uitgeschakeld, wordt de TTL van het Tenantprofiel gebruikt." + } + }, + "aggregate-interval-type": "Aggregatieintervaltype", + "aggregate-interval-value": "Aggregatieintervalwaarde", + "aggregate-interval-value-required": "Aggregatieintervalwaarde is vereist.", + "aggregate-interval-value-min": "Aggregatieintervalwaarde moet ten minste { sec, plural, =0 {0 seconde} =1 {1 seconde} other {# seconden} } zijn.", + "aggregate-interval-value-step-multiple-of": "Aggregatieintervalwaarde moet een deler of veelvoud van 1 dag zijn.", + "aggregate-period": { + "hour": "Uur", + "day": "Dag", + "week": "Week (ma - zo)", + "week-sun-sat": "Week (zo - za)", + "month": "Maand", + "quarter": "Kwartaal", + "year": "Jaar", + "custom": "Aangepast" + }, + "aggregate-period-hint-offset": "Uw aggregatieinterval is: {{ interval }}", + "aggregate-period-hint-offset-and-so-on": "Uw aggregatieinterval is: {{ interval }} en zo verder.", + "entity-aggregation": { + "argument-hint": "Gegevens worden opgehaald van de huidige entiteit.", + "argument-title-hint": "Bepaalt de invoerargumenten die voor aggregatie worden gebruikt.", + "argument-setting-hint": "Nieuwste telemetrie is het enige beschikbare argumenttype voor dit berekende veld.", + "aggregation-interval": "Aggregatieinterval", + "aggregation-interval-hint": "Bepaalt hoe vaak aggregatie wordt uitgevoerd. Voorbeeld: elke 1 uur worden gegevens geaggregeerd om 00:00, 01:00, 02:00, enz. Aggregatieresultaten worden opgeslagen met de tijdstempel die overeenkomt met het begin van het aggregatieinterval.", + "apply-offset": "Offset toepassen op aggregatieinterval", + "apply-offset-hint": "Bepaalt hoeveel het begin van elke aggregatieperiode wordt verschoven (bijv. +10 minuten - 00:10, 01:10).", + "offset-value": "Offsetwaarde", + "offset-value-required": "Offsetwaarde is vereist.", + "offset-value-min": "Offsetwaarde moet een positief geheel getal zijn.", + "offset-value-max": "Offsetwaarde moet kleiner zijn dan de waarde van het aggregatieinterval.", + "wait-delay": "Wachttime-out toepassen voor vertraagde telemetrie", + "wait-delay-hint": "Bepaalt hoe lang er na het einde van het interval op vertraagde telemetrie wordt gewacht. Als dergelijke telemetrie binnenkomt, wordt het resultaat voor dat interval opnieuw berekend.", + "duration": "Duur", + "duration-required": "Duur is vereist.", + "duration-min": "Duur moet minimaal 1 minuut zijn.", + "duration-hint": "Hoelang er wordt gewacht op vertraagde gegevens nadat het interval is afgelopen.", + "produce-intermediate-result": "Tussenresultaat genereren", + "produce-intermediate-result-hint": "Berekent metrieken tijdens het huidige interval om een tussenresultaat te genereren. Updates vinden niet vaker plaats dan één keer per {{ time }}." + }, "hint": { - "arguments-simple-with-rolling": "Berekende velden van het type 'eenvoudig' mogen geen sleutels bevatten met een rollende tijdreeks.", - "arguments-empty": "Argumenten mogen niet leeg zijn.", + "arguments-simple-with-rolling": "Een berekend veld van het type Eenvoudig mag geen sleutels bevatten met het type 'Time series rolling'.", + "arguments-propagate-arguments-with-rolling": "Het type 'Time series rolling' is niet compatibel met propagatie van 'Arguments only'.", + "arguments-propagate-argument-entity-type": "Entiteittype is niet compatibel met propagatie van 'Arguments only'.", + "arguments-propagate-argument-must-current-entity": "Ten minste één argument moet zijn geconfigureerd met het bronentiteittype 'Current entity'.", + "arguments-empty": "Er moet ten minste één argument worden opgegeven.", "expression-required": "Expressie is vereist.", "expression-invalid": "Expressie is ongeldig", "expression-max-length": "De lengte van de expressie moet minder dan 255 tekens zijn.", @@ -1081,12 +1339,218 @@ "argument-name-duplicate": "Een argument met deze naam bestaat al.", "argument-name-max-length": "Argumentnaam mag niet langer zijn dan 256 tekens.", "argument-name-forbidden": "Deze argumentnaam is gereserveerd en mag niet worden gebruikt.", + "output-key-required": "Uitvoersleutel is vereist.", + "output-key-pattern": "Uitvoersleutel is ongeldig.", + "output-key-duplicate": "Er bestaat al een sleutel met deze naam.", + "output-key-max-length": "Uitvoersleutel moet korter zijn dan 256 tekens.", + "output-key-forbidden": "Uitvoersleutel is gereserveerd en kan niet worden gebruikt.", + "entity-type-required": "Entiteittype is vereist", + "name-required": "Naam is vereist.", + "name-pattern": "Naam is ongeldig.", + "name-duplicate": "Er bestaat al een item met deze naam.", + "name-max-length": "Naam moet korter zijn dan 256 tekens.", + "name-forbidden": "Naam is gereserveerd en kan niet worden gebruikt.", "argument-type-required": "Argumenttype is vereist.", "max-args": "Maximum aantal argumenten bereikt.", "decimals-range": "Het aantal decimalen moet een getal zijn tussen 0 en 15.", "expression": "Standaardexpressie toont hoe temperatuur van Fahrenheit naar Celsius wordt omgerekend.", "arguments-entity-not-found": "Doelentiteit van het argument niet gevonden.", - "use-latest-timestamp": "Indien ingeschakeld, wordt de berekende waarde opgeslagen met de meest recente tijdstempel van de telemetrie van de argumenten, in plaats van met de servertijd." + "use-latest-timestamp": "Indien ingeschakeld, wordt de berekende waarde opgeslagen met de meest recente tijdstempel van de telemetrie van de argumenten, in plaats van met de servertijd.", + "entity-coordinates": "Geef de tijdreeksleutels op die de GPS-coördinaten van de entiteit (breedtegraad en lengtegraad) leveren.", + "geofencing-zone-groups": "Definieer één of meer geofencingzonegroepen om te controleren (bijv. 'allowedZones', 'restrictedZones'). Elke groep moet een unieke naam hebben, die wordt gebruikt als voorvoegsel voor uitvoertelemetriesleutels van het berekende veld.", + "perimeter-attribute-key": "Stel de attribuutsleutel in die de perimeterdefinitie van de geofencingzone bevat. De perimeter wordt altijd genomen uit server-side-attributen van de zone-entiteit.", + "report-strategy": "Aanwezigheidsstatus rapporteert of de entiteit zich momenteel BINNEN of BUITEN de zonegroep bevindt. Overgangsgebeurtenissen rapporteren wanneer de entiteit de zonegroep BETRAD of VERLIET.", + "create-relation-with-matched-zones": "Maak automatisch relaties aan en onderhoud deze tussen de entiteit en de zones waarin deze zich momenteel bevindt. Relaties worden verwijderd wanneer de entiteit een zone verlaat en aangemaakt wanneer deze een nieuwe zone binnenkomt.", + "relation-type-required": "Relatietype is vereist.", + "relation-level-required": "Relatieniveau is vereist.", + "relation-level-min": "Minimale waarde voor relatieniveau is 1.", + "relation-level-max": "Maximale waarde voor relatieniveau is {{max}}.", + "geofencing-empty": "Er moet ten minste één zonegroep zijn geconfigureerd.", + "geofencing-entity-not-found": "Geofencingdoelentiteit niet gevonden.", + "max-geofencing-zone": "Maximumaantal geofencingzones bereikt.", + "zone-group-refresh-interval": "Bepaalt hoe vaak zonegroepen die via gerelateerde entiteiten zijn geconfigureerd, worden vernieuwd.", + "zone-group-refresh-interval-required": "Vernieuwingsinterval voor zonegroepen is vereist.", + "zone-group-refresh-interval-min": "Vernieuwingsinterval voor zonegroepen moet ten minste {{ min }} seconden zijn.", + "propagation-path-related-entities": "Definieert een direct pad op één niveau naar een gerelateerde entiteit op basis van de geselecteerde richting en het relatietype. Alleen relaties tussen apparaat-, asset-, klant- en tenantentiteiten worden ondersteund. Het maximumaantal entiteiten dat via het relatiepad wordt opgelost is {{ max }}.", + "data-propagate": "Bepaalt de gegevens die moeten worden gepropageerd vanuit de hieronder geconfigureerde argumenten. 'Alleen argumenten' gebruikt de opgehaalde gegevens rechtstreeks, terwijl 'Berekeningsresultaat' een nieuwe waarde berekent op basis van die gegevens.", + "aggregation-path-related-entities": "Definieert een aggregatiepad op één niveau via directe relaties met bovenliggende of onderliggende entiteiten, op basis van richting en relatietype. Alleen relaties tussen apparaat-, asset-, klant- en tenantentiteiten worden ondersteund. Het maximumaantal entiteiten dat via het relatiepad wordt opgelost is {{ max }}.", + "arguments-aggregation": "Bepaalt de invoerargumenten die worden gebruikt voor filteren en aggregatie.", + "setting-arguments-aggregation": "Gegevens worden opgehaald van gerelateerde entiteiten die zijn geconfigureerd in het aggregatiepad.", + "metrics": "Bepaalt metrieken die worden geaggregeerd op basis van geconfigureerde argumenten.", + "entity-aggregation-metrics": "Bepaalt metrieken die op basis van geconfigureerde argumenten over de opgegeven tijdintervallen worden geaggregeerd.", + "import-invalid-calculated-field-type": "Kan berekend veld niet importeren: Ongeldige structuur van berekend veld.", + "simple-expression-title": "Rekenkundige expressie die bepaalt hoe de berekende waarde wordt berekend.", + "script-title": "TBEL-script dat de berekeningslogica en uitvoerwaarden definieert.", + "simple-arguments": "Rekenkundige expressie die bepaalt hoe de berekende waarde wordt berekend.", + "script-arguments": "Bepaalt de invoerargumenten die beschikbaar zijn voor het script." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Alarmregels", + "alarm-rule": "Alarmregel", + "alarm-rules": "Alarmregels", + "alarm-rules-old": "Oud", + "alarm-rules-actual": "Actueel", + "severities": "Ernstniveaus", + "cleared": "Beëindigingsvoorwaarde", + "delete-title": "Weet u zeker dat u de alarmregel '{{title}}' wilt verwijderen?", + "delete-text": "Let op: na bevestiging kunnen de alarmregel en alle gerelateerde gegevens niet meer worden hersteld.", + "delete-multiple-title": "Weet u zeker dat u { count, plural, =1 {1 alarmregel} other {# alarmregels} } wilt verwijderen?", + "delete-multiple-text": "Let op: na bevestiging worden alle geselecteerde alarmregels verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "create": "Nieuwe alarmregel maken", + "add": "Alarmregel toevoegen", + "copy": "Alarmregelconfiguratie kopiëren", + "details": "Alarmregeldetails", + "no-found": "Geen alarmregels gevonden", + "list": "{ count, plural, =1 {Eén alarmregel} other {Lijst met # alarmregels} }", + "selected-fields": "{ count, plural, =1 {1 alarmregel} other {# alarmregels} } geselecteerd", + "import": "Alarmregel importeren", + "file": "Alarmregelbestand", + "export": "Alarmregel exporteren", + "export-failed-error": "Kan alarmregel niet exporteren: {{error}}", + "entity-type": "Entiteittype", + "entity-type-required": "Entiteittype is vereist.", + "alarm-type": "Alarmtype", + "alarm-type-hint": "Unieke identificatie (bijv. HighTempAlarm) binnen het bereik van de alarminitiator (Apparaat, Asset, enz.) om conflicten te voorkomen.", + "alarm-type-required": "Alarmtype is vereist.", + "alarm-type-pattern": "Alarmtype is ongeldig.", + "alarm-type-max-length": "Alarmtype moet korter zijn dan 256 tekens.", + "clear-alarm": "Alarm beëindigen", + "value-argument": "Argument", + "value-argument-required": "Argument is vereist.", + "static-settings": "Statische instellingen", + "configuration": "Configuratie", + "static-schedule": "Statisch", + "dynamic-schedule": "Dynamisch", + "operation-and": "EN", + "operation-or": "OF", + "condition-during": "Gedurende {{during}}", + "condition-during-dynamic": "Gedurende \"{{ attribute }}\"", + "condition-repeat-times": "Herhaalt { count, plural, =1 {1 keer} other {# keer} }", + "condition-repeat-times-dynamic": "Herhaalt \"{{ attribute }}\" keer", + "filter-preview": "Filtervoorbeeld", + "condition-settings": "Voorwaarde-instellingen", + "static": "Statisch", + "dynamic": "Dynamisch", + "argument-filters": "Argumentfilters", + "argument-name": "Argumentnaam", + "value-type": "Waardetype", + "general": "Algemeen", + "filters": "Filters", + "date-time-hint": "Het argument moet in epoch-millisconden zijn. Voorbeeld: 1698839340000 is gelijk aan 2023-11-01 12:49:00 UTC.", + "operation": "Bewerking", + "value-source": "Waardebron", + "value": "Waarde", + "ignore-case": "Hoofdlettergebruik negeren", + "condition": "Voorwaarde", + "script": "Script", + "add-filter": "Argumentfilter toevoegen", + "edit-filter": "Argumentfilter", + "remove-filter": "Argumentfilter verwijderen", + "no-filter": "Er is ten minste één filter vereist.", + "conditions": { + "simple": "Eenvoudig", + "duration": "Duur", + "repeating": "Herhalend" + }, + "schedule-title": "Planning", + "edit-schedule": "Alarmplanning bewerken", + "schedule-type": "Plannertype", + "schedule-type-required": "Plannertype is vereist.", + "schedule": { + "any-time": "Altijd actief", + "specific-time": "Actief op een specifiek tijdstip", + "custom": "Aangepast" + }, + "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": "Tot", + "schedule-days-of-week-required": "Er moet ten minste één dag van de week worden geselecteerd.", + "tbel": "TBEL", + "expression-type": { + "simple": "Eenvoudig", + "script": "Script" + }, + "operation-type": { + "and": "En", + "or": "Of" + }, + "filter-predicate-type": { + "string": "String", + "numeric": "Numeriek", + "boolean": "Booleaans", + "complex": "Complex" + }, + "alarm-rule-additional-info": "Aanvullende informatie", + "edit-alarm-rule-additional-info": "Aanvullende informatie bewerken", + "alarm-rule-additional-info-placeholder": "Geef hier uw opmerkingen en aanpassingen op om ze te tonen in Alarmdetails onder Aanvullende informatie", + "alarm-rule-additional-info-hint": "Tip: gebruik ${Argumentnaam} om waarden te vervangen van de argumenten die in de alarmregelvoorwaarde worden gebruikt.", + "alarm-rule-additional-info-icon-hint": "Gebruik Argumentnaam om waarden te vervangen van de argumenten die in de alarmregelvoorwaarde worden gebruikt.", + "alarm-rule-mobile-dashboard": "Mobiel dashboard", + "alarm-rule-mobile-dashboard-hint": "Wordt door de mobiele applicatie gebruikt als dashboard voor alarmdetails.", + "alarm-rule-no-mobile-dashboard": "Geen dashboard geselecteerd", + "alarm-rule-condition": "Alarmregelvoorwaarde", + "enter-alarm-rule-condition-prompt": "Voorwaarde toevoegen", + "enter-alarm-rule-clear-condition-prompt": "Beëindigingsvoorwaarde toevoegen", + "edit-alarm-rule-condition": "Alarmvoorwaarde", + "condition-type": "Voorwaardetype", + "condition-type-hint": "De opties \"Duur\" en \"Herhalend\" zijn niet beschikbaar wanneer de bewerking \"Missing for\" in het filter wordt gebruikt.", + "select-alarm-severity": "Alarmernst selecteren", + "add-create-alarm-rule-prompt": "Er is ten minste één triggervoorwaarde vereist.", + "add-create-alarm-rule": "Triggervoorwaarde toevoegen", + "add-clear-alarm-rule": "Beëindigingsvoorwaarde toevoegen", + "condition-duration": "Voorwaardenduur", + "condition-duration-value": "Duurwaarde", + "condition-duration-time-unit": "Tijdseenheid", + "condition-duration-value-range": "Duurwaarde moet in een bereik van 1 tot 2147483647 liggen.", + "condition-duration-value-pattern": "Duurwaarde moet een geheel getal zijn.", + "condition-duration-value-required": "Duurwaarde is vereist.", + "condition-duration-time-unit-required": "Tijdseenheid is vereist.", + "condition-repeating-value": "Aantal gebeurtenissen", + "condition-repeating-value-hint": "Een update van elk alarmregelargument wordt als gebeurtenis geteld", + "condition-repeating-value-range": "Aantal gebeurtenissen moet in een bereik van 1 tot 2147483647 liggen.", + "condition-repeating-value-pattern": "Aantal gebeurtenissen moet een geheel getal zijn.", + "condition-repeating-value-required": "Aantal gebeurtenissen is vereist.", + "create-conditions": "Triggervoorwaarden", + "clear-condition": "Beëindigingsvoorwaarde", + "no-clear-alarm-rule": "Geen beëindigingsvoorwaarde geconfigureerd.", + "advanced-settings": "Geavanceerde instellingen", + "propagate-alarm": "Alarm naar gerelateerde entiteiten propageren", + "alarm-rule-relation-types-list": "Relatietypen", + "alarm-rule-relation-types-list-hint": "Definieert relatietypen om de gerelateerde entiteiten te filteren. Als dit niet is ingesteld, wordt het alarm naar alle gerelateerde entiteiten gepropageerd.", + "propagate-alarm-to-owner": "Alarm naar entiteiteigenaar propageren (Klant of Tenant)", + "propagate-alarm-to-tenant": "Alarm naar Tenant propageren", + "alarm-rule-filter-title": "Alarmregelfilter", + "filter-title": "Filter", + "debugging": "Alarmregeldebugging", + "any-type": "Alle typen", + "enter-alarm-rule-type": "Alarmtype invoeren", + "no-alarm-rule-types-matching": "Geen alarmtypen gevonden die overeenkomen met '{{entitySubtype}}'.", + "alarm-rule-type-list-empty": "Geen alarmtypen geselecteerd.", + "alarm-rule-type-list": "Alarmtypelijst", + "alarm-rule-entity-list": "Entiteitenlijst", + "missing-for": "ontbreekt gedurende", + "time-unit": "Eenheid", + "mode": "Modus", + "type": "Type", + "value-required": "Waarde is vereist.", + "min-value": "Waarde moet 1 of hoger zijn.", + "argument-in-use": "Argument wordt gebruikt als algemeen argument.", + "import-invalid-alarm-rule-type": "Kan alarmregel niet importeren: Ongeldige alarmregelstructuur.", + "no-filter-preview": "Geen filter opgegeven", + "filter-operation": { + "and": "En", + "or": "Of" } }, "ai-models": { @@ -1193,6 +1657,7 @@ "contact": { "country": "Land", "country-required": "Land is verplicht.", + "country-object-required": "Selecteer een geldig land uit de lijst.", "city": "Stad", "state": "Staat / Provincie", "postal-code": "Postcode", @@ -1229,6 +1694,8 @@ "documentation": "Documentatie", "time-left": "{{time}} resterend", "output": "Uitvoer", + "sort-asc": "Oplopend", + "sort-desc": "Aflopend", "suffix": { "s": "s", "ms": "ms" @@ -1365,6 +1832,8 @@ "mobile-order": "Volgorde in mobiele applicatie", "mobile-hide": "Verberg dashboard in mobiele applicatie", "update-image": "Dashboardafbeelding bijwerken", + "update-new-version": "Nieuwe versie uploaden", + "upload-file-to-update": "Bestand uploaden om bij te werken", "take-screenshot": "Schermafbeelding maken", "select-widget-title": "Selecteer widget", "select-widget-value": "{{title}}: selecteer widget", @@ -1733,6 +2202,8 @@ "bootstrap-tab": "Bootstrapclient", "bootstrap-server": "Bootstrapserver", "lwm2m-server": "LwM2M-server", + "client-reboot": "Registratie-update-trigger", + "bootstrap-reboot": "Bootstrap-Request-trigger", "client-publicKey-or-id": "Client publieke sleutel of ID", "client-publicKey-or-id-required": "Client publieke sleutel of ID is verplicht.", "client-publicKey-or-id-tooltip-psk": "De PSK-identificatie is een willekeurige identificatie tot 128 bytes zoals beschreven in de standaard [RFC7925].\nDe PSK-identificatie MOET eerst worden omgezet naar een tekenreeks en vervolgens in UTF-8 worden gecodeerd.", @@ -1780,7 +2251,6 @@ "unable-delete-device-alias-text": "Apparaat-alias '{{deviceAlias}}' kan niet worden verwijderd omdat het wordt gebruikt door de volgende widget(s):
    {{widgetsList}}", "is-gateway": "Is gateway", "overwrite-activity-time": "Activiteitstijd voor verbonden apparaat overschrijven", - "device-filter": "Apparaatfilter", "device-filter-title": "Apparaatfilter", "filter-title": "Filter", "device-state": "Apparaatstatus", @@ -2234,7 +2704,8 @@ "short-id-required": "Korte server-ID is verplicht.", "short-id-range": "Korte server-ID moet tussen {{ min }} en {{ max }} liggen.", "short-id-pattern": "Korte server-ID moet een positief geheel getal zijn.", - "lifetime": "Registratieduur client", + "short-id-pattern-bs": "Kort server-ID mag uitsluitend null zijn", + "lifetime": "Clientregistratielevensduur", "lifetime-required": "Registratieduur is verplicht.", "lifetime-pattern": "Registratieduur moet een positief geheel getal zijn.", "default-min-period": "Minimale periode tussen meldingen (s)", @@ -2328,7 +2799,9 @@ "composite-all-description": "Alle resources worden geobserveerd met één samengesteld Observe-verzoek (efficiënter, minder flexibel)", "composite-by-object": "Samengesteld per object", "composite-by-object-description": "Resources worden gegroepeerd per objecttype en geobserveerd via afzonderlijke samengestelde Observe-verzoeken (gebalanceerde aanpak)" - } + }, + "init-attr-tel-as-obs-strategy": "Attributen en telemetrie initialiseren met Observe-strategie", + "init-attr-tel-as-obs-strategy-hint": "Als false - attributen en telemetrie worden geïnitialiseerd door hun waarden één voor één te lezen.\\nAls true - attributen en telemetrie worden geïnitialiseerd door op hun waarden te abonneren met behulp van de Observe-strategie." }, "snmp": { "add-communication-config": "Communicatieconfiguratie toevoegen", @@ -2644,6 +3117,8 @@ "type-rulenodes": "Regelknopen", "list-of-rulenodes": "{ count, plural, =1 {Eén regelknoop} other {Lijst van # regelknopen} }", "rulenode-name-starts-with": "Regelknopen waarvan de namen beginnen met '{{prefix}}'", + "type-api-key": "API-sleutel", + "type-api-keys": "API-sleutels", "type-current-customer": "Huidige klant", "type-current-tenant": "Huidige tenant", "type-current-user": "Huidige gebruiker", @@ -2665,6 +3140,7 @@ "details": "Entiteitdetails", "no-entities-prompt": "Geen entiteiten gevonden", "no-data": "Geen gegevens om weer te geven", + "show-all-columns": "Alles weergeven", "columns-to-display": "Kolommen om weer te geven", "type-api-usage-state": "API-gebruikstoestand", "type-edge": "Edge", @@ -2710,7 +3186,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {Eén mobiele applicatie} other {Lijst van # mobiele applicaties} }", "type-mobile-app-bundle": "Mobiele bundel", "type-mobile-app-bundles": "Mobiele bundels", - "list-of-mobile-app-bundles": "{ count, plural, =1 {Eén mobiele bundel} other {Lijst van # mobiele bundels} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {Eén mobiele bundel} other {Lijst van # mobiele bundels} }", + "limit-reached": "Limiet bereikt", + "limit-reached-text": "U heeft de limiet van {{ entities }} bereikt. Vraag uw systeembeheerder om uw limiet voor {{ entity }} te verhogen en meer toe te voegen.", + "request-limit-increase": "Limietverhoging aanvragen", + "request-sysadmin-text": "Bent u de systeembeheerder?", + "login-here": "Hier aanmelden", + "to-increase-limit": "om de limiet te verhogen.", + "increase-limit-request-sent-title": "We hebben een geautomatiseerd verzoek naar uw systeembeheerder gestuurd om de limiet te verhogen", + "increase-limit-request-sent-text": "Geef hen de tijd om het verzoek te beoordelen en de instellingen bij te werken. Mogelijk moet u deze pagina vernieuwen om de wijzigingen te zien." }, "entity-field": { "created-time": "Aangemaakt op", @@ -3798,7 +4282,8 @@ "password-link-sent-message": "Resetlink is verzonden", "email": "E-mail", "invalid-email-format": "Ongeldig e-mailformaat.", - "login-with": "Inloggen met {{name}}", + "sign-in-with": "Aanmelden met {{name}}", + "sign-in-to-your-account": "Aanmelden bij uw account", "or": "of", "error": "Inlogfout", "verify-your-identity": "Verifieer uw identiteit", @@ -3817,7 +4302,51 @@ "activation-link-expired": "Activatielink is verlopen", "activation-link-expired-message": "De link om uw profiel te activeren is verlopen. U kunt terugkeren naar de inlogpagina om een nieuwe e-mail te ontvangen.", "reset-password-link-expired": "Wachtwoord resetlink is verlopen", - "reset-password-link-expired-message": "De link om uw wachtwoord opnieuw in te stellen is verlopen. U kunt terugkeren naar de inlogpagina om een nieuwe e-mail te ontvangen." + "reset-password-link-expired-message": "De link om uw wachtwoord opnieuw in te stellen is verlopen. U kunt terugkeren naar de inlogpagina om een nieuwe e-mail te ontvangen.", + "two-fa": "Twee-factorauthenticatie", + "two-fa-required": "Twee-factorauthenticatie is vereist", + "set-up-verification-method": "Stel een verificatiemethode in om door te gaan", + "set-up-verification-method-login": "Stel een verificatiemethode in of meld u aan", + "enable-authenticator-app": "Authenticator-app inschakelen", + "enable-authenticator-app-description": "Voer de beveiligingscode in uit uw authenticator-app", + "enable-authenticator-sms": "SMS-authenticator inschakelen", + "enable-authenticator-sms-description": "Voer een 6-cijferige code in die we zojuist hebben verzonden naar ", + "enable-authenticator-email": "E-mailauthenticator inschakelen", + "enable-authenticator-email-description": "Er is een beveiligingscode verzonden naar uw e-mailadres op ", + "enter-key-manually": "of voer deze 32-cijferige sleutel handmatig in:", + "continue": "Doorgaan", + "confirm": "Bevestigen", + "authenticator-app-success": "Authenticator-app succesvol ingeschakeld", + "authenticator-app-success-description": "De volgende keer dat u zich aanmeldt, moet u een twee-factorauthenticatiecode invoeren", + "authenticator-sms-success": "SMS-authenticator succesvol ingeschakeld", + "authenticator-sms-success-description": "De volgende keer dat u zich aanmeldt, wordt u gevraagd de beveiligingscode in te voeren die naar het telefoonnummer wordt verzonden", + "authenticator-email-success": "E-mailauthenticator succesvol ingeschakeld", + "authenticator-email-success-description": "De volgende keer dat u zich aanmeldt, wordt u gevraagd de beveiligingscode in te voeren die naar uw e-mailadres wordt verzonden", + "authenticator-backup-code-success": "Back-upcode succesvol ingeschakeld", + "authenticator-backup-code-success-description": "De volgende keer dat u zich aanmeldt, wordt u gevraagd de beveiligingscode in te voeren of een van de back-upcodes te gebruiken.", + "add-verification-method": "Verificatiemethode toevoegen", + "get-backup-code": "Back-upcode ophalen", + "copy-key": "Sleutel kopiëren", + "send-code": "Code verzenden", + "email-label": "E-mail", + "email-description": "Voer een e-mail in om als authenticator te gebruiken.", + "sms-description": "Voer een telefoonnummer in om als authenticator te gebruiken.", + "backup-code-description": "Druk de codes af zodat u ze bij de hand heeft wanneer u ze nodig heeft om u aan te melden bij uw account. U kunt elke back-upcode één keer gebruiken.", + "backup-code-warn": "Zodra u deze pagina verlaat, kunnen deze codes niet opnieuw worden weergegeven. Bewaar ze veilig met de onderstaande opties.", + "download-txt": "Downloaden (txt)", + "print": "Afdrukken", + "verification-code": "6-cijferige code", + "verification-code-invalid": "Ongeldig verificatiecodeformaat", + "verification-code-incorrect": "Verificatiecode is onjuist", + "verification-code-many-request": "Te veel verzoeken om verificatiecode te controleren", + "scan-qr-code": "Scan deze QR-code met uw verificatie-app", + "phone-input": { + "phone-input-label": "Telefoonnummer", + "phone-input-required": "Telefoonnummer is vereist", + "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}}" + } }, "mobile": { "add-application": "Applicatie toevoegen", @@ -4185,6 +4714,7 @@ "api-usage-limit": "API-gebruikslimiet", "device-activity": "Apparaatactiviteit", "entities-limit": "Entiteitenlimiet", + "entities-limit-increase-request": "Verzoek om verhoging van entiteitenlimiet", "entity-action": "Entiteithandeling", "general": "Algemeen", "rule-engine-lifecycle-event": "Levenscyclusgebeurtenis van regelengine", @@ -4402,6 +4932,12 @@ "at-least": "Ten minste:", "character": "{ count, plural, =1 {1 teken} other {# tekens} }", "digit": "{ count, plural, =1 {1 cijfer} other {# cijfers} }", + "password-tooltip-min-length": "Minimaal {{minimumLength}} tekens lang", + "password-tooltip-max-length": "Maximaal {{maximumLength}} tekens lang", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} hoofdletter", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} kleine letter", + "password-tooltip-digit": "{{minimumDigits}} cijfer", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} speciaal teken", "incorrect-password-try-again": "Onjuist wachtwoord. Probeer opnieuw", "lowercase-letter": "{ count, plural, =1 {1 kleine letter} other {# kleine letters} }", "new-passwords-not-match": "Nieuw wachtwoord komt niet overeen", @@ -4460,7 +4996,8 @@ "additional-info": "Aanvullende info (JSON)", "invalid-additional-info": "Kan aanvullende info JSON niet ontleden.", "no-relations-text": "Geen relaties gevonden", - "not": "Niet" + "not": "Niet", + "copy-type": "Type kopiëren" }, "resource": { "add": "Bron toevoegen", @@ -5411,7 +5948,7 @@ "time-series": "Tijdreeksen", "latest": "Laatste waarden", "web-sockets": "WebSockets", - "calculated-fields": "Berekende velden" + "calculated-fields-and-alarm-rules": "Berekende velden en alarmregels" }, "save-attribute": { "processing-settings": "Verwerkingsinstellingen", @@ -5606,7 +6143,8 @@ "bad-request-params": "Ongeldige aanvraagparameters", "item-not-found": "Item niet gevonden", "too-many-requests": "Te veel aanvragen", - "too-many-updates": "Te veel updates" + "too-many-updates": "Te veel updates", + "entities-limit-exceeded": "Entiteitenlimiet overschreden" }, "tenant": { "tenant": "Tenant", @@ -5744,6 +6282,27 @@ "max-arguments-per-cf": "Maximaal aantal argumenten per berekend veld", "max-arguments-per-cf-range": "Maximaal aantal argumenten mag niet negatief zijn", "max-arguments-per-cf-required": "Maximaal aantal argumenten is verplicht", + "max-related-level-per-argument": "Maximaal relatieniveau per argument 'Gerelateerde entiteiten'", + "max-related-level-per-argument-range": "Het maximale relatieniveau per argument 'Gerelateerde entiteiten' mag niet lager zijn dan '1'", + "max-related-level-per-argument-required": "Het maximale relatieniveau per argument 'Gerelateerde entiteiten' is vereist", + "min-allowed-scheduled-update-interval": "Minimaal toegestaan update-interval voor argumenten 'Gerelateerde entiteiten' (seconden)", + "min-allowed-scheduled-update-interval-range": "De minimale waarde voor het minimaal toegestane update-interval mag niet negatief zijn", + "min-allowed-deduplication-interval": "Minimaal toegestaan deduplicatie-interval (seconden)", + "min-allowed-deduplication-interval-range": "De waarde van het minimaal toegestane deduplicatie-interval mag niet negatief zijn", + "min-allowed-deduplication-interval-required": "Minimaal toegestaan deduplicatie-interval is vereist", + "intermediate-aggregation-interval": "Tussentijds aggregatie-interval (seconden)", + "intermediate-aggregation-interval-range": "De waarde van het tussentijds aggregatie-interval mag niet lager zijn dan '1'", + "intermediate-aggregation-interval-required": "Tussentijds aggregatie-interval is vereist", + "reevaluation-check-interval": "Herbeoordelingscontrole-interval (seconden)", + "reevaluation-check-interval-range": "De waarde van het herbeoordelingscontrole-interval mag niet lager zijn dan '1'", + "reevaluation-check-interval-required": "Herbeoordelingscontrole-interval is vereist", + "alarms-reevaluation-interval": "Herbeoordelingsinterval voor alarmen (seconden)", + "alarms-reevaluation-interval-range": "De waarde van het herbeoordelingsinterval voor alarmen mag niet lager zijn dan '1'", + "alarms-reevaluation-interval-required": "Herbeoordelingsinterval voor alarmen is vereist", + "min-allowed-aggregation-interval": "Minimaal toegestaan aggregatie-interval (seconden)", + "min-allowed-aggregation-interval-range": "De waarde van het minimaal toegestane aggregatie-interval mag niet negatief zijn", + "min-allowed-aggregation-interval-required": "Minimaal toegestaan aggregatie-interval is vereist", + "min-allowed-scheduled-update-interval-required": "De minimale waarde voor het minimaal toegestane update-interval is vereist", "max-state-size": "Maximale staatsgrootte in KB", "max-state-size-range": "Staatsgrootte mag niet negatief zijn", "max-state-size-required": "Staatsgrootte is verplicht", @@ -5819,6 +6378,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Maximaal aantal abonnementen per reguliere gebruiker", "ws-limit-max-subscriptions-per-public-user": "Maximaal aantal abonnementen per publieke gebruiker", "ws-limit-updates-per-session": "WS-updates per sessie", + "relation-search-entity-limit": "Relatiezoekentiteitenlimiet", + "relation-search-entity-limit-hint": "Beperkt het aantal entiteiten dat op het laatste niveau van het relatiepad wordt opgelost. Van toepassing op argumenten 'Gerelateerde entiteiten' en propagatievelden.", + "relation-search-entity-limit-required": "Relatiezoekentiteitenlimiet is vereist", + "relation-search-entity-limit-range": "Relatiezoekentiteitenlimiet kan niet lager zijn dan '1'", "rate-limits": { "add-limit": "Limiet toevoegen", "and-also-less-than": "en ook minder dan", @@ -6004,7 +6567,9 @@ "default-agg-interval": "Standaard groeperingsinterval", "edit-intervals-list-hint": "Lijst met beschikbare intervalopties kan worden gespecificeerd.", "edit-grouping-intervals-list-hint": "Het is mogelijk om de lijst met groeperingsintervallen en het standaardinterval te configureren.", - "all": "Alles" + "all": "Alles", + "save-current-settings-as-default": "Huidige instellingen opslaan als standaardtijdvenster", + "hide-option-from-end-users": "Optie verbergen voor eindgebruikers" }, "tooltip": { "trigger": "Trigger", @@ -6658,7 +7223,8 @@ "export-relations": "Exporteer relaties", "export-attributes": "Exporteer attributen", "export-credentials": "Exporteer inloggegevens", - "export-calculated-fields": "Exporteer berekende velden", + "export-calculated-fields": "Berekende velden exporteren \nen alarmregels", + "export-alarm-rules": "Alarmregels exporteren", "entity-versions": "Entiteitversies", "versions": "Versies", "created-time": "Aangemaakt op", @@ -6676,6 +7242,7 @@ "load-attributes": "Attributen laden", "load-credentials": "Inloggegevens laden", "load-calculated-fields": "Berekende velden laden", + "load-alarm-rules": "Alarmregels laden", "compare-with-current": "Vergelijk met huidige", "diff-entity-with-version": "Verschil met entiteitversie '{{versionName}}'", "previous-difference": "Vorige verschil", @@ -6885,7 +7452,23 @@ "scan-qr-code": "QR-code scannen", "make-phone-call": "Telefoongesprek voeren", "get-location": "Telefoonlocatie ophalen", - "take-screenshot": "Schermafbeelding maken" + "take-screenshot": "Schermafbeelding maken", + "handle-provision-success-function": "Provisioning-succesfunctie afhandelen", + "get-location-function": "Locatiefunctie ophalen", + "process-launch-result-function": "Startresultaatfunctie verwerken", + "get-phone-number-function": "Telefoonnummerfunctie ophalen", + "process-image-function": "Afbeeldingsfunctie verwerken", + "process-qr-code-function": "QR-codefunctie verwerken", + "process-location-function": "Locatiefunctie verwerken", + "handle-empty-result-function": "Leegresultaatfunctie afhandelen", + "handle-error-function": "Foutfunctie afhandelen", + "handle-non-mobile-fallback-function": "Non-Mobile-terugvalfunctie afhandelen", + "save-to-gallery": "Opslaan in galerij", + "provision-type": "Provisioningtype", + "auto": "Auto", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Aangepaste actiefunctie", "custom-pretty-function": "Aangepaste actie (met HTML-sjabloon) functie", @@ -6894,7 +7477,8 @@ "marker": "Markering", "polygon": "Polygoon", "rectangle": "Rechthoek", - "circle": "Cirkel" + "circle": "Cirkel", + "polyline": "Polylijn" }, "place-map-item": "Kaartobject plaatsen", "map-item-tooltip": { @@ -6906,7 +7490,9 @@ "continue-draw-polygon": "Doorgaan met polygoon tekenen", "finish-draw-polygon": "Teken polygoon beëindigen", "start-draw-circle": "Teken cirkel starten", - "finish-draw-circle": "Teken cirkel beëindigen" + "finish-draw-circle": "Teken cirkel beëindigen", + "start-draw-polyline": "Polylijn tekenen starten", + "finish-draw-polyline": "Polylijn tekenen voltooien" } }, "widgets-bundle": { @@ -7472,10 +8058,18 @@ "update-animation-delay": "Vertraging update-animatie" }, "chart-axis": { + "limit": "Limiet", + "source": "Bron", + "key-value": "Sleutel / Waarde", + "value-required": "Waarde is vereist.", + "entity-key-required": "Entiteitsleutel is vereist.", + "key-required": "Sleutel is vereist.", + "scale-limits": "Schaallimieten", + "scale-appearance": "Schaalweergave", "scale": "Schaal", "scale-min": "min", "scale-max": "max", - "scale-auto": "Automatisch" + "scale-auto": "Auto" }, "bar": { "show-border": "Toon rand", @@ -8016,7 +8610,10 @@ "add-radio-option": "Voeg radio-optie toe", "radio-label-position": "Labelpositie", "radio-label-position-before": "Voor", - "radio-label-position-after": "Na" + "radio-label-position-after": "Na", + "save-image": "Afbeelding opslaan", + "save-to-gallery": "Vastgelegde afbeeldingen automatisch opslaan in Afbeeldingsgalerij", + "public-image": "Maakt de afbeelding beschikbaar voor elke onbevoegde gebruiker" }, "invalid-qr-code-text": "Ongeldige invoertekst voor QR-code. Invoer moet van het type string zijn", "qr-code": { @@ -8311,7 +8908,8 @@ "trips": "Reizen", "markers": "Markeringen", "polygons": "Polygonen", - "circles": "Cirkels" + "circles": "Cirkels", + "polylines": "Polylijnen" }, "data-layer": { "source": "Bron", @@ -8508,10 +9106,29 @@ "finish-circle-hint-with-entity": "Cirkel voor '{{entityName}}': klik om te voltooien en op te slaan", "finish-circle-hint": "Cirkel: klik om tekenen te voltooien" }, - "select-entity": "Selecteer entiteit", - "select-entity-hint": "Tip: klik op de kaart na selectie om positie in te stellen" + "polyline": { + "polyline-key": "Polylijnsleutel", + "polyline-key-required": "Polylijnsleutel is vereist", + "no-polylines": "Geen polylijnen geconfigureerd", + "add-polylines": "Polylijn toevoegen", + "polyline-configuration": "Polylijnconfiguratie", + "remove-polyline": "Polylijn verwijderen", + "edit": "Polylijn bewerken", + "cut": "Polylijngebied uitsnijden", + "rotate": "Polylijn roteren", + "remove-polyline-for": "Polylijn verwijderen voor '{{entityName}}'", + "draw-polyline": "Polylijn tekenen", + "polyline-place-first-point-hint-with-entity": "Polylijn voor '{{entityName}}': klik om het eerste punt te plaatsen", + "polyline-place-first-point-hint": "Polylijn: klik om het eerste punt te plaatsen", + "finish-polyline-hint-with-entity": "Polylijn voor '{{entityName}}': klik om het tekenen te voltooien", + "finish-polyline-hint": "Polylijn: klik om het tekenen te voltooien", + "polyline-place-first-point-cut-hint": "Klik om het eerste punt te plaatsen", + "finish-polyline-cut-hint": "Klik op de eerste markering om te voltooien en op te slaan" + }, + "select-entity": "Entiteit selecteren", + "select-entity-hint": "Tip: klik na selectie op de kaart om de positie in te stellen" }, - "select-entity": "Selecteer entiteit", + "select-entity": "Entiteit selecteren", "select-entity-hint": "Tip: klik na selectie op de kaart om de positie in te stellen", "tooltips": { "placeMarker": "Klik om entiteit '{{entityName}}' te plaatsen", @@ -8949,6 +9566,7 @@ "show-empty-space-hidden-action": "Lege ruimte tonen in plaats van verborgen knopactie", "dont-reserve-space-hidden-action": "Geen ruimte reserveren voor verborgen actieknoppen", "display-timestamp": "Tijdstempel", + "timestamp-column-name": "Tijdstempel", "display-pagination": "Paginering weergeven", "default-page-size": "Standaard paginagrootte", "page-step-settings": "Instellingen paginastappen", @@ -9010,7 +9628,9 @@ "alarm-column-error": "Ten minste één alarmkolom moet worden opgegeven", "table-tabs": "Tabeltabs", "show-cell-actions-menu-mobile": "Toon keuzemenu celacties in mobiele modus", - "disable-sorting": "Sortering uitschakelen" + "disable-sorting": "Sortering uitschakelen", + "sort-by": "Tabbladen sorteren op", + "sort-timestamp-option": "Aanmaaktijd" }, "latest-chart": { "total": "Totaal", @@ -9502,11 +10122,28 @@ "content": "

    Door dashboards voor eindgebruikers aan te maken, kan een klantgebruiker alleen zijn eigen apparaten zien. Gegevens van andere klanten blijven verborgen.

    Volg de documentatie om dit te doen:

    " } } + }, + "api-usage": { + "api-usage": "API-gebruik", + "label": "Label", + "state-name": "Statusnaam", + "status": "Status", + "status-required": "Status is vereist.", + "limit": "Maximale limiet", + "limit-required": "Maximale limiet is vereist.", + "current-number": "Huidig aantal", + "current-number-required": "Huidig aantal is vereist.", + "add-key": "Sleutel toevoegen", + "no-key": "Geen sleutel", + "delete-key": "Sleutel verwijderen", + "target-dashboard-state": "Doeldashboardstatus", + "go-to-main-state": "Naar standaardweergave gaan" } }, "icon": { "icon": "Pictogram", "icons": "Pictogrammen", + "custom": "Aangepast", "select-icon": "Selecteer pictogram", "material-icons": "Material pictogrammen", "show-all": "Toon alle pictogrammen", @@ -9547,6 +10184,7 @@ "items-per-page-separator": "van" }, "language": { + "auto": "Auto", "language": "Taal" } } \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-no_NO.json b/ui-ngx/src/assets/locale/locale.constant-no_NO.json index 9b2a91f75e..7e6e464202 100644 --- a/ui-ngx/src/assets/locale/locale.constant-no_NO.json +++ b/ui-ngx/src/assets/locale/locale.constant-no_NO.json @@ -3652,7 +3652,6 @@ "two-factor-authentication": "Tofaktorautentisering", "passwords-mismatch-error": "Passordene må være like!", "password-again": "Gjenta passord", - "sign-in": "Vennligst logg inn", "username": "Brukernavn (epost)", "remember-me": "Husk meg", "forgot-password": "Glemt passord?", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 6dd6ca467e..7c69ed7751 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -3802,7 +3802,6 @@ "two-factor-authentication": "Uwierzytelnianie dwuskładnikowe", "passwords-mismatch-error": "Wpisane hasła muszą być takie same!", "password-again": "Hasło ponownie", - "sign-in": "proszę, zaloguj się", "username": "Nazwa użytkownika (e-mail)", "remember-me": "Zapamiętaj mnie", "forgot-password": "Zapomniałeś hasła?", 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 a528be78eb..91ffd72b2f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1383,7 +1383,6 @@ "create-password": "Criar senha", "passwords-mismatch-error": "As senhas inseridas devem ser idênticas!", "password-again": "Repetir senha", - "sign-in": "Inscreva-se", "username": "Nome de usuário (e-mail)", "remember-me": "Lembrar-me", "forgot-password": "Esqueceu a senha?", 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 1054b812a1..f1b712e205 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json +++ b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json @@ -1236,7 +1236,6 @@ "create-password": "Creează Parolă", "passwords-mismatch-error": "Parola reintrodusă trebuie să fie identică!", "password-again": "Rescrie Parola", - "sign-in": "Intră în Cont", "username": "Nume Utilizator (Adresa De eMail)", "remember-me": "Ține-mă minte!", "forgot-password": "Ai Uitat Parola?", 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 41dea2e169..7b3b1fb197 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1708,7 +1708,6 @@ "create-password": "Ustvari geslo", "passwords-mismatch-error": "Vnesena gesla morajo biti enaka!", "password-again": "Ponovi geslo", - "sign-in": "Prosimo, prijavite se", "username": "Uporabniško ime (e-pošta)", "remember-me": "Zapomni si me", "forgot-password": "Ste pozabili geslo?", 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 74bd56e2f5..b7f6e8d8f0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -78,6 +78,7 @@ "show-more": "Daha fazla göster", "dont-show-again": "Bir daha gösterme", "see-documentation": "Dokümantasyonu görüntüle", + "see-debug-events": "Hata Ayıklama Olaylarını Görüntüle", "clear": "Temizle", "upload": "Yükle", "delete-anyway": "Yine de sil", @@ -485,6 +486,7 @@ "2fa": { "2fa": "İki faktörlü kimlik doğrulama", "available-providers": "Mevcut sağlayıcılar", + "available-providers-required": "En az bir 2FA sağlayıcısı yapılandırılmalıdır.", "issuer-name": "Yayımlayıcı adı", "issuer-name-required": "Yayımlayıcı adı gereklidir.", "max-verification-failures-before-user-lockout": "Kullanıcı kilitlenmeden önceki maksimum doğrulama başarısızlığı", @@ -513,7 +515,9 @@ "verification-message-template-required": "Doğrulama mesajı şablonu gereklidir.", "within-time": "Belirli süre içinde (sn)", "within-time-pattern": "Süre pozitif bir tamsayı olmalıdır.", - "within-time-required": "Süre gereklidir." + "within-time-required": "Süre gereklidir.", + "force-2fa": "İki Faktörlü Kimlik Doğrulamayı Zorunlu Kıl", + "enforce-for": "Şunun İçin Zorunlu Kıl" }, "jwt": { "security-settings": "JWT güvenlik ayarları", @@ -545,16 +549,11 @@ "slack-settings": "Slack ayarları", "mobile-settings": "Mobil ayarlar", "firebase-service-account-file": "Firebase servis hesabı kimlik bilgileri JSON dosyası", - "select-firebase-service-account-file": "Firebase servis hesabı kimlik bilgileri dosyanızı sürükleyip bırakın veya ", - "trendz": "Trendz", - "trendz-settings": "Trendz ayarları", - "trendz-url": "Trendz URL'si", - "trendz-url-required": "Trendz URL'si gereklidir", - "trendz-api-key": "Trendz API anahtarı", - "trendz-enable": "Trendz'i etkinleştir" + "select-firebase-service-account-file": "Firebase servis hesabı kimlik bilgileri dosyanızı sürükleyip bırakın veya " }, "alarm": { "alarm": "Alarm", + "alarm-list": "Alarm Listesi", "alarms": "Alarmlar", "all-alarms": "Tüm alarmlar", "select-alarm": "Alarm seçin", @@ -655,7 +654,16 @@ "alarm-type": "Alarm türü", "enter-alarm-type": "Alarm türünü girin", "no-alarm-types-matching": "'{{entitySubtype}}' ile eşleşen alarm türü bulunamadı.", - "alarm-type-list-empty": "Seçilmiş alarm türü yok." + "alarm-type-list-empty": "Seçilmiş alarm türü yok.", + "system-comments": { + "acked-by-user": "Alarm, {{userName}} adlı kullanıcı tarafından onaylandı", + "cleared-by-user": "Alarm, {{userName}} adlı kullanıcı tarafından temizlendi", + "assigned-to-user": "Alarm, {{userName}} adlı kullanıcı tarafından {{assigneeName}} adlı kullanıcıya atandı", + "unassigned-to-user": "Alarm, {{userName}} adlı kullanıcı tarafından atamadan çıkarıldı", + "unassigned-from-deleted-user": "{{userName}} adlı kullanıcı silindiği için alarm atamadan çıkarıldı", + "comment-deleted": "{{userName}} adlı kullanıcı yorumunu sildi", + "severity-changed": "Alarm önceliği {{oldSeverity}} değerinden {{newSeverity}} değerine güncellendi" + } }, "alarm-activity": { "add": "Yorum ekle...", @@ -760,6 +768,7 @@ "name-max-length": "Ad 256 karakterden kısa olmalıdır", "label-max-length": "Etiket 256 karakterden kısa olmalıdır", "description": "Açıklama", + "description-required": "Açıklama gereklidir.", "type": "Tür", "type-required": "Tür gereklidir.", "details": "Detaylar", @@ -873,6 +882,9 @@ "alarms-created-monthly-activity": "Aylık oluşturulan alarmlar", "data-points": "Veri noktaları", "data-points-storage-days": "Veri noktası saklama süresi (gün)", + "data-points-storage-days-hourly-activity": "Veri noktaları depolama günleri saatlik etkinlik", + "data-points-storage-days-daily-activity": "Veri noktaları depolama günleri günlük etkinlik", + "data-points-storage-days-monthly-activity": "Veri noktaları depolama günleri aylık etkinlik", "device-api": "Cihaz API'si", "email": "E-posta", "email-messages": "E-posta mesajları", @@ -906,6 +918,7 @@ "rule-node": "Kural Düğümü", "sms": "SMS", "sms-messages": "SMS mesajları", + "sms-messages-hourly-activity": "SMS mesajları saatlik etkinlik", "sms-messages-daily-activity": "Günlük SMS mesajları", "sms-messages-monthly-activity": "Aylık SMS mesajları", "successful": "${entityName} Başarılı", @@ -915,13 +928,40 @@ "telemetry-persistence-hourly-activity": "Telemetri kalıcılığı saatlik etkinliği", "telemetry-persistence-monthly-activity": "Telemetri kalıcılığı aylık etkinliği", "transport": "İletim", + "transport-msg-hourly-activity": "Taşıma mesajları saatlik etkinlik", + "transport-msg-daily-activity": "Taşıma mesajları günlük etkinlik", + "transport-msg-monthly-activity": "Taşıma mesajları aylık etkinlik", "transport-daily-activity": "İletim günlük etkinliği", "transport-data-points": "İletim veri noktaları", - "transport-hourly-activity": "İletim saatlik etkinliği", - "transport-messages": "İletim mesajları", - "transport-monthly-activity": "İletim aylık etkinliği", + "transport-data-points-hourly-activity": "Taşıma veri noktaları saatlik etkinlik", + "transport-data-points-daily-activity": "Taşıma veri noktaları günlük etkinlik", + "transport-data-points-monthly-activity": "Taşıma veri noktaları aylık etkinlik", "view-details": "Detayları görüntüle", - "view-statistics": "İstatistikleri görüntüle" + "view-statistics": "İstatistikleri görüntüle", + "transport-messages": "Taşıma mesajları", + "transport-messages-hourly-activity": "Taşıma mesajları saatlik etkinlik", + "transport-data-point-hourly-activity": "Taşıma veri noktası saatlik etkinlik", + "javascript-function-executions": "JavaScript işlev yürütmeleri", + "javascript-function-executions-hourly-activity": "JavaScript işlev yürütmeleri saatlik etkinlik", + "javascript-function-executions-daily-activity": "JavaScript işlev yürütmeleri günlük etkinlik", + "javascript-function-executions-monthly-activity": "JavaScript işlev yürütmeleri aylık etkinlik", + "tbel-function-executions": "TBEL işlev yürütmeleri", + "tbel-function-executions-hourly-activity": "TBEL işlev yürütmeleri saatlik etkinlik", + "tbel-function-executions-daily-activity": "TBEL işlev yürütmeleri günlük etkinlik", + "tbel-function-executions-monthly-activity": "TBEL işlev yürütmeleri aylık etkinlik", + "created-reports": "Oluşturulan raporlar", + "created-reports-hourly-activity": "Oluşturulan raporlar saatlik etkinlik", + "created-reports-daily-activity": "Oluşturulan raporlar günlük etkinlik", + "created-reports-monthly-activity": "Oluşturulan raporlar aylık etkinlik", + "emails": "Emails", + "emails-hourly-activity": "Emails saatlik etkinlik", + "emails-daily-activity": "Emails günlük etkinlik", + "emails-monthly-activity": "Emails aylık etkinlik", + "status": { + "enabled": "Etkin", + "disabled": "Devre Dışı", + "warning": "Uyarı" + } }, "api-limit": { "cassandra-write-queries-core": "Rest API Cassandra yazma sorguları", @@ -946,6 +986,40 @@ "edge-uplink-messages": "Edge yukarı bağlantı mesajları", "edge-uplink-messages-per-edge": "Edge başına yukarı bağlantı mesajları" }, + "api-key": { + "api-key": "API anahtarı", + "api-keys": "API anahtarları", + "delete-api-key-title": "“{{name}}” API anahtarını silmek istediğinizden emin misiniz?", + "delete-api-key-text": "Dikkat: Onaydan sonra anahtar kurtarılamaz.", + "delete-api-keys-title": "{ count, plural, =1 {1 API anahtarı} other {# API anahtarı} } silmek istediğinizden emin misiniz?", + "delete-api-keys-text": "Dikkat: Onaydan sonra seçilen tüm anahtarlar kurtarılamaz.", + "expiration-date": "Son Kullanma Tarihi", + "date": "tarih", + "description": "Açıklama", + "disable": "Devre Dışı Bırak", + "edit-description": "Açıklamayı Düzenle", + "enable": "API Anahtarını Etkinleştir ", + "expiration-time": "Son Kullanma Saati", + "expiration-time-never": "Asla", + "expiration-time-custom": "Özel", + "generate": "Oluştur", + "generate-title": "API Anahtarı Oluştur", + "generate-text": "Not: API anahtarı, oluşturulduğu kullanıcının izinlerini devralır.", + "generated-api-key-title": "API anahtarı oluşturuldu. Bağlantıyı kontrol edelim!", + "generated-api-key-copy": "API anahtarınızı şimdi kopyalayıp kaydettiğinizden emin olun; çünkü bir daha göremeyeceksiniz.", + "generated-api-key-command": "Bağlantıyı kontrol etmek için aşağıdaki talimatları kullanın. Sonuç olarak, geçerli kullanıcı bilgilerini almanız gerekir:", + "generated-api-key-insecure-url": "Güvenli olmayan bir HTTP bağlantısı üzerinden komut çalıştırmak, API anahtarınızı şifrelenmemiş şekilde gönderir ve ele geçirilmeye karşı savunmasız hâle getirir.", + "list": "{ count, plural, =1 {Bir API anahtarı} other {# API anahtarı listesi} }", + "manage": "Yönet", + "manage-api-keys": "API Anahtarlarını Yönet", + "no-found": "API anahtarı bulunamadı", + "selected-api-keys": "{ count, plural, =1 {1 API anahtarı} other {# API anahtarı} } seçildi", + "search": "API anahtarlarını ara", + "status": "Durum", + "status-active": "Etkin", + "status-inactive": "Etkin Değil", + "status-expired": "Süresi Doldu" + }, "audit-log": { "audit": "Denetim", "audit-logs": "Denetim günlükleri", @@ -999,7 +1073,11 @@ "type-provision-failure": "Cihaz tedarik işlemi başarısız oldu", "type-timeseries-updated": "Telemetri güncellendi", "type-timeseries-deleted": "Telemetri silindi", - "type-sms-sent": "SMS gönderildi" + "type-sms-sent": "SMS gönderildi", + "any-type": "Herhangi Bir Tür", + "audit-log-filter-title": "Denetim Günlüğü Filtresi", + "filter-title": "Filtre", + "filter-types": "Denetim günlüğü türleri" }, "debug-settings": { "label": "Hata Ayıklama Yapılandırması", @@ -1020,12 +1098,25 @@ "selected-fields": "{ count, plural, =1 {1 hesaplanmış alan} other {# hesaplanmış alan} } seçildi", "type": { "simple": "Basit", - "script": "Komut dosyası" + "simple-hint": "Girdi bağımsız değişkenlerine dayalı basit aritmetik hesaplama.", + "script": "Betik", + "script-hint": "TBEL betiği kullanılarak tanımlı bağımsız değişkenler üzerinde hesaplama.", + "geofencing": "Coğrafi Çitleme", + "geofencing-hint": "Varlık GPS konumu ve geçişlerinin, yapılandırılmış coğrafi çitleme bölge gruplarına göre değerlendirilmesi.", + "propagation": "Yayılım", + "propagation-hint": "İlişki yönü ve türüne bağlı olarak verilerin üst veya alt varlıklara yayılması.", + "related-entities-aggregation": "İlgili varlıkların toplulaştırılması", + "related-entities-aggregation-hint": "İlgili varlıklardan en son verilerin toplulaştırılması.", + "time-series-data-aggregation": "Zaman serisi verilerinin toplulaştırılması", + "time-series-data-aggregation-hint": "Geçerli bir varlıktan geçmiş verilerin toplulaştırılması." }, + "preview": "Önizleme", "arguments": "Argümanlar", "decimals-by-default": "Varsayılan ondalık", "debugging": "Hesaplanmış alan hata ayıklama", + "calculated-field-details": "Hesaplanmış alan ayrıntıları", "argument-name": "Argüman adı", + "name": "Ad", "datasource": "Veri kaynağı", "add-argument": "Argüman ekle", "test-script-function": "Komut dosyası işlevini test et", @@ -1037,8 +1128,9 @@ "argument-asset": "Varlık", "argument-customer": "Müşteri", "argument-tenant": "Geçerli kiracı", + "argument-owner": "Geçerli sahip", + "argument-relation-query": "İlgili varlıklar", "argument-type": "Argüman türü", - "see-debug-events": "Hata ayıklama olaylarını görüntüle", "attribute": "Öznitelik", "copy-argument-name": "Argüman adını kopyala", "timeseries-key": "Zaman serisi anahtarı", @@ -1051,12 +1143,14 @@ "shared-attributes": "Paylaşılan öznitelikler", "attribute-key": "Öznitelik anahtarı", "default-value": "Varsayılan değer", + "default-value-required": "Varsayılan değer gereklidir.", "limit": "Maksimum değer", "time-window": "Zaman aralığı", "customer-name": "Müşteri adı", "asset-name": "Varlık adı", "timeseries": "Zaman serisi", "output": "Çıktı", + "output-hint": "Çıktının nasıl işlendiğini tanımlar.", "create": "Yeni hesaplanmış alan oluştur", "file": "Hesaplanmış alan dosyası", "invalid-file-error": "Geçersiz dosya biçimi. Lütfen dosyanın geçerli bir JSON dosyası olduğundan emin olun.", @@ -1070,23 +1164,395 @@ "delete-multiple-text": "Dikkat, onaydan sonra seçilen tüm hesaplanmış alanlar ve ilgili tüm veriler geri alınamaz hale gelecektir.", "test-with-this-message": "Bu mesaj ile test et", "use-latest-timestamp": "En son zaman damgasını kullan", + "entity-coordinates": "Varlık koordinatları", + "latitude-time-series-key": "Enlem zaman serisi anahtarı", + "latitude-time-series-key-required": "Enlem zaman serisi anahtarı gereklidir.", + "longitude-time-series-key": "Boylam zaman serisi anahtarı", + "longitude-time-series-key-required": "Boylam zaman serisi anahtarı gereklidir.", + "geofencing-zone-groups": "Coğrafi çitleme bölge grupları", + "geofencing-zone-groups-settings": "Coğrafi çitleme bölge grubu ayarları", + "target-zone": "Hedef bölge", + "perimeter-key": "Çevre anahtarı", + "report-strategy": "Raporlama stratejisi", + "no-zone-configured": "En az bir bölge gereklidir.", + "no-zone-configured-required": "En az bir bölge grubu yapılandırılmalıdır.", + "add-zone-group": "Bölge Grubu Ekle", + "report-transition-event-only": "Yalnızca geçiş olayları", + "report-presence-status-only": "Yalnızca bulunma durumu", + "report-transition-event-and-presence": "Bulunma durumu ve geçiş olayları", + "perimeter-attribute-key": "Çevre öznitelik anahtarı", + "perimeter-attribute-key-required": "Çevre öznitelik anahtarı gereklidir.", + "perimeter-attribute-key-pattern": "Çevre öznitelik anahtarı geçersiz.", + "entity-zone-relationship": "Varlıktan Bölgelere Giden Yol", + "direction": "İlişki yönü", + "direction-from": "Varlıktan bölgeye", + "direction-to": "Bölgeden varlığa", + "relation-type": "İlişki türü", + "create-relation-with-matched-zones": "Kaynak varlık için eşleşen bölgelerle ilişkiler oluştur", + "relation-level": "İlişki düzeyi", + "fetch-last-available-level": "Yalnızca son kullanılabilir düzeyi getir", + "zone-group-refresh-interval": "Bölge grupları yenileme aralığı", + "copy-zone-group-name": "Bölge grubu adını kopyala", + "open-details-page": "Varlık ayrıntıları sayfasını aç", + "level": "Düzey", + "direction-level": "Yön", + "direction-up": "Yukarı", + "direction-up-parent": "Üste (üst öğeye)", + "direction-down": "Aşağı", + "direction-down-child": "Alta (alt öğeye)", + "add-level": "Düzey Ekle", + "delete-level": "Düzeyi Sil", + "no-level": "Yapılandırılmış düzey yok", + "levels-required": "En az bir düzey yapılandırılmalıdır.", + "max-allowed-levels-error": "İlişki düzeyi izin verilen azami değeri aşıyor.", + "propagation-path-related-entities": "İlgili varlıklara yayılım yolu", + "propagate-type": { + "arguments-only": "Yalnızca bağımsız değişkenler", + "expression-result": "Hesaplama sonucu" + }, + "script": "Betik", + "data-propagate": "Yayılacak veri", + "output-key": "Çıktı anahtarı", + "copy-output-key": "Çıktı anahtarını kopyala", + "aggregation-path-related-entities": "İlgili varlıklara toplulaştırma yolu", + "deduplication-interval": "Yinelenenleri ayıklama aralığı", + "deduplication-interval-min": "Yinelenenleri ayıklama aralığı en az {{ sec }} saniye olmalıdır.", + "deduplication-interval-hint": "Telemetri toplulaştırmaları arasındaki asgari süre.", + "deduplication-interval-required": "Yinelenenleri ayıklama aralığı gereklidir.", + "calculated-field-filter-title": "Hesaplanmış alan filtresi", + "filter-title": "Filtre", + "calculated-field-types": "Hesaplanmış alan türleri", + "events": "Olaylar", + "any-type": "Herhangi Bir Tür", + "metrics": { + "metrics": "Metrikler", + "metrics-empty": "En az bir metrik yapılandırılmalıdır.", + "metric-name": "Metrik adı", + "metric-name-required": "Metrik adı gereklidir.", + "metric-name-pattern": "Metrik adı geçersiz.", + "metric-name-duplicate": "Bu isimde bir metrik zaten mevcut.", + "metric-name-max-length": "Metrik adı 256 karakterden kısa olmalıdır.", + "metric-name-forbidden": "Metrik adı rezerve edilmiştir ve kullanılamaz.", + "copy-metric-name": "Metrik adını kopyala", + "argument-name": "Bağımsız değişken adı", + "aggregation": "Toplama", + "aggregation-type": { + "avg": "Ortalama", + "min": "Minimum", + "max": "Maksimum", + "sum": "Toplam", + "count": "Sayım", + "count-unique": "Benzersiz sayım" + }, + "filtered": "Filtrelenmiş", + "value-source": "Değer kaynağı", + "value-source-hint": "Toplama için değerin nasıl elde edileceğini tanımlar.", + "value-source-type": { + "key": "Anahtar", + "function": "Fonksiyon" + }, + "no-metrics-configured": "En az bir metrik gereklidir.", + "add-metric": "Metrik Ekle", + "max-metrics": "Maksimum metrik sayısına ulaşıldı.", + "metric-settings": "Metrik ayarları", + "filter": "Filtre", + "filter-hint": "Toplama sırasında varlıkların filtrelenmesini sağlar. Filtre fonksiyonu bir boolean değer döndürmeli ve tüm yapılandırılmış bağımsız değişkenleri kullanabilir." + }, + "output-strategy": { + "strategy": "Strateji", + "process-right-away": "Hemen işleme", + "process-rule-chains": "Kural Zincirleri ile işleme", + "save-time-series": "Zaman serilerine kaydet", + "save-database": "Veritabanına kaydet", + "save-latest-values": "Son değerlere kaydet", + "send-web-sockets": "Web Soketlerine gönder", + "save-calculated-fields": "Hesaplanmış alanlara gönder", + "update-attribute-only-on-value-change": "Yalnızca değer değiştiğinde özniteliği güncelle", + "send-attributes-updated-notification": "Öznitelik güncelleme bildirimini gönder", + "ttl": "Özel TTL", + "ttl-required": "TTL gereklidir", + "ttl-min": "Sadece 0 minimum TTL izni verilir", + "processing-parameters": "İşleme parametreleri", + "hint": { + "strategy": "Sonucun hemen işlenip işlenmeyeceğini veya ek işlem için kural zincirine gönderilip gönderilmeyeceğini kontrol eder.", + "processing-options": "İşleme seçenekleri", + "update-attribute-only-on-value-change": "Değer değişse de değişmese de her gelen mesajda öznitelik güncellenir. Bu, API kullanımını artırır ve performansı düşürür.", + "update-attribute-only-on-value-change-enabled": "Öznitelik yalnızca değer değiştiğinde güncellenir. Değer değişmediyse, zaman damgaları güncellenmez ve bildirim gönderilmez.", + "send-attributes-updated-notification": "Öznitelikler güncellendi olayı, varsayılan kural zincirine gönderilir.", + "save-time-series": "Zaman serisi verisi, veritabanındaki ts_kv tablosuna kaydedilir.", + "save-database": "Öznitelik verisi veritabanına kaydedilir.", + "save-latest-values": "Yeni değer daha güncelse, zaman serisi verisi veritabanındaki ts_kv_latest tablosuna güncellenir.", + "send-web-sockets-attribute": "WebSocket aboneliklerine, öznitelik verisi güncellemeleri hakkında bildirim gönderir.", + "send-web-sockets-time-series": "WebSocket aboneliklerine, zaman serisi verisi güncellemeleri hakkında bildirim gönderir.", + "save-calculated-fields-attribute": "Hesaplanmış alanlara, öznitelik verisi güncellemeleri hakkında bildirim gönderir.", + "save-calculated-fields-time-series": "Hesaplanmış alanlara, zaman serisi verisi güncellemeleri hakkında bildirim gönderir.", + "ttl": "Zaman serisi verisinin saklama süresini tanımlar. Devre dışı bırakıldığında, Kiracı Profili TTL'si kullanılır." + } + }, + "aggregate-interval-type": "Toplama aralığı türü", + "aggregate-interval-value": "Toplama aralığı değeri", + "aggregate-interval-value-required": "Toplama aralığı değeri gereklidir.", + "aggregate-interval-value-min": "Toplama aralığı değeri en az { sec, plural, =0 {0 saniye} =1 {1 saniye} other {# saniye} } olmalıdır.", + "aggregate-interval-value-step-multiple-of": "Toplama aralığı değeri, 1 günün katları veya böleni olmalıdır.", + "aggregate-period": { + "hour": "Saat", + "day": "Gün", + "week": "Hafta (Pzt - Pazar)", + "week-sun-sat": "Hafta (Pazar - Cumartesi)", + "month": "Ay", + "quarter": "Çeyrek", + "year": "Yıl", + "custom": "Özel" + }, + "aggregate-period-hint-offset": "Toplama aralığınız: {{ interval }} olacak.", + "aggregate-period-hint-offset-and-so-on": "Toplama aralığınız: {{ interval }} olacak ve devam edecek.", + "entity-aggregation": { + "argument-hint": "Veri mevcut varlıktan alınacaktır.", + "argument-title-hint": "Toplama için kullanılan giriş bağımsız değişkenlerini tanımlar.", + "argument-setting-hint": "Son telemetri, bu hesaplanmış alan için mevcut tek bağımsız değişken türüdür.", + "aggregation-interval": "Toplama aralığı", + "aggregation-interval-hint": "Toplamanın ne sıklıkla yapılacağını tanımlar. Örnek: her 1 saat, 00:00, 01:00, 02:00, vb. saatlerde veri toplar. Toplama sonuçları, toplama aralığının başlangıcına karşılık gelen zaman damgası ile saklanır.", + "apply-offset": "Toplama aralığına offset uygula", + "apply-offset-hint": "Her toplama döneminin başlangıcını ne kadar kaydıracağını tanımlar (örneğin, +10 dakika - 00:10, 01:10).", + "offset-value": "Offset değeri", + "offset-value-required": "Offset değeri gereklidir.", + "offset-value-min": "Offset değeri pozitif bir tam sayı olmalıdır.", + "offset-value-max": "Offset değeri, toplama aralığı değerinden küçük olmalıdır.", + "wait-delay": "Gecikmeli telemetri için bekleme süresi uygula", + "wait-delay-hint": "Aralık bittiğinde gecikmeli telemetriyi beklemek için ne kadar süre bekleyeceğinizi tanımlar. Eğer böyle bir telemetri gelirse, o aralık için sonuç yeniden hesaplanacaktır.", + "duration": "Süre", + "duration-required": "Süre gereklidir.", + "duration-min": "Süre en az 1 dakika olmalıdır.", + "duration-hint": "Aralık bittiğinde gecikmeli veriyi beklemek için ne kadar süre bekleyeceğinizi tanımlar.", + "produce-intermediate-result": "Ara sonuç üret", + "produce-intermediate-result-hint": "Mevcut aralıkta metrikler hesaplanarak ara sonuç üretilir. Güncellemeler, her {{ time }}'de bir kez yapılacaktır." + }, "hint": { - "arguments-simple-with-rolling": "Basit türde hesaplanmış alan zaman serisi kaydırma tipi anahtar içermemelidir.", - "arguments-empty": "Argümanlar boş olmamalıdır.", + "arguments-simple-with-rolling": "Basit türdeki hesaplanmış alan, zaman serisi yuvarlama türüyle anahtarlar içermemelidir.", + "arguments-propagate-arguments-with-rolling": "'Zaman serisi yuvarlama' türü, 'Yalnızca bağımsız değişkenler' yayılımıyla uyumsuzdur.", + "arguments-propagate-argument-entity-type": "Varlık türü, 'Yalnızca bağımsız değişkenler' yayılımıyla uyumsuzdur.", + "arguments-propagate-argument-must-current-entity": "En az bir bağımsız değişken, 'Mevcut varlık' kaynak varlık türü ile yapılandırılmalıdır.", + "arguments-empty": "En az bir bağımsız değişken belirtilmelidir.", "expression-required": "İfade gereklidir.", - "expression-invalid": "İfade geçersiz", - "expression-max-length": "İfade uzunluğu 255 karakterden az olmalıdır.", - "argument-name-required": "Argüman adı gereklidir.", - "argument-name-pattern": "Argüman adı geçersiz.", - "argument-name-duplicate": "Bu adda bir argüman zaten mevcut.", - "argument-name-max-length": "Argüman adı 256 karakterden kısa olmalıdır.", - "argument-name-forbidden": "Bu argüman adı rezerve edilmiştir ve kullanılamaz.", - "argument-type-required": "Argüman türü gereklidir.", - "max-args": "Maksimum argüman sayısına ulaşıldı.", - "decimals-range": "Varsayılan ondalık sayısı 0 ile 15 arasında olmalıdır.", - "expression": "Varsayılan ifade, sıcaklığı Fahrenheit'tan Celsius'a dönüştürmeyi gösterir.", - "arguments-entity-not-found": "Argüman hedef varlığı bulunamadı.", - "use-latest-timestamp": "Etkinleştirilirse, hesaplanan değer sunucu zamanı yerine argümanlardan gelen telemetri için en son zaman damgası ile kaydedilir." + "expression-invalid": "İfade geçersiz.", + "expression-max-length": "İfade uzunluğu 255 karakterden kısa olmalıdır.", + "argument-name-required": "Bağımsız değişken adı gereklidir.", + "argument-name-pattern": "Bağımsız değişken adı geçersiz.", + "argument-name-duplicate": "Bu isimde bir bağımsız değişken zaten mevcut.", + "argument-name-max-length": "Bağımsız değişken adı 256 karakterden kısa olmalıdır.", + "argument-name-forbidden": "Bağımsız değişken adı rezerve edilmiştir ve kullanılamaz.", + "output-key-required": "Çıktı anahtarı gereklidir.", + "output-key-pattern": "Çıktı anahtarı geçersiz.", + "output-key-duplicate": "Bu isimde bir anahtar zaten mevcut.", + "output-key-max-length": "Çıktı anahtarı 256 karakterden kısa olmalıdır.", + "output-key-forbidden": "Çıktı anahtarı rezerve edilmiştir ve kullanılamaz.", + "entity-type-required": "Varlık türü gereklidir.", + "name-required": "Ad gereklidir.", + "name-pattern": "Ad geçersiz.", + "name-duplicate": "Bu isimde bir ad zaten mevcut.", + "name-max-length": "Ad 256 karakterden kısa olmalıdır.", + "name-forbidden": "Ad rezerve edilmiştir ve kullanılamaz.", + "argument-type-required": "Bağımsız değişken türü gereklidir.", + "max-args": "Maksimum bağımsız değişken sayısına ulaşıldı.", + "decimals-range": "Ondalıklar varsayılan olarak 0 ile 15 arasında bir sayı olmalıdır.", + "expression": "Varsayılan ifade, bir sıcaklık değerini Fahrenheit'ten Celsius'a nasıl dönüştüreceğini gösterir.", + "arguments-entity-not-found": "Bağımsız değişken hedef varlığı bulunamadı.", + "use-latest-timestamp": "Etkinleştirilirse, hesaplanan değer, bağımsız değişkenlerin telemetrisindeki en son zaman damgası kullanılarak saklanacaktır, sunucu zamanı yerine.", + "entity-coordinates": "Varlık GPS koordinatlarını (enlem ve boylam) sağlayan zaman serisi anahtarlarını belirtin.", + "geofencing-zone-groups": "Kontrol edilecek bir veya daha fazla coğrafi çitleme bölgesi grubu tanımlayın (örneğin 'allowedZones', 'restrictedZones'). Her grup, hesaplanmış alan çıktısı telemetri anahtarları için önek olarak kullanılacak benzersiz bir ada sahip olmalıdır.", + "perimeter-attribute-key": "Coğrafi çitleme bölgesi çevre tanımını içeren öznitelik anahtarını ayarlayın. Çevre her zaman bölge varlıklarının sunucu tarafı özniteliklerinden alınır.", + "report-strategy": "Bulunma durumu, varlığın şu anda bölge grubunun İÇİNDE mi yoksa DIŞINDA mı olduğunu rapor eder. Geçiş olayları, varlığın bölge grubuna GİRDİĞİ veya ÇIKTIĞI zamanı rapor eder.", + "create-relation-with-matched-zones": "Varlık ile şu anda içinde bulunduğu bölgeler arasında ilişkiler otomatik olarak oluşturulup korunur. Varlık bir bölgeden ayrıldığında ilişkiler kaldırılır ve yeni bir bölgeye girdiğinde oluşturulur.", + "relation-type-required": "İlişki türü gereklidir.", + "relation-level-required": "İlişki düzeyi gereklidir.", + "relation-level-min": "Minimum ilişki düzeyi değeri 1'dir.", + "relation-level-max": "Maksimum ilişki düzeyi değeri {{max}}'dir.", + "geofencing-empty": "En az bir bölge grubu yapılandırılmalıdır.", + "geofencing-entity-not-found": "Coğrafi çitleme hedef varlığı bulunamadı.", + "max-geofencing-zone": "Maksimum coğrafi çitleme bölgesi sayısına ulaşıldı.", + "zone-group-refresh-interval": "İlgili varlıklar aracılığıyla yapılandırılan bölge gruplarının ne sıklıkla yenileneceğini tanımlar.", + "zone-group-refresh-interval-required": "Bölge gruplarının yenileme aralığı gereklidir.", + "zone-group-refresh-interval-min": "Bölge grubu yenileme aralığı en az {{ min }} saniye olmalıdır.", + "propagation-path-related-entities": "Seçilen yön ve ilişki türüne göre, ilgili bir varlığa doğrudan, tek seviyeli bir yolu tanımlar. Yalnızca cihaz, varlık, müşteri ve kiracı varlıkları arasındaki ilişkiler desteklenir. İlişki yolu ile çözülmüş maksimum varlık sayısı {{ max }}'dir.", + "data-propagate": "Aşağıda yapılandırılan bağımsız değişkenlerden hangi verilerin yayılacağını tanımlar. 'Yalnızca bağımsız değişkenler', alınan verileri doğrudan kullanırken, 'İfade sonucu' bu verilerden yeni bir değer hesaplar.", + "aggregation-path-related-entities": "Yön ve ilişki türüne dayalı olarak, üst veya alt varlıklarla doğrudan ilişkiler aracılığıyla tek seviyeli bir toplama yolunu tanımlar. Yalnızca cihaz, varlık, müşteri ve kiracı varlıkları arasındaki ilişkiler desteklenir. İlişki yolu ile çözülmüş maksimum varlık sayısı {{ max }}'dir.", + "arguments-aggregation": "Filtreleme ve toplama için kullanılan giriş bağımsız değişkenlerini tanımlar.", + "setting-arguments-aggregation": "Veriler, toplama yolunda yapılandırılan ilgili varlıklardan alınacaktır.", + "metrics": "Yapılandırılmış bağımsız değişkenlere dayalı olarak toplanan metrikleri tanımlar.", + "entity-aggregation-metrics": "Belirtilen zaman aralıklarında yapılandırılmış bağımsız değişkenlere dayalı olarak toplanan metrikleri tanımlar.", + "import-invalid-calculated-field-type": "Hesaplanmış alan ithal edilemedi: Geçersiz hesaplanmış alan yapısı.", + "simple-expression-title": "Hesaplanan değerin nasıl hesaplanacağını tanımlayan aritmetik ifade.", + "script-title": "Hesaplama mantığını ve çıktı değerlerini tanımlayan TBEL betiği.", + "simple-arguments": "Hesaplanan değerin nasıl hesaplanacağını tanımlayan aritmetik ifade.", + "script-arguments": "Betiğe mevcut olan giriş bağımsız değişkenlerini tanımlar." + } + }, + "alarm-rule": { + "alarm-rules-tab": "Alarm kuralları", + "alarm-rule": "Alarm kuralı", + "alarm-rules": "Alarm kuralları", + "alarm-rules-old": "Eski", + "alarm-rules-actual": "Geçerli", + "severities": "Öncelikler", + "cleared": "Koşul temizlendi", + "delete-title": "“{{title}}” alarm kuralını silmek istediğinizden emin misiniz?", + "delete-text": "Dikkat, onaydan sonra alarm kuralı ve tüm ilişkili veriler geri getirilemez olacaktır.", + "delete-multiple-title": "{ count, plural, =1 {1 alarm kuralı} other {# alarm kuralı} } silmek istediğinizden emin misiniz?", + "delete-multiple-text": "Dikkat, onaydan sonra tüm seçilen alarm kuralları silinecek ve tüm ilişkili veriler geri getirilemez olacaktır.", + "create": "Yeni alarm kuralı oluştur", + "add": "Alarm kuralı ekle", + "copy": "Alarm kuralı yapılandırmasını kopyala", + "details": "Alarm kuralı ayrıntıları", + "no-found": "Alarm kuralı bulunamadı", + "list": "{ count, plural, =1 {Bir alarm kuralı} other {# alarm kuralı listesi} }", + "selected-fields": "{ count, plural, =1 {1 alarm kuralı} other {# alarm kuralı} } seçildi", + "import": "Alarm kuralı içe aktar", + "file": "Alarm kuralı dosyası", + "export": "Alarm kuralı dışa aktar", + "export-failed-error": "Alarm kuralı dışa aktarılamadı: {{error}}", + "entity-type": "Varlık türü", + "entity-type-required": "Varlık türü gereklidir.", + "alarm-type": "Alarm türü", + "alarm-type-hint": "Alarm kaynağı (Cihaz, Varlık, vb.) kapsamında çakışmaları önlemek için benzersiz tanımlayıcı (örneğin, HighTempAlarm).", + "alarm-type-required": "Alarm türü gereklidir.", + "alarm-type-pattern": "Alarm türü geçersiz.", + "alarm-type-max-length": "Alarm türü 256 karakterden kısa olmalıdır.", + "clear-alarm": "Alarmı temizle", + "value-argument": "Bağımsız değişken", + "value-argument-required": "Bağımsız değişken gereklidir.", + "static-settings": "Statik ayarlar", + "configuration": "Yapılandırma", + "static-schedule": "Statik", + "dynamic-schedule": "Dinamik", + "operation-and": "VE", + "operation-or": "VEYA", + "condition-during": "{{during}} süresince", + "condition-during-dynamic": "\"{{ attribute }}\" süresince", + "condition-repeat-times": "{ count, plural, =1 {1 kez} other {# kez} } tekrarlar", + "condition-repeat-times-dynamic": "\"{{ attribute }}\" kadar tekrarlar", + "filter-preview": "Filtre önizlemesi", + "condition-settings": "Koşul ayarları", + "static": "Statik", + "dynamic": "Dinamik", + "argument-filters": "Bağımsız değişken filtreleri", + "argument-name": "Bağımsız değişken adı", + "value-type": "Değer türü", + "general": "Genel", + "filters": "Filtreler", + "date-time-hint": "Bağımsız değişken epoch milisaniye cinsinden olmalıdır. Örnek: 1698839340000, 2023-11-01 12:49:00 UTC'ye eşittir.", + "operation": "İşlem", + "value-source": "Değer kaynağı", + "value": "Değer", + "ignore-case": "Büyük/küçük harf duyarlılığını göz ardı et", + "condition": "Koşul", + "script": "Betik", + "add-filter": "Bağımsız değişken filtresi ekle", + "edit-filter": "Bağımsız değişken filtresi", + "remove-filter": "Bağımsız değişken filtresini kaldır", + "no-filter": "En az bir filtre gereklidir.", + "conditions": { + "simple": "Basit", + "duration": "Süre", + "repeating": "Tekrarlayan" + }, + "schedule-title": "Zamanlama", + "edit-schedule": "Alarm zamanlamasını düzenle", + "schedule-type": "Zamanlayıcı türü", + "schedule-type-required": "Zamanlayıcı türü gereklidir.", + "schedule": { + "any-time": "Her zaman etkin", + "specific-time": "Belirli bir zamanda etkin", + "custom": "Özel" + }, + "schedule-day": { + "monday": "Pazartesi", + "tuesday": "Salı", + "wednesday": "Çarşamba", + "thursday": "Perşembe", + "friday": "Cuma", + "saturday": "Cumartesi", + "sunday": "Pazar" + }, + "schedule-days": "Günler", + "schedule-time": "Zaman", + "schedule-time-from": "Başlangıç", + "schedule-time-to": "Bitiş", + "schedule-days-of-week-required": "En az bir hafta günü seçilmelidir.", + "tbel": "TBEL", + "expression-type": { + "simple": "Basit", + "script": "Betik" + }, + "operation-type": { + "and": "Ve", + "or": "Veya" + }, + "filter-predicate-type": { + "string": "Dize", + "numeric": "Sayısal", + "boolean": "Mantıksal", + "complex": "Karmaşık" + }, + "alarm-rule-additional-info": "Ekstra bilgi", + "edit-alarm-rule-additional-info": "Ekstra bilgiyi düzenle", + "alarm-rule-additional-info-placeholder": "Lütfen alarm ayrıntılarında Ekstra bilgi altında görüntülenmesi için yorumlarınızı ve düzenlemelerinizi buraya yazın", + "alarm-rule-additional-info-hint": "İpucu: alarm kuralı koşulunda kullanılan bağımsız değişkenlerin değerlerini yerine koymak için ${Bağımsız değişken adı} kullanın.", + "alarm-rule-additional-info-icon-hint": "Bağımsız değişkenlerin değerlerini yerine koymak için Bağımsız değişken adını kullanın.", + "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": "Hiçbir gösterge paneli seçilmedi", + "alarm-rule-condition": "Alarm kuralı koşulu", + "enter-alarm-rule-condition-prompt": "Koşul ekle", + "enter-alarm-rule-clear-condition-prompt": "Temizleme koşulu ekle", + "edit-alarm-rule-condition": "Alarm koşulu", + "condition-type": "Koşul türü", + "condition-type-hint": "\"Süre\" ve \"Tekrarlayan\" seçenekleri, filtrede \"Eksik\" işlemi kullanıldığında mevcut değildir.", + "select-alarm-severity": "Alarm önceliğini seçin", + "add-create-alarm-rule-prompt": "En az bir tetikleyici koşul gereklidir.", + "add-create-alarm-rule": "Tetikleyici koşulu ekle", + "add-clear-alarm-rule": "Temizleme koşulu ekle", + "condition-duration": "Koşul süresi", + "condition-duration-value": "Süre değeri", + "condition-duration-time-unit": "Zaman birimi", + "condition-duration-value-range": "Süre değeri 1 ile 2147483647 arasında olmalıdır.", + "condition-duration-value-pattern": "Süre değeri tamsayı olmalıdır.", + "condition-duration-value-required": "Süre değeri gereklidir.", + "condition-duration-time-unit-required": "Zaman birimi gereklidir.", + "condition-repeating-value": "Olay sayısı", + "condition-repeating-value-hint": "Herhangi bir alarm kuralı bağımsız değişkeninin güncellenmesi olay olarak sayılır.", + "condition-repeating-value-range": "Olay sayısı 1 ile 2147483647 arasında olmalıdır.", + "condition-repeating-value-pattern": "Olay sayısı tamsayı olmalıdır.", + "condition-repeating-value-required": "Olay sayısı gereklidir.", + "create-conditions": "Tetikleyici koşullar", + "clear-condition": "Temizleme koşulu", + "no-clear-alarm-rule": "Temizleme koşulu yapılandırılmadı.", + "advanced-settings": "Gelişmiş ayarlar", + "propagate-alarm": "Alarmı ilgili varlıklara yay", + "alarm-rule-relation-types-list": "İlişki türleri", + "alarm-rule-relation-types-list-hint": "İlgili varlıkları filtrelemek için ilişki türlerini tanımlar. Belirtilmezse, alarm tüm ilgili varlıklara yayılacaktır.", + "propagate-alarm-to-owner": "Alarmı varlık sahibine yay (Müşteri veya Kiracı)", + "propagate-alarm-to-tenant": "Alarmı Kiracıya yay", + "alarm-rule-filter-title": "Alarm kuralı filtresi", + "filter-title": "Filtre", + "debugging": "Alarm kuralı hata ayıklama", + "any-type": "Herhangi bir tür", + "enter-alarm-rule-type": "Alarm türünü girin", + "no-alarm-rule-types-matching": "‘{{entitySubtype}}’ için eşleşen alarm türleri bulunamadı.", + "alarm-rule-type-list-empty": "Hiç alarm türü seçilmedi.", + "alarm-rule-type-list": "Alarm türü listesi", + "alarm-rule-entity-list": "Varlık listesi", + "missing-for": "Eksik", + "time-unit": "Zaman birimi", + "mode": "Mod", + "type": "Tür", + "value-required": "Değer gereklidir.", + "min-value": "Değer 1 veya daha büyük olmalıdır.", + "argument-in-use": "Bağımsız değişken genel bağımsız değişken olarak kullanılıyor.", + "import-invalid-alarm-rule-type": "Alarm kuralı içe aktarılamadı: Geçersiz alarm kuralı yapısı.", + "no-filter-preview": "Filtre belirtilmedi", + "filter-operation": { + "and": "Ve", + "or": "Veya" } }, "ai-models": { @@ -1111,15 +1577,17 @@ "mistral-ai": "Mistral AI", "anthropic": "Anthropic", "amazon-bedrock": "Amazon Bedrock", - "github-models": "GitHub Modelleri" + "github-models": "GitHub Modelleri", + "ollama": "Ollama" }, - "name-required": "Ad gerekli.", + "name-required": "Ad zorunludur.", "name-max-length": "Ad en fazla 255 karakter olmalıdır.", "provider": "Sağlayıcı", "api-key": "API anahtarı", - "api-key-required": "API anahtarı gerekli.", + "api-key-required": "API anahtarı zorunludur.", + "api-key-open-ai-required": "Resmî OpenAI API'si kullanılırken API anahtarı zorunludur.", "project-id": "Proje Kimliği", - "project-id-required": "Proje kimliği gerekli.", + "project-id-required": "Proje Kimliği zorunludur", "location": "Konum", "location-required": "Konum gerekli.", "service-account-key-file": "Hizmet hesabı anahtar dosyası", @@ -1154,17 +1622,34 @@ "frequency-penalty": "Frekans cezası", "frequency-penalty-hint": "Bir belirtecin metinde geçme sıklığına göre olasılığına ceza uygular.", "max-output-tokens": "Maksimum çıktı belirteçleri", - "max-output-tokens-min": "0'dan büyük olmalıdır.", "max-output-tokens-hint": "Modelin tek bir yanıtta üretebileceği maksimum belirteç sayısını ayarlar.", + "context-length": "Bağlam uzunluğu", + "context-length-hint": "Token cinsinden bağlam penceresinin boyutunu tanımlar. Bu değer, hem kullanıcının girdisini hem de üretilen yanıtı içeren model için toplam bellek sınırını belirler.", "endpoint": "Uç nokta", "endpoint-required": "Uç nokta gereklidir.", + "baseurl": "Base URL", + "baseurl-required": "Base URL zorunludur.", "service-version": "Servis versiyonu", "check-connectivity": "Bağlantıyı kontrol et", "check-connectivity-success": "Test isteği başarılı oldu", "check-connectivity-failed": "Test isteği başarısız oldu", "no-model-matching": "'{{entity}}' ile eşleşen model bulunamadı.", "model-required": "Model gereklidir.", - "no-model-text": "Model bulunamadı." + "no-model-text": "Model bulunamadı.", + "authentication": "Kimlik doğrulama", + "authentication-basic-hint": "Standart HTTP Basic kimlik doğrulamasını kullanır. Kullanıcı adı ve parola birleştirilir, Base64 ile kodlanır ve Ollama sunucusuna yapılan her istekte \"Authorization\" başlığında gönderilir.", + "authentication-token-hint": "Bearer token kimlik doğrulamasını kullanır. Sağlanan token, Ollama sunucusuna yapılan her istekte doğrudan \"Authorization\" başlığında gönderilecektir.", + "authentication-type": { + "none": "Yok", + "basic": "Basic", + "token": "Token" + }, + "username": "Kullanıcı adı", + "username-required": "Kullanıcı adı zorunludur.", + "password": "Parola", + "password-required": "Parola zorunludur.", + "token": "Token", + "token-required": "Token zorunludur." }, "confirm-on-exit": { "message": "Kaydedilmemiş değişiklikleriniz var. Bu sayfadan ayrılmak istediğinizden emin misiniz?", @@ -1174,6 +1659,7 @@ "contact": { "country": "Ülke", "country-required": "Ülke gereklidir.", + "country-object-required": "Lütfen listeden geçerli bir ülke seçin.", "city": "Şehir", "state": "Eyalet / İl", "postal-code": "Posta Kodu", @@ -1210,6 +1696,8 @@ "documentation": "Dokümantasyon", "time-left": "{{time}} kaldı", "output": "Çıktı", + "sort-asc": "Artan", + "sort-desc": "Azalan", "suffix": { "s": "sn", "ms": "ms" @@ -1346,6 +1834,8 @@ "mobile-order": "Mobil uygulamadaki pano sırası", "mobile-hide": "Panoyu mobil uygulamada gizle", "update-image": "Pano görselini güncelle", + "update-new-version": "Yeni sürüm yükle", + "upload-file-to-update": "Yükseltmek için dosya yükle", "take-screenshot": "Ekran görüntüsü al", "select-widget-title": "Widget seç", "select-widget-value": "{{title}}: widget seç", @@ -1714,6 +2204,8 @@ "bootstrap-tab": "Başlatma İstemcisi", "bootstrap-server": "Başlatma Sunucusu", "lwm2m-server": "LwM2M Sunucusu", + "client-reboot": "Kayıt Güncelleme Tetikleyicisi", + "bootstrap-reboot": "Bootstrap-Request Tetikleyicisi", "client-publicKey-or-id": "İstemci Genel Anahtarı veya Kimliği", "client-publicKey-or-id-required": "İstemci Genel Anahtarı veya Kimliği gereklidir.", "client-publicKey-or-id-tooltip-psk": "[RFC7925] standardına göre PSK tanımlayıcısı en fazla 128 baytlık rastgele bir tanımlayıcıdır.\nTanımlayıcı önce karakter dizisine çevrilmeli ve ardından UTF-8 ile kodlanmalıdır.", @@ -1761,7 +2253,6 @@ "unable-delete-device-alias-text": "'{{deviceAlias}}' cihaz takma adı aşağıdaki widget(lar) tarafından kullanıldığı için silinemiyor:
    {{widgetsList}}", "is-gateway": "Ağ geçidi mi", "overwrite-activity-time": "Bağlı cihaz için etkinlik zamanını üzerine yaz", - "device-filter": "Cihaz filtresi", "device-filter-title": "Cihaz Filtresi", "filter-title": "Filtre", "device-state": "Cihaz durumu", @@ -2215,6 +2706,7 @@ "short-id-required": "Kısa sunucu kimliği gereklidir.", "short-id-range": "Kısa sunucu kimliği {{ min }} ile {{ max }} arasında olmalıdır.", "short-id-pattern": "Kısa sunucu kimliği pozitif bir tamsayı olmalıdır.", + "short-id-pattern-bs": "Kısa sunucu ID'si yalnızca null olmalıdır", "lifetime": "İstemci kayıt süresi", "lifetime-required": "İstemci kayıt süresi gereklidir.", "lifetime-pattern": "İstemci kayıt süresi pozitif bir tamsayı olmalıdır.", @@ -2309,7 +2801,9 @@ "composite-all-description": "Tüm kaynaklar tek Composite Observe isteğiyle gözlemlenir (daha verimli, daha az esnek)", "composite-by-object": "Nesnelere göre birleştir", "composite-by-object-description": "Kaynaklar nesne türüne göre gruplanır ve ayrı Composite Observe istekleri ile gözlemlenir (dengeli yaklaşım)" - } + }, + "init-attr-tel-as-obs-strategy": "Öznitelikleri ve telemetrileri Observe stratejisi ile başlat", + "init-attr-tel-as-obs-strategy-hint": "Eğer yanlışsa - öznitelikler ve telemetri değerleri tek tek okunarak başlatılır.\\nEğer doğruysa - öznitelikler ve telemetri, Observe stratejisi kullanılarak değerlerine abone olunarak başlatılır." }, "snmp": { "add-communication-config": "İletişim yapılandırması ekle", @@ -2625,6 +3119,8 @@ "type-rulenodes": "Kural düğümleri", "list-of-rulenodes": "{ count, plural, =1 {Bir kural düğümü} other {# kural düğümü listesi} }", "rulenode-name-starts-with": "'{{prefix}}' ile başlayan kural düğümleri", + "type-api-key": "API anahtarı", + "type-api-keys": "API anahtarları", "type-current-customer": "Mevcut Müşteri", "type-current-tenant": "Mevcut Kiracı", "type-current-user": "Mevcut Kullanıcı", @@ -2646,6 +3142,7 @@ "details": "Varlık ayrıntıları", "no-entities-prompt": "Varlık bulunamadı", "no-data": "Görüntülenecek veri yok", + "show-all-columns": "Tümünü Göster", "columns-to-display": "Görüntülenecek sütunlar", "type-api-usage-state": "API Kullanım Durumu", "type-edge": "Uç", @@ -2691,7 +3188,15 @@ "list-of-mobile-apps": "{ count, plural, =1 {Bir mobil uygulama} other {# mobil uygulama listesi} }", "type-mobile-app-bundle": "Mobil paket", "type-mobile-app-bundles": "Mobil paketler", - "list-of-mobile-app-bundles": "{ count, plural, =1 {Bir mobil paket} other {# mobil paket listesi} }" + "list-of-mobile-app-bundles": "{ count, plural, =1 {Bir mobil paket} other {# mobil paket listesi} }", + "limit-reached": "Sınır aşıldı", + "limit-reached-text": "{{ entities }} sınırına ulaştınız. Daha fazla eklemek için lütfen Sistem Yöneticinizden {{ entity }} sınırınızı artırmasını isteyin.", + "request-limit-increase": "Sınır artışı talep et", + "request-sysadmin-text": "Sistem Yöneticisi misiniz?", + "login-here": "Buradan giriş yap", + "to-increase-limit": "sınırı artırmak için.", + "increase-limit-request-sent-title": "Sistem Yöneticinize sınır artışı için otomatik bir talep gönderdik", + "increase-limit-request-sent-text": "Lütfen talepleri inceleyip ayarları güncellemeleri için biraz zaman verin. Değişiklikleri görmek için bu sayfayı yenileyebilirsiniz." }, "entity-field": { "created-time": "Oluşturulma zamanı", @@ -3779,7 +4284,8 @@ "password-link-sent-message": "Sıfırlama bağlantısı gönderildi", "email": "E-posta", "invalid-email-format": "Geçersiz e-posta formatı.", - "login-with": "{{name}} ile giriş yap", + "sign-in-with": "{{name}} ile giriş yap", + "sign-in-to-your-account": "Hesabınıza giriş yapın", "or": "veya", "error": "Giriş hatası", "verify-your-identity": "Kimliğinizi doğrulayın", @@ -3798,7 +4304,51 @@ "activation-link-expired": "Aktivasyon bağlantısının süresi doldu", "activation-link-expired-message": "Profilinizi aktifleştirmek için gönderilen bağlantının süresi doldu. Yeni bir e-posta almak için giriş sayfasına dönebilirsiniz.", "reset-password-link-expired": "Şifre sıfırlama bağlantısının süresi doldu", - "reset-password-link-expired-message": "Şifre sıfırlama bağlantısının süresi doldu. Yeni bir e-posta almak için giriş sayfasına dönebilirsiniz." + "reset-password-link-expired-message": "Şifre sıfırlama bağlantısının süresi doldu. Yeni bir e-posta almak için giriş sayfasına dönebilirsiniz.", + "two-fa": "İki faktörlü kimlik doğrulama", + "two-fa-required": "İki faktörlü kimlik doğrulama gereklidir", + "set-up-verification-method": "Devam etmek için bir doğrulama yöntemi ayarlayın", + "set-up-verification-method-login": "Bir doğrulama yöntemi ayarlayın veya giriş yapın", + "enable-authenticator-app": "Authenticator uygulamasını etkinleştir", + "enable-authenticator-app-description": "Lütfen doğrulama uygulamanızdan güvenlik kodunu girin", + "enable-authenticator-sms": "SMS doğrulayıcıyı etkinleştir", + "enable-authenticator-sms-description": "Az önce gönderdiğimiz 6 haneli kodu girin", + "enable-authenticator-email": "E-posta doğrulayıcıyı etkinleştir", + "enable-authenticator-email-description": "Bir güvenlik kodu, e-posta adresinize gönderildiği için lütfen kontrol edin", + "enter-key-manually": "veya bu 32 haneli anahtarı manuel olarak girin:", + "continue": "Devam et", + "confirm": "Onayla", + "authenticator-app-success": "Authenticator uygulaması başarıyla etkinleştirildi", + "authenticator-app-success-description": "Bir dahaki sefere giriş yaptığınızda, iki faktörlü kimlik doğrulama kodu girmeniz gerekecek", + "authenticator-sms-success": "SMS doğrulayıcı başarıyla etkinleştirildi", + "authenticator-sms-success-description": "Bir dahaki sefere giriş yaptığınızda, telefon numarasına gönderilecek güvenlik kodunu girmeniz istenecektir", + "authenticator-email-success": "E-posta doğrulayıcı başarıyla etkinleştirildi", + "authenticator-email-success-description": "Bir dahaki sefere giriş yaptığınızda, e-posta adresinize gönderilecek güvenlik kodunu girmeniz istenecektir", + "authenticator-backup-code-success": "Yedek kod başarıyla etkinleştirildi", + "authenticator-backup-code-success-description": "Bir dahaki sefere giriş yaptığınızda, güvenlik kodunu girmeniz veya yedek kodlardan birini kullanmanız istenecektir.", + "add-verification-method": "Doğrulama yöntemi ekle", + "get-backup-code": "Yedek kodu al", + "copy-key": "Anahtarı kopyala", + "send-code": "Kodu gönder", + "email-label": "E-posta", + "email-description": "Doğrulayıcı olarak kullanmak için bir e-posta adresi girin.", + "sms-description": "Doğrulayıcı olarak kullanmak için bir telefon numarası girin.", + "backup-code-description": "Kodları yazdırarak, hesabınıza giriş yapmak için gerektiğinde kullanabilirsiniz. Her yedek kodu bir kez kullanılabilir.", + "backup-code-warn": "Bu sayfayı terk ettiğinizde, bu kodlar bir daha gösterilemez. Aşağıdaki seçenekleri kullanarak güvenli bir şekilde saklayın.", + "download-txt": "İndir (txt)", + "print": "Yazdır", + "verification-code": "6 haneli kod", + "verification-code-invalid": "Geçersiz doğrulama kodu formatı", + "verification-code-incorrect": "Doğrulama kodu yanlış", + "verification-code-many-request": "Doğrulama kodunu kontrol etmek için çok fazla istek yapıldı", + "scan-qr-code": "Bu QR kodunu doğrulama uygulamanızla tarayın", + "phone-input": { + "phone-input-label": "Telefon numarası", + "phone-input-required": "Telefon numarası gereklidir", + "phone-input-validation": "Telefon numarası geçersiz veya mümkün değil", + "phone-input-pattern": "Geçersiz telefon numarası. E.164 formatında olmalıdır, örn. {{phoneNumber}}", + "phone-input-hint": "Telefon Numarası E.164 formatında olmalıdır, örn. {{phoneNumber}}" + } }, "mobile": { "add-application": "Uygulama ekle", @@ -4166,6 +4716,7 @@ "api-usage-limit": "API kullanım limiti", "device-activity": "Cihaz etkinliği", "entities-limit": "Varlık sınırı", + "entities-limit-increase-request": "Varlık sınırı artırma talebi", "entity-action": "Varlık işlemi", "general": "Genel", "rule-engine-lifecycle-event": "Kural motoru yaşam döngüsü olayı", @@ -4383,6 +4934,12 @@ "at-least": "En az:", "character": "{ count, plural, =1 {1 karakter} other {# karakter} }", "digit": "{ count, plural, =1 {1 rakam} other {# rakam} }", + "password-tooltip-min-length": "En az {{minimumLength}} karakter uzunluğunda olmalı", + "password-tooltip-max-length": "En fazla {{maximumLength}} karakter uzunluğunda olmalı", + "password-tooltip-uppercase": "{{minimumUppercaseLetters}} büyük harf karakter", + "password-tooltip-lowercase": "{{minimumLowercaseLetters}} küçük harf karakter", + "password-tooltip-digit": "{{minimumDigits}} rakam", + "password-tooltip-special-characters": "{{minimumSpecialCharacters}} özel karakter", "incorrect-password-try-again": "Hatalı şifre. Lütfen tekrar deneyin", "lowercase-letter": "{ count, plural, =1 {1 küçük harf} other {# küçük harf} }", "new-passwords-not-match": "Yeni şifreler eşleşmedi", @@ -4441,7 +4998,8 @@ "additional-info": "Ek bilgi (JSON)", "invalid-additional-info": "Ek bilgi json'u ayrıştırılamadı.", "no-relations-text": "İlişki bulunamadı", - "not": "Değil" + "not": "Değil", + "copy-type": "Kopyalama türü" }, "resource": { "add": "Kaynak ekle", @@ -4478,15 +5036,21 @@ "jks": "JKS", "js-module": "JS modülü", "lwm2m-model": "LWM2M modeli", - "pkcs-12": "PKCS #12" + "pkcs-12": "PKCS #12", + "general": "Genel" }, "resource-sub-type": "Alt tür", "sub-type": { - "image": "resim", - "scada-symbol": "Scada sembolü", + "image": "Görsel", + "scada-symbol": "SCADA sembolü", "extension": "Uzantı", "module": "Modül" - } + }, + "resource-is-in-use": "Kaynak diğer varlıklar tarafından kullanılıyor", + "resources-are-in-use": "Kaynaklar diğer varlıklar tarafından kullanılıyor", + "resource-is-in-use-text": "Kaynak '{{title}}' aşağıdaki varlıklar tarafından kullanıldığı için silinmedi:", + "resources-are-in-use-text": "Tüm kaynaklar silinmedi çünkü bazıları diğer varlıklar tarafından kullanılıyor.
    İlgili varlıkları, ilgili kaynak satırındaki Referanslar düğmesine tıklayarak görüntüleyebilirsiniz.
    Bu kaynakları yine de silmek istiyorsanız, aşağıdaki tabloda onları seçin ve Seçilenleri Sil düğmesine tıklayın.", + "delete-resource-in-use-text": "Kaynağı yine de silmek istiyorsanız Yine de Sil düğmesine tıklayın." }, "javascript": { "add": "JavaScript kaynağı ekle", @@ -4931,26 +5495,26 @@ "request-method": "İstek yöntemi", "use-simple-client-http-factory": "Basit HTTP istemci fabrikasını kullan", "ignore-request-body": "İstek içeriği olmadan", - "parse-to-plain-text": "Düz metne dönüştür", - "parse-to-plain-text-hint": "Seçildiğinde, istek içeriği JSON dizisinden düz metne dönüştürülür, örn. msg = \"Hello,\\t\"world\"\" will be parsed to Hello, \"world\"", - "read-timeout": "Okuma zaman aşımı (milisaniye)", - "read-timeout-hint": "0 değeri, sınırsız zaman aşımı anlamına gelir", - "max-parallel-requests-count": "Maksimum paralel istek sayısı", - "max-parallel-requests-count-hint": "0 değeri, paralel işlemeye sınırsız izin verir", - "max-response-size": "Maksimum yanıt boyutu (KB)", - "max-response-size-hint": "HTTP mesajlarını çözümlerken/şifrelerken ayrılacak maksimum bellek miktarı (örneğin JSON veya XML yükleri için)", - "headers": "Başlıklar", - "headers-hint": "Başlık/değer alanlarında meta veriden değer almak için ${metadataKey}, mesaj içeriğinden değer almak için $[messageKey] kullanın", - "header": "Başlık", - "header-required": "Başlık gereklidir", + "parse-to-plain-text": "Düz metne ayrıştır", + "parse-to-plain-text-hint": "Seçilirse, istek gövdesindeki mesaj yükü JSON dizgesinden düz metne dönüştürülür; örn. msg = \"Hello,\\t\"world\"\" ifadesi Hello, \"world\" olarak ayrıştırılır", + "read-timeout": "Okuma zaman aşımı (ms cinsinden)", + "read-timeout-hint": "0 değeri sonsuz zaman aşımı anlamına gelir", + "max-parallel-requests-count": "Maksimum eşzamanlı istek sayısı", + "max-parallel-requests-count-hint": "0 değeri paralel işlemde herhangi bir sınır olmadığını belirtir", + "max-response-size": "Maksimum yanıt boyutu (KB cinsinden)", + "max-response-size-hint": "HTTP iletilerini (JSON veya XML yükleri gibi) kodlarken ya da kodunu çözerken veri arabelleğe alma için ayrılan maksimum bellek miktarı", + "headers": "Üstbilgiler", + "headers-hint": "Üstbilgi/değer alanlarında, meta veriden değer almak için ${metadataKey}, ileti gövdesinden değer almak için $[messageKey] kullanın", + "header": "Üstbilgi", + "header-required": "Üstbilgi zorunludur", "value": "Değer", - "value-required": "Değer gereklidir", + "value-required": "Değer zorunludur", "topic-pattern": "Konu deseni", "key-pattern": "Anahtar deseni", - "key-pattern-hint": "İsteğe bağlı. Geçerli bir bölüm numarası belirtilirse kayıt gönderilirken kullanılır. Belirtilmemişse anahtar kullanılır. Her ikisi de belirtilmemişse round-robin yöntemi kullanılır.", - "topic-pattern-required": "Konu deseni gereklidir", + "key-pattern-hint": "İsteğe bağlıdır. Geçerli bir bölüm (partition) numarası belirtilirse, kayıt gönderilirken bu değer kullanılır. Herhangi bir bölüm belirtilmezse, bunun yerine anahtar kullanılır. Her ikisi de belirtilmezse, bölümler döngüsel olarak (round-robin) atanır.", + "topic-pattern-required": "Konu deseni zorunludur", "topic": "Konu", - "topic-required": "Konu gereklidir", + "topic-required": "Konu zorunludur", "bootstrap-servers": "Başlangıç sunucuları", "bootstrap-servers-required": "Başlangıç sunucuları değeri gereklidir", "other-properties": "Diğer özellikler", @@ -5386,7 +5950,7 @@ "time-series": "Zaman serisi", "latest": "Son değerler", "web-sockets": "WebSockets", - "calculated-fields": "Hesaplanmış alanlar" + "calculated-fields-and-alarm-rules": "Hesaplanmış alanlar ve alarm kuralları" }, "save-attribute": { "processing-settings": "İşleme ayarları", @@ -5432,32 +5996,33 @@ "ai": { "ai-model": "Yapay Zeka modeli", "model": "Model", - "ai-model-hint": "Bu kural düğümü tarafından gönderilen istekleri işlemek için önceden yapılandırılmış bir yapay zeka modeli seçin veya yeni bir tane yapılandırmak için \"Yeni oluştur\" seçeneğini kullanın.", + "ai-model-hint": "Bu kural düğümü tarafından gönderilen istekleri işlemek için önceden yapılandırılmış Yapay Zeka modelini seçin veya yeni bir tane yapılandırmak için \"Yeni oluştur\" seçeneğini kullanın.", "prompt-settings": "İstem ayarları", - "prompt-settings-hint": "İsteğe bağlı sistem istemi, yapay zekanın genel rolünü ve kısıtlamalarını belirlerken, kullanıcı istemi gerçekleştirilmesi gereken belirli görevi tanımlar. Her iki alan da şablonlaştırmayı destekler.", + "prompt-settings-hint": "İsteğe bağlı sistem istemi, YZ'nin genel rolünü ve kısıtlarını belirlerken, kullanıcı istemi gerçekleştirilecek belirli görevi tanımlar. Her iki alan da şablonlaştırmayı destekler.", "system-prompt": "Sistem istemi", - "system-prompt-max-length": "Sistem istemi en fazla 500000 karakter olmalıdır.", - "system-prompt-blank": "Sistem istemi boş olmamalıdır.", + "system-prompt-max-length": "Sistem istemi 500000 karakter veya daha az olmalıdır.", + "system-prompt-blank": "Sistem istemi boş bırakılmamalıdır.", "user-prompt": "Kullanıcı istemi", - "user-prompt-required": "Kullanıcı istemi gereklidir.", - "user-prompt-max-length": "Kullanıcı istemi en fazla 500000 karakter olmalıdır.", - "user-prompt-blank": "Kullanıcı istemi boş olmamalıdır.", - "response-format": "Yanıt formatı", + "user-prompt-required": "Kullanıcı istemi zorunludur.", + "user-prompt-max-length": "Kullanıcı istemi 500000 karakter veya daha az olmalıdır.", + "user-prompt-blank": "Kullanıcı istemi boş bırakılmamalıdır.", + "response-format": "Yanıt biçimi", "response-text": "Metin", "response-json": "JSON", "response-json-schema": "JSON Şeması", - "response-format-hint-TEXT": "Modelin geçerli bir JSON nesnesi olup olmayabileceği rastgele metin üretmesine olanak tanır. Çıktı geçerli bir JSON nesnesi değilse, otomatik olarak \"response\" anahtarı altında bir JSON nesnesine sarılır.", - "response-format-hint-JSON": "Modelin geçerli bir JSON yanıtı üretmesi gereklidir. Çıktı geçerli bir JSON nesnesi değilse, otomatik olarak \"response\" anahtarı altında bir JSON nesnesine sarılır.", - "response-format-hint-JSON_SCHEMA": "Modelin, sağlanan şemada tanımlanan belirli yapı ve veri türleriyle eşleşen bir JSON üretmesi gereklidir. Çıktı geçerli bir JSON nesnesi değilse, otomatik olarak \"response\" anahtarı altında bir JSON nesnesine sarılır.", - "response-json-schema-hint": "Geçerli herhangi bir JSON Şeması girilebilir, ancak bu kural düğümü yalnızca sınırlı bir alt kümesini destekler. Ayrıntılar için düğüm belgelerine bakın.", - "response-json-schema-required": "JSON Şeması gereklidir", + "response-format-hint-TEXT": "Modelin rastgele metin üretmesine izin verir; bu metin geçerli bir JSON nesnesi olabilir veya olmayabilir. Çıktı geçerli bir JSON nesnesi değilse, otomatik olarak \"response\" anahtarının altında bir JSON nesnesine sarılır.", + "response-format-hint-JSON": "Modelin geçerli bir JSON olan bir yanıt üretmesi gerekir. Çıktı geçerli bir JSON nesnesi değilse, otomatik olarak \"response\" anahtarının altında bir JSON nesnesine sarılır.", + "response-format-hint-JSON_SCHEMA": "Modelin, sağlanan şemada tanımlanan belirli yapı ve veri türleriyle eşleşen bir JSON üretmesi gerekir. Çıktı geçerli bir JSON nesnesi değilse, otomatik olarak \"response\" anahtarının altında bir JSON nesnesine sarılır.", + "response-json-schema-hint": "Her geçerli JSON Şeması girilebilir, ancak bu kural düğümü bunun özelliklerinin yalnızca sınırlı bir alt kümesini destekler. Ayrıntılar için düğüm belgelerine bakın.", + "response-json-schema-required": "JSON Şeması zorunludur", "advanced-settings": "Gelişmiş ayarlar", "timeout": "Zaman aşımı", - "timeout-hint": "Yapay zeka modelinden yanıt beklemek için maksimum süre. \nSüre aşılırsa istek sonlandırılır.", - "timeout-required": "Zaman aşımı gereklidir", + "timeout-hint": "İstek sonlandırılmadan önce YZ modelinden \nyanıt beklemek için azami süre.", + "timeout-required": "Zaman aşımı zorunludur", "timeout-validation": "1 saniye ile 10 dakika arasında olmalıdır.", - "force-acknowledgement": "Zorunlu onaylama", - "force-acknowledgement-hint": "Etkinleştirilirse, gelen mesaj anında onaylanır. Modelin yanıtı ayrı, yeni bir mesaj olarak kuyruğa alınır." + "force-acknowledgement": "Onayı zorla", + "force-acknowledgement-hint": "Etkinleştirilirse, gelen ileti hemen onaylanır. Modelin yanıtı daha sonra ayrı, yeni bir ileti olarak kuyruğa alınır.", + "ai-resources": "Yapay Zeka kaynakları" } }, "timezone": { @@ -5580,7 +6145,8 @@ "bad-request-params": "Hatalı istek parametreleri", "item-not-found": "Öğe bulunamadı", "too-many-requests": "Çok fazla istek yapıldı", - "too-many-updates": "Çok fazla güncelleme yapıldı" + "too-many-updates": "Çok fazla güncelleme yapıldı", + "entities-limit-exceeded": "Varlık sınırı aşıldı" }, "tenant": { "tenant": "Kiracı", @@ -5718,6 +6284,27 @@ "max-arguments-per-cf": "Hesaplanmış alan başına maksimum argüman sayısı", "max-arguments-per-cf-range": "Hesaplanmış alan başına maksimum argüman sayısı negatif olamaz", "max-arguments-per-cf-required": "Hesaplanmış alan başına maksimum argüman sayısı gereklidir", + "max-related-level-per-argument": "'İlgili varlıklar' bağımsız değişkeni için maksimum ilişki düzeyi", + "max-related-level-per-argument-range": "'İlgili varlıklar' bağımsız değişkeni için ilişki düzeyi maksimum sayısı '1' den küçük olamaz", + "max-related-level-per-argument-required": "'İlgili varlıklar' bağımsız değişkeni için ilişki düzeyi maksimum sayısı gereklidir", + "min-allowed-scheduled-update-interval": "'İlgili varlıklar' bağımsız değişkenleri için minimum izin verilen güncelleme aralığı (saniye)", + "min-allowed-scheduled-update-interval-range": "Minimum izin verilen güncelleme aralığı negatif olamaz", + "min-allowed-deduplication-interval": "Minimum izin verilen yinelenen ayıklama aralığı (saniye)", + "min-allowed-deduplication-interval-range": "Minimum izin verilen yinelenen ayıklama aralığı değeri negatif olamaz", + "min-allowed-deduplication-interval-required": "Minimum izin verilen yinelenen ayıklama aralığı gereklidir", + "intermediate-aggregation-interval": "Ara toplama aralığı (saniye)", + "intermediate-aggregation-interval-range": "Ara toplama aralığı değeri '1' den küçük olamaz", + "intermediate-aggregation-interval-required": "Ara toplama aralığı gereklidir", + "reevaluation-check-interval": "Yeniden değerlendirme kontrol aralığı (saniye)", + "reevaluation-check-interval-range": "Yeniden değerlendirme kontrol aralığı değeri '1' den küçük olamaz", + "reevaluation-check-interval-required": "Yeniden değerlendirme kontrol aralığı gereklidir", + "alarms-reevaluation-interval": "Alarm yeniden değerlendirme aralığı (saniye)", + "alarms-reevaluation-interval-range": "Alarm yeniden değerlendirme aralığı değeri '1' den küçük olamaz", + "alarms-reevaluation-interval-required": "Alarm yeniden değerlendirme aralığı gereklidir", + "min-allowed-aggregation-interval": "Minimum izin verilen toplama aralığı (saniye)", + "min-allowed-aggregation-interval-range": "Minimum izin verilen toplama aralığı değeri negatif olamaz", + "min-allowed-aggregation-interval-required": "Minimum izin verilen toplama aralığı gereklidir", + "min-allowed-scheduled-update-interval-required": "Minimum izin verilen güncelleme aralığı sayısı gereklidir", "max-state-size": "Durumun maksimum boyutu (KB)", "max-state-size-range": "Durumun maksimum boyutu (KB) negatif olamaz", "max-state-size-required": "Durumun maksimum boyutu (KB) gereklidir", @@ -5757,19 +6344,19 @@ "rule-engine-exceptions-ttl-days": "Kural Motoru istisnaları TTL süresi (gün)", "rule-engine-exceptions-ttl-days-required": "Kural Motoru istisnaları TTL süresi gereklidir", "rule-engine-exceptions-ttl-days-range": "Kural Motoru istisnaları TTL süresi negatif olamaz", - "max-rule-node-executions-per-message": "Mesaj başına Kural düğümü yürütme maksimum sayısı", - "max-rule-node-executions-per-message-required": "Mesaj başına Kural düğümü yürütme maksimum sayısı gereklidir.", - "max-rule-node-executions-per-message-range": "Mesaj başına Kural düğümü yürütme maksimum sayısı negatif olamaz", - "max-emails": "Gönderilen e-posta maksimum sayısı", - "max-emails-required": "Gönderilen e-posta maksimum sayısı gereklidir.", - "max-emails-range": "Gönderilen e-posta maksimum sayısı negatif olamaz", + "max-rule-node-executions-per-message": "Mesaj başına kural düğümü yürütme maksimum sayısı", + "max-rule-node-executions-per-message-required": "Mesaj başına kural düğümü yürütme maksimum sayısı zorunludur.", + "max-rule-node-executions-per-message-range": "Mesaj başına kural düğümü yürütme maksimum sayısı negatif olamaz", + "max-emails": "Gönderilen e-postaların maksimum sayısı", + "max-emails-required": "Gönderilen e-postaların maksimum sayısı zorunludur.", + "max-emails-range": "Gönderilen e-postaların maksimum sayısı negatif olamaz", "sms-enabled": "SMS etkin", - "max-sms": "Gönderilen SMS maksimum sayısı", - "max-sms-required": "Gönderilen SMS maksimum sayısı gereklidir.", - "max-sms-range": "Gönderilen SMS maksimum sayısı negatif olamaz", - "max-created-alarms": "Oluşturulan alarm maksimum sayısı", - "max-created-alarms-required": "Oluşturulan alarm maksimum sayısı gereklidir.", - "max-created-alarms-range": "Oluşturulan alarm maksimum sayısı negatif olamaz", + "max-sms": "Gönderilen SMS'lerin maksimum sayısı", + "max-sms-required": "Gönderilen SMS'lerin maksimum sayısı zorunludur.", + "max-sms-range": "Gönderilen SMS'lerin maksimum sayısı negatif olamaz", + "max-created-alarms": "Oluşturulan alarmların maksimum sayısı", + "max-created-alarms-required": "Oluşturulan alarmların maksimum sayısı zorunludur.", + "max-created-alarms-range": "Oluşturulan alarmların maksimum sayısı negatif olamaz", "no-queue": "Tanımlı Kuyruk yok", "add-queue": "Kuyruk Ekle", "queues-with-count": "Kuyruklar ({{count}})", @@ -5793,6 +6380,10 @@ "ws-limit-max-subscriptions-per-regular-user": "Normal kullanıcı başına abonelik maksimum sayısı", "ws-limit-max-subscriptions-per-public-user": "Genel kullanıcı başına abonelik maksimum sayısı", "ws-limit-updates-per-session": "Oturum başına WS güncellemeleri", + "relation-search-entity-limit": "İlişki arama varlık sınırı", + "relation-search-entity-limit-hint": "İlişki yolunun son seviyesinde çözümlenen varlık sayısını sınırlar. 'İlgili varlıklar' bağımsız değişkenlerine ve Yayılma alanlarına uygulanır.", + "relation-search-entity-limit-required": "İlişki arama varlık sınırı gereklidir", + "relation-search-entity-limit-range": "İlişki arama varlık sınırı '1' den küçük olamaz", "rate-limits": { "add-limit": "Sınır ekle", "and-also-less-than": "ve ayrıca daha az", @@ -5978,7 +6569,9 @@ "default-agg-interval": "Varsayılan gruplama aralığı", "edit-intervals-list-hint": "Kullanılabilir aralık seçeneklerinin listesi belirtilebilir.", "edit-grouping-intervals-list-hint": "Gruplama aralıkları listesi ve varsayılan gruplama aralığı yapılandırılabilir.", - "all": "Tümü" + "all": "Tümü", + "save-current-settings-as-default": "Geçerli ayarları varsayılan zaman penceresi olarak kaydet", + "hide-option-from-end-users": "Son kullanıcılardan seçeneği gizle" }, "tooltip": { "trigger": "Tetikleyici", @@ -5987,9 +6580,10 @@ "label": "Etiket", "value": "Değer", "date": "Tarih", - "show-date-time-interval": "Tarih ve saat aralığını göster", - "show-date-time-interval-hint": "Veri toplamaya göre tarih ve saat aralığını göster.", + "show-date-time-interval": "Tarih-saat aralığını göster", + "show-date-time-interval-hint": "Veri toplulaştırmasına göre tarih-saat aralığını göster.", "hide-zero-tooltip-values": "Sıfır değerleri gizle", + "show-stack-total": "Yığın modunda toplam değeri göster", "background-color": "Arka plan rengi", "background-blur": "Arka plan bulanıklığı" }, @@ -6631,7 +7225,8 @@ "export-relations": "İlişkileri dışa aktar", "export-attributes": "Öznitelikleri dışa aktar", "export-credentials": "Kimlik bilgilerini dışa aktar", - "export-calculated-fields": "Hesaplanmış alanları dışa aktar", + "export-calculated-fields": "Hesaplanmış alanları ve alarm kurallarını dışa aktar", + "export-alarm-rules": "Alarm kurallarını dışa aktar", "entity-versions": "Varlık sürümleri", "versions": "Sürümler", "created-time": "Oluşturulma zamanı", @@ -6649,6 +7244,7 @@ "load-attributes": "Öznitelikleri yükle", "load-credentials": "Kimlik bilgilerini yükle", "load-calculated-fields": "Hesaplanmış alanları yükle", + "load-alarm-rules": "Alarm kurallarını yükle", "compare-with-current": "Mevcut ile karşılaştır", "diff-entity-with-version": "'{{versionName}}' sürümü ile farkları karşılaştır", "previous-difference": "Önceki fark", @@ -6858,7 +7454,23 @@ "scan-qr-code": "QR Kodu tara", "make-phone-call": "Telefon araması yap", "get-location": "Telefon konumunu al", - "take-screenshot": "Ekran görüntüsü al" + "take-screenshot": "Ekran görüntüsü al", + "handle-provision-success-function": "Sağlama başarı fonksiyonunu işle", + "get-location-function": "Konum alma fonksiyonu", + "process-launch-result-function": "Başlatma sonucu fonksiyonunu işle", + "get-phone-number-function": "Telefon numarası alma fonksiyonu", + "process-image-function": "Resim işleme fonksiyonu", + "process-qr-code-function": "QR kodunu işleme fonksiyonu", + "process-location-function": "Konum işleme fonksiyonu", + "handle-empty-result-function": "Boş sonuç fonksiyonunu işle", + "handle-error-function": "Hata fonksiyonunu işle", + "handle-non-mobile-fallback-function": "Mobil olmayan geri dönüş fonksiyonunu işle", + "save-to-gallery": "Galeriye kaydet", + "provision-type": "Sağlama türü", + "auto": "Otomatik", + "wi-fi": "Wi-Fi", + "ble": "BLE", + "soft-ap": "Soft AP" }, "custom-action-function": "Özel eylem işlevi", "custom-pretty-function": "Özel eylem (HTML şablonlu) işlevi", @@ -6867,7 +7479,8 @@ "marker": "İşaretçi", "polygon": "Poligon", "rectangle": "Dikdörtgen", - "circle": "Daire" + "circle": "Daire", + "polyline": "Çokgen" }, "place-map-item": "Harita öğesi yerleştir", "map-item-tooltip": { @@ -6879,7 +7492,9 @@ "continue-draw-polygon": "Poligon çizimine devam et", "finish-draw-polygon": "Poligon çizimini bitir", "start-draw-circle": "Daire çizmeye başla", - "finish-draw-circle": "Daire çizimini bitir" + "finish-draw-circle": "Daire çizimini bitir", + "start-draw-polyline": "Çokgen çizmeye başla", + "finish-draw-polyline": "Çokgen çizmeyi bitir" } }, "widgets-bundle": { @@ -7445,9 +8060,17 @@ "update-animation-delay": "Güncelleme animasyon gecikmesi" }, "chart-axis": { + "limit": "Sınır", + "source": "Kaynak", + "key-value": "Anahtar / Değer", + "value-required": "Değer gereklidir.", + "entity-key-required": "Varlık anahtarı gereklidir.", + "key-required": "Anahtar gereklidir.", + "scale-limits": "Ölçek sınırları", + "scale-appearance": "Ölçek görünümü", "scale": "Ölçek", "scale-min": "min", - "scale-max": "maks", + "scale-max": "max", "scale-auto": "Otomatik" }, "bar": { @@ -7989,7 +8612,10 @@ "add-radio-option": "Radyo seçeneği ekle", "radio-label-position": "Etiket konumu", "radio-label-position-before": "Önce", - "radio-label-position-after": "Sonra" + "radio-label-position-after": "Sonra", + "save-image": "Resmi kaydet", + "save-to-gallery": "Yakalanan resimleri otomatik olarak Resim Galerisi'ne kaydet", + "public-image": "Resmi, yetkisiz kullanıcılar için erişilebilir hale getir" }, "invalid-qr-code-text": "QR kod için geçersiz giriş metni. Giriş metni dize türünde olmalıdır", "qr-code": { @@ -8284,7 +8910,8 @@ "trips": "Rotalar", "markers": "İşaretçiler", "polygons": "Poligonlar", - "circles": "Daireler" + "circles": "Daireler", + "polylines": "Çokgenler" }, "data-layer": { "source": "Kaynak", @@ -8481,11 +9108,30 @@ "finish-circle-hint-with-entity": "'{{entityName}}' için daire: tamamlamak ve kaydetmek için tıklayın", "finish-circle-hint": "Daire: çizimi tamamlamak için tıklayın" }, + "polyline": { + "polyline-key": "Çokgen anahtarı", + "polyline-key-required": "Çokgen anahtarı gereklidir", + "no-polylines": "Yapılandırılmış çokgen yok", + "add-polylines": "Çokgen ekle", + "polyline-configuration": "Çokgen yapılandırması", + "remove-polyline": "Çokgeni kaldır", + "edit": "Çokgeni düzenle", + "cut": "Çokgen alanını kes", + "rotate": "Çokgeni döndür", + "remove-polyline-for": "'{{entityName}}' için çokgeni kaldır", + "draw-polyline": "Çokgen çiz", + "polyline-place-first-point-hint-with-entity": "'{{entityName}}' için çokgen: İlk noktayı yerleştirmek için tıklayın", + "polyline-place-first-point-hint": "Çokgen: İlk noktayı yerleştirmek için tıklayın", + "finish-polyline-hint-with-entity": "'{{entityName}}' için çokgen: Çizmeyi bitirmek için tıklayın", + "finish-polyline-hint": "Çokgen: Çizmeyi bitirmek için tıklayın", + "polyline-place-first-point-cut-hint": "İlk noktayı yerleştirmek için tıklayın", + "finish-polyline-cut-hint": "İlk işarete tıklayarak bitir ve kaydet" + }, "select-entity": "Varlık seç", - "select-entity-hint": "İpucu: seçimden sonra haritaya tıklayarak konum belirleyin" + "select-entity-hint": "İpucu: Seçim yaptıktan sonra, konum belirlemek için haritaya tıklayın" }, "select-entity": "Varlık seç", - "select-entity-hint": "İpucu: seçimden sonra haritaya tıklayarak konum belirleyin", + "select-entity-hint": "İpucu: Seçim yaptıktan sonra, konum belirlemek için haritaya tıklayın", "tooltips": { "placeMarker": "'{{entityName}}' varlığını yerleştirmek için tıklayın", "firstVertex": "'{{entityName}}' için poligon: ilk noktayı yerleştirmek için tıklayın", @@ -8922,6 +9568,7 @@ "show-empty-space-hidden-action": "Gizli hücre buton eylemi yerine boş alan göster", "dont-reserve-space-hidden-action": "Gizli eylem düğmeleri için alan ayırma", "display-timestamp": "Zaman damgası", + "timestamp-column-name": "Zaman damgası", "display-pagination": "Sayfalama göster", "default-page-size": "Varsayılan sayfa boyutu", "page-step-settings": "Sayfa adımı ayarları", @@ -8983,7 +9630,9 @@ "alarm-column-error": "En az bir alarm sütunu belirtilmelidir", "table-tabs": "Tablo sekmeleri", "show-cell-actions-menu-mobile": "Mobil modda hücre eylem açılır menüsünü göster", - "disable-sorting": "Sıralamayı devre dışı bırak" + "disable-sorting": "Sıralamayı devre dışı bırak", + "sort-by": "Sekmeleri şuna göre sırala", + "sort-timestamp-option": "Oluşma zamanı" }, "latest-chart": { "total": "Toplam", @@ -9367,7 +10016,7 @@ }, "functions": { "title": "Fonksiyonlar", - "pe-feature-tooltip": "Yalnızca ThingsBoard\nProfesyonel Sürümde", + "pe-feature-tooltip": "Yalnızca ThingsBoard\nProfessional Edition", "switch-to-pe": "PE sürümüne geç", "alarms": "Alarmlar", "dashboards": "Panolar", @@ -9475,11 +10124,28 @@ "content": "

    Son kullanıcı panoları oluşturarak, müşteri kullanıcısı yalnızca kendi cihazlarını görebilir ve diğer müşterilerin verileri gizli olur.

    Nasıl yapılacağına dair dökümantasyona göz atın:

    " } } + }, + "api-usage": { + "api-usage": "API kullanımı", + "label": "Etiket", + "state-name": "Durum adı", + "status": "Durum", + "status-required": "Durum gereklidir.", + "limit": "Maksimum sınır", + "limit-required": "Maksimum sınır gereklidir.", + "current-number": "Mevcut sayı", + "current-number-required": "Mevcut sayı gereklidir.", + "add-key": "Anahtar ekle", + "no-key": "Anahtar yok", + "delete-key": "Anahtarı sil", + "target-dashboard-state": "Hedef pano durumu", + "go-to-main-state": "Varsayılan görünüme git" } }, "icon": { "icon": "Simge", "icons": "Simgeler", + "custom": "Özel", "select-icon": "Simge seç", "material-icons": "Material simgeleri", "show-all": "Tüm simgeleri göster", @@ -9520,6 +10186,7 @@ "items-per-page-separator": "toplam" }, "language": { + "auto": "Auto", "language": "Dil" } -} +} \ No newline at end of file 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 acb837a587..b8c67f655c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -1793,7 +1793,6 @@ "create-password": "Створити пароль", "passwords-mismatch-error": "Введені паролі повинні бути однаковими!", "password-again": "Введіть пароль ще раз", - "sign-in": "Будь ласка, увійдіть в систему", "username": "Ім'я користувача (ел. пошта)", "remember-me": "Запам'ятати мене", "forgot-password": "Забули пароль?", 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 024a836551..2e308570f2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -3473,7 +3473,6 @@ "two-factor-authentication": "双因素认证", "passwords-mismatch-error": "输入的密码必须相同!", "password-again": "再次输入密码", - "sign-in": "登录 ", "username": "用户名(电子邮件)", "remember-me": "记住我", "forgot-password": "忘记密码?", 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 7b0575f739..08fc40e4b1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2404,7 +2404,6 @@ "two-factor-authentication": "雙因素身份驗證", "passwords-mismatch-error": "輸入的密碼必須相同!", "password-again": "再次輸入密碼", - "sign-in": "登入 ", "username": "用戶名(電子郵件)", "remember-me": "記住我", "forgot-password": "忘記密碼?", diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 85fe81d28e..1a6ee74f80 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -107,6 +107,9 @@ } } } + &.disabled { + color: var(--mdc-outlined-text-field-disabled-input-text-color); + } .mat-expansion-panel { &.tb-settings { box-shadow: none; @@ -160,6 +163,13 @@ } } + .tb-form-panel-disabled { + color: var(--mdc-outlined-text-field-disabled-input-text-color); + .tb-form-panel { + color: var(--mdc-outlined-text-field-disabled-input-text-color); + } + } + .tb-form-panel-title { font-weight: 500; font-size: 16px; @@ -168,10 +178,11 @@ } &.tb-required::after { - font-size: 13px; - color: rgba(0, 0, 0, .54); + font-size: inherit; + color: inherit; vertical-align: top; - content: " *"; + content: "*"; + margin-left: 1px; } } .tb-form-panel-hint { @@ -257,6 +268,12 @@ flex: 1; } } + &.flex-lt-lg { + @media #{$mat-lt-lg} { + width: auto; + flex: 1; + } + } } .fixed-title-width { min-width: 200px; @@ -282,10 +299,11 @@ margin: 8px 0; } .tb-required::after { - font-size: 13px; - color: rgba(0, 0, 0, .54); + font-size: inherit; + color: inherit; vertical-align: top; - content: " *"; + content: "*"; + margin-left: 1px; } } @@ -422,10 +440,13 @@ } } .mat-mdc-form-field-infix { + --mat-form-field-container-vertical-padding: 8px; padding-top: 8px; padding-bottom: 8px; min-height: 40px; width: auto; + font-weight: 400; + font-size: 14px; .mdc-text-field__input, .mat-mdc-select { font-weight: 400; font-size: 14px; diff --git a/ui-ngx/src/scss/constants.scss b/ui-ngx/src/scss/constants.scss index aca02be591..922e7b3290 100644 --- a/ui-ngx/src/scss/constants.scss +++ b/ui-ngx/src/scss/constants.scss @@ -36,4 +36,4 @@ $tb-secondary-color: #527dad; $tb-hue3-color: #a7c1de; $tb-dark-primary-color: #9fa8da; - +$tb-primary-color-light: #7986cb; diff --git a/ui-ngx/src/scss/mixins.scss b/ui-ngx/src/scss/mixins.scss index 054c63ecf7..95a28784e9 100644 --- a/ui-ngx/src/scss/mixins.scss +++ b/ui-ngx/src/scss/mixins.scss @@ -26,6 +26,10 @@ width: #{$size}px; height: #{$size}px; } + img { + width: #{$size}px; + height: #{$size}px; + } } @mixin tb-mat-icon-button-size($size) { diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index e712dedba8..cdac88d00b 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -636,6 +636,43 @@ pre.tb-highlight { } } + .tb-autofilled:has(input:-webkit-autofill){ + --mdc-outlined-text-field-label-text-color: var(--mdc-outlined-text-field-input-text-color); + --mdc-outlined-text-field-hover-label-text-color: var(--mdc-outlined-text-field-input-text-color); + .mat-mdc-form-field-flex { + &:before { + display: block; + height: auto; + content: ""; + position: absolute; + inset: 0; + background: #7986cb; + opacity: 0.5; + pointer-events: none; + border-radius: 4px; + } + } + + .mdc-floating-label { + --mat-mdc-form-field-label-transform: translateY(calc(calc(6.7px + var(--mat-form-field-container-height) / 2) * -1)) scale(var(--mat-mdc-form-field-floating-label-scale, 0.75)); + } + .mat-mdc-form-field-infix { + input { + &:-webkit-autofill, + &:-webkit-autofill:hover, + &:-webkit-autofill:focus, + &:-webkit-autofill:active { + -webkit-background-clip: text !important; + -webkit-text-fill-color: var(--mdc-outlined-text-field-input-text-color); + } + &::spelling-error { + color: var(--mdc-outlined-text-field-input-text-color); + text-decoration: none; + } + } + } + } + // Material table .mat-toolbar.mat-primary { @@ -692,7 +729,7 @@ pre.tb-highlight { mat-toolbar.mat-mdc-table-toolbar:not(.mat-primary), .mat-mdc-cell, .mat-expansion-panel-header, mat-card-header.mat-mdc-card-header { button.mat-mdc-icon-button { .mat-icon { - color: rgba(0, 0, 0, .54); + color:var(--mat-icon-color, rgba(0, 0, 0, .54)); } &[disabled][disabled] { .mat-icon { @@ -760,11 +797,11 @@ pre.tb-highlight { transition: background-color .2s; &:hover:not(.tb-current-entity) { - background-color: #f4f4f4; + background-color: var(--tb-hover-color, #f4f4f4); } &.tb-current-entity { - background-color: #e9e9e9; + background-color: var(--tb-current-entity-color, #e9e9e9); } &.tb-pointer { @@ -816,7 +853,7 @@ pre.tb-highlight { vertical-align: middle; border-width: 0; border-bottom-width: 1px; - border-bottom-color: rgba(0, 0, 0, 0.12); + border-bottom-color: var(--mat-table-row-item-outline-color,rgba(0, 0, 0, 0.12)); border-style: solid; text-overflow: ellipsis; touch-action: auto !important; @@ -896,7 +933,7 @@ pre.tb-highlight { } .mat-icon { - svg { + svg, img { vertical-align: inherit; } &.tb-mat-12 { diff --git a/ui-ngx/src/theme.scss b/ui-ngx/src/theme.scss index 24ab7de9f1..9b2c005280 100644 --- a/ui-ngx/src/theme.scss +++ b/ui-ngx/src/theme.scss @@ -25,7 +25,7 @@ $tb-mat-indigo: ( 50: #e8eaf6, 100: #c5cae9, 200: #9fa8da, - 300: #7986cb, + 300: $tb-primary-color-light, 400: #5c6bc0, 500: $tb-primary-color, 600: $tb-secondary-color, diff --git a/ui-ngx/tailwind.config.js b/ui-ngx/tailwind.config.js index e90e873df3..a10fac840c 100644 --- a/ui-ngx/tailwind.config.js +++ b/ui-ngx/tailwind.config.js @@ -84,7 +84,8 @@ module.exports = { '25': '6.25rem', '37.5': '9.375rem', '62.5': '15.625rem', - '72.5': '18.125rem' + '72.5': '18.125rem', + '40%': '40%' }, flex: { full: '1 1 100%'